How to ignore SSL cert trust errors in Feign?
Asked Answered
T

8

23

How can I achieve curl -k in feign client?

I know I can do this. Just want to know if there's a way to ignore or disable.

new Client.Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier)
Toggle answered 17/4, 2017 at 7:41 Comment(0)
P
29

Disclaimer

You should not actually do this for a number of very good reasons. The easiest way to fix SSL issues is to actually follow SSL best practices and use valid certificates. There are excellent projects online such as https://letsencrypt.org/ that will even allow you to get great security for free if the host is publicly accessible (if it has a real hostname that can be verified against).

USE AT YOUR OWN RISK. MAKE SURE YOU UNDERSTAND YOU ARE VIOLATING A LOT OF BEST PRACTICES AND ONLY USE THIS IF YOU UNDERSTAND THAT.

If you cause some type of major problem using this example code, you are liable.

Real talk

I had the same problem dealing with internal (publicly non-accessible) services that I wanted to call from a spring-boot application and I solved it using the following code.

Brief Overview

A great many people will tell you that you can either accept all certificates, hard-code your particular cert in it, or something else. You can actually allow only certain trusted hosts through the codepath, which is what I am attempting here for an additional layer of security.

In this example code, you can pass multiple hosts to the classes and it should allow requests to only those hosts to be issued with invalid certificates, and everything else will go through the normal chain of command.

This is not really production grade code, but hopefully you will get some use out of it.

Enough lecturing, what follows may interest you the most.

The Code

This is using for Java 8 and spring-boot.

Configuration

@Configuration
    public class FeignClientConfiguration {

    @Bean
    public Client client() throws NoSuchAlgorithmException, 
        KeyManagementException {

        return new Client.Default(
            new NaiveSSLSocketFactory("your.host.here"),
            new NaiveHostnameVerifier("your.host.here"));
    }
}

NaiveHostnameVerifier

public class NaiveHostnameVerifier implements HostnameVerifier {
    private final Set<String> naivelyTrustedHostnames;

    private final HostnameVerifier hostnameVerifier =
        HttpsURLConnection.getDefaultHostnameVerifier();

    public NaiveHostnameVerifier(String ... naivelyTrustedHostnames) {
        this.naivelyTrustedHostnames =
                Collections.unmodifiableSet(
                    new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));
    }

    @Override
    public boolean verify(String hostname, SSLSession session) {
        return naivelyTrustedHostnames.contains(hostname) ||
                hostnameVerifier.verify(hostname, session);
    }
}

NaiveSSLSocketFactory

public class NaiveSSLSocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory sslSocketFactory = 
                    (SSLSocketFactory) SSLSocketFactory.getDefault();

    private final SSLContext alwaysAllowSslContext;
    private final Set<String> naivelyTrustedHostnames;

    public NaiveSSLSocketFactory(String ... naivelyTrustedHostnames) 
        throws NoSuchAlgorithmException, KeyManagementException {

        this.naivelyTrustedHostnames = 
                Collections.unmodifiableSet(
                    new HashSet<>(Arrays.asList(naivelyTrustedHostnames)));

        alwaysAllowSslContext = SSLContext.getInstance("TLS");
        TrustManager tm = new X509TrustManager() {

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) 
                throws CertificateException {}

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                    return null;
            }
        };

        alwaysAllowSslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return sslSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return sslSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(socket, host, port, autoClose) 
            : sslSocketFactory.createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port) 
            : sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
        return (naivelyTrustedHostnames.contains(host)) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localAddress, localPort) 
            : sslSocketFactory.createSocket(host, port, localAddress, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return (naivelyTrustedHostnames.contains(host.getHostName())) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port) 
            : sslSocketFactory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
        return (naivelyTrustedHostnames.contains(host.getHostName())) 
            ? alwaysAllowSslContext.getSocketFactory().createSocket(host, port, localHost, localPort) 
            : sslSocketFactory.createSocket(host, port, localHost, localPort);
    }
}

References

I borrowed heavily from this answer:

Trusting all certificates using HttpClient over HTTPS

Preuss answered 21/6, 2017 at 1:14 Comment(1)
Thanks for the detailed answer.Toggle
F
22

When using Spring Cloud Netflix >= 1.4.4.RELEASE you can also do the following:

Add okhttp client maven dependency:

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-okhttp</artifactId>
    </dependency>

And add the following properties:

feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true

Reference: https://github.com/spring-cloud/spring-cloud-netflix/issues/2729

