This commit is contained in:
git
2026-04-21 11:34:21 +10:00
37 changed files with 247 additions and 91 deletions
+1
View File
@@ -24,6 +24,7 @@ monitoring/influxdb/*
!monitoring/influxdb/docker-compose.yml
secrets/*
!secrets/.env.secrets.example
!secrets/inventory.json
!.env.example
core/traefik/certs/*
!core/traefik/certs/.gitkeep
+6
View File
@@ -49,6 +49,12 @@ Do not run:
- `terraform apply`
- `terraform destroy`
If `terraform init` fails because access to `registry.terraform.io` is forbidden, do not summarize the error vaguely. Report the exact stderr. Continue with:
- `terraform fmt -check -recursive`
- static review of changed `.tf` files
Only run `terraform validate` when provider installation is available locally or registry access succeeds.
## Ansible rules
Allowed:
+2 -2
View File
@@ -17,8 +17,8 @@ If you only read one section, read **[Source-of-truth boundaries](docs/source-of
- Docker environment composition and `services-up.sh`: [docs/docker-environment.md](docs/docker-environment.md)
- Terraform workflows (brownfield import/reconciliation): [docs/terraform-workflows.md](docs/terraform-workflows.md)
- Infrastructure inventory intent and outputs: [docs/infrastructure-inventory.md](docs/infrastructure-inventory.md)
- Deployment prerequisites and secrets setup: [DEPLOYMENT.md](DEPLOYMENT.md)
- Secrets inventory: [SECURITY_SECRETS_INVENTORY.md](SECURITY_SECRETS_INVENTORY.md)
- Deployment prerequisites and secrets setup: [docs/deployment-prerequisites.md](docs/deployment-prerequisites.md)
- Secrets inventory: [docs/security-secrets.md](docs/security-secrets.md)
Terraform subtrees:
-55
View File
@@ -1,55 +0,0 @@
# Security Secrets Inventory
This inventory tracks values expected in local secret material and where they are consumed.
## Scope and authority
- Canonical example template: `secrets/.env.secrets.example`
- Runtime-loaded secret env file: `secrets/stack-secrets.env` (local, non-committed)
- Docker secret files: `secrets/*.txt` (local, non-committed)
For setup steps, see [DEPLOYMENT.md](DEPLOYMENT.md).
## Secrets expected in `secrets/.env.secrets.example`
| Variable | Used by | Purpose / Notes |
|---|---|---|
| `NEXTCLOUD_DB_USER` | `apps/nextcloud/docker-compose.yml` | Nextcloud database username (non-secret identifier but environment-specific). |
| `NEXTCLOUD_ADMIN_USER` | `apps/nextcloud/docker-compose.yml` | Initial Nextcloud admin username. |
| `NEXTCLOUD_SMTP_FROM_ADDRESS` | `apps/nextcloud/docker-compose.yml` | SMTP sender local-part for outbound mail configuration. |
| `NEXTCLOUD_SMTP_DOMAIN` | `apps/nextcloud/docker-compose.yml` | SMTP sender domain for outbound mail configuration. |
| `NEXTCLOUD_SMTP_NAME` | `apps/nextcloud/docker-compose.yml` | Derived from address + domain in the example file. |
| `PASSBOLT_DB_NAME` | `apps/passbolt/docker-compose.yml` | Passbolt database name. |
| `PASSBOLT_DB_USER` | `apps/passbolt/docker-compose.yml` | Passbolt database username. |
| `PASSBOLT_GPG_SERVER_KEY_FINGERPRINT` | `apps/passbolt/docker-compose.yml` | Passbolt server GPG key fingerprint. |
| `GRAMPSWEB_SECRET_KEY` | `apps/gramps/docker-compose.yml` | Secret key used by Gramps Web for session/security signing. |
| `GRAMPSWEB_EMAIL_HOST_USER` | `apps/gramps/docker-compose.yml` | SMTP username for Gramps outbound email. |
| `GRAMPSWEB_EMAIL_HOST_PASSWORD` | `apps/gramps/docker-compose.yml` | SMTP password for Gramps outbound email. |
| `GOTIFY_DEFAULTUSER_NAME` | `monitoring/gotify/docker-compose.yml` | Gotify default username. |
| `GOTIFY_DEFAULTUSER_PASS` | `monitoring/gotify/docker-compose.yml` | Gotify default user password. |
| `INFLUXDB_INIT_USERNAME` | `monitoring/prometheus/docker-compose.yml` | InfluxDB initial username. |
| `PIHOLE_PASSWORD` | `monitoring/prometheus/docker-compose.yml` | Exporter auth / Pi-hole integration password. |
## Secrets managed outside `.env.secrets.example`
The following sensitive values are intentionally provided via Docker secret files (`*_FILE`) or other mounted secret files instead of the shared `.env` template:
- Database/root passwords for Nextcloud, Passbolt, and supporting services.
- Redis runtime password loaded from a Docker secret.
- `DOCKER_INFLUXDB_INIT_PASSWORD` loaded from Docker secret in monitoring.
- Uptime Kuma basic-auth password loaded via `password_file` in Prometheus config.
- Core stack secret values injected via env substitution (for example Authelia and CrowdSec values).
## Commit safety rules
Never commit:
- `secrets/stack-secrets.env`
- `secrets/*.txt` real secret files
- real Terraform `.tfvars` containing credentials
- Terraform state files with sensitive runtime metadata
## Related docs
- [DEPLOYMENT.md](DEPLOYMENT.md)
- [docs/source-of-truth.md](docs/source-of-truth.md)
@@ -44,6 +44,6 @@ If migrating from older states where secrets were committed, rotate upstream val
## Related docs
- [SECURITY_SECRETS_INVENTORY.md](SECURITY_SECRETS_INVENTORY.md)
- [docs/docker-environment.md](docs/docker-environment.md)
- [docs/source-of-truth.md](docs/source-of-truth.md)
- [`./security-secrets.md`](./security-secrets.md)
- [`./docker-environment.md`](./docker-environment.md)
- [`./source-of-truth.md`](./source-of-truth.md)
+1 -1
View File
@@ -46,7 +46,7 @@ Use this when focused on one service family.
### Deployment prerequisites
Before runtime operations, follow [DEPLOYMENT.md](../DEPLOYMENT.md) to create required local secret files.
Before runtime operations, follow [deployment-prerequisites.md](deployment-prerequisites.md) to create required local secret files.
## What not to do
+2 -2
View File
@@ -17,8 +17,8 @@ This page explains where to find authoritative files quickly.
- `services-up.sh` — runtime composition entrypoint for multi-compose environment.
- `default-network.yml` — shared docker network definitions used across compose files.
- `default-environment.env` — non-secret default env values for compose rendering.
- `DEPLOYMENT.md` — prerequisite setup before runtime operations.
- `SECURITY_SECRETS_INVENTORY.md` — tracked secret-variable inventory and usage map.
- `docs/deployment-prerequisites.md` — prerequisite setup before runtime operations.
- `docs/security-secrets.md` — secrets documentation and inventory model.
## Terraform layout
+52
View File
@@ -0,0 +1,52 @@
# Security Secrets
## Overview
This page explains how secret material is organized in this repository and where to find both human-readable and machine-readable references.
For machine-readable inventory metadata, use [`../secrets/inventory.json`](../secrets/inventory.json).
## Scope and authority
- Canonical example template: [`../secrets/.env.secrets.example`](../secrets/.env.secrets.example)
- Runtime-loaded secret env file (local, non-committed): `../secrets/stack-secrets.env`
- Docker secret files (local, non-committed): `../secrets/*.txt`
Treat the example template as the canonical shape for expected environment variables.
## Secret material types
1. **Template variables in `.env.secrets.example`**
- Document expected variable names and usage expectations.
2. **Local runtime env file (`stack-secrets.env`)**
- Holds local runtime secret values loaded during compose rendering.
3. **Local Docker secret files (`*.txt`)**
- Hold password/token material consumed via `*_FILE` style configuration.
4. **Externally managed secret inputs**
- Some values are managed outside shared templates and provided through file mounts or environment substitution.
## Machine-readable inventory
- Primary automation source: [`../secrets/inventory.json`](../secrets/inventory.json)
- Human guidance source: this page
Automation should parse `secrets/inventory.json` directly rather than scraping Markdown tables.
## Setup and deployment prerequisites
Before running compose operations, follow [`./deployment-prerequisites.md`](./deployment-prerequisites.md).
## Commit safety rules
Never commit:
- `secrets/stack-secrets.env`
- real `secrets/*.txt` secret files
- real Terraform `.tfvars` files containing credentials
- Terraform state files with sensitive runtime metadata
## Related docs
- [`./deployment-prerequisites.md`](./deployment-prerequisites.md)
- [`./docker-environment.md`](./docker-environment.md)
- [`./source-of-truth.md`](./source-of-truth.md)
+1 -1
View File
@@ -9,7 +9,7 @@ This repository has multiple layers. Knowing the authority for each layer preven
| Application/runtime container composition | `services-up.sh` + Compose files under `core/`, `apps/`, `monitoring/` | What runs in the Docker environment and how services are wired. |
| Docker shared baseline inputs | `default-network.yml`, `default-environment.env`, `secrets/stack-secrets.env` | Shared network/env material applied during compose rendering. |
| Infrastructure inventory and reconciliation | Terraform under `infrastructure/terraform/` | Codified inventory of existing infrastructure and relationships, especially Proxmox VMs and selected Docker mirrors. |
| Secret policy and inventory | `SECURITY_SECRETS_INVENTORY.md` + local secret files in `secrets/` | What secrets exist and where they are expected. |
| Secret policy and inventory | `docs/security-secrets.md` + `secrets/inventory.json` + local secret files in `secrets/` | What secrets exist, where they are expected, and what automation should parse. |
## Practical meaning
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "authelia" {
restart = local.docker_containers["authelia"].restart_policy
labels = local.docker_containers["authelia"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "crowdsec" {
restart = local.docker_containers["crowdsec"].restart_policy
labels = local.docker_containers["crowdsec"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "docker_socket_proxy" {
restart = local.docker_containers["docker-socket-proxy"].restart_policy
labels = local.docker_containers["docker-socket-proxy"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "docker_update_exporter" {
restart = local.docker_containers["docker-update-exporter"].restart_policy
labels = local.docker_containers["docker-update-exporter"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "error_pages" {
restart = local.docker_containers["error-pages"].restart_policy
labels = local.docker_containers["error-pages"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "gitea" {
restart = local.docker_containers["gitea"].restart_policy
labels = local.docker_containers["gitea"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "gotify" {
restart = local.docker_containers["gotify"].restart_policy
labels = local.docker_containers["gotify"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "grafana" {
restart = local.docker_containers["grafana"].restart_policy
labels = local.docker_containers["grafana"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "gramps_redis" {
restart = local.docker_containers["gramps-redis"].restart_policy
labels = local.docker_containers["gramps-redis"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "gramps_web_celery" {
restart = local.docker_containers["gramps-web-celery"].restart_policy
labels = local.docker_containers["gramps-web-celery"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "gramps_web" {
restart = local.docker_containers["gramps-web"].restart_policy
labels = local.docker_containers["gramps-web"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "influxdb" {
restart = local.docker_containers["influxdb"].restart_policy
labels = local.docker_containers["influxdb"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "monitor_kuma" {
restart = local.docker_containers["monitor-kuma"].restart_policy
labels = local.docker_containers["monitor-kuma"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "mtls_bridge" {
restart = local.docker_containers["mtls-bridge"].restart_policy
labels = local.docker_containers["mtls-bridge"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "nextcloud_db" {
restart = local.docker_containers["nextcloud-db"].restart_policy
labels = local.docker_containers["nextcloud-db"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "nextcloud_redis" {
restart = local.docker_containers["nextcloud-redis"].restart_policy
labels = local.docker_containers["nextcloud-redis"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "nextcloud_webapp" {
restart = local.docker_containers["nextcloud-webapp"].restart_policy
labels = local.docker_containers["nextcloud-webapp"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "node_exporter" {
restart = local.docker_containers["node-exporter"].restart_policy
labels = local.docker_containers["node-exporter"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "node_red" {
restart = local.docker_containers["node-red"].restart_policy
labels = local.docker_containers["node-red"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "passbolt_db" {
restart = local.docker_containers["passbolt-db"].restart_policy
labels = local.docker_containers["passbolt-db"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "passbolt_webapp" {
restart = local.docker_containers["passbolt-webapp"].restart_policy
labels = local.docker_containers["passbolt-webapp"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "pihole_exporter" {
restart = local.docker_containers["pihole-exporter"].restart_policy
labels = local.docker_containers["pihole-exporter"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "portainer" {
restart = local.docker_containers["portainer"].restart_policy
labels = local.docker_containers["portainer"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "prometheus" {
restart = local.docker_containers["prometheus"].restart_policy
labels = local.docker_containers["prometheus"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
@@ -4,11 +4,11 @@ resource "docker_container" "searxng-webapp" {
restart = local.docker_containers["searxng-webapp"].restart_policy
labels = local.docker_containers["searxng-webapp"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,11 +4,11 @@ resource "docker_container" "telegraf" {
restart = local.docker_containers["telegraf"].restart_policy
labels = local.docker_containers["telegraf"].useful_labels
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+1 -1
View File
@@ -4,7 +4,6 @@ resource "docker_container" "traefik" {
restart = local.docker_containers["traefik"].restart_policy
labels = local.docker_containers["traefik"].useful_labels
network_mode = "core_traefik"
ports {
@@ -64,6 +63,7 @@ resource "docker_container" "traefik" {
lifecycle {
ignore_changes = [
env,
labels,
]
}
}
+152
View File
@@ -0,0 +1,152 @@
{
"scope_and_authority": {
"canonical_example_template": "secrets/.env.secrets.example",
"runtime_loaded_secret_env_file": "secrets/stack-secrets.env",
"docker_secret_files_pattern": "secrets/*.txt"
},
"env_template_variables": [
{
"variable": "NEXTCLOUD_DB_USER",
"used_by": "apps/nextcloud/docker-compose.yml",
"purpose": "Nextcloud database username (non-secret identifier but environment-specific)."
},
{
"variable": "NEXTCLOUD_ADMIN_USER",
"used_by": "apps/nextcloud/docker-compose.yml",
"purpose": "Initial Nextcloud admin username."
},
{
"variable": "NEXTCLOUD_SMTP_FROM_ADDRESS",
"used_by": "apps/nextcloud/docker-compose.yml",
"purpose": "SMTP sender local-part for outbound mail configuration."
},
{
"variable": "NEXTCLOUD_SMTP_DOMAIN",
"used_by": "apps/nextcloud/docker-compose.yml",
"purpose": "SMTP sender domain for outbound mail configuration."
},
{
"variable": "NEXTCLOUD_SMTP_NAME",
"used_by": "apps/nextcloud/docker-compose.yml",
"purpose": "SMTP display/sender name derived from address + domain in the example file."
},
{
"variable": "PASSBOLT_DB_NAME",
"used_by": "apps/passbolt/docker-compose.yml",
"purpose": "Passbolt database name."
},
{
"variable": "PASSBOLT_DB_USER",
"used_by": "apps/passbolt/docker-compose.yml",
"purpose": "Passbolt database username."
},
{
"variable": "PASSBOLT_GPG_SERVER_KEY_FINGERPRINT",
"used_by": "apps/passbolt/docker-compose.yml",
"purpose": "Passbolt server GPG key fingerprint."
},
{
"variable": "GRAMPSWEB_SECRET_KEY",
"used_by": "apps/gramps/docker-compose.yml",
"purpose": "Secret key used by Gramps Web for session/security signing."
},
{
"variable": "GRAMPSWEB_EMAIL_HOST_USER",
"used_by": "apps/gramps/docker-compose.yml",
"purpose": "SMTP username for Gramps outbound email."
},
{
"variable": "GRAMPSWEB_EMAIL_HOST_PASSWORD",
"used_by": "apps/gramps/docker-compose.yml",
"purpose": "SMTP password for Gramps outbound email."
},
{
"variable": "GOTIFY_DEFAULTUSER_NAME",
"used_by": "monitoring/gotify/docker-compose.yml",
"purpose": "Gotify default username."
},
{
"variable": "GOTIFY_DEFAULTUSER_PASS",
"used_by": "monitoring/gotify/docker-compose.yml",
"purpose": "Gotify default user password."
},
{
"variable": "INFLUXDB_INIT_USERNAME",
"used_by": "monitoring/prometheus/docker-compose.yml",
"purpose": "InfluxDB initial username."
},
{
"variable": "PIHOLE_PASSWORD",
"used_by": "monitoring/prometheus/docker-compose.yml",
"purpose": "Exporter auth / Pi-hole integration password."
}
],
"file_based_secrets": [
{
"path": "secrets/nextcloud_db_root_password.txt",
"purpose": "Nextcloud MariaDB root password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/nextcloud_db_password.txt",
"purpose": "Nextcloud MariaDB application user password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/nextcloud_admin_password.txt",
"purpose": "Initial Nextcloud admin password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/nextcloud_smtp_password.txt",
"purpose": "Nextcloud SMTP account password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/nextcloud_redis_password.txt",
"purpose": "Nextcloud Redis runtime password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/passbolt_db_password.txt",
"purpose": "Passbolt database user password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/influxdb_init_password.txt",
"purpose": "InfluxDB initialization password file.",
"managed_by": "local_file",
"committed": false
},
{
"path": "secrets/prometheus_kuma_basic_auth_password.txt",
"purpose": "Uptime Kuma Prometheus scrape basic-auth password file.",
"managed_by": "local_file",
"committed": false
}
],
"externally_managed_secrets": [
"Database/root passwords for Nextcloud, Passbolt, and supporting services are provided via Docker secret files.",
"Redis runtime password is loaded from a Docker secret file.",
"DOCKER_INFLUXDB_INIT_PASSWORD is loaded from a Docker secret in monitoring.",
"Uptime Kuma basic-auth password is loaded via password_file in Prometheus configuration.",
"Core stack secret values (for example Authelia and CrowdSec values) are injected via environment substitution."
],
"commit_safety_rules": [
"Never commit secrets/stack-secrets.env.",
"Never commit real secrets/*.txt files.",
"Never commit real Terraform .tfvars containing credentials.",
"Never commit Terraform state files with sensitive runtime metadata."
],
"related_docs": [
"docs/security-secrets.md",
"docs/deployment-prerequisites.md",
"docs/source-of-truth.md"
]
}