Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a1ee389dc | ||
|
|
63808ccd20 | ||
|
|
3b524ec118 |
@@ -3,6 +3,8 @@ design/.DS_Store
|
||||
|
||||
# Secrets and local environment
|
||||
.env*
|
||||
!deploy/.env.example
|
||||
deploy/.env
|
||||
deploy/secrets/
|
||||
|
||||
# Build artifacts and dependencies
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# MealQuest production environment. Copy to deploy/.env (never committed)
|
||||
# and replace the placeholders. The service fails fast: a missing or invalid
|
||||
# required variable stops it at boot with the variable named.
|
||||
#
|
||||
# Secrets are never set here. They live in deploy/secrets/ (never committed),
|
||||
# one value per file, owned by the container user and readable only by it:
|
||||
#
|
||||
# mkdir -p deploy/secrets && chmod 700 deploy/secrets
|
||||
# head -c 32 /dev/urandom | base64 > deploy/secrets/secret_key
|
||||
# printf '%s\n' '<Authentik client secret>' > deploy/secrets/oidc_client_secret
|
||||
# sudo chown 10001:10001 deploy/secrets/* && sudo chmod 600 deploy/secrets/*
|
||||
#
|
||||
# deploy/compose.yaml mounts them at /run/secrets/<name> and points the
|
||||
# *_FILE variables at them.
|
||||
|
||||
# --- Compose -----------------------------------------------------------------
|
||||
|
||||
# Host address the service is published on, as <address>:8090. Production
|
||||
# binds the LAN address 10.60.1.14 only; 127.0.0.1 suits a dev machine.
|
||||
MEALQUEST_BIND_ADDRESS=127.0.0.1
|
||||
|
||||
# --- Service -----------------------------------------------------------------
|
||||
|
||||
# Public origin the household opens. Fixes the OIDC redirect URI
|
||||
# (<url>/auth/callback) and the Secure cookies. Must be https:// (plain
|
||||
# http:// is accepted only for localhost, 127.0.0.1 and [::1]).
|
||||
MEALQUEST_PUBLIC_URL=https://mealquest.cubecraftlabs.com
|
||||
|
||||
# How long a graceful shutdown may take before in-flight requests are cut.
|
||||
MEALQUEST_SHUTDOWN_TIMEOUT=10s
|
||||
|
||||
# Optional. Comma-separated IPs/CIDRs of reverse proxies whose
|
||||
# X-Forwarded-For/-Proto headers are honoured. Production lists Traefik;
|
||||
# unset trusts no peer.
|
||||
MEALQUEST_TRUSTED_PROXY=10.60.1.33
|
||||
|
||||
# IANA zone the household settings are created with on first boot. Required
|
||||
# until those settings exist; ignored afterwards (change the zone in
|
||||
# household settings instead). Confirm the household's zone before the first
|
||||
# deploy.
|
||||
MEALQUEST_DEFAULT_TZ=America/New_York
|
||||
|
||||
# --- Authentik OIDC ----------------------------------------------------------
|
||||
|
||||
# Issuer of the Authentik application "mealquest". Discovered at boot, so it
|
||||
# must be reachable from the container. Must be https://.
|
||||
MEALQUEST_OIDC_ISSUER=https://auth.cubecraftlabs.com/application/o/mealquest/
|
||||
MEALQUEST_OIDC_CLIENT_ID=mealquest
|
||||
# Comma-separated Authentik groups whose members may sign in.
|
||||
MEALQUEST_OIDC_ALLOWED_GROUPS=mealquest-admin
|
||||
|
||||
# Optional session lifetimes. A session ends after IDLE unused and always MAX
|
||||
# after sign-in; MAX must be at least IDLE. Defaults: 720h and 4320h.
|
||||
MEALQUEST_SESSION_IDLE=720h
|
||||
MEALQUEST_SESSION_MAX=4320h
|
||||
|
||||
# --- Set by deploy/compose.yaml; do not set here -----------------------------
|
||||
#
|
||||
# MEALQUEST_HTTP_ADDR=:8080
|
||||
# MEALQUEST_DATA_DIR=/data
|
||||
# MEALQUEST_SECRET_KEY_FILE=/run/secrets/secret_key
|
||||
# Base64 of 32 random bytes; encrypts secrets at rest (AES-256-GCM).
|
||||
# MEALQUEST_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc_client_secret
|
||||
# The Authentik provider's client secret.
|
||||
|
||||
# --- Planned; not read by the service yet -------------------------------------
|
||||
#
|
||||
# Added to deploy/compose.yaml and this file by the features that use them.
|
||||
#
|
||||
# Web Push (Feature 19). Generate the key pair with cmd/vapidgen.
|
||||
# MEALQUEST_VAPID_PUBLIC_KEY=<base64url VAPID public key>
|
||||
# MEALQUEST_VAPID_PRIVATE_KEY_FILE=/run/secrets/vapid_private_key
|
||||
# MEALQUEST_VAPID_SUBJECT=mailto:<owner address>
|
||||
#
|
||||
# Google Calendar OAuth web client (Feature 16).
|
||||
# MEALQUEST_GOOGLE_CLIENT_ID=<client id>.apps.googleusercontent.com
|
||||
# MEALQUEST_GOOGLE_CLIENT_SECRET_FILE=/run/secrets/google_client_secret
|
||||
#
|
||||
# SearXNG recipe search sidecar on the compose network (Feature 07).
|
||||
# MEALQUEST_SEARXNG_URL=http://searxng:8080
|
||||
@@ -0,0 +1,61 @@
|
||||
# Production stack for MealQuest on 10.60.1.14 (D-018). Build and start on
|
||||
# the host with:
|
||||
#
|
||||
# docker compose -f deploy/compose.yaml up -d --build
|
||||
#
|
||||
# Configuration comes from deploy/.env (copy deploy/.env.example) and secrets
|
||||
# from deploy/secrets/; neither is committed. Compose bind-mounts file secrets
|
||||
# with their host owner and mode, so each file in deploy/secrets/ must be
|
||||
# owned by the container user 10001:10001 with mode 0600.
|
||||
name: mealquest
|
||||
|
||||
services:
|
||||
mealquest:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
user: "10001:10001"
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- path: .env
|
||||
required: true
|
||||
# Fixed by this file's container shape (listen port, volume, secret
|
||||
# mounts); deploy/.env must not set them.
|
||||
environment:
|
||||
MEALQUEST_HTTP_ADDR: ":8080"
|
||||
MEALQUEST_DATA_DIR: /data
|
||||
MEALQUEST_SECRET_KEY_FILE: /run/secrets/secret_key
|
||||
MEALQUEST_OIDC_CLIENT_SECRET_FILE: /run/secrets/oidc_client_secret
|
||||
secrets:
|
||||
- secret_key
|
||||
- oidc_client_secret
|
||||
volumes:
|
||||
- mealquest-data:/data
|
||||
ports:
|
||||
- "${MEALQUEST_BIND_ADDRESS:?set MEALQUEST_BIND_ADDRESS in deploy/.env}:8090:8080"
|
||||
networks:
|
||||
- mealquest
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 3s
|
||||
start_period: 10s
|
||||
retries: 3
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
|
||||
secrets:
|
||||
secret_key:
|
||||
file: ./secrets/secret_key
|
||||
oidc_client_secret:
|
||||
file: ./secrets/oidc_client_secret
|
||||
|
||||
volumes:
|
||||
mealquest-data:
|
||||
name: mealquest-data
|
||||
|
||||
networks:
|
||||
mealquest:
|
||||
name: mealquest
|
||||
@@ -0,0 +1,22 @@
|
||||
# Applies the MealQuest DOCKER-USER rules (/usr/local/sbin/mealquest-firewall).
|
||||
#
|
||||
# PartOf= re-runs this unit whenever docker.service is stopped or restarted,
|
||||
# and WantedBy=docker.service starts it whenever docker.service starts. Both
|
||||
# are one-way: a failure here fails this unit visibly and never fails
|
||||
# docker.service.
|
||||
#
|
||||
# Start MealQuest only after this unit has succeeded:
|
||||
# systemctl start mealquest-firewall && docker compose up -d
|
||||
[Unit]
|
||||
Description=Restrict MealQuest port 8090 to Traefik (DOCKER-USER)
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
PartOf=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/sbin/mealquest-firewall
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target docker.service
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
# Restrict the MealQuest published host port (10.60.1.14:8090) to Traefik
|
||||
# (10.60.1.33) and the Docker host itself (10.60.1.14); drop every other
|
||||
# source.
|
||||
#
|
||||
# DOCKER-USER runs after Docker's DNAT, so the destination there is the
|
||||
# container address and port, not the published ones. Match the original
|
||||
# destination address and port with conntrack instead of -d/--dport.
|
||||
#
|
||||
# The rules must sit at the top of DOCKER-USER, ahead of any other project's
|
||||
# rules (an earlier RETURN or DROP would decide MealQuest traffic first).
|
||||
# Every run deletes all rules tagged with the mealquest-firewall comment and
|
||||
# re-inserts them at positions 1-3, so re-runs leave the same ordered rules
|
||||
# and no duplicates. Installed as /usr/local/sbin/mealquest-firewall.
|
||||
set -eu
|
||||
|
||||
tag="-m comment --comment mealquest-firewall"
|
||||
match="-m conntrack --ctorigdst 10.60.1.14 --ctorigdstport 8090 --ctdir ORIGINAL"
|
||||
|
||||
rules=$(iptables -w -S DOCKER-USER)
|
||||
printf '%s\n' "$rules" | while read -r _ chain spec; do
|
||||
case " $spec " in
|
||||
*" --comment mealquest-firewall "*) iptables -w -D "$chain" $spec ;;
|
||||
esac
|
||||
done
|
||||
|
||||
iptables -w -I DOCKER-USER 1 -s 10.60.1.33/32 $match $tag -j RETURN
|
||||
iptables -w -I DOCKER-USER 2 -s 10.60.1.14/32 $match $tag -j RETURN
|
||||
iptables -w -I DOCKER-USER 3 $match $tag -j DROP
|
||||
Reference in New Issue
Block a user