Fibrilliform answered 12/11, 2018 at 8:23 Comment(3)
is this solution leads to any security breach?Morse
disableSslValidation also works for apache httpclient. see my answer below.Salmonoid
@kashiviswanath well... you can ask if disabling validation of SSL certificate leads to any security breach. Those options are doing this thing ;) Accepting entrusted certificates in my opinion should be done per use case.Nananne
S
10

With current versions of spring-cloud-starter-openfeign suppressing hostname verification works as follows.

When using apache httpclient:

In application.yml set disable-ssl-validation property

feign.httpclient.disable-ssl-validation: true

In pom.xml add feign-httpclient dependency.

<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
</dependency>

If you prefer okhttp you must enable okhttp with another application property and add feign-okhttp dependency:

feign.httpclient.disableSslValidation=true
feign.httpclient.enabled=false
feign.okhttp.enabled=true

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

For httpclient5 (hc5), property disable-ssl-validation sadly does not turn off hostname verification (yet?), here's the ticket: https://github.com/spring-cloud/spring-cloud-openfeign/issues/625

Application properties for enabling hc5.

feign.httpclient.disableSslValidation=true
feign.httpclient.hc5.enabled=true

Maven dependency to add

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-hc5</artifactId>
</dependency>

NOTE: The tricky part for me was that I missed to add feign-httpclient as a dependency. In this case, a default feign client with enabled hostname verification is used.

Salmonoid answered 11/11, 2021 at 8:50 Comment(0)
C
8

Override via feign configuration

@Bean
public Client feignClient()
{
    Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
    return trustSSLSockets;
}


private SSLSocketFactory getSSLSocketFactory() {
    try {
        TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        };

        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        return sslContext.getSocketFactory();
    } catch (Exception exception) {
    }
    return null;
}
Centesimo answered 22/7, 2019 at 5:50 Comment(0)
V
6

feign.httpclient.disableSslValidation = true is not worked for me.

Create Client bean in Configuration by the following code is worked:

import feign.Client;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class ClientConfiguration {

    @Bean
    public Client feignClient() {
        return new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
    }

    private SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
            return sslContext.getSocketFactory();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

}

pom.xml might needs add dependencies:

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.8</version>
    </dependency>
Vestiary answered 19/6, 2019 at 5:30 Comment(2)
I couldn't find most of you imports, can you elaborate your sample and add the whole needed parts ?Rousing
Hello, thanks for the update, I hadn't the apache commons dependency. This doesn't work for me though, I still had the ssl validation errors. I finally added the certificate to the jvm truststoreRousing
U
2

Disable ssl Validation by adding below property in application.yaml.

feign.httpclient.disableSslValidation=true

or as a VM argument

-Dfeign.httpclient.disableSslValidation=true

Udelle answered 29/11, 2018 at 8:27 Comment(0)
F
1

Add below class to your repository and use this class as configuration

this piece of code worked for me:

@Configuration
public class SSLSocketClient {


@Bean
public Client feignClient() {
    return new Client.Default(getSSLSocketFactory(),getHostnameVerifier());
}

//SSLSocketFactory
// Install the all-trusting trust manager
public static SSLSocketFactory getSSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, getTrustManager(), new SecureRandom());
        return sslContext.getSocketFactory();
    }
    catch (Exception e) {
        throw new RuntimeException(e);
    }
}

//TrustManager
// trust manager that does not validate certificate chains
private static TrustManager[] getTrustManager() {
    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {

        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[]{};
        }
    }};
    return trustAllCerts;
}

//HostnameVerifier
public static HostnameVerifier getHostnameVerifier() {
    HostnameVerifier hostnameVerifier = new HostnameVerifier() {
        @Override
        public boolean verify(String s, SSLSession sslSession)
        {
            return true;
        }
    };
    return hostnameVerifier;
}}
Fixing answered 17/3, 2021 at 10:46 Comment(0)
P
0

I have used below approach and added this config in my feign client. Works flawlessly.

@Configuration
@Slf4j
public class FeignClientConfig {
@Bean
Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
}

@Bean
public Client feignClient() {
    return new ApacheHttpClient(getHttpClient());
}

@Bean
Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) {
    return new SpringFormEncoder(new SpringEncoder(converters));
}

private CloseableHttpClient getHttpClient() {
    int timeout = 10000;
    try {
        SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustSelfSignedStrategy()).build();
        sslContext.init(null, getTrustManagers(), new java.security.SecureRandom());
        RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout).build();
        return HttpClientBuilder.create().useSystemProperties().setDefaultRequestConfig(config).setSSLContext(sslContext).setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    } catch (Exception e) {
        // handle error
    }
    return null;
}

private TrustManager[] getTrustManagers() {
    return new TrustManager[]{new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }};
}
Pin answered 12/6, 2024 at 6:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.