Caddy is a relatively new and easy-to-use web server written in Go. It has a few notable features, including being able to automatically request and renew free SSL certificates from Let’s Encrypt.
In my environment, I have multiple web/application servers, none of which are directly accessible from the internet, and I have one Internet-facing server running Caddy. I am using Caddy to achieve the following requirements:
- Host multiple web sites/applications on a single public IP address.
- Secure Internet traffic to my web/application servers using SSL certificates.
- Perform TLS termination on the Internet-facing Caddy server to simplify administration of SSL certificates. In other words, I don’t have to manage certificates on multiple servers.
Some of the configurations in this guide were taken from David Leonard’s blog, especially getting Caddy to run as a service. Many thanks to David for sharing his config!
CentOS Preparation
For this walk-through, I built a VM in VMware ESXi 6.0 with 1 GB RAM and 1 vCPU, and I chose to perform a “minimal” installation of CentOS 7. The CentOS installation is relatively self explanatory, so I will forgo talking about that part. However, I do recommend that you configure your instance of CentOS with either a static IP address or a DHCP reservation, which will help in creating port-forwarding rules on your router.
Once CentOS is installed, we need to install open-vm-tools
and apply system updates via yum
. Please note that if you are not using VMware, you do not need to run the first command.
yum install -y open-vm-tools
yum update -y
reboot
For security reasons, we are going to create a service account without root privileges for caddy
. We’ll name the account caddy
.
adduser caddy -s /sbin/nologin
Caddy Installation
For the actual installation of Caddy, you will need to get the URL for the latest version directly from the Caddy download page. We don’t need any of the additional features, but we do need the Linux 64-bit version.
In these commands, we will download the latest version of Caddy and extract the archive to a temporary directory.
curl -L -O https://caddyserver.com/download/builds/162307125800956/caddy_linux_amd64_custom.tar.gz
mkdir caddy
tar zxvf caddy_linux_amd64_custom.tar.gz -C ./caddy
Next, we will move the caddy
binary into place and create a directory for logging.
mkdir /opt/caddy
cp ./caddy/caddy /opt/caddy
chown -R caddy:caddy /opt/caddy
mkdir /var/log/caddy
chown caddy:caddy /var/log/caddy
At this point, the original archive and extracted files can be safely deleted.
rm -f caddy_linux_amd64_custom.tar.gz
rm -rf caddy
Normally, a process must have root privileges to bind to ports 80 and 443. This command will allow caddy
to bind to these ports without root privileges.
setcap cap_net_bind_service=+ep /opt/caddy/caddy
Caddy Configuration
After installing Caddy, we need to create the configuration file using a text editor such as vi
.
vi /opt/caddy/prod.caddy
There are multiple # Comments
in the configuration file to show what I am doing, but for more options, please refer to the Caddy documentation. The contents of /opt/caddy/prod.caddy
are listed below:
# Get rid of 'www' for the main web site
www.example.com {
# Redirect to the main web site, keeping the full URL (accomplished with '{uri}')
redir https://example.com{uri}
}
# The main web site, without 'www'
example.com {
# Enable compression for files that are not compressed (e.g. compress .html but not .jpg)
gzip
# Reverse proxy all requests ( '/' ) to the backend server ( 'web01.internal.example.com' )
proxy / web01.internal.example.com {
# Tell the backend web server which protocol was used to connect to the Caddy server
proxy_header X-Forwarded-Proto {scheme}
# Tell the backend web server the IP address of the client
proxy_header X-Forwarded-For {remote}
# Tell the backend web server the hostname requested by the client
proxy_header Host {host}
}
}
# Another application
app.example.com {
gzip
# On the backend server, this application runs on a port other than 80
proxy / app01.internal.example.com:8090 {
proxy_header X-Forwarded-Proto {scheme}
proxy_header X-Forwarded-For {remote}
proxy_header Host {host}
}
}
Running Caddy as a Service
By default, Caddy runs from the command line. This means that it will not start up automatically after a system reboot. We will create a systemd
service to allow caddy
to run when the system starts up.
First, create a file for the caddy
service.
vi /etc/systemd/system/caddy.service
The contents of /etc/systemd/system/caddy.service
are listed below:
; see `man systemd.unit` for configuration details
; the man section also explains *specifiers* `%x`
[Unit]
Description=Caddy HTTP/2 web server
Documentation=https://caddyserver.com/docs
After=network-online.target
Wants=network-online.target
Wants=systemd-networkd-wait-online.service
[Service]
; run user for caddy
User=caddy
ExecStart=/opt/caddy/caddy -agree=true -conf=/opt/caddy/prod.caddy
Restart=on-failure
; create a private temp folder that is not shared with other processes
PrivateTmp=true
; limit the number of file descriptors, see `man systemd.exec` for more limit settings
LimitNOFILE=8192
[Install]
WantedBy=multi-user.target
Now, we will allow the caddy
service to run when the system reboots.
systemctl enable caddy
If you want to start caddy
without rebooting, you can run this command.
systemctl start caddy
Finally, this command will show whether caddy
is running or not.
systemctl status caddy
The output will look something like this when caddy
is running.
● caddy.service - Caddy HTTP/2 web server
Loaded: loaded (/etc/systemd/system/caddy.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2016-07-30 18:00:00 CDT; 1h 43min ago
Docs: https://caddyserver.com/docs
Main PID: 861 (caddy)
CGroup: /system.slice/caddy.service
└─861 /opt/caddy/caddy -agree=true -conf=/opt/caddy/prod.caddy
Summary
We now have a reverse proxy server that automatically generates and maintains SSL certificates. In the example configuration, users who navigate to http://www.example.com/about
are automatically redirected to https://example.com/about
, and the web application is accessible at https://app.example.com
. Unless explicitly told not to, Caddy will redirect all unencrypted HTTP requests to secure HTTPS requests. And all of that for free!