Skip to content

Commit 0e60379

Browse files
committed
SSL proxy with Caddy and mkcert
1 parent 3105f56 commit 0e60379

9 files changed

Lines changed: 227 additions & 1 deletion

File tree

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DOMAIN=local.example.com
2+
UPSTREAM_URL=http://host.docker.internal:3000

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ certs/
88
.env
99

1010
# AI agents
11-
.claude/
11+
.claude/

CLAUDE.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
A Caddy-based reverse proxy for local development with automatic SSL certificate generation.
8+
9+
## Architecture
10+
11+
- **mkcert container**: Generates SSL certificates on first run, stores in `./certs/`
12+
- **Caddy container**: Reverse proxy with HTTPS, depends on mkcert completing first
13+
- **Bind mount**: Certs stored locally in `./certs/` for easy access
14+
15+
## Key Commands
16+
17+
```bash
18+
# Generate certificates (first time only)
19+
docker-compose --profile setup run --rm mkcert
20+
21+
# Start the proxy
22+
docker-compose up -d --build
23+
24+
# View logs
25+
docker-compose logs -f caddy
26+
27+
# Stop the proxy
28+
docker-compose down
29+
30+
# Regenerate certificates
31+
rm -rf certs/* && docker-compose --profile setup run --rm mkcert
32+
33+
# Install CA on macOS
34+
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./certs/${DOMAIN}.rootCA.pem
35+
```
36+
37+
## Configuration
38+
39+
Environment variables (set in `.env`):
40+
- `DOMAIN` - Domain name for SSL cert (default: `localhost`)
41+
- `UPSTREAM_URL` - URL for your local app (default: `http://host.docker.internal:3000`)
42+
43+
## Files
44+
45+
- **config/Caddyfile**: Proxy rules, TLS config, CSP header removal
46+
- **scripts/mkcert/entrypoint.sh**: Script that generates certs if they don't exist
47+
- **docker-compose.yml**: Service definitions with mkcert → caddy dependency
48+
- **Dockerfile.mkcert**: Alpine image with mkcert for cert generation
49+
- **Dockerfile.caddy**: Minimal Caddy image
50+
51+
## Ports
52+
53+
- `8080` → HTTP (redirects to HTTPS on 8443)
54+
- `8443` → HTTPS (proxies to `${UPSTREAM_URL}`)

Dockerfile.caddy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM caddy:2-alpine
2+
3+
EXPOSE 80 443
4+
5+
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

Dockerfile.mkcert

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM alpine:3.19
2+
3+
ARG MKCERT_VERSION=1.4.4
4+
5+
RUN apk add --no-cache ca-certificates nss-tools curl \
6+
&& ARCH=$(uname -m) \
7+
&& case "$ARCH" in \
8+
aarch64) MKCERT_ARCH="linux-arm64" ;; \
9+
x86_64) MKCERT_ARCH="linux-amd64" ;; \
10+
*) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
11+
esac \
12+
&& curl -L "https://github.com/FiloSottile/mkcert/releases/download/v${MKCERT_VERSION}/mkcert-v${MKCERT_VERSION}-${MKCERT_ARCH}" -o /usr/local/bin/mkcert \
13+
&& chmod +x /usr/local/bin/mkcert
14+
15+
COPY scripts/mkcert/entrypoint.sh /entrypoint.sh
16+
RUN chmod +x /entrypoint.sh
17+
18+
ENTRYPOINT ["/entrypoint.sh"]

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# SSL Proxy
2+
3+
A Dockerized Caddy reverse proxy with automatic SSL certificate generation for local development.
4+
5+
## Features
6+
7+
- Automatic SSL certificate generation via mkcert
8+
- Strips Content-Security-Policy headers
9+
- HTTP to HTTPS redirect
10+
- Configurable domain and upstream URL
11+
12+
## Quick Start
13+
14+
1. Configure your domain in `.env`:
15+
16+
```
17+
DOMAIN=local.example.com
18+
UPSTREAM_URL=http://host.docker.internal:3000
19+
```
20+
21+
`UPSTREAM_URL` must include the scheme and port.
22+
23+
2. Add to `/etc/hosts`:
24+
25+
```
26+
127.0.0.1 local.example.com
27+
```
28+
29+
3. Generate certificates (first time only):
30+
31+
```bash
32+
docker-compose --profile setup run --rm mkcert
33+
```
34+
35+
4. Install the CA certificate (one-time):
36+
37+
```bash
38+
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./certs/local.example.com.rootCA.pem
39+
```
40+
41+
5. Start the proxy:
42+
43+
```bash
44+
docker-compose up -d
45+
```
46+
47+
6. Visit: `https://local.example.com:8443`
48+
49+
Note (Linux): Requires Docker Engine 20.10+ for `host-gateway` support.
50+
51+
## Configuration
52+
53+
| Variable | Default | Description |
54+
| --------------- | ----------- | ---------------------- |
55+
| `DOMAIN` | `localhost` | Domain for SSL cert |
56+
| `UPSTREAM_URL` | `http://host.docker.internal:3000` | URL for your local app |
57+
58+
## Ports
59+
60+
- `8080` - HTTP (redirects to HTTPS)
61+
- `8443` - HTTPS
62+
63+
## Layout
64+
65+
```
66+
├── config/Caddyfile # Caddy configuration
67+
├── scripts/mkcert/entrypoint.sh # Cert generation script
68+
├── docker-compose.yml # Service definitions
69+
├── Dockerfile.caddy # Caddy image
70+
├── Dockerfile.mkcert # Certificate generator
71+
└── .env # Your configuration
72+
```

