KEYCLOAK-19138 nginx x509 client trusted certificate lookup

This commit is contained in:
Youssef El Houti 2022-09-01 14:26:46 +02:00 committed by Pedro Igor
parent 860c3fbbd3
commit 7f58c1c570
3 changed files with 104 additions and 2 deletions

View file

@ -165,6 +165,9 @@ to load additional certificates from headers `CERT_CHAIN_0` to `CERT_CHAIN_9` if
|certificate-chain-length
| The maximum length of the certificate chain.
|trust-proxy-verification
| Enable trusting NGINX proxy certificate verification, instead of forwarding the certificate to keycloak and verifying it in keycloak.
|===
==== Configuring the NGINX provider

View file

@ -1,5 +1,7 @@
package org.keycloak.services.x509;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
/**
@ -13,12 +15,30 @@ import org.keycloak.models.KeycloakSession;
public class NginxProxySslClientCertificateLookupFactory extends AbstractClientCertificateFromHttpHeadersLookupFactory {
private final static Logger logger = Logger.getLogger(NginxProxySslClientCertificateLookupFactory.class);
private final static String PROVIDER = "nginx";
protected final static String TRUST_PROXY_VERIFICATION = "trust-proxy-verification";
protected boolean trustProxyVerification = false;
@Override
public void init(Config.Scope config) {
super.init(config);
trustProxyVerification = config.getBoolean(TRUST_PROXY_VERIFICATION, false);
logger.tracev("{0}: ''{1}''", TRUST_PROXY_VERIFICATION, trustProxyVerification);
}
@Override
public X509ClientCertificateLookup create(KeycloakSession session) {
return new NginxProxySslClientCertificateLookup(sslClientCertHttpHeader,
sslChainHttpHeaderPrefix, certificateChainLength, session);
if (trustProxyVerification) {
return new NginxProxyTrustedClientCertificateLookup(sslClientCertHttpHeader,
sslChainHttpHeaderPrefix, certificateChainLength);
} else {
return new NginxProxySslClientCertificateLookup(sslClientCertHttpHeader,
sslChainHttpHeaderPrefix, certificateChainLength, session);
}
}
@Override

View file

@ -0,0 +1,79 @@
package org.keycloak.services.x509;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.common.util.PemException;
import org.keycloak.common.util.PemUtils;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
/**
* The NGINX Trusted Provider verify extract end user X.509 certificate sent during TLS mutual authentication,
* verifies it against provided CA the and forwarded in an HTTP header along with a new header ssl-client-verify: SUCCESS.
*
* NGINX configuration must have :
* <code>
* server {
* ...
* ssl_client_certificate path-to-trusted-ca.crt;
* ssl_verify_client on|optional;
* ssl_verify_depth 2;
* ...
* location / {
* ...
* proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
* ...
* }
* </code>
*
* Note that $ssl_client_cert is deprecated, use only $ssl_client_escaped_cert with this implementation
*
* @author <a href="mailto:youssef.elhouti@tailosoft.com">Youssef El Houti</a>
* @version $Revision: 1 $
* @since 01/09/2022
*/
public class NginxProxyTrustedClientCertificateLookup extends AbstractClientCertificateFromHttpHeadersLookup {
private static final Logger log = Logger.getLogger(NginxProxyTrustedClientCertificateLookup.class);
public NginxProxyTrustedClientCertificateLookup(String sslCientCertHttpHeader,
String sslCertChainHttpHeaderPrefix,
int certificateChainLength) {
super(sslCientCertHttpHeader, sslCertChainHttpHeaderPrefix, certificateChainLength);
}
@Override
protected X509Certificate getCertificateFromHttpHeader(HttpRequest request, String httpHeader) throws GeneralSecurityException {
X509Certificate certificate = super.getCertificateFromHttpHeader(request, httpHeader);
if (certificate == null) {
return null;
}
String validCertificateResult = getHeaderValue(request, "ssl-client-verify");
if ("SUCCESS".equals(validCertificateResult)) {
return certificate;
} else {
log.warn("nginx could not verify the certificate: ssl-client-verify: " + validCertificateResult);
return null;
}
}
@Override
protected X509Certificate decodeCertificateFromPem(String pem) throws PemException {
if (pem == null) {
log.warn("End user TLS Certificate is NULL! ");
return null;
}
try {
pem = java.net.URLDecoder.decode(pem, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error("Cannot URL decode the end user TLS Certificate : " + pem,e);
}
return PemUtils.decodeCertificate(pem);
}
}