Search code examples
javaspring-bootauthenticationsslnetflix-eureka

Eureka - Unable to register eureka server in itself by using Two Way SSL


I've been trying to make two ssl to secure the communication between microservices. It is actually working when service A want to register into EurekaServer. But when I try to register eureka into the same eureka server or other instance of eureka I always got a bad certificate exception.

This is my application.yml from eureka server

server:
  port: ${SERVER_SSL_PORT:${PORT:10100}}
  ssl:
    enabled: true
    client-auth: need
    key-alias: ${JKS_ALIAS}
    key-password: ${JKS_KEY_PASSWORD}
    key-store-type: ${JKS_TYPE}
    key-store-provider: ${JKS_PROVIDER:SUN}
    key-store: ${JKS_PATH}
    key-store-password: ${JKS_KEY_PASSWORD}
    trust-store: ${JKS_PATH}
    trust-store-password: ${JKS_KEY_PASSWORD}
    trust-store-provider: ${JKS_PROVIDER:SUN}
    trust-store-type: ${JKS_TYPE}

## EUREKA CONFIGURATION
eureka:
  client:
    enabled: true
    register-with-eureka: true
    fetch-registry: false
    service-url:
      defaultZone: ${SVC_REGISTRY:https://localhost:14102/eureka}
    prefer-same-zone-eureka: true
    healthcheck:
      enabled: true
    tls:
      enabled: true
      key-store: ${JKS_PATH}
      key-password: ${JKS_KEY_PASSWORD}
      key-store-type: ${JKS_TYPE}
      key-store-password: ${JKS_KEYSTORE_PASSWORD}
      trust-store: ${JKS_PATH}
      trust-store-password: ${JKS_KEY_PASSWORD}
      trust-store-type: ${JKS_TYPE}
  instance:
    instance-id: ${SERVER_NAME:${spring.application.name}:${server.port}}@${eureka.instance.hostname}
    hostname: ${SERVER_HOST:localhost}
    home-page-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}
    status-page-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/info
    health-check-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/health
    secure-health-check-url: https://${eureka.instance.hostname}:${server.port}${server.contextPath:}${management.endpoints.web.base-path:}/health
    lease-renewal-interval-in-seconds: 10
    prefer-ip-address: true
    metadata-map:
      instanceId: ${eureka.instance.instance-id}
      zone: ${SERVER_ZONE:local}
    non-secure-port-enabled: false
    secure-port-enabled: true
    secure-port: ${server.port}

I also try to add this configuration to modify the JerseyClient

    @Bean
    @SneakyThrows
    public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs2() {
        

        DiscoveryClient.DiscoveryClientOptionalArgs args = new DiscoveryClient.DiscoveryClientOptionalArgs();

        EurekaJerseyClientImpl.EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientImpl.EurekaJerseyClientBuilder()
                .withClientName("DiscoveryClient-HTTPClient-Custom")
                .withUserAgent("Java-EurekaClient")
                .withConnectionTimeout(config.getEurekaServerConnectTimeoutSeconds() * 1000)
                .withReadTimeout(config.getEurekaServerReadTimeoutSeconds() * 1000)
                .withMaxConnectionsPerHost(config.getEurekaServerTotalConnectionsPerHost())
                .withMaxTotalConnections(config.getEurekaServerTotalConnections())
                .withConnectionIdleTimeout(config.getEurekaConnectionIdleTimeoutSeconds() * 1000)
                .withEncoderWrapper(CodecWrappers.getEncoder(config.getEncoderName()))
                .withDecoderWrapper(CodecWrappers.resolveDecoder(config.getDecoderName(), config.getClientDataAccept()))
                .withCustomSSL(sslContext());
               
        EurekaJerseyClient jerseyClient = clientBuilder.build();
        args.setEurekaJerseyClient(jerseyClient);//Provide custom EurekaJerseyClient to override default one
        return args;
    }

    @Bean
    public SSLContext sslContext() throws Exception {
        log.info("initialize ssl context bean with keystore {} ", env.getProperty("JKS_PATH"));
        char[] password = env.getProperty("JKS_KEY_PASSWORD").toCharArray();
        return new SSLContextBuilder()
                .loadTrustMaterial(ResourceUtils.getFile(env.getProperty("JKS_PATH")), password).build();
    }

But I keep getting this error

2022-09-01 18:44:01.522 INFO 26829 --- [tbeatExecutor-0] c.n.d.s.t.d.RedirectingEurekaHttpClient : Request execution error. endpoint=DefaultEndpoint{ serviceUrl='https://XXXXXXXXXXXXXXXXXXXX:DDDD/eureka/}, exception=javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate stacktrace=com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187) at com.sun.jersey.api.client.Client.handle(Client.java:652) at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682) at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74) at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:529) at

Certificate should be fine since it is the same cert I used to make 2way SSL between service A and registry. I am also testing this on a real server with valid certificate.

Any clue on this?

Thanks in advance! Appreciate it


Solution

  • I found the root cause of my problem. I had this property as true

    prefer-ip-address: true
    

    but it should be false since my certificates have hostnames register, not IPs. So when it try to validate the certs it was comparing hostnames vs ip which are different and it was throwing bad_certificate exception.