auto-issue certs on nginx deploy, document one-domain-per-conf convention
This commit is contained in:
+5
-2
@@ -114,7 +114,7 @@ Developer pushes to hantim-server
|
|||||||
-> deploy-<app>.yml triggers (based on changed paths under docker/<app>/)
|
-> deploy-<app>.yml triggers (based on changed paths under docker/<app>/)
|
||||||
-> SSH to deploy@hantim as deploy-<app>
|
-> SSH to deploy@hantim as deploy-<app>
|
||||||
-> deploy.sh: git pull, docker compose pull, docker compose up -d
|
-> deploy.sh: git pull, docker compose pull, docker compose up -d
|
||||||
-> (nginx only): test config with nginx -t before applying
|
-> (nginx only): auto-issues certs for new domains, then tests config with nginx -t
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setup change (push to hantim-server)
|
### Setup change (push to hantim-server)
|
||||||
@@ -131,7 +131,10 @@ Developer pushes change to scripts/configure.sh
|
|||||||
- **App name = domain** (e.g., `hcsuzuki.net`)
|
- **App name = domain** (e.g., `hcsuzuki.net`)
|
||||||
- **Container name** = domain with dots replaced by underscores (e.g.,
|
- **Container name** = domain with dots replaced by underscores (e.g.,
|
||||||
`hcsuzuki_net`)
|
`hcsuzuki_net`)
|
||||||
- **Cert name** = domain (stored at `/etc/letsencrypt/live/<domain>/`)
|
- **Cert name** = config filename without `.conf` (stored at `/etc/letsencrypt/live/<name>/`)
|
||||||
|
- **One domain per conf file** -- `deploy.sh` derives the cert name from the conf
|
||||||
|
filename and collects all `server_name` values for that cert. Putting unrelated
|
||||||
|
domains in the same conf file will bundle them into one cert incorrectly.
|
||||||
- **Image name** = `git.timothykim.net/hantim/<domain>:latest`
|
- **Image name** = `git.timothykim.net/hantim/<domain>:latest`
|
||||||
- All HTTP traffic redirects to `https://www.<domain>`
|
- All HTTP traffic redirects to `https://www.<domain>`
|
||||||
- Bare domain HTTPS redirects to `https://www.<domain>`
|
- Bare domain HTTPS redirects to `https://www.<domain>`
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ Actions workflows.
|
|||||||
- **App name = domain** (e.g., `hcsuzuki.net`)
|
- **App name = domain** (e.g., `hcsuzuki.net`)
|
||||||
- **Container name** = domain with dots replaced by underscores (e.g., `hcsuzuki_net`)
|
- **Container name** = domain with dots replaced by underscores (e.g., `hcsuzuki_net`)
|
||||||
- **Image** = `git.timothykim.net/hantim/<domain>:latest`
|
- **Image** = `git.timothykim.net/hantim/<domain>:latest`
|
||||||
- **Cert name** = domain (at `/etc/letsencrypt/live/<domain>/`)
|
- **Cert name** = config filename without `.conf` (at `/etc/letsencrypt/live/<name>/`)
|
||||||
|
- **One domain per conf file** -- deploy.sh derives cert name from filename
|
||||||
- All HTTP redirects to HTTPS; bare domain redirects to `www.<domain>`
|
- All HTTP redirects to HTTPS; bare domain redirects to `www.<domain>`
|
||||||
- The `shared` Docker network is created by nginx compose and used by all apps
|
- The `shared` Docker network is created by nginx compose and used by all apps
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -119,9 +119,16 @@ git push
|
|||||||
**What happens:**
|
**What happens:**
|
||||||
1. `deploy-nginx.yml` workflow triggers (path match)
|
1. `deploy-nginx.yml` workflow triggers (path match)
|
||||||
2. `deploy.sh` pulls latest code
|
2. `deploy.sh` pulls latest code
|
||||||
3. Tests nginx config with `nginx -t` -- if it fails, deploy aborts and the
|
3. If any conf files reference certs that don't exist yet, nginx is briefly
|
||||||
|
stopped and certs are issued via standalone certbot (auto-restarts on failure)
|
||||||
|
4. Tests nginx config with `nginx -t` -- if it fails, deploy aborts and the
|
||||||
running nginx is untouched
|
running nginx is untouched
|
||||||
4. Runs `docker compose up -d` and reloads nginx
|
5. Runs `docker compose up -d` and reloads nginx
|
||||||
|
|
||||||
|
**Note:** Each conf file must contain server blocks for only one domain. The
|
||||||
|
cert name is derived from the filename (e.g. `hcsuzuki.net.conf` -> cert
|
||||||
|
`hcsuzuki.net`). Redirect-only domains like `hcsuzukiviolin.com` get their
|
||||||
|
own conf file.
|
||||||
|
|
||||||
## 7. Re-run configure on existing server
|
## 7. Re-run configure on existing server
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -54,7 +54,34 @@ if ! docker compose pull; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$APP" = "nginx" ]; then
|
if [ "$APP" = "nginx" ]; then
|
||||||
# Test config before applying — a bad config (e.g. missing cert) would take down all sites
|
# Issue certs for any new domains (brief nginx downtime while certbot binds port 80)
|
||||||
|
missing_certs=false
|
||||||
|
for conf in /opt/hantim/docker/nginx/conf.d/*.conf; do
|
||||||
|
cert_name=$(basename "$conf" .conf)
|
||||||
|
if [ ! -d "/etc/letsencrypt/live/$cert_name" ]; then
|
||||||
|
missing_certs=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$missing_certs" = true ]; then
|
||||||
|
docker stop nginx 2>/dev/null || true
|
||||||
|
trap 'docker start nginx 2>/dev/null || true' EXIT
|
||||||
|
for conf in /opt/hantim/docker/nginx/conf.d/*.conf; do
|
||||||
|
cert_name=$(basename "$conf" .conf)
|
||||||
|
if [ ! -d "/etc/letsencrypt/live/$cert_name" ]; then
|
||||||
|
domains=$(grep -oP 'server_name\s+\K[^;]+' "$conf" | tr ' ' '\n' | sort -u)
|
||||||
|
domain_args=""
|
||||||
|
for d in $domains; do
|
||||||
|
domain_args="$domain_args -d $d"
|
||||||
|
done
|
||||||
|
echo "Issuing cert for $cert_name..."
|
||||||
|
certbot certonly --standalone --non-interactive --agree-tos \
|
||||||
|
--register-unsafely-without-email --cert-name "$cert_name" $domain_args
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
trap - EXIT
|
||||||
|
fi
|
||||||
|
# Test config before applying — a bad config would take down all sites
|
||||||
docker compose run --rm -T nginx nginx -t
|
docker compose run --rm -T nginx nginx -t
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user