Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ jobs:
docker run -d --name upstream -p 3000:80 nginx:alpine
sleep 2

- name: Create .env
- name: Create env file
run: |
echo "DOMAIN=${{ env.DOMAIN }}" > .env
echo "HTTP_PORT=${{ env.HTTP_PORT }}" >> .env
echo "HTTPS_PORT=${{ env.HTTPS_PORT }}" >> .env
echo "UPSTREAM_URL=${{ env.UPSTREAM_URL }}" >> .env
echo "DOMAIN=${{ env.DOMAIN }}" > .env.${{ env.DOMAIN }}
echo "HTTP_PORT=${{ env.HTTP_PORT }}" >> .env.${{ env.DOMAIN }}
echo "HTTPS_PORT=${{ env.HTTPS_PORT }}" >> .env.${{ env.DOMAIN }}
echo "UPSTREAM_URL=${{ env.UPSTREAM_URL }}" >> .env.${{ env.DOMAIN }}

- name: Add test domain to hosts
run: echo "127.0.0.1 ${{ env.DOMAIN }}" | sudo tee -a /etc/hosts

- name: Build images
run: docker compose build
run: docker compose --env-file .env.${{ env.DOMAIN }} build

- name: Generate certificates
run: docker compose --profile setup run --rm mkcert
run: docker compose --env-file .env.${{ env.DOMAIN }} --profile setup run --rm mkcert

- name: Verify certificates exist
run: |
Expand All @@ -47,13 +47,13 @@ jobs:
test -f certs/${{ env.DOMAIN }}.rootCA.pem

- name: Start proxy
run: docker compose up -d
run: docker compose --env-file .env.${{ env.DOMAIN }} up -d

- name: Wait for Caddy to start
run: sleep 3

- name: Check Caddy is running
run: docker compose ps --status running --services | grep -q '^caddy$'
run: docker compose --env-file .env.${{ env.DOMAIN }} ps --status running --services | grep -q '^caddy$'

- name: Test HTTP redirect
run: |
Expand All @@ -64,10 +64,10 @@ jobs:
curl -s --cacert certs/${{ env.DOMAIN }}.rootCA.pem https://${{ env.DOMAIN }}:${{ env.HTTPS_PORT }} | grep -q "nginx"
- name: Show logs on failure
if: failure()
run: docker compose logs
run: docker compose --env-file .env.${{ env.DOMAIN }} logs

- name: Stop proxy
if: always()
run: |
docker compose down
docker compose --env-file .env.${{ env.DOMAIN }} down
docker rm -f upstream || true
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ certs/
.DS_Store

# env files
.env
.env*
!.env.example

# AI agents
# Editors
.claude/
.idea/
.vscode/
85 changes: 32 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,90 +11,74 @@ A Dockerized Caddy reverse proxy with automatic SSL certificate generation for l

## Quick Start

1. Configure your domain in `.env`:
1. Create an env file for your domain (e.g., `.env.local.example.com`):

```
DOMAIN=local.example.com
UPSTREAM_URL=http://host.docker.internal:3000
HTTP_PORT=8080
HTTPS_PORT=8443
```

> [!WARNING]
> `UPSTREAM_URL` must include the scheme and port.
> [!WARNING]
> `UPSTREAM_URL` must include the scheme and port.

2. Add your domain to the hosts file:

**macOS/Linux:**
Edit `/etc/hosts`
2. Add your domain to hosts file:

```bash
# macOS/Linux
sudo sh -c 'echo "127.0.0.1 local.example.com" >> /etc/hosts'
```

**Windows (PowerShell as Administrator):**
Edit `C:\Windows\System32\drivers\etc\hosts`

```powershell
# Windows (PowerShell as Admin)
Add-Content -Path C:\Windows\System32\drivers\etc\hosts -Value "127.0.0.1 local.example.com"
```

3. Generate certificates (first time only):
3. Generate certificates:

```bash
docker compose --profile setup run --rm mkcert
docker compose --env-file .env.local.example.com --profile setup run --rm mkcert
```

4. Install the CA certificate (one-time):

Replace `local.example.com` with your configured domain.

**macOS:**
4. Install CA certificate (one-time per domain):

```bash
# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./certs/local.example.com.rootCA.pem
```

**Linux (Debian/Ubuntu):**

