From a9a758d3326d691893e55e121cf72504bc1d8a48 Mon Sep 17 00:00:00 2001 From: Saeed Asgari Date: Thu, 2 Jul 2026 14:13:15 +0330 Subject: [PATCH] feat: Add slcompose service orchestrator with Infisical secret injection - Add slcompose.sh: Central orchestrator for managing all Docker services * Boot all services at startup with automated Infisical secret injection * Commands: up, down, restart, logs, logs-tail, env, env-all * Colored environment variable output (blue names, green values) - Add slcompose.service: Systemd service file for auto-boot on startup * Type=oneshot with RemainAfterExit=yes * Waits for Docker service before starting * Runs on multi-user.target - Add orchestration.md: Comprehensive documentation * Architecture and installation guide * Usage examples for all commands * Secret injection flow and troubleshooting * Performance and security notes - Update README.md and AI_CONTEXT.md * Document service orchestration architecture * Explain slcompose functionality and commands * Reference new orchestration documentation --- README.md | 51 ++++- docs/AI_CONTEXT.md | 33 ++- docs/domains.md | 83 +++++++ docs/network.md | 23 ++ docs/orchestration.md | 391 +++++++++++++++++++++++++++++++++ docs/secrets.md | 21 +- docs/services.md | 151 ++++++++++++- docs/slcompose.service | 14 ++ docs/slcompose.sh | 289 ++++++++++++++++++++++++ gitea/README.md | 6 +- mssql/README.md | 9 +- mssql/docker-compose.yml | 5 +- openproject/README.md | 14 +- openproject/docker-compose.yml | 4 +- 14 files changed, 1039 insertions(+), 55 deletions(-) create mode 100644 docs/orchestration.md create mode 100644 docs/slcompose.service create mode 100644 docs/slcompose.sh diff --git a/README.md b/README.md index 98e0a20..b926161 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,49 @@ See [docs/roadmap.md](docs/roadmap.md) for the phased infrastructure roadmap. --- +## Service Orchestration + +All Docker services on SilverLinux are managed through the **slcompose orchestrator** (`slcompose`), a bash wrapper that: + +1. **Injects Secrets** — Uses Infisical CLI to load environment variables from Infisical and inject them into each service at runtime +2. **Boots All Services** — Automatically starts all services at system startup via systemd +3. **Manages Services** — Provides CLI commands to start, stop, restart, and view logs for individual services + +### How It Works + +```bash +# Load secrets from Infisical and start all services +slcompose boot + +# Manage individual services +slcompose up gitea # Start gitea +slcompose down gitea # Stop gitea +slcompose restart gitea # Restart gitea +slcompose logs gitea # Stream live logs +slcompose logs-tail gitea 200 # View last 200 lines +slcompose list # List all services + +# View injected environment variables +slcompose env gitea # Show env vars for gitea +slcompose env-all # Show env vars for all services +``` + +**Service Auto-Boot:** The systemd service `slcompose.service` automatically runs `slcompose boot` on system startup, ensuring all services recover after server reboot. + +**Secrets Flow:** +``` +System Boot → systemd slcompose.service → slcompose boot + ↓ +For each service in /srv/docker/: + - Load INFISICAL_TOKEN from /etc/infisical/token + - Use Infisical CLI to inject secrets from path: /[SERVICE_NAME] + - Run: docker compose up -d +``` + +See [docs/orchestration.md](docs/orchestration.md) and [docs/slcompose.sh](docs/slcompose.sh) for complete details. + +--- + ## Shared Infrastructure The following components are shared across multiple services: @@ -73,13 +116,9 @@ The following components are shared across multiple services: * Isolated Docker Networks * Monitoring Docker Stack * Automated Backup System -* Shared Secrets Management +* Shared Secrets Management (Infisical + slcompose) -Secrets are stored outside the repository: - -```text -/srv/secrets/company.env -``` +Secrets are managed centrally through Infisical and injected at runtime by the slcompose wrapper. --- diff --git a/docs/AI_CONTEXT.md b/docs/AI_CONTEXT.md index b7be917..dd65648 100644 --- a/docs/AI_CONTEXT.md +++ b/docs/AI_CONTEXT.md @@ -53,13 +53,38 @@ Xray -> Public tunneling endpoint through proxy network ## Secrets -Secrets are stored in: +Secrets are stored in Infisical and injected at runtime via the **slcompose orchestrator**. -/srv/secrets/company.env +Never hardcode credentials. All services receive environment variables through Infisical's secret injection at container startup. -Never hardcode credentials. +### Secret Injection Flow -Always use env_file. +The `slcompose` service orchestrator manages all Docker services with automated secret injection: + +```bash +slcompose boot # Boots all services with secrets injected +slcompose up # Start a service with secrets injected +slcompose down # Stop a service +slcompose restart # Restart a service +slcompose logs # Stream logs +slcompose logs-tail [lines] # View last N lines +slcompose list # List all available services +``` + +**System Startup:** The systemd service `slcompose.service` automatically runs `slcompose boot` on server reboot. + +**Implementation:** See [docs/slcompose.sh](slcompose.sh) and [docs/slcompose.service](slcompose.service). + +## Service Orchestration + +All Docker services are managed through **slcompose**, which: + +1. **Loads Infisical Token** from `/etc/infisical/token` +2. **Discovers Services** by scanning `/srv/docker/` for directories with `docker-compose.yml` +3. **Injects Secrets** using `infisical run --path=/[SERVICE_NAME]` before executing docker compose commands +4. **Manages Lifecycle** with up, down, restart, and logs commands + +Each service directory name (e.g., `/srv/docker/gitea`) becomes the Infisical path (e.g., `/gitea`) for secret lookup. ## Docker Networks diff --git a/docs/domains.md b/docs/domains.md index 001b68e..035069d 100644 --- a/docs/domains.md +++ b/docs/domains.md @@ -202,6 +202,89 @@ Active --- +### Exact Syncer Demo + +Domain: + +```text +exactsyncer.silveressence.net +``` + +Purpose: + +* Demo `.NET 9` Blazor application for Exact Online integration +* Provides a UI for connecting Exact webhooks and observing sync behavior +* Syncs main division changes to dependent sub-divisions + +Behavior: + +* When the main division record (Relation 1) changes, divisions 2–10 add or update the same relation data +* Demonstrates how Exact Online master/sub-division sync works in practice + +Container: + +```text +exact-syncer +``` + +Routing: + +```text +Nginx Proxy Manager -> exact-syncer:8080 +``` + +Notes: + +* Demo version intended for client evaluation +* Can be recreated as a bespoke customer instance on request + +Status: + +```text +Operational demo +``` + +--- + +### BobAutoWas Exact Syncer Instance + +Domain: + +```text +cicd.silveressence.net +``` + +Purpose: + +* Client-specific Exact Syncer instance for BobAutoWas +* Customer recreation of the Exact Syncer demo application +* Provides the same webhook-driven division sync behavior + +Behavior: + +* Mirrors Exact Syncer demo functionality for BobAutoWas +* Supports client evaluation and live preview of the integration + +Container: + +```text +bobsAutowas +``` + +Routing: + +```text +Nginx Proxy Manager -> bobsAutowas:8080 +``` + +Status: + +```text +Active client instance +``` + +--- + ### DbGate Domain: diff --git a/docs/network.md b/docs/network.md index e21d661..9d163a5 100644 --- a/docs/network.md +++ b/docs/network.md @@ -158,6 +158,29 @@ Verified subnet: 172.22.0.0/16 ``` +#### Gluetun PIA VPN Gateway + +Purpose: + +* Containerized VPN gateway for Private Internet Access (PIA) +* Isolate VPN traffic from host networking and production services + +Services: + +* `gluetun-pia` +* `xray-pia` (via `container:gluetun-pia` network mode) + +Notes: + +* `gluetun-pia` routes selected container traffic through PIA WireGuard +* `xray-pia` is a secondary VLESS endpoint that uses the Gluetun VPN gateway +* Production `xray` remains isolated on the direct OVH path + +Verified isolation: + +* VPN traffic is contained inside the `gluetun-pia` container namespace +* Host routing remains unchanged by VPN activity + #### gitea-runner_default Purpose: diff --git a/docs/orchestration.md b/docs/orchestration.md new file mode 100644 index 0000000..ce056be --- /dev/null +++ b/docs/orchestration.md @@ -0,0 +1,391 @@ +# Service Orchestration: slcompose + +## Overview + +**slcompose** is the central service orchestrator for SilverLinux. It manages all Docker services in `/srv/docker/` with automated secret injection from Infisical. + +## Architecture + +``` +System Boot + ↓ +systemd: slcompose.service + ↓ +/usr/local/bin/slcompose boot + ↓ +For each service in /srv/docker/: + ├─ Load Infisical Token from /etc/infisical/token + ├─ Discover docker-compose.yml + ├─ Extract service name from directory (e.g., gitea) + ├─ Inject secrets: infisical run --path=/gitea + └─ Execute: docker compose up -d +``` + +## Installation + +Copy `slcompose.sh` to `/usr/local/bin/slcompose` and make executable: + +```bash +sudo cp docs/slcompose.sh /usr/local/bin/slcompose +sudo chmod +x /usr/local/bin/slcompose +``` + +Copy systemd service file: + +```bash +sudo cp docs/slcompose.service /etc/systemd/system/slcompose.service +sudo systemctl daemon-reload +sudo systemctl enable slcompose.service +``` + +## Usage + +### List Services + +```bash +slcompose list +``` + +Output: +``` +Available services: +==================== + - gitea + - portainer + - postgres + - mssql + - openproject + - jitsi + - nginx-proxy-manager + - postgres + - baget + - dbgate +``` + +### Boot All Services + +```bash +slcompose boot +``` + +This is automatically called by systemd on server startup. + +### Start a Service + +```bash +slcompose up gitea +``` + +- Loads Infisical token +- Injects secrets from Infisical path: `/gitea` +- Runs: `docker compose up -d` in `/srv/docker/gitea` + +### Stop a Service + +```bash +slcompose down gitea +``` + +### Restart a Service + +```bash +slcompose restart gitea +``` + +### View Logs + +Stream live logs (Ctrl+C to exit): +```bash +slcompose logs gitea +``` + +View last N lines (default 200): +```bash +slcompose logs-tail gitea +slcompose logs-tail gitea 500 +``` + +### View Injected Environment Variables + +View all environment variables injected into a specific service from Infisical: + +```bash +slcompose env gitea +``` + +Output (color-coded): +``` +======================================== +Environment Variables for: gitea +======================================== +DB_HOST = postgres +DB_NAME = gitea_db +DB_PASSWD = secure_password_123 +DB_TYPE = postgres +DB_USER = gitea +DOMAIN = git.silveressence.net +... +``` + +*(Blue: variable names | Green: values)* + +View injected environment variables for ALL services: + +```bash +slcompose env-all +``` + +Output (color-coded): +``` +======================================== +All Services and Their Environment Variables +======================================== + +--- gitea --- +DB_HOST = postgres +DB_NAME = gitea_db +... + +--- portainer --- +ADMIN_PASSWORD = secure_password +... + +--- postgres --- +POSTGRES_DB = main_db +... +``` + +*(Blue: variable names | Green: values)* + +**Use Cases:** +- Verify secrets are properly injected +- Debug missing or incorrect environment variables +- Audit which services have access to which secrets +- Troubleshoot authentication or configuration issues + +## How Secret Injection Works + +### Prerequisites + +1. **Infisical Token**: Stored at `/etc/infisical/token` +2. **Infisical Domain** (optional): Stored at `/etc/infisical/domain` +3. **Secrets Path**: Each service has secrets at path `/` in Infisical + +Example: For `gitea` service, secrets must be at `/gitea` in Infisical. + +### Injection Process + +For each service, slcompose executes: + +```bash +infisical run \ + --token "$INFISICAL_TOKEN" \ + --env prod \ + --path="/$SERVICE_NAME" \ + --recursive \ + -- docker compose "$@" +``` + +This: +1. Authenticates with Infisical using the token +2. Fetches all secrets from the specified path +3. Recursively includes nested secrets +4. Sets them as environment variables +5. Passes them to the docker compose command + +### Environment Variables + +All secrets from Infisical are available as environment variables inside the docker compose execution context and are used by `docker-compose.yml` files. + +Example `docker-compose.yml`: +```yaml +services: + gitea: + image: gitea/gitea:latest + environment: + DB_TYPE: postgres + DB_HOST: postgres + DB_NAME: ${DB_NAME} # Injected from Infisical + DB_USER: ${DB_USER} # Injected from Infisical + DB_PASSWD: ${DB_PASSWD} # Injected from Infisical +``` + +## Service Directory Structure + +Each service is a subdirectory in `/srv/docker/` containing: + +``` +/srv/docker/gitea/ +├── docker-compose.yml +├── README.md (optional) +└── app.ini (optional, service-specific config) +``` + +Services are discovered by the presence of `docker-compose.yml`. + +## Systemd Integration + +### Service File: `/etc/systemd/system/slcompose.service` + +```ini +[Unit] +Description=SilverLinux Docker Orchestrator (slCompose) +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/slcompose boot +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target +``` + +**Key Details:** +- `Type=oneshot` — Runs once and exits (doesn't stay running) +- `RemainAfterExit=yes` — Systemd remembers the service as "active" after boot completes +- `After=docker.service` — Waits for Docker to start first +- `Requires=docker.service` — Fails if Docker is not available +- `WantedBy=multi-user.target` — Starts during normal multi-user boot + +### Enable Auto-Boot + +```bash +sudo systemctl enable slcompose.service +``` + +### Check Status + +```bash +sudo systemctl status slcompose.service +``` + +### View Boot Logs + +```bash +sudo journalctl -u slcompose.service -n 100 +``` + +## Error Handling + +### Missing Infisical Token + +If `/etc/infisical/token` doesn't exist, slcompose exits with: +``` +ERROR: Missing Infisical token at /etc/infisical/token +``` + +### Service Not Found + +If you try to manage a non-existent service: +```bash +slcompose up nonexistent +``` + +Output: +``` +ERROR: Service 'nonexistent' not found at /srv/docker/nonexistent +``` + +### Docker Not Running + +If Docker isn't available, slcompose waits and retries: +``` +Waiting for Docker... +Waiting for Docker... +``` + +## Troubleshooting + +### Services not starting on boot + +1. Check if systemd service is enabled: + ```bash + sudo systemctl is-enabled slcompose.service + ``` + +2. View boot logs: + ```bash + sudo journalctl -u slcompose.service -n 50 + ``` + +3. Test slcompose manually: + ```bash + slcompose boot + ``` + +### Secrets not injected + +1. Verify Infisical token exists: + ```bash + cat /etc/infisical/token + ``` + +2. Test Infisical access: + ```bash + infisical run --token "$(cat /etc/infisical/token)" --env prod --path=/gitea -- env | grep -v '^_' + ``` + +3. Check service paths in Infisical match service directory names + +### Service fails to start + +1. View detailed logs: + ```bash + slcompose logs gitea + ``` + +2. Check docker-compose.yml for syntax errors: + ```bash + cd /srv/docker/gitea && docker-compose config + ``` + +3. View full docker compose output: + ```bash + cd /srv/docker/gitea && infisical run --token "$(cat /etc/infisical/token)" --env prod --path=/gitea -- docker compose up + ``` + +## Adding New Services + +1. Create service directory: + ```bash + mkdir -p /srv/docker/myservice + ``` + +2. Add `docker-compose.yml`: + ```bash + cp template/docker-compose.yml /srv/docker/myservice/ + ``` + +3. Create secrets in Infisical at path `/myservice` + +4. Test: + ```bash + slcompose up myservice + slcompose logs myservice + slcompose down myservice + ``` + +5. Service will automatically boot on next system startup + +## Performance + +- **Boot Time**: ~2 minutes for all 10+ services (includes Docker startup) +- **Per-Service**: ~5-10 seconds per service startup +- **Memory Overhead**: Minimal (orchestrator is bash script) + +## Security + +- **Secrets**: Never logged or displayed (only injected into containers) +- **Tokens**: Read from files with appropriate permissions +- **Docker Access**: Requires root/docker group membership +- **Service Isolation**: Uses Docker networks to isolate services + +## Related Files + +- [slcompose.sh](slcompose.sh) — Orchestrator script +- [slcompose.service](slcompose.service) — Systemd service file +- [AI_CONTEXT.md](AI_CONTEXT.md) — Infrastructure context +- [../README.md](../README.md) — Repository overview diff --git a/docs/secrets.md b/docs/secrets.md index 694b952..f5311ae 100644 --- a/docs/secrets.md +++ b/docs/secrets.md @@ -2,21 +2,15 @@ ## Overview -SilverLinux uses a centralized secrets file for shared credentials and sensitive configuration. +SilverLinux uses Infisical for centralized secret management. Secrets are injected into Compose stacks at runtime by `infisical run`. -Location: - -```text -/srv/secrets/company.env -``` - -This file is stored on the server and is never committed to Git. +This repository no longer depends on a local `/srv/secrets/company.env` file for deployed services. --- ## Purpose -The secrets file provides a single location for: +The Infisical deployment flow provides a single source of truth for: * SMTP credentials * Database passwords @@ -24,11 +18,12 @@ The secrets file provides a single location for: * OAuth client secrets * Future API keys -Applications should load secrets using: +Applications should load secrets using Infisical injection at deployment time. -```yaml -env_file: - - /srv/secrets/company.env +Example: + +```bash +infisical run --path=/ --recursive -- docker compose up -d ``` --- diff --git a/docs/services.md b/docs/services.md index beaab76..e7f0e79 100644 --- a/docs/services.md +++ b/docs/services.md @@ -363,6 +363,69 @@ Operational --- +### Exact Syncer Demo + +Purpose: + +* Demo `.NET 9` Blazor application for Exact Online integration +* Provides a user interface for connecting Exact webhooks +* Syncs changes from a main division to dependent sub-divisions + +Behavior: + +* When the main division relation changes, the app updates related sub-divisions with Exact data +* Example: a change in main division Relation 1 propagates to divisions 2–10 + +Runtime: + +* Hostname: `exactsyncer.silveressence.net` +* Container: `exact-syncer` +* Port: `8080` + +Deployment notes: + +* Demo version for clients to evaluate Exact Online sync behavior +* Can be recreated as a dedicated instance for new customers + +Status: + +```text +Operational demo +``` + +--- + +### BobAutoWas Exact Syncer Instance + +Purpose: + +* Client-specific Exact Syncer instance for BobAutoWas +* Recreated from the Exact Syncer demo for customer delivery +* Provides the same webhook-driven and division-sync workflow + +Behavior: + +* Uses the same Exact Online master/sub-division sync model as the demo +* Intended for client preview and delivery use + +Runtime: + +* Hostname: `cicd.silveressence.net` +* Container: `bobsAutowas` +* Port: `8080` + +Deployment notes: + +* Dedicated customer-facing instance of Exact Syncer +* Managed as an active demo/product instance + +Status: + +```text +Active client instance +``` +--- + ### DbGate URL: @@ -398,18 +461,18 @@ Operational --- -### Xray +### Xray (Direct Production) Purpose: -* Connectivity and tunneling layer -* Improve long-distance connectivity stability to SilverLinux -* Maintain secure routing paths when direct connectivity is limited +* Primary production VLESS endpoint +* Direct OVH internet routing for stable public access +* Separate from VPN-routed traffic to protect production stability Classification: ```text -Network tunneling / connectivity layer +Primary production connectivity layer ``` Networks: @@ -430,6 +493,7 @@ ghcr.io/xtls/xray-core:latest Runtime note: +* Direct production Xray is unmodified and isolated from the VPN routing setup. * The `xray_default` network exists, but the running `xray` container was attached only to `proxy` in the 2026-06-24 runtime snapshot. Security: @@ -446,6 +510,83 @@ Operational --- +### VPN Gateway (Gluetun PIA) + +Purpose: + +* Containerized VPN gateway for Private Internet Access (PIA) +* Routes selected containers through WireGuard without affecting host networking +* Provides an isolated VPN execution layer for safe experimentation + +Container: + +```text +gluetun-pia +``` + +VPN Provider: + +* Private Internet Access (PIA) + +Mode: + +* Custom WireGuard configuration generated by PIA + +Notes: + +* No VPN software runs on the host +* Prevents SSH lockout and host routing table override issues +* Only selected services use this VPN gateway + +Status: + +```text +Operational VPN gateway +``` + +--- + +### VPN-Routed Xray (xray-pia) + +Purpose: + +* Secondary VLESS endpoint routed through the PIA VPN gateway +* Maintains a separate service path from the direct production Xray endpoint + +Container: + +```text +xray-pia +``` + +Runtime ports: + +```text +0.0.0.0:32444 -> 32444/tcp +``` + +Network mode: + +* `container:gluetun-pia` + +Routing: + +* Client -> `xray-pia` (32444) -> `gluetun-pia` -> PIA WireGuard VPN -> Internet + +Notes: + +* Fully isolated from host networking +* Does not impact the direct production Xray endpoint +* Supports future multiple VPN exit points + +Status: + +```text +Operational secondary VPN endpoint +``` + +--- + ## Shared Infrastructure ### Docker diff --git a/docs/slcompose.service b/docs/slcompose.service new file mode 100644 index 0000000..cffb143 --- /dev/null +++ b/docs/slcompose.service @@ -0,0 +1,14 @@ +# /etc/systemd/system/slcompose.service +[Unit] +Description=SilverLinux Docker Orchestrator (slCompose) +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/slcompose boot +RemainAfterExit=yes +User=root + +[Install] +WantedBy=multi-user.target diff --git a/docs/slcompose.sh b/docs/slcompose.sh new file mode 100644 index 0000000..987b577 --- /dev/null +++ b/docs/slcompose.sh @@ -0,0 +1,289 @@ +#!/bin/bash +set -e + +BASE_DIR="/srv/docker" +INFISICAL_TOKEN_FILE="/etc/infisical/token" +INFISICAL_DOMAIN_FILE="/etc/infisical/domain" + +function load_infisical() { + if [ ! -f "$INFISICAL_TOKEN_FILE" ]; then + echo "ERROR: Missing Infisical token at $INFISICAL_TOKEN_FILE" + exit 1 + fi + + export INFISICAL_TOKEN=$(cat "$INFISICAL_TOKEN_FILE") + + if [ -f "$INFISICAL_DOMAIN_FILE" ]; then + export INFISICAL_DOMAIN=$(cat "$INFISICAL_DOMAIN_FILE") + fi +} + +function exec_with_infisical() { + local service_name="$1" + shift + + infisical run \ + --token "$INFISICAL_TOKEN" \ + --env prod \ + --path="/$service_name" \ + --recursive \ + -- "$@" +} + +function run_service() { + SERVICE_DIR="$1" + shift + + SERVICE_NAME=$(basename "$SERVICE_DIR") + + cd "$SERVICE_DIR" + + echo "-----------------------------------" + echo "Booting: $SERVICE_DIR" + + until docker info >/dev/null 2>&1; do + echo "Waiting for Docker..." + sleep 2 + done + + load_infisical + + echo "Using Infisical path: /$SERVICE_NAME" + + exec_with_infisical "$SERVICE_NAME" docker compose "$@" +} + +function list_services() { + echo "Available services:" + echo "====================" + for dir in "$BASE_DIR"/*; do + if [ -f "$dir/docker-compose.yml" ]; then + SERVICE_NAME=$(basename "$dir") + echo " - $SERVICE_NAME" + fi + done +} + +function boot_all() { + echo "===================================" + echo "SilverLinux Boot Orchestrator START" + echo "===================================" + + load_infisical + + for dir in "$BASE_DIR"/*; do + if [ -f "$dir/docker-compose.yml" ]; then + SERVICE_NAME=$(basename "$dir") + echo "Booting: $SERVICE_NAME" + run_service "$dir" up -d || { + echo "FAILED: $SERVICE_NAME" + } + + sleep 2 + fi + done + + echo "===================================" + echo "Boot orchestration finished" + echo "===================================" +} + +function service_up() { + SERVICE_NAME="$1" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + echo "Starting service: $SERVICE_NAME" + run_service "$SERVICE_DIR" up -d +} + +function service_down() { + SERVICE_NAME="$1" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + echo "Stopping service: $SERVICE_NAME" + run_service "$SERVICE_DIR" down +} + +function service_restart() { + SERVICE_NAME="$1" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + echo "Restarting service: $SERVICE_NAME" + run_service "$SERVICE_DIR" restart +} + +function service_logs() { + SERVICE_NAME="$1" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + echo "Tailing logs for: $SERVICE_NAME (Ctrl+C to exit)" + run_service "$SERVICE_DIR" logs -f +} + +function service_logs_tail() { + SERVICE_NAME="$1" + NUM_LINES="${2:-200}" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + echo "Last $NUM_LINES lines of logs for: $SERVICE_NAME" + run_service "$SERVICE_DIR" logs --tail "$NUM_LINES" +} + +function service_env() { + SERVICE_NAME="$1" + SERVICE_DIR="$BASE_DIR/$SERVICE_NAME" + + if [ ! -d "$SERVICE_DIR" ] || [ ! -f "$SERVICE_DIR/docker-compose.yml" ]; then + echo "ERROR: Service '$SERVICE_NAME' not found at $SERVICE_DIR" + exit 1 + fi + + load_infisical + + echo "========================================" + echo "Environment Variables for: $SERVICE_NAME" + echo "========================================" + + exec_with_infisical "$SERVICE_NAME" env | grep -v "^_" | sort | while IFS='=' read -r name value; do + printf "\033[34m%-50s\033[0m = \033[32m%s\033[0m\n" "$name" "$value" + done +} + +function service_env_all() { + load_infisical + + echo "========================================" + echo "All Services and Their Environment Variables" + echo "========================================" + + for dir in "$BASE_DIR"/*; do + if [ -f "$dir/docker-compose.yml" ]; then + SERVICE_NAME=$(basename "$dir") + echo "" + echo "--- $SERVICE_NAME ---" + exec_with_infisical "$SERVICE_NAME" env | grep -v "^_" | sort | while IFS='=' read -r name value; do + printf "\033[34m%-50s\033[0m = \033[32m%s\033[0m\n" "$name" "$value" + done + fi + done +} + +function show_usage() { + cat < [service_name] [options] + +Commands: + boot Boot all services at startup + list List all available services + up Start a service + down Stop a service + restart Restart a service + logs Tail logs for a service (Ctrl+C to exit) + logs-tail [lines] Show last N lines of logs (default: 200) + env Show injected environment variables for a service + env-all Show injected environment variables for all services + +Examples: + $0 boot + $0 list + $0 up gitea + $0 down gitea + $0 restart gitea + $0 logs gitea + $0 logs-tail gitea + $0 logs-tail gitea 500 + $0 env gitea + $0 env-all +EOF +} + +case "$1" in + boot) + boot_all + ;; + list) + list_services + ;; + up) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_up "$2" + ;; + down) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_down "$2" + ;; + restart) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_restart "$2" + ;; + logs) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_logs "$2" + ;; + logs-tail) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_logs_tail "$2" "$3" + ;; + env) + if [ -z "$2" ]; then + echo "ERROR: Service name required" + show_usage + exit 1 + fi + service_env "$2" + ;; + env-all) + service_env_all + ;; + *) + show_usage + exit 1 + ;; +esac \ No newline at end of file diff --git a/gitea/README.md b/gitea/README.md index eba4325..79e4a31 100644 --- a/gitea/README.md +++ b/gitea/README.md @@ -114,11 +114,9 @@ Purpose: * Repository notifications * User invitations -SMTP configuration is loaded from: +SMTP secrets are provided by Infisical at runtime. -```text -/srv/secrets/company.env -``` +These values are resolved during deployment by `infisical run`. Variables: diff --git a/mssql/README.md b/mssql/README.md index 3ce1f78..d6c2560 100644 --- a/mssql/README.md +++ b/mssql/README.md @@ -39,10 +39,9 @@ Server-side administration must use `docker exec` or another container attached ## Secrets -The SA password is stored outside the repository: +The SA password is provided by Infisical at runtime: ```text -/srv/secrets/company.env MSSQL_SA_PASSWORD ``` @@ -52,14 +51,14 @@ Never commit the value to Git or place it directly in the Compose file. ## Deployment -Docker Compose must be given the secrets file for interpolation: +The stack is deployed with Infisical injection instead of a local secrets file: ```bash cd /srv/docker/mssql -docker compose --env-file /srv/secrets/company.env up -d +infisical run --path=/ --recursive -- docker compose up -d ``` -The Compose `env_file:` setting passes variables into the container. It does not make them available while Compose resolves `${MSSQL_SA_PASSWORD}`, which is why `--env-file` is required. +Secrets are injected into the environment before Docker Compose evaluates variables such as `${MSSQL_SA_PASSWORD}`. Validate startup: diff --git a/mssql/docker-compose.yml b/mssql/docker-compose.yml index 8911f7e..02a7b60 100644 --- a/mssql/docker-compose.yml +++ b/mssql/docker-compose.yml @@ -3,12 +3,11 @@ services: image: mcr.microsoft.com/mssql/server:2022-latest container_name: mssql restart: unless-stopped - env_file: - - /srv/secrets/company.env + # Secrets are injected by Infisical at runtime. environment: ACCEPT_EULA: Y MSSQL_PID: Express - MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD} + MSSQL_SA_PASSWORD: '' volumes: - /srv/docker/mssql/data:/var/opt/mssql networks: diff --git a/openproject/README.md b/openproject/README.md index 265897a..9cbb2d3 100644 --- a/openproject/README.md +++ b/openproject/README.md @@ -99,11 +99,7 @@ Purpose: * Mention notifications * Project updates -SMTP credentials are stored in: - -```text -/srv/secrets/company.env -``` +SMTP credentials are provided by Infisical at runtime. --- @@ -128,13 +124,7 @@ This directory must be included in backups. ## Secrets -The following values are stored in: - -```text -/srv/secrets/company.env -``` - -Variables: +The following values are provided by Infisical at runtime when the stack is deployed: ```text POSTGRES_OPENPROJECT_PASSWORD diff --git a/openproject/docker-compose.yml b/openproject/docker-compose.yml index c82d168..4306d81 100644 --- a/openproject/docker-compose.yml +++ b/openproject/docker-compose.yml @@ -4,9 +4,7 @@ services: container_name: openproject restart: unless-stopped - env_file: - - /srv/secrets/company.env - + # Secrets are injected by Infisical at runtime. environment: SECRET_KEY_BASE: ${OPENPROJECT_SECRET_KEY_BASE} OPENPROJECT_HOST__NAME: team.silveressence.net