1. Caddy version (caddy version
):
v2.5.0 h1:eRHzZ4l3X6Ag3kUt8nj5IxATprhqKq/wToP7OHlXWA0=
2. How I run Caddy:
Build caddy using the following command:
xcaddy build --with github.com/caddyserver/ntlm-transport --with github.com/caddyserver/certmagic --with github.com/lolPants/caddy-requestid --with github.com/chukmunnlee/caddy-openapi --with github.com/mholt/caddy-l4 --with github.com/greenpau/caddy-security --with github.com/vrongmeal/caddygit/module/git --with github.com/caddy-dns/cloudflare
Built caddy executable is moved to /usr/bin/caddy
Caddy is then launched via systemd as a service. To configure I use the AdminAPI for CI/CD (config stored in a github repo, and webhook to jenkins to trigger deployment)
a. System environment:
Ubuntu 22.04, caddy installed directly on the host (not containerized)
b. Command:
systemctl start caddy-api
c. Service/unit/compose file:
# caddy-api.service
#
# For using Caddy with its API.
#
# This unit is "durable" in that it will automatically resume
# the last active configuration if the service is restarted.
#
# See https://caddyserver.com/docs/install for instructions.
[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 --resume
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
d. My complete Caddyfile or JSON config:
{
"apps": {
"http": {
"servers": {
"main": {
"listen": [
":80",
":443"
],
"routes": [
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "docker.system32.uk:5055"
}
],
"transport": {
"protocol": "http_ntlm"
}
}
]
}
]
}
],
"match": [
{
"host": [
"media.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.228:443"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"hrvy.uk",
"*.hrvy.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.1.245:32400"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"plex.media.system32.uk",
"plex.media.jordanharvey.xyz"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.1.245:8181"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"manage.media.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "10.10.2.202:8123"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"apps-homeassistant.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.32:10000"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"apps-budibase.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.42:9090"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"apps-cockpit.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.30:80"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"apps-passman.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "10.20.1.8:80"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"apps-zabbix.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.29:443"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"assets.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.22:80"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"git.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.223:80"
}
]
}
]
}
]
}
],
"match": [
{
"host": [
"live-tv.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.220:10000"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"apps-wsadmin.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "docker.system32.uk:9443"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"apps-portainer.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "resvmdocker02.docker.system32.uk:49154"
}
],
"transport": {
"protocol": "http_ntlm"
}
}
]
}
]
}
],
"match": [
{
"host": [
"jenkins.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.225:80"
}
],
"transport": {
"protocol": "http_ntlm"
}
}
]
}
]
}
],
"match": [
{
"host": [
"hrm.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.1.52:5001"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"phone.hrvy.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "resvmdocker02.docker.system32.uk:30080"
}
],
"transport": {
"protocol": "http_ntlm"
}
}
]
}
]
}
],
"match": [
{
"host": [
"apps-baserow.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "resvmdocker02.docker.system32.uk:28080"
}
],
"transport": {
"protocol": "http_ntlm"
}
}
]
}
]
}
],
"match": [
{
"host": [
"support.system32.uk"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.190:443"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"jordanharvey.space",
"*.jordanharvey.space"
]
}
],
"terminal": true
},
{
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "192.168.113.220:443"
}
],
"transport": {
"protocol": "http_ntlm",
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
],
"match": [
{
"host": [
"apps-ipam.system32.uk",
"e-learn.system32.uk"
]
}
],
"terminal": true
}
],
"automatic_https": {
"disable": false,
"disable_redirects": false,
"disable_certificates": false
},
"experimental_http3": true,
"allow_h2c": false
}
}
},
"tls": {
"automation": {
"policies": [{
"renewal_window_ratio": 20,
"issuers": [{
"module": "acme",
"email": "REDACTED",
"acme_timeout": "1h",
"challenges": {
"http": {
"disabled": false
},
"tls-alpn": {
"disabled": false
},
"dns": {
"provider": {
"name": "cloudflare",
"api_token": "REDACTED"
},
"resolvers": ["8.8.8.8", "8.8.4.4"]
}
}
}]
}]
}
}
}
}
Output from curl -v:
root@resvmcaddy01:/home/netjo# curl -v http://apps-portainer.system32.uk
* Trying 192.168.113.42:80...
* Connected to apps-portainer.system32.uk (192.168.113.42) port 80 (#0)
> GET / HTTP/1.1
> Host: apps-portainer.system32.uk
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< Cache-Control: max-age=31536000
< Content-Type: text/html; charset=utf-8
< Date: Fri, 06 May 2022 15:04:43 GMT
< Last-Modified: Sun, 03 Apr 2022 21:01:32 GMT
< Server: Caddy
< Vary: Accept-Encoding
< X-Content-Type-Options: nosniff
< X-Xss-Protection: 1; mode=block
< Transfer-Encoding: chunked
<!doctype html><html lang="en" ng-app="portainer" ng-strict-di><head><meta charset="utf-8"/><title>Portainer</title><meta name="description" content=""/><meta name="author" content="Portainer.io"/><base id="base"/><script>var path = window.location.pathname.replace(/^\/+|\/+$/g, '');
var basePath = path ? '/' + path + '/' : '/';
document.getElementById('base').href = basePath;</script><!--[if lt IE 9]>
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--><link rel="apple-touch-icon" sizes="180x180" href="63a301f0574f1a696ce6.png"/><link rel="icon" type="image/png" sizes="32x32" href="2dcfc527d067d4ae3424.png"/><link rel="icon" type="image/png" sizes="16x16" href="112a479c093f4729251d.png"/><link rel="mask-icon" href="7ee8aae1b407ce0e809b.svg" color="#5bbad5"/><link rel="shortcut icon" href="data:image/vnd.microsoft.icon;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/><meta name="msapplication-config" content="6d50eaeb9f128c130ed9.xml"/><meta name="theme-color" content="#ffffff"/><script defer="defer" src="runtime.0175fd64064c5694cbe7.js"></script><script defer="defer" src="vendor.d832a8d7973ffeb155ad.js"></script><script defer="defer" src="main.c9df9695b55bbd734e82.js"></script><link href="vendor.d42617657dafd12b6ec2.css" rel="stylesheet"><link href="main.a47ce99f78f7b95e94af.css" rel="stylesheet"></head><body ng-controller="MainController"><div id="page-wrapper" ng-class="{
open:
toggle && ['portainer.auth', 'portainer.internal-auth', 'portainer.init.admin', 'portainer.init.endpoint', 'portainer.init.license'].indexOf($state.current.name) === -1,
nopadding:
['portainer.auth', 'portainer.internal-auth', 'portainer.init.admin', 'portainer.init.endpoint', 'portainer.init.license', 'portainer.logout'].indexOf(
$state.current.name
) > -1 || applicationState.loading
}" ng-cloak><div id="sideview" ui-view="sidebar" ng-if="!applicationState.loading"></div><div id="content-wrapper"><div class="page-content* Connection #0 to host apps-portainer.system32.uk left intact
"><div class="page-wrapper" ng-if="applicationState.loading"><div class="container simple-box"><div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3"><div class="row"><img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo"/> <img ng-if="!logo" src="29d4ee6d4a5c786588a7.svg" class="simple-box-logo" alt="Portainer"/></div><div class="row" style="text-align: center">Loading Portainer... <i class="fa fa-cog fa-spin" style="margin-left: 5px"></i></div></div></div></div><div id="view" ui-view="content" ng-if="!applicationState.loading"></div></div></div></div></body></html>
3. The problem I’m having:
Caddy doesn’t appear to be redirecting from HTTP to HTTPS… Pretty much all domains should be https. However I can still load websites over http. Not sure if thats because of the plugins installed? I can still navigate to the secured version of the website manually.
4. Error messages and/or full log output:
The below line is repeated every 10 minutes, for each domain in the config file above. Although SSL seems to be working?
May 06 15:05:50 resvmcaddy01 caddy[49551]: May 06 15:05:50 resvmcaddy01 caddy[49551]: {"level":"info","ts":1651849550.8699167,"logger":"tls.cache.maintenance","msg":"certificate expires soon; queuing for renewal","identifiers":["apps-homeassistant.system32.uk"],"remaining":7531270.130083634}
5. What I already tried:
Added automatic_https section under the server with no luck
Removed config routes down to 1 simple site (not protected with SSL internally)
Lots of research points to people using Caddyfile’s, which I’ve tried and no luck again.