```bash
sudo cp ./certs/local.example.com.rootCA.pem /usr/local/share/ca-certificates/local.example.com.crt
sudo update-ca-certificates
```

**Linux (Fedora/RHEL):**

```bash
sudo cp ./certs/local.example.com.rootCA.pem /etc/pki/ca-trust/source/anchors/local.example.com.pem
sudo update-ca-trust
```
# Linux (Debian/Ubuntu)
sudo cp ./certs/local.example.com.rootCA.pem /usr/local/share/ca-certificates/local.example.com.crt && sudo update-ca-certificates

**Linux (Arch):**
# Linux (Fedora/RHEL)
sudo cp ./certs/local.example.com.rootCA.pem /etc/pki/ca-trust/source/anchors/local.example.com.pem && sudo update-ca-trust

```bash
# Linux (Arch)
sudo trust anchor ./certs/local.example.com.rootCA.pem
```

**Windows (PowerShell as Administrator):**

```powershell
# Windows (PowerShell as Admin)
Import-Certificate -FilePath .\certs\local.example.com.rootCA.pem -CertStoreLocation Cert:\LocalMachine\Root
```

If `.pem` import fails, convert to `.cer` first:

```powershell
openssl x509 -in .\certs\local.example.com.rootCA.pem -out .\certs\local.example.com.rootCA.cer
Import-Certificate -FilePath .\certs\local.example.com.rootCA.cer -CertStoreLocation Cert:\LocalMachine\Root
```

5. Start the proxy:

```bash
docker compose up -d
docker compose --env-file .env.local.example.com up -d
```

6. Visit: `https://local.example.com:8443`

Note (Linux): Requires Docker Engine 20.10+ for `host-gateway` support.
> [!NOTE]
> Linux requires Docker Engine 20.10+ for `host-gateway` support.

## Running Multiple Domains

Run multiple instances by creating separate env files with different ports:

```bash
docker compose --env-file .env.local.example.com up -d
docker compose --env-file .env.local.another.com up -d
```

Each instance runs in its own project namespace based on the domain name.

## Configuration

Expand All @@ -105,11 +89,6 @@ Note (Linux): Requires Docker Engine 20.10+ for `host-gateway` support.
| `HTTPS_PORT` | `8443` | HTTPS port (proxy) |
| `UPSTREAM_URL` | `http://host.docker.internal:3000` | URL for your local app |

## Ports

- `HTTP_PORT` (default `8080`) - HTTP (redirects to HTTPS)
- `HTTPS_PORT` (default `8443`) - HTTPS

## Layout

```
Expand Down
6 changes: 4 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
name: ssl-proxy-${DOMAIN:-localhost}
services:
mkcert:
build:
context: .
dockerfile: Dockerfile.mkcert
container_name: mkcert
container_name: mkcert-${DOMAIN:-localhost}
hostname: ${DOMAIN:-localhost}
profiles:
- setup
environment:
Expand All @@ -15,7 +17,7 @@ services:
build:
context: .
dockerfile: Dockerfile.caddy
container_name: ssl-proxy
container_name: ssl-proxy-${DOMAIN:-localhost}
ports:
- "${HTTP_PORT:-8080}:80"
- "${HTTPS_PORT:-8443}:443"
Expand Down
11 changes: 7 additions & 4 deletions scripts/mkcert/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ KEY_FILE="/certs/${DOMAIN}.key.pem"
CA_FILE="/certs/${DOMAIN}.rootCA.pem"

if [ ! -f "$CERT_FILE" ]; then
echo "Generating SSL certificate for ${DOMAIN}..."
echo ""
echo "Generating SSL certificate for ${DOMAIN}... 🔐"
echo ""
mkcert -install
mkcert -cert-file "$CERT_FILE" \
-key-file "$KEY_FILE" \
"$DOMAIN"
cp "$(mkcert -CAROOT)/rootCA.pem" "$CA_FILE"
echo "=== Certificate generated ==="
echo ""
echo "Certificate generated ✅"
else
echo "Certificate already exists for ${DOMAIN}, skipping generation."
echo "Certificate already exists for ${DOMAIN}, skipping generation ⏭️"
fi

echo ""
echo "=== Install CA certificate ==="
echo "Install CA certificate 📋"
echo ""
echo "macOS:"
echo " sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ./certs/${DOMAIN}.rootCA.pem"
Expand Down