caddy: Unexpected TLS alert for IP-only certificate
1. What version of Caddy are you using (caddy -version)?
Caddy (untracked dev build) (unofficial)
commit f6e50890b3d81cb04146bd7b2c2b59e99830849a
2. What are you trying to do?
Use TLS with IP addresses (not DNS) for local development. Everything has worked fine up to and including commit 22dfb140d0b3587dc9de4f2f15f93a9f319becf2 right before the TLS alert commit. I am not sure if this is a problem with (1) Caddy correctly handling invalid certificates or (2) Caddy incorrectly handling valid certificates. Nothing jumped out at me when examining the commit diffs so I will not be surprised if the fault is with the certificates. Any insights are appreciated.
3. What is your entire Caddyfile?
https://192.168.1.2 {
tls cert/pki/issued/192.168.1.2.bundle.crt cert/pki/private/192.168.1.2.key
}
More details on the certificates can be found below.
4. How did you run Caddy (give the full command and describe the execution environment)?
caddy -conf ./Caddyfile
5. Please paste any relevant HTTP request(s) here.
curl --cacert cert/pki/ca.crt https://192.168.1.2
6. What did you expect to see?
<h1>Hello from Caddy</h1>
This is what I get with versions of Caddy before the TLS alert commit.
7. What did you see instead (give full error messages and/or log)?
curl: (35) error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error
8. How can someone who is starting from scratch reproduce the bug as minimally as possible?
Here are the steps in the form of a shell script. Sorry for the involved sequence, but nothing is easy when it comes to TLS certificates.
# --- Obtain, install and initialize easyrsa ---
wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.5/EasyRSA-nix-3.0.5.tgz
mkdir -p ~/tmp/caddy
tar -xzf EasyRSA-nix-3.0.5.tgz -C ~/tmp/caddy
mv ~/tmp/caddy/EasyRSA-3.0.5 ~/tmp/caddy/cert
cd ~/tmp/caddy/cert
cat > vars <<end-of-vars
set_var EASYRSA_PKI "$PWD/pki"
set_var EASYRSA_DN "cn_only"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 3650
set_var EASYRSA_CRL_DAYS 180
set_var EASYRSA_NS_SUPPORT "no"
set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate"
set_var EASYRSA_TEMP_FILE "$EASYRSA_PKI/extensions.temp"
end-of-vars
./easyrsa init-pki
# --- Generate certificate authority, specify CA name interactively ---
./easyrsa build-ca nopass
# --- Generate self-signed server certificate with IP SAN ---
./easyrsa --subject-alt-name="IP:192.168.1.2" build-server-full 192.168.1.2 nopass
cat pki/issued/192.168.1.2.crt pki/ca.crt > pki/issued/192.168.1.2.bundle.crt
#--- Fire up caddy with minimal configuration
cd ..
cat > index.html <<end-of-index
<h1>Hello from Caddy</h1>
end-of-index
cat > Caddyfile <<end-of-cfg
https://192.168.1.2 {
tls cert/pki/issued/192.168.1.2.bundle.crt cert/pki/private/192.168.1.2.key
}
end-of-cfg
caddy -conf ./Caddyfile &
# --- Test with curl
curl --cacert cert/pki/ca.crt https://192.168.1.2
# With caddy before TLS alert commit: <h1>Hello from Caddy</h1>
# With caddy after commit: curl: (35) error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 20 (6 by maintainers)
Commits related to this issue
- Partially fix raw IP request regression (#2356) — committed to Zenexer/caddy by deleted user 6 years ago
- Partially fix raw IP request regression (#2356) — committed to Zenexer/caddy by Zenexer 6 years ago
- Partially fix raw IP request regression (#2356) — committed to Zenexer/caddy by Zenexer 6 years ago
- handshake: Use local listener IP to find cert if no SNI provided See mholt/caddy#2356 — committed to caddyserver/certmagic by mholt 5 years ago
@mholt I’m totally BSing my way through this because I have almost no experience with Go, but from poking around with breakpoints in GoLand/IntelliJ, I think I found the issue. It looks like you’re choosing a config based exclusively on the server name passed through SNI in the TLS handshake. Doesn’t that mean raw IP addresses never really worked? It would work if there was only one config, but otherwise they’d just get a random certificate, no?
Specifically, here’s what I’m seeing: in handshake.go -> GetConfigForClient, clientHello.ServerName is an empty string when a raw IP address is requested with curl (or Chrome, for that matter).
There’s no easy fix for this; it would require multiple significant changes:
This won’t really be intuitive to end users–we can guess what they’re trying to do, but it’s likely that people will end up with conflicting certificates or endpoints. Perhaps it would make more sense to just say Caddy requires SNI, which means raw IP addresses can’t be used–they never worked reliably anyway, since a Caddyfile with multiple configs would inevitably have served the wrong certificate a good portion of the time. The only situation in which it would’ve reliably worked in old versions is when there was a single config in the Caddyfile.
Ultimately, this seems like a shortcoming in SNI; I don’t really see why IP addresses wouldn’t be passed. With ESNI on the horizon, perhaps now is a good time to campaign for this behavior to be changed.
@jung-kurt, from what I’ve gathered, you’re basically just getting lucky with it working in old versions. Caddy doesn’t actually know which certificate to use, so it picks a random certificate–but you only have one certificate in your entire Caddyfile, so it always chooses the right one. This random behavior was removed in #2339 because several security researchers (including myself) pushed for such a change on the grounds that it could lead to information disclosure, though the severity and classification of this issue was disputed.
Disclaimer: Again, I don’t really know what I’m doing here, and I’ve made numerous assumptions, any number of which could be inaccurate.
Thanks, @Zenexer, your changes work perfectly for both my simple example shown above and for my more involved development setup. Great work!
Great, I’ll see if I can cook up an intermediary PR. I’m not experienced with Go, though, so someone else will need to very thoroughly review it.