spring-cloud / spring-cloud-consul

Spring Cloud Consul
http://cloud.spring.io/spring-cloud-consul/
Apache License 2.0
813 stars 541 forks source link

Certificate rotation #778

Open juananinca opened 2 years ago

juananinca commented 2 years ago

Is your feature request related to a problem? Please describe. If consul certificates are rotated, the running services won't be able to interact with consul anymore.

Describe the solution you'd like It would work to have a watch over the certificates. Whenever a change is made on the consul tls config files, the consul client bean will reload the new certificates and be able to comunicate with consul with the news certs.

Describe alternatives you've considered I have looked for any other issue created, but I didn't find anything related. Any other aproach to deal with the certificate rotation would be welcomed.

spencergibb commented 1 year ago

How would the client watch certificates? Where are they located?

spring-cloud-issues commented 1 year ago

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

juananinca commented 1 year ago

I did a workaround in order to make consul-api library to be able to refresh the certs when needed. This use case happens in a spring boot application along with 'org.springframework.cloud:spring-cloud-starter-consul'. This workaround is far from being a PR but I think it shows my point.

I implemented a cron bean in my application with the https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java class and every time the cert is modified, the consulClient bean is asked to refresh its certs from the ConsulRawClient's httpTransport property.

I added a new attribute called lastTlsConfigLoaded which is a com.ecwid.consul.transport.TLSConfig in the ConsulRawClient class. Thus when the new method refreshTlsConnection is called can use the last tls config used to create a new HttpTransport object. Besides the httpTransport now is not final.

public class ConsulRawClient {

    [....]

    private HttpTransport httpTransport;
    private final String agentAddress;
    private TLSConfig lastTlsConfigLoaded;

    public static final class Builder {

        [....]

        private TLSConfig lastTlsConfigLoaded;

        public static ConsulRawClient.Builder builder() {
            return new ConsulRawClient.Builder();
        }

        [.....]

        public Builder setTlsConfig(TLSConfig tlsConfig) {
            this.httpTransport = new DefaultHttpsTransport(tlsConfig);
            this.lastTlsConfigLoaded = tlsConfig;
            return this;
        }

        [....]

        public ConsulRawClient build() {
            ConsulRawClient consulRawClient = new ConsulRawClient(httpTransport, agentHost, agentPort, agentPath);
            consulRawClient.lastTlsConfigLoaded = this.lastTlsConfigLoaded;
            return consulRawClient;
        }
    }

    [....]

    public void refreshTlsConnection() {
        this.httpTransport = new DefaultHttpsTransport(this.lastTlsConfigLoaded);
    }

}

And some additional changes have to been made in the ConsulClient class, adding a new ConsulRawClient attribute:

public class ConsulClient implements
        AclClient,
        AgentClient,
        CatalogClient,
        CoordinateClient,
        EventClient,
        HealthClient,
        KeyValueClient,
        QueryClient,
        SessionClient,
        StatusClient {

    [.....]
    private final ConsulRawClient consulRawClient;

    public ConsulClient(ConsulRawClient rawClient) {
        this.consulRawClient = rawClient;
        aclClient = new AclConsulClient(this.consulRawClient);
        agentClient = new AgentConsulClient(this.consulRawClient);
        [....]
    }

    [.....]

    public void refreshTlsConnection() {
        this.consulRawClient.refreshTlsConnection();
    }
}

Note: it is important to point that this workaround won't work if the new certs are located in another path than the original one.