diff --git a/ReadMe.md b/ReadMe.md index 4a92019..5f2d89b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -72,6 +72,14 @@ This repository follows a strict Pull Request-based workflow to ensure changes a 5. **Merge**: Once the plan is approved, merge the Pull Request into `main`. 6. **Apply Changes**: The `run-apply.yml` action must be triggered manually from the GitHub Actions UI to apply the changes to production. This is a deliberate safety measure. +## ⚙️ Configuration + +This project is configured primarily through YAML files located in the `config/` directory. For detailed instructions on how to structure these files, please refer to the documentation in the `docs/` directory: + +* **Stack Configuration Guide**: [stack-config.md](docs/stack-config.md) +* **Service Configuration Guide**: [service-config.md) + + ## 🔐 Secrets Management Secrets are managed securely without ever being committed to the repository. diff --git a/config/stacks/karakeep.yaml b/config/stacks/karakeep.yaml index 34d3699..744ed6d 100644 --- a/config/stacks/karakeep.yaml +++ b/config/stacks/karakeep.yaml @@ -8,7 +8,7 @@ karakeep: networks: karakeep: internal: true - driver: "overlay" + driver: "bridge" services: karakeep: dns: diff --git a/docs/service-config.md b/docs/service-config.md new file mode 100644 index 0000000..41b0183 --- /dev/null +++ b/docs/service-config.md @@ -0,0 +1,72 @@ +## ⚙️ Standalone Service Definitions + +Standalone service definition files define individual services or global configuration parameters that are directly consumed by specific Terraform modules. These are distinct from the more structured "Stack Configuration Files" and are typically used for services that don't fit into a multi-service stack pattern. + +### File Location + +All standalone service definition files must be placed in the `config/services/` directory. Each file should have a `.yaml` extension. Terraform will merge all top-level keys from all `.yaml` files found in this directory into a single `local.services` object. + +### Structure + +Each service definition file should define a top-level key that represents a unique identifier for the service or configuration. Under this key, you define the specific parameters for that service. + +```yaml +your_service_identifier: # e.g., flaresolverr, deluge-vpn + # Required: The canonical name of the service (used for Docker container name, etc.) + service_name: "MyStandaloneService" + # Required: The Docker image name and tag + image_name: "myrepo/my-standalone-service:latest" + # Required: The static IP address assigned to the container on its primary bridge network (e.g., br1) + ip_address: "192.168.5.X" + # Optional: The port the service listens on inside the container (used for web_ui, etc.) + service_port: 8080 + # Optional: List of environment variables in "KEY=VALUE" format + env: + - "TZ=America/Chicago" + - "SOME_VAR=some_value" + # Optional: List of bind mounts in "host_path:container_path[:ro]" format + mounts: + - "/mnt/user/data:/data" + - "/etc/localtime:/etc/localtime:ro" + # Optional: Linux capabilities to add to the container + capabilities: + add: + - "NET_ADMIN" + - "SYS_PTRACE" + drop: + - "MKNOD" + - "SETUID" + # Optional: URL to an icon for the service (e.g., for Unraid UI) + icon: "https://example.com/icon.png" + # Optional: URL for the service's web UI (if not automatically derived) + web_ui: "http://my-service.local:8080" + # Other optional parameters that can be passed to the underlying 'docker' module: + # container_restart: "unless-stopped" # Default: "unless-stopped" + # container_user: "1000:100" # Default: null + # container_ports: [] # List of objects {internal=number, external=optional(number), ...} + # container_volumes: [] # List of objects for Docker-managed volumes + # container_dns_servers: ["8.8.8.8"] # Default: ["8.8.8.8", "1.1.1.1"] + # container_privileged_mode: false # Default: false + # container_network_mode: "bridge" # Default: null + +# Example of a non-Docker configuration: +authentik: + admin-user: "your_authentik_admin_username" +``` + +### Field Meanings and Usage + +* **`your_service_identifier`**: This is the top-level key in your YAML file (e.g., `flaresolverr`, `deluge-vpn`). It acts as a unique identifier for this specific service configuration within the `local.services` map. +* **`service_name`**: (Required) The human-readable name of the service, often used as the Docker container name. +* **`image_name`**: (Required) The Docker image to pull, including its tag (e.g., `binhex/arch-delugevpn:2.2`). +* **`ip_address`**: (Required) The static IPv4 address assigned to the container on its primary network interface (e.g., `br1`). +* **`service_port`**: (Optional) The port the service listens on *inside* the container. This is used by modules that need to construct URLs or proxy configurations. +* **`env`**: (Optional) A list of `KEY=VALUE` strings that will be set as environment variables inside the container. +* **`mounts`**: (Optional) A list of bind mount strings in the format `host_path:container_path[:ro]`. +* **`capabilities.add`**: (Optional) A list of Linux capabilities to add to the container (e.g., `NET_ADMIN`). +* **`icon`**: (Optional) A URL to an icon for the service, often used by UI tools like Unraid. +* **`web_ui`**: (Optional) A direct URL to the service's web interface. If not provided, some modules might attempt to construct one from `ip_address` and `service_port`. + +### Special Cases + +* **`authentik`**: This top-level key in `config/services.yaml` is not a Docker service definition. Instead, it holds global configuration parameters for your Authentik instance (e.g., `admin-user`) that are consumed by other Terraform modules. \ No newline at end of file diff --git a/docs/stack-config.md b/docs/stack-config.md new file mode 100644 index 0000000..7bf22ed --- /dev/null +++ b/docs/stack-config.md @@ -0,0 +1,141 @@ + +## ⚙️ Stack Configuration Files + +Stack configuration files define groups of related services and their specific settings. These files are written in YAML and are automatically discovered and loaded by Terraform. + +### File Location + +All stack configuration files must be placed in the `config/stacks/` directory. Each file should have a `.yaml` extension. Terraform will merge all `.yaml` files found in this directory into a single configuration object. + +### Structure + +Each stack configuration file should define a top-level key that represents the name of the stack (e.g., `karakeep`, `arr_services`). Under this key, you define global settings for the stack and a list of `services`. + +```yaml +your_stack_name: + # Optional: Global environment variables for all services in this stack + env: + - "GLOBAL_ENV_VAR=value" + # Optional: Global bind mounts for all services in this stack + mounts: + - "/path/on/host:/path/in/container" + # Optional: Global Docker volumes for all services in this stack + volumes: + - "my_volume:/data" + # Optional: List of secret names to be generated by Terraform for this stack + generated_secrets: + - "API_KEY" + - "DATABASE_PASSWORD" + # Optional: DNS zone name for services in this stack (overrides global if set) + zone_name: "yourdomain.com" + # Optional: Custom Docker networks to be created for this stack + networks: + my_custom_network: + internal: true + driver: "bridge" + options: {} # Optional: Driver options for the network + # Required: A map of services to be deployed within this stack + services: + service_one_key: # Unique key for the service within this stack + # Required: Display name of the service + service_name: "My Awesome Service" + # Required: Docker image name and tag + image_name: "myrepo/my-service:latest" + # Optional: Description of the service + description: "A brief description of what this service does." + # Optional: URL to an icon for the service (e.g., for Unraid UI) + icon: "https://example.com/icon.png" + # Optional: Service-specific environment variables + env: + - "SERVICE_ENV_VAR=value" + - "SECRET_VAR=${API_KEY}" # References a generated secret + # Optional: Service-specific bind mounts + mounts: + - "/path/on/host:/path/in/container" + # Optional: Service-specific Docker volumes + volumes: [] # List of volume objects (see docker module variables for structure) + # Optional: Linux capabilities to add/drop for the container + capabilities: + add: ["NET_ADMIN"] + drop: ["SYS_ADMIN"] + # Optional: Network configuration for the service + network: + # Optional: Is this service internal-only (no external proxy)? Default: false + internal: false + # Optional: The port the service listens on inside the container + service_port: 8080 + # Optional: Static IP address for the container on br0/br1 + ip_address: "192.168.5.100" + # Optional: List of networks to attach the container to (e.g., "br1", "my_custom_network") + networks: + - "br1" + - "my_custom_network" + # Optional: DNS configuration for the service + dns: + # Optional: Enable DNS record and proxy host creation. Default: false + enabled: true + # Optional: Is this DNS record/proxy internal-only? Default: true + internal: false + # Optional: The domain name for this service (e.g., "service.yourdomain.com") + domain_name: "service.yourdomain.com" + # Optional: Nginx Proxy Manager Access List ID for this service + # If not set, defaults to "Internal Only" for internal services, "CloudFlare Only" for external. + access_list_id: "your_custom_access_list_id" + # Optional: Authentication configuration for the service + auth: + # Optional: Enable authentication for this service. Default: false + enabled: true + # Optional: Enable proxy authentication (e.g., Authentik forward auth). Default: false + proxy: true + # Optional: OAuth configuration for the service + oauth: + # Optional: Enable OAuth authentication. Default: false + enabled: true + # Optional: Authentik group for this application. Default: "Uncategorized" + group: "My App Group" + # Optional: Map of environment variable names to Authentik OAuth output keys. + # The key is the desired environment variable name (e.g., "OAUTH_CLIENT_ID"). + # The value is the Authentik OAuth provider output attribute name (e.g., "client_id", "client_secret", "provider_info_url"). + keys: + OAUTH_CLIENT_ID: "client_id" + OAUTH_CLIENT_SECRET: "client_secret" + OAUTH_WELL_KNOWN_URL: "provider_info_url" + # Optional: List of OAuth scopes to request. + scopes: + - "openid" + - "profile" + - "email" + # Optional: Additional redirect URIs for the OAuth provider. + # These are appended to the base domain redirect URI. + redirect_uris: + - "/oauth/callback" + - "/login/oauth/callback" + + service_two_key: # Another service definition... + # ... +``` + +### Field Meanings and Usage + +* **`env` (Stack/Service Level)**: A list of `KEY=VALUE` strings for environment variables. Service-level `env` is merged with stack-level `env`. Values like `${SECRET_NAME}` will be replaced by dynamically generated secrets. +* **`mounts` (Stack/Service Level)**: A list of bind mount strings in `host_path:container_path[:ro]`. Service-level `mounts` are merged with stack-level `mounts`. +* **`volumes` (Stack/Service Level)**: A list of Docker volume configurations. +* **`generated_secrets`**: A list of string names (e.g., `"API_KEY"`) for secrets that Terraform should generate using `openssl rand -base64 36` (or similar) and inject into environment variables. +* **`zone_name`**: The DNS zone (e.g., `yourdomain.com`) under which service domains will be created. +* **`networks` (Stack Level)**: Defines custom Docker networks to be created for this stack. These are separate from `br0` and `br1`. +* **`services`**: The core of the stack, defining individual Docker containers. + * **`service_name`**: The name of the Docker container and the base for Authentik application names. + * **`image_name`**: The Docker image to pull (e.g., `linuxserver/sonarr:latest`). + * **`network.internal`**: If `true`, the service is not exposed externally via Nginx Proxy Manager. + * **`network.service_port`**: The port the service listens on *inside* the container. + * **`network.ip_address`**: A static IP address to assign to the container on `br0` or `br1` if specified in `network.networks`. + * **`network.networks`**: A list of Docker networks to attach the container to. This can include `br0`, `br1`, or custom networks defined in `stack.networks`. + * **`dns.enabled`**: If `true`, Terraform will create a DNS record and an Nginx Proxy Manager host for this service. + * **`dns.domain_name`**: The full domain name for the service (e.g., `sonarr.yourdomain.com`). + * **`auth.enabled`**: If `true`, authentication is configured for this service. + * **`auth.proxy`**: If `true`, Nginx Proxy Manager will forward authentication requests to Authentik. The `service_port` for the DNS record will automatically point to Authentik's port. + * **`auth.oauth.enabled`**: If `true`, an Authentik OAuth2 provider and application are created for this service. + * **`auth.oauth.group`**: The Authentik group to associate with the OAuth application. + * **`auth.oauth.keys`**: Maps desired environment variable names (e.g., `OAUTH_CLIENT_ID`) to Authentik OAuth provider output attributes (e.g., `client_id`, `client_secret`, `provider_info_url`). + * **`auth.oauth.scopes`**: OAuth scopes to request from Authentik. + * **`auth.oauth.redirect_uris`**: Additional relative paths (e.g., `/oauth/callback`) that will be appended to the service's domain name to form valid OAuth redirect URIs. \ No newline at end of file diff --git a/terraform/modules/docker-stack/main.tf b/terraform/modules/docker-stack/main.tf index b6fdc26..4312ef3 100644 --- a/terraform/modules/docker-stack/main.tf +++ b/terraform/modules/docker-stack/main.tf @@ -5,6 +5,7 @@ resource "random_password" "generated" { special = true } + module "custom_network" { count = length(local.creatable_networks) > 0 ? 1 : 0 source = "../../modules/docker-network"