Matching Ciphers with Microsoft 365

1. Caddy version (caddy version):

v2.4.3 h1:Y1FaV2N4WO3rBqxSYA8UZsZTQdN+PwcoOcAiZTM8C0I=

2. How I run Caddy:

systemd

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

a. System environment:

$ lsb_release -a

No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal

b. Command:

sudo systemctl start caddy.service

c. Service/unit/compose file:

see above

d. My complete Caddyfile or JSON config:

{
    debug
        on_demand_tls {
                ask http://127.0.0.1:5000/_domain_check
        }
    #acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

:443 {

    # API RELATED CONFIGS

    # allow password reset
    @api_password_reset {
        header_regexp apihost Host api\.(.*)
        path /password_reset*
    }
    handle @api_password_reset {
        redir https://www.{re.apihost.1}{uri} permanent
    }

    # allow activate
    @api_activate {
        header_regexp apihost Host api\.(.*)
        path /activate*
    }
    handle @api_activate {
        redir https://www.{re.apihost.1}{uri} permanent
    }

    # allow admin
    @api_admin {
        header Host api.*
        path /admin*
    }
    handle @api_admin {
        reverse_proxy 127.0.0.1:5001
    }
    # allow crossbar-api-clients
    @api_client {
        header Host api.*
        header User-Agent crossbar-api-client
    }
    handle @api_client {
        reverse_proxy 127.0.0.1:5001 {
                fail_duration 0s
                max_fails 100000
                unhealthy_status 5xx
        }
    }

    # api static assets
    @api_static {
        header Host api.*
        path /static*
    }
    handle @api_static {
        file_server /static/* {
            root /var/www/cb/api/api/
        }
    }

    # api media assets
    @api_media {
        header Host api.*
        path /media*
    }
    handle @api_static {
        file_server /media/* {
            root /var/www/cb/api/api/
        }
    }
    # send non crossbar-api-clients to 403 - must go last!
    @api_the_rest {
        header Host api.*
    }
    handle @api_the_rest {
        header {
            Content-Type "text/html; charset=UTF-8"
        }
        respond "Forbidden 禁止の" 403
    }

    @marketing_app header Host a.crossbar.org
    handle @marketing_app {
        reverse_proxy 127.0.0.1:5003
    }

    @www_app header Host crossbar.org
    handle @www_app {
        file_server /static/* {
            root /var/www/cb/www/
        }
    }
    handle @www_app {
        reverse_proxy 127.0.0.1:5002
    }

    # APP RELATED CONFIGS
    @app header Host www.*

    # serve static files
    handle @app {
        file_server /static/* {
            root /var/www/cb/app/
        }
    }
    # proxy to uwsgi server and/or redirec to www
    handle @app {
        reverse_proxy 127.0.0.1:5000
    }

    # send non-www to www
    @needs_www {
        not header Host api.*
    }
    handle @needs_www {
        redir https://www.{host}{uri}
    }

    # old domain redirects
    @crossbarhq_root header Host crossbarhq.com
    handle @crossbarhq_root {
        redir https://crossbar.org{uri} permanent
    }
    @crossbarhq_www header Host www.crossbarhq.com
    handle @crossbarhq_www {
        redir https://crossbar.org{uri} permanent
    }


    tls josh.anyan@nope.org {
        on_demand
        dns route53 {
            max_retries 10
            aws_profile "default"
        }
        ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    }

    log {
        output file /tmp/caddy.log {
            roll_size 100MiB
            roll_keep 10
            roll_keep_for 336h
        }
    }
}

3. The problem I’m having:

Microsoft is failing to import our ical (calendar) feeds. After much digging, it seems narrowed down to not finding mutual agreed upon ciphers. Below are some logs when running a Microsoft ‘SSL’ test. I believe this is the set of ciphers Microsoft is ok with. Looking over what is available in Caddy and in the MS’s cipher list, I see TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as a potential overlap. It doesn’t seem to be available by default, so I’ve specified it in my Caddyfile above. My challenge is now making sure the cipher is being made available during negotiation. From what I can tell, it isn’t, but I might be mistaken there or somewhere else in my logic here. I also tried some tools to look at available ciphers and I’m not seeing the extra one. See the 2nd and 3rd log output below.

4. Error messages and/or full log output:

Log snippet during MS SSL test

Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.1123366,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2241: tls: client offered only unsupported versions: []"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.1143734,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2243: tls: client offered only unsupported versions: [302 301]"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.1222816,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2242: tls: client offered only unsupported versions: [301]"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.1266778,"logger":"http.stdlib","msg":"http: TLS handshake error from 172.30.1.80:44262: EOF"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.1501157,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2244: read tcp 172.30.1.135:443->52.109.8.10:2244: read: connection reset by peer"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.2230287,"logger":"http.stdlib","msg":"http: TLS handshake error from 172.30.1.80:58467: EOF"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.2257447,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2245: read tcp 172.30.1.135:443->52.109.8.10:2245: read: connection reset by peer"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.3117049,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2246: read tcp 172.30.1.135:443->52.109.8.10:2246: read: connection reset by peer"}
Sep 23 15:44:00 ip-172-30-1-135 caddy[526]: {"level":"debug","ts":1632411840.3403382,"logger":"http.stdlib","msg":"http: TLS handshake error from 52.109.8.10:2247: tls: no cipher suite supported by both client and server"}

Option 1 to check ciphers - nmap

$ nmap -sV --script ssl-enum-ciphers -p 443 crossbar.org
Starting Nmap 7.92 ( https://nmap.org ) at 2021-09-23 10:38 CDT
Nmap scan report for crossbar.org (54.165.12.75)
Host is up (0.079s latency).
rDNS record for 54.165.12.75: ec2-54-165-12-75.compute-1.amazonaws.com

PORT    STATE SERVICE  VERSION
443/tcp open  ssl/http Caddy httpd
|_http-server-header: Caddy
| ssl-enum-ciphers:
|   TLSv1.2:
|     ciphers:
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|     compressors:
|       NULL
|     cipher preference: server
|   TLSv1.3:
|     ciphers:
|       TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A
|       TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A
|       TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A
|     cipher preference: client
|_  least strength: A

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.52 seconds

Option 2 to check ciphers - sslcan

$ sslscan crossbar.org
Version: 2.0.10-static
OpenSSL 1.1.1l  24 Aug 2021

Connected to 54.165.12.75

Testing SSL server crossbar.org on port 443 using SNI name crossbar.org

  SSL/TLS Protocols:
SSLv2     disabled
SSLv3     disabled
TLSv1.0   disabled
TLSv1.1   disabled
TLSv1.2   enabled
TLSv1.3   enabled

  TLS Fallback SCSV:
Server supports TLS Fallback SCSV

  TLS renegotiation:
Session renegotiation not supported

  TLS Compression:
OpenSSL version does not support compression
Rebuild with zlib1g-dev package for zlib support

  Heartbleed:
TLSv1.3 not vulnerable to heartbleed
TLSv1.2 not vulnerable to heartbleed

  Supported Server Cipher(s):
Preferred TLSv1.3  128 bits  TLS_AES_128_GCM_SHA256        Curve 25519 DHE 253
Accepted  TLSv1.3  256 bits  TLS_CHACHA20_POLY1305_SHA256  Curve 25519 DHE 253
Accepted  TLSv1.3  256 bits  TLS_AES_256_GCM_SHA384        Curve 25519 DHE 253
Preferred TLSv1.2  256 bits  ECDHE-ECDSA-AES256-GCM-SHA384 Curve 25519 DHE 253
Accepted  TLSv1.2  128 bits  ECDHE-ECDSA-AES128-GCM-SHA256 Curve 25519 DHE 253
Accepted  TLSv1.2  256 bits  ECDHE-ECDSA-CHACHA20-POLY1305 Curve 25519 DHE 253

  Server Key Exchange Group(s):
TLSv1.3  128 bits  secp256r1 (NIST P-256)
TLSv1.3  128 bits  x25519
TLSv1.2  128 bits  secp256r1 (NIST P-256)
TLSv1.2  128 bits  x25519

  SSL Certificate:
Signature Algorithm: sha256WithRSAEncryption
ECC Curve Name:      prime256v1
ECC Key Strength:    128

Subject:  crossbar.org
Altnames: DNS:crossbar.org
Issuer:   R3

Not valid before: Sep 17 20:51:09 2021 GMT
Not valid after:  Dec 16 20:51:08 2021 GMT

5. What I already tried:

I think I sort of merged this into my problem above.

6. Links to relevant resources:

Linked inline above, but also:
https://testconnectivity.microsoft.com/tests/SslServer/input

Is your certificate using RSA or EC? To use an RSA cipher suite I believe you need an RSA certificate, which you’d have to configure since Caddy defaults to EC for performance reasons. You can change the cert type using key_type in the tls directive, although I think that might only apply to the next time it obtains/renews a cert.

Thanks for the quick reply Matt.

It looks like I’m using the default EC. The cert key is EC 256 bits

So my path forward is to add to my tls something like:

tls {
  key_type rsa2048
}

From what I’ve quickly skimmed online, it looks like you’re correct that I need the cert to match the cipher suite. Worse case, I might have to spin up another server with the RSA key in the config and regen all my certs. A bit of a pain, but doable.

I also saw mention of the possibility of supporting both RSA and EC certs and responding with the appropriate one based on client support. That’d be a pretty awesome trick to support.

Thanks again

1 Like

Yeah, it’s unfortunate that your clients require inefficient cryptography. RSA is on the way out and I’m not sure I’m thrilled with the idea of implementing the complexity of supporting both in a modern web server.

As for your case, I think regenerating RSA certs is probably the best solution. (I fixed a typo in your post btw, key_type.)

@matt Sorry to revive this post. I just came across Multiple certificate support (e.g. ECDSA & RSA) · Issue #1575 · caddyserver/caddy · GitHub
Unless I’m misunderstanding, the last comment (2020-04-01) on it mentions you added support for providing ECDSA & RSA certs at the same time. Am I misunderstanding that?

Oh; yeah, you can manually configure this, using the public_key_algorithm property of a connection policy. I forgot about that. :sweat_smile:

How does this work? When a client tries to negotiate using only RSA, we only have EC, Caddy would get an RSA cert, then respond with that?

Also, is there some documentation on how to configure this?

Thanks Matt!

You’d have to manually load an RSA cert into Caddy; currently it doesn’t support automatically managing both EC+RSA certificates. (Not particularly interested in implementing that.)

You can configure custom TLS connection policies here: JSON Config Structure - Caddy Documentation

Gotcha, so I’m back to using RSA for everything. Thanks!

This topic was automatically closed after 30 days. New replies are no longer allowed.