Caddy ignores protocol and ciphers directive

Hi all,

I want to configure Caddy so that only tls1.2 is used and for the ciphers ECDHE-ECDSA-AES256-GCM-SHA384 and ECDHE-ECDSA-AES128-GCM-SHA256; the key_type is p256.

According to the observatory by Mozilla and the other scanners my server is still serving tls1.1 and tls1.2 and additionally to the above ciphers also ECDHE-ECDSA-AES256-SHA and ECDHE-ECDSA-AES128-SHA. Only the key_type directive is used.

After changing the Caddyfile, I deleted the /etc/ssl/caddy folder to get the new key and restarted Caddy. I’m using version Caddy 0.9.1 (+e8e5595 Sun Sep 18 15:41:37 UTC 2016) on Archlinux.

Here is my Caddyfile for the relevant domain: {
        root /path/foo/
        log /path/foo/
        errors /path/foo


        tls {
                protocols tls1.2
                ciphers ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256
                key_type p256

        header / {
                Strict-Transport-Security "max-age=31536000; includeSubdomains"
                X-XSS-Protection "1; mode=block"
                X-Content-Type-Options "nosniff"
                X-Frame-Options "DENY"
                Content-Security-Policy "default-src 'self'; script-src 'self'"

I’m getting:

http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Anyway, turning http2 off and using your Caddyfile, sslscan shows:

Version: 1.11.6
OpenSSL 1.0.2h  3 May 2016

OpenSSL version does not support SSLv2
SSLv2 ciphers will not be detected

Testing SSL server on port 443

  TLS renegotiation:
Session renegotiation not supported

  TLS Compression:
Compression disabled

TLS 1.2 not vulnerable to heartbleed
TLS 1.1 not vulnerable to heartbleed
TLS 1.0 not vulnerable to heartbleed

  Supported Server Cipher(s):
Preferred TLSv1.2  256 bits  ECDHE-ECDSA-AES256-GCM-SHA384 Curve P-256 DHE 256
Accepted  TLSv1.2  128 bits  ECDHE-ECDSA-AES128-GCM-SHA256 Curve P-256 DHE 256

Which is expected.

Do you know what you’re doing when you modify the cipher suites while running http2? There’s a stern warning about this in the docs:

Note: The HTTP/2 spec blacklists over 275 cipher suites for security reasons. Unless you know what you’re doing, it’s best to accept the default cipher suite settings.

Thanks for your reply matt :slight_smile:

TLS_ECDHE_RSA_WITH_AES128_GCM_SHA256 is missing, because the certificate is ECC instead of RSA, so ECDSA is used. That should be no problem.

According to the source code of config.go http2 blocks all but the 4 GCM ciphers:

// Note that, at time of writing, HTTP/2 blacklists 276 cipher suites,
// including all but four of the suites below (the four GCM suites).
// See

So in my case, only ECDHE-ECDSA-AES256-GCM-SHA384 and ECDHE-ECDSA-AES128-GCM-SHA256 should be used.

But why is Caddy still sending tls1.1 and those two ciphers with only SHA1? I need to get rid of these to get the modern compatibility ranking from Mozilla.

protocol: If min and max are the same, it need only be specified once.
ciphers... If you list any, only the ones you specify will be allowed.

I thought I did that?

Thanks for your help.

I think you still have to allow it for HTTP/2 to be configured properly, however. So I had to go look this up, and it says right in the HTTP/2 spec:

To avoid this problem causing TLS handshake failures, deployments of HTTP/2 that use TLS
with the P-256 elliptic curve [FIPS186].

So I guess keep the RSA suite in there.

No idea, it doesn’t show that for me when I try it here.

I’m going to try to get a new Caddy release out this week – try that or build from source sooner and see if it was fixed in the interim? I can’t remember, we made a few little tweaks to TLS stuff recently.

I guess specs don’t lie, so I put those two RSA ciphers in there. Thanks for looking that up.

So my current Caddyfile looks like this:

tls {
        protocols tls1.2
        key_type p256

And ssllabs --no-failed results in:

Supported Server Cipher(s):
    Accepted  TLS11  256 bits  ECDHE-ECDSA-AES256-SHA
    Accepted  TLS11  128 bits  ECDHE-ECDSA-AES128-SHA
    Accepted  TLS12  256 bits  ECDHE-ECDSA-AES256-GCM-SHA384
    Accepted  TLS12  256 bits  ECDHE-ECDSA-AES256-SHA
    Accepted  TLS12  128 bits  ECDHE-ECDSA-AES128-GCM-SHA256
    Accepted  TLS12  128 bits  ECDHE-ECDSA-AES128-SHA

  Preferred Server Cipher(s):
    TLSv1  0 bits    (NONE)
    TLS11  256 bits  ECDHE-ECDSA-AES256-SHA
    TLS12  256 bits  ECDHE-ECDSA-AES256-GCM-SHA384

I will try the new release and report back.
Thanks @matt for this great project. :slight_smile:

Ok, so I tried the new 0.9.2 and the problem persisted, BUT I found the solution after deleting all other domains.

In addition to the domain above, I also had the following block in my Caddyfile (and another domain with corresponding subdomains but that shouldn’t be relevant): {

After deleting this block, sslscan showed the correct values for, with only TLS1.2 being activated with the right ciphers. Copying the tls directive from the main domain to www subdomain resulted in both being served correctly.

So what’s the reason I’m apparently getting the default tls values from the www subdomain altough I’m directly visiting the main domain? And is it possible to write one single tls directive which is used by all domains or do I have to copy it into each block?

I found the following snippet in the docs but I guess that’s for multiple sites which share the exact same config:

For sites which share the same configuration, specify multiple addresses:

localhost:2020,, {

Thanks in advance.

Edit: The other domains actually do matter. I need to copy the tls directive into every block in the Caddyfile to get the right values.

Ah, that’s because, in the code:

// Go with the widest range of protocol versions
if config.MinVersion == 0 || cfg.ProtocolMinVersion < config.MinVersion {
	config.MinVersion = cfg.ProtocolMinVersion
if cfg.ProtocolMaxVersion > config.MaxVersion {
	config.MaxVersion = cfg.ProtocolMaxVersion

See, multiple sites are served on the same listener if they share a port. So we have to reduce multiple site configs down to a single TLS config. How to do that is not really obvious.

So a site with the default TLS settings will still allow TLS 1.1, and when combined with a different site that only allows TLS 1.2, the widest range of protocols is used, meaning TLS 1.1 and TLS 1.2 are both permitted. Which explains why you need to copy the TLS block to all the sites.

Hm, sorry about that. I chose to go with the widest range of protocols for compatibility reasons, but maybe we should invert the rule, so that it’s the narrowest range? What is more expected, and makes more sense?

That makes sense.

The defaults in Caddy are already very secure so I would stick with the compatibility route. By choosing the narrowest range, one would just lose clients because they can’t connect anymore, just because one domain is more restricted.

The only thing is, that according to that list here there is almost no browser out there, that supports TLS1.1 but not 1.2.
Google Chrome from 2012.09 to 2013.10 is the only relevant client I guess. But that’s probably not the right thread to discuss this.

Would it not be possible to do something like this? {
    tls {
        tls 1.2
        key_type p256
} {
    root /foo/bar
} {
    root /foo/bar
} {
    root /foo/bar

Kinda like CSS.

Edit: Oh and maybe there should be a note in the docs, that the tls protocol must be the same for every domain.

You’ve given us some good things to think about, maybe more feedback would be valuable here.

But as for cascading site definitions, no, probably won’t go there, sorry.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.