Error using tls ciphers directive

1. The problem I’m having:

I’m trying to use the tls ciphers directive to specify a list of allowed cipher suites. A customer wants to exclude CBC ciphers.

We realize it’s preferable not to change the defaults and we’ve advised our customer accordingly. However, we want to make sure we can disable the CBC ciphers if they insist.

2. Error messages and/or full log output:

root-web.1.ztu1dhvb9oba@ubuntu-s-8vcpu-16gb-intel-nyc3-01    | {"level":"info","ts":1680806830.4784403,"msg":"using provided configuration","config_file":"/tmpapp/CaddyfileForContainer","config_adapter":""}
root-web.1.ztu1dhvb9oba@ubuntu-s-8vcpu-16gb-intel-nyc3-01    | Error: adapting config using caddyfile: /tmpapp/CaddyfileForContainer:9: unrecognized directive: ciphers

3. Caddy version:

Built off commit f8b59e77f83c05da87bd5e3780fb7522b863d462 on master branch

4. How I installed and ran Caddy:

Built docker image containing executable mentioned above

a. System environment:

Alpine docker image

b. Command:

./caddy run --config /tmpapp/CaddyfileForContainer

c. Service/unit/compose file:

FROM alpine:3.16.4

ENV CADDYPATH=/tmpapp
ENV XDG_DATA_HOME=/tmpapp
ENV XDG_CONFIG_HOME=/tmpapp

WORKDIR /app
COPY . .

RUN set -eux && \
    apk add --no-cache libcap nss-tools && \
    adduser -u 10654 -g amag -s /bin/false --disabled-password amag && chown amag:amag -R /app && \
    setcap CAP_NET_BIND_SERVICE=+eip /app/caddy
USER amag

EXPOSE 8080 8443

HEALTHCHECK --interval=5s --timeout=5s --start-period=5s --retries=3 CMD \
    wget -S -q --spider http://localhost:8080/health-check || wget -S -q --spider http://localhost:8080/health-check 2>&1 | grep "308 Permanent Redirect" || exit 1

ENTRYPOINT ["/app/run.sh"]

d. My complete Caddy config:

# Header
{
        debug
        https_port 8443
        http_port 8080
}

