diff --git a/core/authelia/docker-compose.yml b/core/authelia/docker-compose.yml index 79defa2..eb1d9b6 100644 --- a/core/authelia/docker-compose.yml +++ b/core/authelia/docker-compose.yml @@ -1,6 +1,6 @@ services: authelia: - profiles: ["core","all","authelia"] + profiles: ["core","all","authelia", "traefik"] image: authelia/authelia restart: always build: diff --git a/core/crowdsec/docker-compose.yml b/core/crowdsec/docker-compose.yml index 5e0519c..fe4cfb3 100644 --- a/core/crowdsec/docker-compose.yml +++ b/core/crowdsec/docker-compose.yml @@ -1,6 +1,6 @@ services: crowdsec: - profiles: ["core","all","crowdsec"] + profiles: ["core","all","crowdsec", "traefik"] # image: crowdsecurity/crowdsec:latest build: ${PROJECT_ROOT}/core/crowdsec container_name: crowdsec diff --git a/core/error-pages/docker-compose.yml b/core/error-pages/docker-compose.yml index cee041b..b1ef5d8 100644 --- a/core/error-pages/docker-compose.yml +++ b/core/error-pages/docker-compose.yml @@ -1,6 +1,6 @@ services: error-pages: - profiles: ["core","all","error-pages"] + profiles: ["core","all","error-pages", "traefik"] image: tarampampam/error-pages:3 restart: always container_name: error-pages diff --git a/monitoring/docker-exporter/docker-compose.yml b/monitoring/docker-exporter/docker-compose.yml index 4c935a4..79574e9 100644 --- a/monitoring/docker-exporter/docker-compose.yml +++ b/monitoring/docker-exporter/docker-compose.yml @@ -1,6 +1,6 @@ services: docker-update-exporter: - profiles: ["monitoring","all","docker-exporter"] + profiles: ["monitoring","all","docker-exporter", "prometheus"] build: context: ${PROJECT_ROOT}/monitoring/docker-exporter container_name: docker-update-exporter diff --git a/monitoring/docker-socket-proxy/docker-compose.yml b/monitoring/docker-socket-proxy/docker-compose.yml index cfb7e56..5bb4982 100644 --- a/monitoring/docker-socket-proxy/docker-compose.yml +++ b/monitoring/docker-socket-proxy/docker-compose.yml @@ -1,6 +1,6 @@ services: docker-socket-proxy: - profiles: ["monitoring","all","docker-socket-proxy"] + profiles: ["monitoring","all","docker-socket-proxy", "core", "traefik", "prometheus"] image: tecnativa/docker-socket-proxy:latest container_name: docker-socket-proxy hostname: docker-socket-proxy diff --git a/monitoring/influxdb-service/docker-compose.yml b/monitoring/influxdb-service/docker-compose.yml deleted file mode 100644 index 6dc4389..0000000 --- a/monitoring/influxdb-service/docker-compose.yml +++ /dev/null @@ -1,42 +0,0 @@ -services: - influxdb: - profiles: ["monitoring","all","influxdb-service"] - image: influxdb:2.7 - container_name: influxdb - restart: unless-stopped -# env_file: -# - ${PROJECT_ROOT}/secrets/stack-secrets.env - volumes: - - ${PROJECT_ROOT}/monitoring/influxdb:/var/lib/influxdb2 - environment: - DOCKER_INFLUXDB_INIT_MODE: ${INFLUXDB_INIT_MODE} - DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUXDB_INIT_USERNAME} - DOCKER_INFLUXDB_INIT_PASSWORD_FILE: /run/secrets/influxdb_init_password - DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_INIT_ORG} - DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUXDB_INIT_BUCKET} - secrets: - - influxdb_init_password - networks: -# - edge -# - traefik_reverse_proxy - - traefik - - monitor - labels: - - "traefik.http.routers.influxdb.rule=Host(`influxdb.lan.ddnsgeek.com`)" - - "traefik.enable=true" - - "traefik.http.routers.influxdb.entrypoints=websecure" - - "traefik.http.routers.influxdb.tls.certresolver=myresolver" - - "io.portainer.accesscontrol.public" - - "traefik.http.services.influxdb.loadbalancer.server.port=8086" - - "traefik.http.routers.influxdb.middlewares=authelia" - - "traefik.docker.network=core_traefik" - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8086/health || exit 1"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 10s - -secrets: - influxdb_init_password: - file: ${PROJECT_ROOT}/secrets/influxdb_init_password.txt diff --git a/monitoring/node-exporter/docker-compose.yml b/monitoring/node-exporter/docker-compose.yml index a0a04f8..f7f769f 100644 --- a/monitoring/node-exporter/docker-compose.yml +++ b/monitoring/node-exporter/docker-compose.yml @@ -1,6 +1,6 @@ services: node-exporter: - profiles: ["monitoring","all","node-exporter"] + profiles: ["monitoring","all","node-exporter", "prometheus"] image: prom/node-exporter:latest container_name: node-exporter pid: host diff --git a/monitoring/pihole-exporter/docker-compose.yml b/monitoring/pihole-exporter/docker-compose.yml index d9343d1..c681841 100644 --- a/monitoring/pihole-exporter/docker-compose.yml +++ b/monitoring/pihole-exporter/docker-compose.yml @@ -1,6 +1,6 @@ services: pihole-exporter: - profiles: ["monitoring","all","pihole-exporter"] + profiles: ["monitoring","all","pihole-exporter", "prometheus"] image: ekofr/pihole-exporter:latest container_name: pihole-exporter # env_file: diff --git a/monitoring/telegraf/docker-compose.yml b/monitoring/telegraf/docker-compose.yml index 241b96e..15e0d3a 100644 --- a/monitoring/telegraf/docker-compose.yml +++ b/monitoring/telegraf/docker-compose.yml @@ -1,6 +1,6 @@ services: telegraf: - profiles: ["monitoring","all","telegraf"] + profiles: ["monitoring","all","telegraf", "prometheus"] image: telegraf:latest container_name: telegraf restart: unless-stopped diff --git a/service-access-policy.md b/service-access-policy.md new file mode 100644 index 0000000..e09b067 --- /dev/null +++ b/service-access-policy.md @@ -0,0 +1,78 @@ +# Service Access Policy and External Exposure Hardening + +## 1) Service classification + +| Service/Host | Classification | Rationale | +|---|---|---| +| `auth.lan.ddnsgeek.com` | `authenticated-public` | Public identity/login entrypoint; internet-accessible but requires user authentication. | +| `nextcloud.lan.ddnsgeek.com` | `authenticated-public` | Internet-facing collaboration app that must remain reachable to authenticated users. | +| `passbolt.lan.ddnsgeek.com` | `authenticated-public` | Public password-management portal with strong authentication controls. | +| `gitea.lan.ddnsgeek.com` | `authenticated-public` | Public developer endpoint with account-based access. | +| `searxng.lan.ddnsgeek.com` | `public` | Intended anonymous/search access endpoint. | +| `familytree.lan.ddnsgeek.com` | `authenticated-public` | End-user app; externally reachable but login-protected. | +| `shifts.lan.ddnsgeek.com` | `authenticated-public` | End-user app; externally reachable but login-protected. | +| `stockfill.lan.ddnsgeek.com` | `authenticated-public` | End-user app; externally reachable but login-protected. | +| `gotify.lan.ddnsgeek.com` | `private-admin` | Admin/ops notification backend; should not be internet reachable. | +| `grafana.lan.ddnsgeek.com` | `private-admin` | Infrastructure admin/observability console. | +| `prometheus.lan.ddnsgeek.com` | `private-admin` | Monitoring datastore/query interface. | +| `node-red.lan.ddnsgeek.com` | `private-admin` | Automation runtime and flow editor. | +| `traefik.lan.ddnsgeek.com` | `private-admin` | Reverse-proxy admin/dashboard surface. | +| `portainer.lan.ddnsgeek.com` | `private-admin` | Container management plane. | +| `influxdb.lan.ddnsgeek.com` | `private-admin` | Metrics datastore admin/API surface. | +| `kuma.lan.ddnsgeek.com` | `private-admin` | Monitoring admin surface. | +| `monitor-kuma.lan.ddnsgeek.com` | `private-admin` | Monitoring admin surface. | +| `edge.lan.ddnsgeek.com` | `private-admin` | Edge/network administration plane. | + +## 2) Required controls for `private-admin` + +Apply **at least one** trusted-path control (preferably layered): + +- Private network only (no public DNS / no internet route). +- WireGuard/Tailscale/OpenVPN access gate. +- mTLS client certificate requirement at reverse proxy. +- Source IP allowlist at firewall and reverse proxy. + +Minimum target state for all `private-admin` hosts: + +- Public internet: connection refused/timeout, or immediate `403` for untrusted source. +- Trusted path (VPN/mTLS/allowlisted IP): normal authenticated access. + +## 3) Gateway auth hardening + +For all `public` and `authenticated-public` services: + +- Keep SSO and MFA enforcement at the identity gateway. +- Enforce lockout/backoff on `/login`, `/oauth/*`, `/auth/*`, `/api/auth/*`. +- Rate-limit by source IP + account identifier to deter credential stuffing. + +Suggested baseline: + +- Soft limit: `10 req/min` per IP for auth endpoints. +- Burst: `20`. +- Temporary block: `15 min` after repeated failures. +- Account lockout: `5-10` consecutive failed attempts (with secure unlock flow). + +## 4) WAF / reverse-proxy protections + +Deploy one of: + +- WAF managed rules for bot/credential-stuffing signatures. +- Reverse-proxy failed-auth throttling and tarpit/delay policy. + +Implement logging + alerting thresholds: + +- High failed-auth rate from one IP/CIDR. +- Password spray pattern across many usernames. +- Geo/ASN anomalies for sensitive apps. + +## 5) External re-test procedure + +Re-test from a non-trusted external network and record outcomes. + +Success criteria: + +- Every `private-admin` host is inaccessible without VPN/mTLS/allowlisted source. +- `public` and `authenticated-public` hosts remain reachable. +- Auth endpoints trigger rate-limit/lockout controls under failed-attempt simulation. + +Use `./scripts/retest-external-access.sh` for a repeatable external validation pass.