config/Caddyfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
:80 {
2+
redir https://{$DOMAIN:localhost}:8443{uri} permanent
3+
}
4+
5+
{$DOMAIN:localhost} {
6+
tls /etc/caddy/certs/{$DOMAIN:localhost}.pem /etc/caddy/certs/{$DOMAIN:localhost}.key.pem
7+
8+
reverse_proxy {$UPSTREAM_URL:http://host.docker.internal:3000} {
9+
header_down -Content-Security-Policy
10+
header_down -Content-Security-Policy-Report-Only
11+
}
12+
13+
log {
14+
output stdout
15+
format console
16+
}
17+
}

docker-compose.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
services:
2+
mkcert:
3+
build:
4+
context: .
5+
dockerfile: Dockerfile.mkcert
6+
container_name: mkcert
7+
profiles:
8+
- setup
9+
environment:
10+
- DOMAIN=${DOMAIN:-localhost}
11+
volumes:
12+
- ./certs:/certs
13+
14+
caddy:
15+
build:
16+
context: .
17+
dockerfile: Dockerfile.caddy
18+
container_name: ssl-proxy
19+
ports:
20+
- "8080:80"
21+
- "8443:443"
22+
environment:
23+
- DOMAIN=${DOMAIN:-localhost}
24+
- UPSTREAM_URL=${UPSTREAM_URL:-http://host.docker.internal:3000}
25+
extra_hosts:
26+
- "host.docker.internal:host-gateway"
27+
volumes:
28+
- ./config/Caddyfile:/etc/caddy/Caddyfile:ro
29+
- ./certs:/etc/caddy/certs:ro
30+
- caddy_data:/data
31+
- caddy_config:/config
32+
restart: unless-stopped
33+
34+
volumes:
35+
caddy_data:
36+
caddy_config:

scripts/mkcert/entrypoint.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/sh
2+
set -e
3+
4+
DOMAIN="${DOMAIN:-localhost}"
5+
CERT_FILE="/certs/${DOMAIN}.pem"
6+
KEY_FILE="/certs/${DOMAIN}.key.pem"
7+
CA_FILE="/certs/${DOMAIN}.rootCA.pem"
8+
9+
if [ ! -f "$CERT_FILE" ]; then
10+
echo "Generating SSL certificate for ${DOMAIN}..."
11+
mkcert -install
12+
mkcert -cert-file "$CERT_FILE" \
13+
-key-file "$KEY_FILE" \
14+
"$DOMAIN"
15+
cp "$(mkcert -CAROOT)/rootCA.pem" "$CA_FILE"
16+
echo "=== Certificate generated ==="
17+
else
18+
echo "Certificate already exists for ${DOMAIN}, skipping generation."
19+
fi
20+
21+
echo "Install CA on macOS:"
22+
echo " sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./certs/${DOMAIN}.rootCA.pem"

0 commit comments

Comments
 (0)