docs(roles): add argument_specs and README for traefik, authentik, drawio, garage, nextcloud

Each of the five roles touched in this branch now ships:

* meta/argument_specs.yml: typed schema for every variable in
  defaults/main.yml plus the optional inputs surfaced via this
  branch (traefik_extra_hosts, authentik_host_rewrite_domains,
  authentik_proxy_apps.mode / .allowed_groups, drawio_extra_domains,
  drawio_authentik_forward_auth*, garage_webui_authentik_forward_auth*).
  All five specs load cleanly through ansible-core's
  ArgumentSpecValidator.

* README.md: replaces the ansible-galaxy boilerplate (where it was
  still in place) with a focused write-up — service vars, required
  secrets, ForwardAuth/idempotency notes, dependencies, and a working
  example playbook. authentik and garage READMEs are rewritten to cover
  the new knobs while preserving their existing content.
This commit is contained in:
Simon Bärlocher 2026-05-26 14:16:47 +02:00
parent a9c33baed9
commit 1dcff92240
No known key found for this signature in database
GPG key ID: 63DE20495932047A
10 changed files with 1348 additions and 143 deletions

View file

@ -1,113 +1,116 @@
Garage
======
# Garage
Ansible role to deploy Garage S3-compatible object storage using Docker Compose.
Ansible role to deploy [Garage](https://garagehq.deuxfleurs.fr/) S3-compatible
object storage via Docker Compose, with declarative key/bucket
provisioning and an optional WebUI behind htpasswd or authentik
ForwardAuth.
Requirements
------------
## Requirements
- Docker and Docker Compose installed on the target host
- Ansible collection: `community.docker`
- Traefik reverse proxy (for external access)
- `htpasswd` (from `apache2-utils` / `httpd-tools`) when the WebUI is
enabled and authentik ForwardAuth is *not* used
- Traefik with a shared `garage_traefik_network` (default `proxy`)
Role Variables
--------------
## Role variables
Key variables defined in `defaults/main.yml`:
Full spec with types and defaults: `meta/argument_specs.yml`. The most
common overrides:
**Base Configuration:**
- `docker_compose_base_dir`: Base directory for Docker Compose files (default: `/etc/docker/compose`)
- `docker_volume_base_dir`: Base directory for Docker volumes (default: `/srv/data`)
### Service
**Garage Configuration:**
- `garage_service_name`: Service name (default: `garage`)
- `garage_image`: Garage Docker image (default: `dxflrs/garage:v2.1.0`)
- `garage_s3_domain`: Domain for S3 API endpoint (default: `storage.local.test`)
- `garage_web_domain`: Domain for S3 web endpoint (default: `web.storage.local.test`)
- `garage_webui_domain`: Domain for web console (default: `console.storage.local.test`)
- `garage_s3_domains`: FQDNs the S3 router accepts. First entry is the
canonical hostname and is used as `root_domain` in `garage.toml`.
- `garage_web_domain`, `garage_webui_domain`: separate hostnames for
the S3-website endpoint and the console.
- `garage_image`, `garage_replication_factor`, `garage_db_engine`,
`garage_s3_region`.
**Garage Storage Configuration:**
- `garage_replication_factor`: Replication factor (default: `1`)
- `garage_compression_level`: Compression level (default: `1`)
- `garage_db_engine`: Database engine (default: `lmdb`)
- `garage_s3_region`: S3 region (default: `us-east-1`)
### Required secrets
**Garage Ports:**
- `garage_s3_api_port`: S3 API port (default: `3900`)
- `garage_s3_web_port`: S3 web port (default: `3902`)
- `garage_admin_port`: Admin API port (default: `3903`)
- `garage_rpc_port`: RPC port (default: `3901`)
Generate with `openssl rand -hex 32` (32 bytes / 64 hex chars):
**Garage Security:**
- `garage_rpc_secret`: RPC secret for node communication
- `garage_admin_token`: Admin API token
- `garage_metrics_token`: Metrics API token
- `garage_rpc_secret`: node-to-node RPC secret
- `garage_admin_token`: admin API token
- `garage_metrics_token`: metrics endpoint token
**Garage WebUI Configuration:**
- `garage_webui_enabled`: Enable web UI (default: `true`)
- `garage_webui_image`: WebUI Docker image (default: `khairul169/garage-webui:latest`)
- `garage_webui_port`: WebUI port (default: `3909`)
- `garage_webui_username`: WebUI username (default: `admin`)
- `garage_webui_password`: WebUI password in plaintext (default: `admin`)
### WebUI authentication
**Traefik Configuration:**
- `garage_traefik_network`: Traefik network name (default: `proxy`)
- `garage_internal_network`: Internal network name (default: `internal`)
- `garage_use_ssl`: Enable SSL (default: `true`)
Three modes:
Dependencies
------------
1. **htpasswd** (default): `garage_webui_username` / `garage_webui_password`
in plaintext. The role hashes the password with
`htpasswd -nbBC 10`, persists the hash on disk, and re-verifies with
`htpasswd -vbB` so unchanged passwords don't churn the play.
2. **authentik ForwardAuth**: set
`garage_webui_authentik_forward_auth: true` and
`garage_webui_authentik_forward_auth_url:
"https://auth.example.com/outpost.goauthentik.io/auth/traefik"`.
`AUTH_USER_PASS` is dropped from the container env so authentik is
the only gate.
3. **Disabled**: `garage_webui_enabled: false`.
This role requires:
- Traefik reverse proxy to be configured and the `proxy` network to be created
- `htpasswd` utility (from `apache2-utils` package) for generating bcrypt password hashes
### Layout bootstrap
Example Playbook
----------------
Setting `garage_bootstrap_enabled: true` runs the bootstrap task, which
joins the local node to the layout (`zone: garage_bootstrap_zone`,
capacity: `garage_bootstrap_capacity`) on the first run. The check
tolerates the 16-char truncation that `garage layout show` performs.
### Declarative S3 keys and buckets
```yaml
garage_s3_keys:
- name: nextcloud
buckets:
- name: nextcloud-data
permissions: [read, write]
- name: backup
buckets:
- name: restic-prod
permissions: [read, write, owner]
```
The role:
- Lists existing keys (`garage key list`), creates missing ones
- Lists existing buckets (`garage bucket list`), creates missing ones
- Reads current permissions via `garage bucket info` and runs
`garage bucket allow` only when the current RWO flags for the key
don't already match the desired permissions
`stdout` parsing is hardened against ANSI escapes and interleaved INFO
log lines, so probe noise no longer produces spurious changes.
## Dependencies
- Traefik network (`garage_traefik_network`, default `proxy`)
- Internal network (`garage_internal_network`, default `internal`)
## Example playbook
```yaml
- hosts: storage_servers
roles:
- role: garage
- role: digitalboard.core.garage
vars:
garage_s3_domain: "storage.example.com"
garage_rpc_secret: "your-secure-rpc-secret"
garage_admin_token: "your-admin-token"
garage_webui_enabled: true
garage_webui_username: "admin"
garage_webui_password: "secure-password"
garage_s3_domains:
- "storage.example.com"
- "storage.int.example.com"
garage_rpc_secret: "{{ vault_garage_rpc_secret }}"
garage_admin_token: "{{ vault_garage_admin_token }}"
garage_metrics_token: "{{ vault_garage_metrics_token }}"
garage_bootstrap_enabled: true
garage_webui_authentik_forward_auth: true
garage_webui_authentik_forward_auth_url: "https://auth.example.com/outpost.goauthentik.io/auth/traefik"
garage_s3_keys:
- name: nextcloud
buckets:
- name: nextcloud-data
permissions: [read, write]
```
**Note:** The WebUI password is specified in plaintext and will be automatically hashed using bcrypt during deployment. The role uses `htpasswd` to generate a secure bcrypt hash that is then properly escaped for use in Docker Compose.
## License
Post-Installation
-----------------
After deployment, you need to configure the Garage cluster:
1. Connect to the node and get the node ID:
```bash
docker exec -ti garage /garage node id
```
2. Configure the node layout:
```bash
docker exec -ti garage /garage layout assign -z dc1 -c 1G <node-id>
docker exec -ti garage /garage layout apply --version 1
```
3. Create a key for S3 access:
```bash
docker exec -ti garage /garage key create my-key
```
4. Create a bucket:
```bash
docker exec -ti garage /garage bucket create my-bucket
docker exec -ti garage /garage bucket allow my-bucket --read --write --key my-key
```
License
-------
MIT-0
MIT-0

View file

@ -0,0 +1,169 @@
---
argument_specs:
main:
short_description: Deploy Garage S3-compatible object storage via Docker Compose.
description:
- Renders a Compose stack for Garage with traefik labels, configures the
node layout on first run, and (optionally) provisions S3 keys, buckets
and per-key permissions declaratively.
- The optional WebUI can be protected by classic htpasswd or by
authentik ForwardAuth.
options:
docker_compose_base_dir:
type: path
default: /etc/docker/compose
docker_volume_base_dir:
type: path
default: /srv/data
garage_service_name:
type: str
default: garage
garage_docker_compose_dir:
type: path
description: Defaults to C({{ docker_compose_base_dir }}/{{ garage_service_name }}).
garage_docker_volume_dir:
type: path
description: Defaults to C({{ docker_volume_base_dir }}/{{ garage_service_name }}).
garage_image:
type: str
default: dxflrs/garage:v2.1.0
garage_s3_domains:
type: list
elements: str
default: ['storage.local.test']
description:
- FQDNs the garage S3 router accepts. The first entry is the
canonical domain and is used as the virtual-hosted-style
C(root_domain) in C(garage.toml). Further entries cover internal
C(*.int.*) names.
garage_web_domain:
type: str
default: web.storage.local.test
description: Hostname serving the S3-website endpoint.
garage_webui_domain:
type: str
default: console.storage.local.test
description: Hostname serving the WebUI console.
garage_webui_enabled:
type: bool
default: true
garage_webui_image:
type: str
default: khairul169/garage-webui:latest
garage_webui_port:
type: int
default: 3909
garage_webui_username:
type: str
default: admin
description: htpasswd username. Ignored when C(garage_webui_authentik_forward_auth=true).
garage_webui_password:
type: str
default: admin
description:
- Plaintext password; hashed with C(htpasswd -nbBC 10) and persisted
on disk so re-runs don't churn. Ignored when authentik ForwardAuth
is enabled.
garage_webui_authentik_forward_auth:
type: bool
default: false
description:
- When true the C(AUTH_USER_PASS) env-var is dropped from the WebUI
container and traefik attaches a ForwardAuth middleware pointing
at the URL below. authentik is then the only gate; htpasswd is
disabled.
garage_webui_authentik_forward_auth_url:
type: str
default: ''
description:
- Required when C(garage_webui_authentik_forward_auth=true).
Typically C(https://auth.example.com/outpost.goauthentik.io/auth/traefik).
garage_s3_api_port:
type: int
default: 3900
garage_s3_web_port:
type: int
default: 3902
garage_admin_port:
type: int
default: 3903
garage_rpc_port:
type: int
default: 3901
garage_replication_factor:
type: int
default: 1
garage_compression_level:
type: int
default: 1
garage_db_engine:
type: str
choices: [lmdb, sqlite, sled]
default: lmdb
garage_s3_region:
type: str
default: us-east-1
garage_rpc_secret:
type: str
required: true
description: Hex secret for node-to-node RPC. Generate with C(openssl rand -hex 32).
garage_admin_token:
type: str
required: true
garage_metrics_token:
type: str
required: true
garage_traefik_network:
type: str
default: proxy
garage_internal_network:
type: str
default: internal
garage_use_ssl:
type: bool
default: true
garage_bootstrap_enabled:
type: bool
default: false
description: When true the bootstrap task ensures the node is in the layout.
garage_bootstrap_zone:
type: str
default: dc1
description: Zone label assigned during layout bootstrap.
garage_bootstrap_capacity:
type: str
default: 1G
description: Capacity string passed to C(garage layout assign -c).
garage_s3_keys:
type: list
elements: dict
default: []
description:
- Declarative key + bucket + permission provisioning. The role
creates missing keys, missing buckets, and runs C(bucket allow)
only when the current RWO flags for a given key don't match.
options:
name:
type: str
required: true
buckets:
type: list
elements: dict
description: Buckets this key gets access to.
options:
name:
type: str
required: true
permissions:
type: list
elements: str
choices: [read, write, owner]
required: true