How to Create an Authentication Portal for Your Domains with Caddy
How to Create an Authentication Portal for Your Domains with Caddy
Caddy is a modern web server that automatically provides HTTPS via Let’s Encrypt. However, the standard Caddy build does not include the caddy-security module, which is required to create an authentication portal and manage access to your domains. In this article, I’ll walk you through building Caddy with this module using xcaddy, configuring an authentication portal, and securing your web applications.
Prerequisites
Before starting, ensure you have:
- A server with an operating system (e.g., Ubuntu 22.04).
- Command-line access with superuser privileges (
sudo). - Basic knowledge of web servers and configuration files.
- A domain or subdomain (e.g.,
example.com) with DNS records pointing to your server. - Go installed (version 1.16 or higher) for building Caddy.
Step 1: Building Caddy with the caddy-security Module
Since the caddy-security module isn’t included in the standard Caddy build, we’ll compile a custom version using xcaddy.
1.1. Installing xcaddy
xcaddy is a tool for building Caddy with additional modules. Install it as follows:
sudo apt update
sudo apt install -y golang
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
After running these commands, xcaddy will be available in ~/go/bin/xcaddy. Ensure ~/go/bin is in your $PATH for global access:
export PATH=$PATH:~/go/bin
1.2. Building Caddy with caddy-security
Now, build Caddy with the caddy-security module:
~/go/bin/xcaddy build --with github.com/greenpau/caddy-security
This command creates an executable caddy file in the current directory.
1.3. Installing the Custom Caddy Build
Move the compiled file to a system directory for global access:
sudo mv caddy /usr/bin/
Verify the version to ensure the module is included:
caddy version
The output should show a custom build version of Caddy.
Step 2: Setting Up User Storage
The authentication portal in Caddy uses a JSON file to store user data. We’ll create a users.json file using a script.
2.1. Creating a Data Directory
Create a directory for storing data and set appropriate permissions:
sudo mkdir -p /data/.local/caddy
sudo chown caddy:caddy /data/.local/caddy
2.2. Generating a User with a Script
Use this script to simplify user creation. It prompts for user details and generates users.json.
Save this code as generate_users_json.sh:
#!/bin/bash
# Check dependencies
if ! command -v python3 &> /dev/null; then
echo "Error: Python3 is not installed. Install it with: 'sudo apt install python3'."
exit 1
fi
if ! python3 -c "import bcrypt" &> /dev/null; then
echo "Error: Python bcrypt module is not installed. Install it with: 'pip3 install bcrypt'."
exit 1
fi
if ! command -v uuidgen &> /dev/null; then
echo "Error: uuidgen is not installed. Install it with: 'sudo apt install uuid-runtime'."
exit 1
fi
# Prompt for input
read -p "Enter username: " USERNAME
read -p "Enter email: " EMAIL
read -s -p "Enter password: " PASSWORD
echo
# Validate input
if [[ -z "$USERNAME" || -z "$EMAIL" || -z "$PASSWORD" ]]; then
echo "Error: All fields must be filled."
exit 1
fi
if [[ ${#USERNAME} -lt 3 || ${#USERNAME} -gt 50 || "$USERNAME" =~ [^a-z] ]]; then
echo "Error: Username must be 3-50 characters, lowercase letters (a-z) only."
exit 1
fi
if [[ ${#PASSWORD} -lt 8 ]]; then
echo "Error: Password must be at least 8 characters."
exit 1
fi
# Extract domain
DOMAIN=$(echo "$EMAIL" | cut -d'@' -f2)
if [[ -z "$DOMAIN" ]]; then
echo "Error: Invalid email format."
exit 1
fi
# Generate values
UUID=$(uuidgen)
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
HASH=$(python3 -c "import bcrypt; print(bcrypt.hashpw('$PASSWORD'.encode(), bcrypt.gensalt(10)).decode())")
# Create users.json
cat <<EOF > /data/.local/caddy/users.json
{
"version": "1.1.7",
"policy": {
"password": {
"min_length": 8,
"max_length": 128
},
"user": {
"min_length": 3,
"max_length": 50,
"allow_non_alpha_numeric": false,
"allow_uppercase": false
}
},
"revision": 1,
"last_modified": "$DATE",
"users": [
{
"id": "$UUID",
"username": "$USERNAME",
"email_address": {
"address": "$EMAIL",
"domain": "$DOMAIN"
},
"email_addresses": [
{
"address": "$EMAIL",
"domain": "$DOMAIN"
}
],
"passwords": [
{
"purpose": "generic",
"algorithm": "bcrypt",
"hash": "$HASH",
"cost": 10,
"expired_at": "0001-01-01T00:00:00Z",
"created_at": "$DATE",
"disabled_at": "0001-01-01T00:00:00Z"
}
],
"created": "$DATE",
"last_modified": "$DATE",
"roles": [
{
"name": "admin",
"organization": "authp"
}
]
}
]
}
EOF
# Set permissions
sudo chown caddy:caddy /data/.local/caddy/users.json
sudo chmod 640 /data/.local/caddy/users.json
echo "File /data/.local/caddy/users.json created successfully!"
echo "Restart Caddy: sudo systemctl restart caddy"
How to Use the Script:
- Save it as
generate_users_json.sh. - Make it executable:
chmod +x generate_users_json.sh - Run it:
./generate_users_json.sh - Enter details:
- Username (e.g.,
john). - Email (e.g.,
john@example.com). - Password (minimum 8 characters).
- Username (e.g.,
The script creates /data/.local/caddy/users.json with a user assigned the authp/admin role.
Step 3: Configuring Caddy for Authentication
Now, configure the Caddyfile to enable the authentication portal.
3.1. Base Caddy Configuration
Open /etc/caddy/Caddyfile:
sudo nano /etc/caddy/Caddyfile
Add global settings at the top:
{
storage file_system {
root /var/lib/caddy
}
email your-email@example.com
order authenticate before respond
order authorize before respond
security {
local identity store localdb {
realm local
path /data/.local/caddy/users.json
}
authentication portal my_portal {
crypto default token lifetime 3600
enable identity store localdb
cookie domain .example.com
ui {
links {
"Dashboard" "/dashboard" icon "las la-tachometer-alt"
"My Identity" "/auth/whoami" icon "las la-user"
}
}
transform user {
match origin local
action add role authp/admin
}
}
authorization policy my_policy {
set auth url /auth
allow roles authp/admin
acl rule {
comment "Allow authenticated admins"
match role authp/admin
allow stop log info
}
acl rule {
comment "Deny all others"
match any
deny log warn
}
}
}
}
- Replace
your-email@example.comwith your email. - Replace
.example.comwith your domain (e.g.,.mydomain.com).
3.2. Domain Configuration
Add a block for your domain:
sub.example.com {
route {
handle /auth {
rewrite * /auth
request_header +X-Forwarded-Prefix /auth
authenticate with my_portal
}
route /auth* {
authenticate with my_portal
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote}
header_up Host {host}
}
}
route /* {
authorize with my_policy
reverse_proxy 127.0.0.1:8080 {
header_up X-Real-IP {remote}
header_up Host {host}
}
}
}
log {
output file /var/log/caddy/sub.example.com-access.log {
roll_size 30mb
roll_keep 10
roll_keep_for 720h
}
level ERROR
}
}
- Replace
sub.example.comwith your domain. - Adjust
reverse_proxyto match your backend (address and port).
Step 4: Validating and Restarting Caddy
4.1. Validate Configuration
Check the Caddyfile for errors:
caddy validate --config /etc/caddy/Caddyfile
If you see Valid configuration, proceed.
4.2. Restart Caddy
Apply changes:
sudo systemctl restart caddy
Verify the status:
systemctl status caddy
The service should be active (running).
Step 5: Testing the Authentication Portal
- Open a browser and go to
https://sub.example.com/auth. - Enter the username and password from
users.json. - After logging in, verify access to protected resources.
If access fails, check:
- Data in
users.json. - Role consistency in the configuration.
Additional Tips
Multiple Domains
For different domains, create separate portals:
authentication portal another_portal {
cookie domain .anotherdomain.com
...
}
Logging
Enable debug logs:
{
log {
output file /var/log/caddy/caddy.log
level DEBUG
}
}
Security
- Keep Caddy updated.
- Use strong passwords.
- Configure a firewall (e.g.,
ufw) to allow only ports 80 and 443.
Conclusion
You now have a fully configured authentication portal with Caddy. This setup is scalable—add users, domains, and policies as needed. For further assistance, refer to the Caddy documentation or ask in the community!
- Our community Openode.XYZ OpeNode.xyz
- Aeza VPS (+15% on payment) Aeza.net
- Best EU hosting (+1 month free) Kamatera.com
- VPS hosting - 4vps.su (-10% discount!) 4VPS.su
- TG Channel TG-Channel GIG.ovh