1. Caddy version (caddy version
):
2.4.6 in docker container (2.4.6-alpine)
2. How I run Caddy:
With docker-compose
a. System environment:
b. Command:
docker-compose -f ds.yml up --build
c. Service/unit/compose file:
services:
caddy:
image: caddy:2.4.6-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile_completo:/etc/caddy/Caddyfile:ro
- ./certs_servidor:/certs
tomcat_dataserver:
depends_on:
- "db"
image: tomcat:6.0.43
volumes:
- ./dataserver.war:/usr/local/tomcat/webapps/dataserver.war
restart: unless-stopped
tty: true
d. My complete Caddyfile or JSON config:
`caddy fmt`
{
auto_https disable_redirects
debug
}
http://dataserver.local.domain {
reverse_proxy tomcat_dataserver:8080
}
https://dataserver.local.domain, dataserver.local.domain:443 {
tls /certs/dataserver_local_domain.crt /certs/dataserver_local_domain.key {
protocols tls1.2 tls1.2
ciphers TLS_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
client_auth {
mode require_and_verify
trusted_ca_cert_file /certs/local_domain_ca.crt
}
}
reverse_proxy tomcat_dataserver:8080
}
3. The problem I’m having:
I´m dockerizing two old application, one is a Java6 command line application (let´s call it client) and other a Java6 web application working on a tomcat6 container (called dataserver).
The old configuration was using an apache server acting as proxy for tomcat via ajp with SSL and client certificates:
<VirtualHost *:443>
ServerName dataserver.local.domain
ErrorLog logs/dataserver.local.domain-ssl-error_log
CustomLog logs/dataserver.local.domain-ssl-access_log common
<Proxy *>
AddDefaultCharset Off
Order deny,allow
Allow from all
</Proxy>
SSLEngine On
SSLCertificateFile /etc/pki/tls/certs/dataserver_local_domain.crt
SSLCertificateKeyFile /etc/pki/tls/private/dataserver_local_domain.key
<Location /dataserver>
SSLVerifyClient require
SSLVerifyDepth 2
</Location>
SSLCACertificateFile /etc/ssl/certs/local_domain_ca.crt
SSLOptions +ExportCertData +StdEnvVars
ProxyPass /dataserver/ ajp://dataserver.local.domain:8009/dataserver/
ProxyPassReverse /dataserver/ ajp://dataserver.local.domain:8009/dataserver/
</VirtualHost>
After some tweaks with the ciphers (java application is old and we can´t upgrade as per client requirements) and java truststores I have all the communication working between the client and the dataserver, with caddy handling the client authentication. The problem I have is thats there is an extra check in the dataserver code when receiving data, as it reads the client certificate, extracts the CN from it and do some checks with it against a database before allowing to go further:
String boxNumber = "";
String certificateDomain = PropertyReader.getInstance("general").get("certificate.domain");
String[] subkeys = certificateDomain.split("\\.");
String patternString = "box(\\d{6})";
for (String subkey : subkeys) {
patternString += "\\."+subkey;
}
Pattern pattern = Pattern.compile(patternString);
X509Certificate[] certs = (X509Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate");
if (certs != null) {
for (X509Certificate clientCert : certs) {
try {
// Trick: The DN obtained is in RFC2253 format, which is the same as used for LDAP DN.
LdapName ldapDN = new LdapName(clientCert.getSubjectDN().getName());
for (Rdn rdn : ldapDN.getRdns()) {
if (rdn.getType().contentEquals("CN")) {
Matcher matcher = pattern.matcher(rdn.getValue().toString());
if (matcher.find()) {
boxNumber = matcher.group(1);
logger.trace(rdn.getType() + ": " + boxNumber);
} else {
logger.warn("Received valid certificate from a box, but that does not belong to a box. The {} is {}",
rdn.getType(), rdn.getValue().toString());
}
}
}
} catch (InvalidNameException e) {
logger.error("Invalid name in client certificate", e);
}
}
} else {
logger.error("No certs available.");
}
return boxNumber;
In this case when the client sends data to the dataserver, the line
X509Certificate[] certs = (X509Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate")
Produces null so the variable boxNumber is empty and no further checks can be made.
In order to work in apache proxy, the directive SSLOptions +ExportCertData +StdEnvVars solved this problem as stated in apache documentation:
ExportCertData
When this option is enabled, additional CGI/SSI environment variables are created: SSL_SERVER_CERT, SSL_CLIENT_CERT and SSL_CLIENT_CERT_CHAIN_n (with n = 0,1,2,..). These contain the PEM-encoded X.509 Certificates of server and client for the current HTTPS connection and can be used by CGI scripts for deeper Certificate checking. Additionally all other certificates of the client certificate chain are provided, too. This bloats up the environment a little bit which is why you have to use this option to enable it on demand.
Additional documentation from Tomcat
I´m unable to replicate this last part of the configuration in Caddy. It´s there any way of getting the same config as apache?
Thanks in advance for the help
4. Error messages and/or full log output:
No errors, apart from the log on the dataserver stating “No certs available”
5. What I already tried:
Lots of forum and Google searching without results. Also been through all the old documentation for deploying the dataserver and it doesn´t state anything about extra configuration or headers on Tomcat.