Be careful, don’t be trapped by Eureka

Time:2021-4-17

Eureka isNetflixThe developed service discovery framework is based onRESTThe service is mainly used to locate and run inAWSIn order to achieve the goal of load balancing and middle layer service failure transfer. Spring cloud integrates it into its subproject spring cloud Netflix to realize the service discovery function of spring cloud.

Eureka consists of two components: Eureka server and Eureka client. Let’s not talk about the specific deployment here. Let’s just talk about the problem

Eureka client registration needs to configure the server address, similar to the following configuration

eureka:
  instance:
    hostname: hello-service
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
  client:
    register-with-eureka: true 
    fetch-registry: true
    service-url: 
        defaultZone: "http://localhost:8761/eureka/" 

After this configuration, the client will register with Eureka registry, and you can see in the Eureka interface:

Be careful, don't be trapped by Eureka

However, if the interface is exposed to the outside, the registration information will be leaked, and general companies are not allowed to expose the background interface without security authentication

So try to encrypt the Eureka interface

Introducing security

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Eureka server adds basic authentication:

spring:
  application:
    name: eureka-server 
  security: 
    basic:
      enabled: true
    user:
      name: admin
      password: 123456

After the configuration is completed, it is found that the user name and password are required to login to the Eureka interface
Be careful, don't be trapped by Eureka

But after logging in, I found that the Hello service was not registered

Be careful, don't be trapped by Eureka

Ouch, it should be because the client did not configure authentication information. We found the client authentication configuration mode on the official website

https://cloud.spring.io/sprin…

Be careful, don't be trapped by Eureka

Therefore, modify the configuration of the Hello service as follows:

eureka:
  instance:
    hostname: hello-service
    prefer-ip-address: true
    instance-id: ${eureka.instance.hostname}:${server.port}
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://admin:[email protected]:8761/eureka/

After restarting the Hello service, I found that the registration was not successful. After adding basic authentication, cross domain access is not supported. My God, you big hole, the service registration must be cross domain,

As a result, the configuration is increased rapidly,Remove cross domain interception

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
     //  http.csrf (). Disable(); // one way is to close CSRF directly, and the other is to configure URL to release
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

Finally, I can see the lovely HELLO service in the interface. However, there is another problem. Now this kind of plaintext password is not allowed to appear in the configuration or code. What should I do?

The first thing I think of is password encryption, so I find passwordencoderfactors encryption in spring security

PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123456")

Then put the encrypted result into Eureka server configuration file:

security: 
  basic:
    enabled: true 
  user:
    name: admin
    Password: '{bcrypt} $2A $10 $mhh7ogkrb91yduo3f883jugdmhz2o6mit95.8ukqec6ed4z2xyhmm' // must have quotation marks

What about Eureka client? The same is true

client:
  register-with-eureka: true
  fetch-registry: true
  service-url:
    defaultZone: http://admin:{bcrypt}[email protected]alhost:8761/eureka/

However, it will not decrypt when Eureka is registered

Be careful, don't be trapped by Eureka

Failed to parse Eureka server address. Even if quotation marks are added, the same error will be reported

The server will not decrypt the messages registered by Eureka client. What should we do?

Be careful, don't be trapped by Eureka

As you can see from the figure above, if you want to achieve more complex requirements, you need to inject the clientfilter method, so, to get started

@Configuration
@Priority(Integer.MIN_VALUE)
public class UserCilentFilter extends ClientFilter {
    @Override
    public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
        try {
            String originUrl = clientRequest.getURI().toString();
            if(originUrl.contains("@")){
                return this.getNext().handle(clientRequest);
            }
            String userNameAndPwd = "http://admin"+ jiemi("'{bcrypt}$2a$10$mhH7ogkRB91YDUO3F883JugDMHz2o6miT95.8ukqEc6Ed4Z2xyHmm'");
            String addUserInfoUrl = originUrl.replaceFirst("http://", userNameAndPwd);
            clientRequest.setURI(new URI(addUserInfoUrl));
        } catch (URISyntaxException e) {
            // FIXME: 2021/4/2
        }
        return this.getNext().handle(clientRequest);
    }

    private String jiemi(String pwd) {
        //Fixme: decryption
        return pwd;
    }


    @Bean
    public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
        DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
        discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new UserCilentFilter()));
        return discoveryClientOptionalArgs;
    }
}

The idea is to letWhen other clients register, remove the user name and password, and then add the user name and password verified by basic in the custom filter when there is no user name and password

Then start the test, but it still doesn’t work. When other services register, they will be intercepted by other security filters. If they can’t get to the custom interceptor, they will return authentication failure, even @ priority( Integer.MIN_ Value) highest priority

Is it possible to intercept further ahead? Is it feasible to add a servlet request interceptor?

@Configuration
public class ServerRequestAuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) throws IOException, ServletException {
        //Business implementation, according to the request IP or parameters to determine whether the registration or access can be performed
//        String addUserInfoUrl = originUrl.replaceFirst("http://", "http://admin:[email protected]");
        filterChain.doFilter(request, response);
    }
}

But suppose that after such modification, the login web interface will also go to this interceptor, which will also increase authentication

That is to say, this directly increases authentication, and it is impossible to distinguish whether other clients register or access from the interface

There’s no good way. Just let go of Eureka registration in the interceptor of security without authentication

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/eureka/**").permitAll();
    super.configure(http);
}

In this way, in addition to the direct access interface, other Eureka related registration and query do not need authentication

After layered authentication, find out if there are other ways to achieve the same purpose, and find out

eureka:
  dashboard:
    enabled: false

After the startup script is set, the effect is similar and cannot be accessed in the Eureka main interface

Be careful, don't be trapped by Eureka

All of the above operations are forinformation safetyAnother component that we often forget to consider is spring boot activator. For the endpoint provided by spring boot activator, we can take the following measures to reduce the risk of security attack as much as possible

  1. Minimum granularity exposure endpoint. Only open and expose the really used endpoint, not configure it: management.endpoints.web . exposure.include= *。
  2. Configure an independent access port for the endpoint, so as to separate it from the port of the web service, so as to avoid exposing the endpoint of the actor by mistake when exposing the web service. Example: management.port=8099 .
  3. Spring boot starter security dependency is introduced to configure access control for endpoint of actor.
  4. Carefully evaluate whether it is necessary to introduce spring boot stat actor. In my personal experience, I haven’t met any requirements so far, which can only be solved by introducing spring boot stat actor. If you don’t understand the security risks mentioned above, I suggest you remove the dependency first.

information safetyIt has become a problem that major companies have to consider, so accurate permission control is also essential. I hope this paper can inspire you in the use of spring cloud related component security control.

If you think what I wrote is OK, please like it. I don’t mind if I click three times.

☞☞ once a week, it’s better than immortals. After reading the praise, form the habit ☜☜