Generalize reverse proxy source allowlist
This commit is contained in:
+2
-1
@@ -3,7 +3,8 @@ IMAGE_PORT=8191
|
|||||||
IMAGE_REPOSITORY_PATH=/home/letrhee/sam_rebuild/image
|
IMAGE_REPOSITORY_PATH=/home/letrhee/sam_rebuild/image
|
||||||
IMAGE_UID=1000
|
IMAGE_UID=1000
|
||||||
IMAGE_GID=1000
|
IMAGE_GID=1000
|
||||||
CADDY_SOURCE_CIDR=172.30.1.75/32
|
# Comma- or space-separated source CIDRs of hosts that reverse-proxy to port 8191.
|
||||||
|
TRUSTED_PROXY_CIDRS=172.30.1.75/32
|
||||||
|
|
||||||
IMAGE_REMOTE_URL=https://gitea.hided.net/devsam/image.git
|
IMAGE_REMOTE_URL=https://gitea.hided.net/devsam/image.git
|
||||||
IMAGE_REPOSITORY_FULL_NAME=devsam/image
|
IMAGE_REPOSITORY_FULL_NAME=devsam/image
|
||||||
|
|||||||
@@ -39,11 +39,14 @@ remote branch tip, rejects dirty or non-fast-forward updates, and serializes all
|
|||||||
Git changes. The initial and only allowed branch is `master` unless
|
Git changes. The initial and only allowed branch is `master` unless
|
||||||
`IMAGE_ALLOWED_BRANCHES` is explicitly expanded.
|
`IMAGE_ALLOWED_BRANCHES` is explicitly expanded.
|
||||||
|
|
||||||
The current Caddy host reaches this server from `172.30.1.75`. Keep the observed
|
The service is reverse-proxy agnostic. Its current reverse proxy happens to
|
||||||
source in the untracked `.env`; re-check it whenever Caddy networking changes.
|
reach this server from `172.30.1.75`, but Caddy, Nginx, HAProxy, or another
|
||||||
The published port binds all server interfaces, so production also needs a
|
proxy can be used. Keep every observed proxy source CIDR in the untracked
|
||||||
host `DOCKER-USER` (or equivalent) firewall rule allowing that source CIDR to
|
`TRUSTED_PROXY_CIDRS` value, separated by commas or spaces, and re-check the
|
||||||
TCP 8191 and rejecting other sources. Nginx applies the same source allowlist.
|
value whenever proxy networking changes. The published port binds all server
|
||||||
|
interfaces, so production also needs a host `DOCKER-USER` (or equivalent)
|
||||||
|
firewall rule allowing those source CIDRs to TCP 8191 and rejecting other
|
||||||
|
sources. The static Nginx edge applies the same source allowlist.
|
||||||
|
|
||||||
### Prepare
|
### Prepare
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ docker compose build
|
|||||||
Apply and inspect the dedicated Docker ingress chain with root privileges:
|
Apply and inspect the dedicated Docker ingress chain with root privileges:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo env CADDY_SOURCE_CIDR=172.30.1.75/32 IMAGE_PORT=8191 \
|
sudo env TRUSTED_PROXY_CIDRS=172.30.1.75/32 IMAGE_PORT=8191 \
|
||||||
./deploy/scripts/firewall-8191.sh apply
|
./deploy/scripts/firewall-8191.sh apply
|
||||||
sudo ./deploy/scripts/firewall-8191.sh check
|
sudo ./deploy/scripts/firewall-8191.sh check
|
||||||
```
|
```
|
||||||
@@ -109,7 +112,7 @@ signed administration command:
|
|||||||
./deploy/scripts/admin-deploy.sh <branch> [expected-commit]
|
./deploy/scripts/admin-deploy.sh <branch> [expected-commit]
|
||||||
```
|
```
|
||||||
|
|
||||||
The administration route is not proxied through Nginx or Caddy.
|
The administration route is not exposed through the public reverse proxy.
|
||||||
|
|
||||||
### Tests and rollback
|
### Tests and rollback
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
read_only: true
|
read_only: true
|
||||||
environment:
|
environment:
|
||||||
CADDY_SOURCE_CIDR: ${CADDY_SOURCE_CIDR:?Set CADDY_SOURCE_CIDR to the direct Caddy source CIDR}
|
TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:?Set TRUSTED_PROXY_CIDRS to the direct reverse-proxy source CIDR list}
|
||||||
ports:
|
ports:
|
||||||
- "${IMAGE_BIND_ADDRESS:-0.0.0.0}:${IMAGE_PORT:-8191}:8080"
|
- "${IMAGE_BIND_ADDRESS:-0.0.0.0}:${IMAGE_PORT:-8191}:8080"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
if [ -z "${CADDY_SOURCE_CIDR:-}" ]; then
|
trusted_proxy_cidrs=${TRUSTED_PROXY_CIDRS:-${CADDY_SOURCE_CIDR:-}}
|
||||||
echo "CADDY_SOURCE_CIDR is required" >&2
|
|
||||||
|
if [ -z "$trusted_proxy_cidrs" ]; then
|
||||||
|
echo "TRUSTED_PROXY_CIDRS is required" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
envsubst '${CADDY_SOURCE_CIDR}' \
|
: > /tmp/trusted-proxy-allow.conf
|
||||||
< /etc/image/default.conf.template \
|
for trusted_proxy_cidr in $(printf '%s' "$trusted_proxy_cidrs" | tr ',' ' '); do
|
||||||
> /tmp/nginx.conf
|
case "$trusted_proxy_cidr" in
|
||||||
|
*[!0-9A-Fa-f:./]*)
|
||||||
|
echo "Invalid trusted proxy CIDR: $trusted_proxy_cidr" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
printf 'allow %s;\n' "$trusted_proxy_cidr" >> /tmp/trusted-proxy-allow.conf
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -s /tmp/trusted-proxy-allow.conf ]; then
|
||||||
|
echo "TRUSTED_PROXY_CIDRS must contain at least one CIDR" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp /etc/image/default.conf.template /tmp/nginx.conf
|
||||||
|
|
||||||
exec nginx -c /tmp/nginx.conf -g 'daemon off;'
|
exec nginx -c /tmp/nginx.conf -g 'daemon off;'
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ http {
|
|||||||
|
|
||||||
allow 127.0.0.1;
|
allow 127.0.0.1;
|
||||||
allow ::1;
|
allow ::1;
|
||||||
allow ${CADDY_SOURCE_CIDR};
|
include /tmp/trusted-proxy-allow.conf;
|
||||||
deny all;
|
deny all;
|
||||||
|
|
||||||
location = /healthz {
|
location = /healthz {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
action=${1:-check}
|
action=${1:-check}
|
||||||
source_cidr=${CADDY_SOURCE_CIDR:-}
|
trusted_proxy_cidrs=${TRUSTED_PROXY_CIDRS:-${CADDY_SOURCE_CIDR:-}}
|
||||||
image_port=${IMAGE_PORT:-8191}
|
image_port=${IMAGE_PORT:-8191}
|
||||||
chain=SAM_IMAGE_INGRESS
|
chain=SAM_IMAGE_INGRESS
|
||||||
|
|
||||||
@@ -24,15 +24,31 @@ case "$action" in
|
|||||||
;;
|
;;
|
||||||
apply)
|
apply)
|
||||||
require_root
|
require_root
|
||||||
if [ -z "$source_cidr" ]; then
|
if [ -z "$trusted_proxy_cidrs" ]; then
|
||||||
echo "CADDY_SOURCE_CIDR is required" >&2
|
echo "TRUSTED_PROXY_CIDRS is required" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
rule_count=0
|
||||||
|
for trusted_proxy_cidr in $(printf '%s' "$trusted_proxy_cidrs" | tr ',' ' '); do
|
||||||
|
case "$trusted_proxy_cidr" in
|
||||||
|
*[!0-9A-Fa-f:./]*)
|
||||||
|
echo "Invalid trusted proxy CIDR: $trusted_proxy_cidr" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
rule_count=$((rule_count + 1))
|
||||||
|
done
|
||||||
|
if [ "$rule_count" -eq 0 ]; then
|
||||||
|
echo "TRUSTED_PROXY_CIDRS must contain at least one CIDR" >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
iptables -n -L DOCKER-USER >/dev/null
|
iptables -n -L DOCKER-USER >/dev/null
|
||||||
iptables -n -L "$chain" >/dev/null 2>&1 || iptables -N "$chain"
|
iptables -n -L "$chain" >/dev/null 2>&1 || iptables -N "$chain"
|
||||||
iptables -F "$chain"
|
iptables -F "$chain"
|
||||||
iptables -A "$chain" -s "$source_cidr" -j ACCEPT
|
|
||||||
iptables -A "$chain" -j DROP
|
iptables -A "$chain" -j DROP
|
||||||
|
for trusted_proxy_cidr in $(printf '%s' "$trusted_proxy_cidrs" | tr ',' ' '); do
|
||||||
|
iptables -I "$chain" 1 -s "$trusted_proxy_cidr" -j ACCEPT
|
||||||
|
done
|
||||||
iptables -C DOCKER-USER -p tcp -m conntrack --ctorigdstport "$image_port" -j "$chain" 2>/dev/null \
|
iptables -C DOCKER-USER -p tcp -m conntrack --ctorigdstport "$image_port" -j "$chain" 2>/dev/null \
|
||||||
|| iptables -I DOCKER-USER 1 -p tcp -m conntrack --ctorigdstport "$image_port" -j "$chain"
|
|| iptables -I DOCKER-USER 1 -p tcp -m conntrack --ctorigdstport "$image_port" -j "$chain"
|
||||||
;;
|
;;
|
||||||
|
|||||||
Reference in New Issue
Block a user