# CADDY Body
tls /run/secrets/rootweb_idm_pem /run/secrets/rootweb_idm_key {
    ciphers 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_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
}

        # START InBody Customization
        # END InBody Customization

        encode gzip
        log
        file_server

        header {
                # Disallow the site to be rendered within a frame on different domain (clickjacking protection)
                +X-Frame-Options "SAMEORIGIN"

                -Server
                -x-powered-by

                +Access-Control-Allow-Headers Range
                +Access-Control-Expose-Headers "Accept-Ranges, Content-Encoding, Content-Length, Content-Range"

                #Implement Security Headers
                X-Content-Type-Options "nosniff"
                Strict-Transport-Security "max-age=31536000; includeSubDomains"
                Content-Security-Policy "default-src data: https:; font-src data: https:; frame-src 'self' https: mailto: tel:; img-src data: https: http://localhost:1337 http://localhost:1338; script-src https: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; connect-src https: ws://localhost:* wss: 'self' http://localhost:1337/* *.firebaseio.com"
                Permissions-Policy "accelerometer=(), camera=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()"
                Referrer-Policy "strict-origin-when-cross-origin"
        }

        # Enabling CORS for CDN Only
        # The default Same-Origin Policy is enough for the web and apis
        #######################################################################
        # Set mime type for specific file as content sniffing is now disabled
        # and browser needs to still behave as expected:
        # - opening the file inside a tab as text or image for example,
        # - downloading the file instead of opening it as text...
        #######################################################################

        @exe {
                path *.exe
        }
        header @exe Content-Type application/octet-stream

        @ttf {
                path *.ttf
        }
        header @ttf Content-Type font/ttf

        @woff {
                path *.woff
        }
        header @woff Content-Type font/woff

        @woff2 {
                path *.woff2
        }
        header @woff2 Content-Type font/woff2

        @mem {
                path *.mem
        }
        header @mem Content-Type application/octet-stream

        @ico {
                path *.ico
        }
        header @ico Content-Type image/x-icon

        @pk {
                path *.pkpass
        }
        header @pk Content-Type application/vnd.apple.pkpass

        #######################################################################

        header /cdn* {
                +Access-Control-Allow-Origin "*"
        }

        #mime .xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

        @xlsx {
                path *.xlsx
        }
        header @xlsx Content-Type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

        @txt {
                path *.txt
        }
        header @txt Content-Disposition attachment

        @csv {
                path *.csv
        }
        header @csv Content-Disposition attachment

        respond /health-check 200

        #################################################
        # Rewrite rules to eliminate case issues. Proxy
        # routes are case sensitive
        #################################################
        redir /G4S.Portal.Web/ /AMAG.Auth
        # rewrite /G4S.Portal.Web /AMAG.Portal - Above line used instead to bring new prospect signups to login screen vs Portal.
        rewrite /Amag.Auth /AMAG.Auth
        rewrite / /AMAG.Auth
        rewrite /OneFacilityLoginWeb/Login/Login.mvc /AMAG.Auth

        @amag-auth {
                path /G4S.Portal.Web
                path /Amag.Auth
                path /AMAG.Auth
                path /
                path /OneFacilityLoginWeb/Login/Login.mvc
        }

        # CDN Image Resizer
        @cdn_resize {
                path /cdn/photos*
                not query ""
                not query random=*
        }
        rewrite @cdn_resize /display/resize/0x0{path}

        reverse_proxy /display/* http://picfit:7319


        # DOCUMENTATION
        redir /documentation /documentation/

        route /documentation/* {
            uri strip_prefix /documentation
            root * /app/cdn/documentation
            file_server
        }

        # S3 CDN

        ##################################################
        # Self hosted kestrel (.net core)
        ##################################################

        reverse_proxy /AMAG.Auth* auth-web:7325

        reverse_proxy /AMAG.Portal* portal-web:7326

        reverse_proxy /amag.sso* sso-web:7328

        ## Below two proxies needed since JS points to old locations
        #reverse_proxy /G4S.Portal.Web/public/ localhost:7326/AMAG.Portal/public/ {
        #       without /G4S.Portal.Web/public/
        #}

        reverse_proxy /G4S.Portal.Web/public* portal-web:7326

        reverse_proxy /Amag.Login.Web/Login/Login* auth-web:7325


        ##################################################
        # Self hosted React Apps (caddy)
        ##################################################

        route /G4S.Registration.Web/* {
                uri strip_prefix /G4S.Registration.Web
                reverse_proxy registration-web:7320
        }

        @cac-web {
                path /Amag.CAC.Web/*
                path /AMAG.CAC.Web/*
        }

        route @cac-web {
                uri strip_prefix /AMAG.CAC.Web
                uri strip_prefix /Amag.CAC.Web
                reverse_proxy cac-web:7323
        }

        route /G4S.IdentityManagement.Web/* {
                uri strip_prefix /G4S.IdentityManagement.Web
                reverse_proxy idm-web:7324
        }

        route /G4S.VMS.Web/* {
                uri strip_prefix /G4S.VMS.Web
                reverse_proxy vms-web:7327
        }


        ##################################################
        # Self hosted ReST APIs (servicestack)
        ##################################################

        route /G4S.Registration.WebApi/api/* {
                uri strip_prefix /G4S.Registration.WebApi/api
                reverse_proxy registration-api:7120
        }

        route /AMAG.Symmetry.WebApi/api/* {
                uri strip_prefix /AMAG.Symmetry.WebApi/api
                reverse_proxy symmetry-api:7121
        }

        route /G4S.Core.WebApi/api/* {
                uri strip_prefix /G4S.Core.WebApi/api
                reverse_proxy core-api:7122
        }

        @cac-api {
                path /Amag.CAC.WebApi/*
                path /AMAG.CAC.WebApi/*
        }

        route @cac-api {
                uri strip_prefix /AMAG.CAC.WebApi/api
                uri strip_prefix /Amag.CAC.WebApi/api
                reverse_proxy cac-api:7123
        }

        route /G4S.IdentityManagement.WebApi/api/* {
                uri strip_prefix /G4S.IdentityManagement.WebApi/api
                reverse_proxy idm-api:7124
        }

        route /G4S.VMS.WebApi/api/* {
                uri strip_prefix /G4S.VMS.WebApi/api
                reverse_proxy vms-api:7127
        }

        route /AMAG.Public/api/* {
                uri strip_prefix /AMAG.Public/api
                reverse_proxy public-api:7130
        }

        handle_path /G4S.VMS.WebSocketHub {
                rewrite * /websockethub?{query}
                reverse_proxy vms-websocket:5100
        }

        redir / /AMAG.Portal
        #################################################
        # IIS hosted applications
        # NOTE: one entry can cover both web & webApi
        #################################################
}

5. Links to relevant resources:

The first line after your global options (what you call header) needs to be a site address.

Your Caddyfile somehow tries to create two site blocks, with site addresses that are directive names (and options).

encode gzip {
  # other directives
}
# and another one
tls /run/secrets/rootweb_idm_pem /run/secrets/rootweb_idm_key {
  # empty
}

Matching encode and gzip and tls as literal vhost names (domain names).

So your tls block essentially looks like

example.com {
  ciphers <cipher_suites...>
}

But for it to work it would need to look like

example.com {
  tls {
    ciphers <cipher_suites...>
  }
}

or

example.com

tls {
  ciphers <cipher_suites...>
}

(if you only have a single site block)

Ref: Caddyfile Concepts — Caddy Documentation

3 Likes

I figured I was doing something silly…thanks for identifying it so quickly!

1 Like

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