Commit Graph
56 Commits
Author SHA1 Message Date
Hermes Agent service account 009f244739 feat(semaphore): add config-as-code via Semaphore REST API
Adds an idempotent configuration pass that drives a freshly-deployed
Semaphore instance into its desired state via the REST API. Declared
in group_vars/all/semaphore.yml, applied by tasks/configure.yml,
toggled by semaphore_configure feature flag (default off).

Object types managed:
  - Project (mk-labs)
  - Keys (ansible-vault-pass, gitea-deploy, jarvis-ssh)
  - Repositories (homelab on gitea)
  - Inventories (production -> ansible/inventory.yml in homelab repo)
  - Environments (default with ANSIBLE_HOST_KEY_CHECKING=False)
  - Templates (day0_linux_baseline + variants, day1_deploy_semaphore)
    with survey vars for runtime parameters

Each object found-or-created by name; existing ones never modified.
no_log on token-bearing calls to keep secrets out of stdout.

Inputs (already in vault):
  vault_semaphore_api_token
  vault_jarvis_ssh_private_key
  vault_gitea_deploy_key
  vault_ansible_vault_password
2026-05-29 22:44:15 -05:00
Hermes Agent service account 2f87039f17 vault: add jarvis SSH key, gitea deploy key, and Semaphore API token
Three new vault entries required for Semaphore config-as-code:
  - vault_jarvis_ssh_private_key (matches jarvis_ssh_public_key in
    group_vars/all/vars; used by Semaphore to SSH to the fleet)
  - vault_gitea_deploy_key (existing deploy key on the homelab
    repo; used by Semaphore to clone)
  - vault_semaphore_api_token (minted from the Semaphore UI; used
    by Ansible to drive Semaphore configuration)

These wire up the inputs the upcoming semaphore configure step
will consume.
2026-05-29 22:37:34 -05:00
Hermes Agent service account 72fa38e928 fix(semaphore): force container restart when Quadlet template changes
Quadlet regenerates the systemd unit on daemon-reload but does not
restart the running container — the process keeps its original
environment until restarted. Add explicit 'state: restarted' tasks
gated on the Quadlet template's changed status so env-var updates
actually take effect on re-runs.
2026-05-29 21:53:47 -05:00
Hermes Agent service account 9e68802090 fix(semaphore): make host-agnostic by omitting SEMAPHORE_WEB_ROOT
The SPA was rendering <base href="https://semaphore.local.mk-labs.cloud/">
regardless of which hostname served the page, causing the imagineering
alias to load the same UI but rewrite all in-page navigation back to the
semaphore hostname. Confusing for users hitting either Traefik alias.

Leave SEMAPHORE_WEB_ROOT empty so Semaphore emits relative URLs and is
fully host-agnostic. Both DNS names (semaphore + imagineering) now serve
cleanly without preference between them.

The template only emits SEMAPHORE_WEB_ROOT when the variable is set, so
the env-var is absent from the container when not needed.
2026-05-29 21:52:33 -05:00
Hermes Agent service account d05cfcf317 fix(semaphore): provide SEMAPHORE_ADMIN_* env vars for non-interactive first boot
The v2.18 image's entrypoint runs the setup wizard on first boot. Without
the SEMAPHORE_ADMIN_* variables it prompts on stdin, fails with 'Username
cannot be empty', and the container exits — leading to a crash loop.

Set:
  SEMAPHORE_ADMIN=admin
  SEMAPHORE_ADMIN_NAME=Administrator
  SEMAPHORE_ADMIN_EMAIL=admin@local.mk-labs.cloud
  SEMAPHORE_ADMIN_PASSWORD={{ vault_semaphore_admin_password }}
  SEMAPHORE_PLAYBOOK_PATH=/var/lib/semaphore/playbooks

The env-var bootstrap path is stable in v2.x; only the legacy
'semaphore user add' CLI invocation was unreliable. Drop the manual
user-add step from the README.
2026-05-29 21:38:44 -05:00
Hermes Agent service account 80f810fb0c feat(semaphore): rewrite role with rootful Podman Quadlet + PostgreSQL
Complete rewrite of the semaphore role. Supersedes three prior
iterations whose admin-user-creation logic was unreliable across
Semaphore CLI versions.

Architecture:
  - Rootful Podman Quadlet under /etc/containers/systemd/
  - Separate PostgreSQL 16-alpine container on a user-defined
    podman network (semaphore-net)
  - Named volumes for both data stores (semaphore_data,
    semaphore_postgres_data) so container recreation is
    non-destructive
  - Pinned image tags: semaphoreui/semaphore:v2.18.5-ansible2.16.5
    and postgres:16-alpine
  - Post-deploy HTTP health check fails the playbook if Semaphore
    doesn't respond on /api/ping within ~60s

Admin user creation remains intentionally manual after first deploy;
the role README documents the exact podman exec command.

Removes the duplicate deploy_semaphore.yml and the now-unneeded
cleanup_semaphore.yml; day1_deploy_semaphore.yml is the canonical
entry point.
2026-05-29 21:34:22 -05:00
Hermes Agent service account 9153324795 fix(linux-baseline): correct MOTD padding math
Previous template used inline arithmetic that miscounted the box
width, leaving the right border misaligned on real hostnames. Switch
to computed labels + a fixed inner_width so any hostname / OS string
pads to the same border position.
2026-05-29 20:56:38 -05:00
Hermes Agent service account 91b5817e5f feat(ansible): add linux-baseline role and day0_linux_baseline playbook
Introduces a single, idempotent baseline role to supersede the
overlapping day0-baseline and common roles. Capabilities are
feature-flagged so they can be toggled per-host:

  - packages (common + OS-family + per-host extras)
  - timezone + locale
  - chrony time sync against sundial
  - baseline users (jarvis admin account with SSH key + NOPASSWD sudo)
  - SSH hardening via /etc/ssh/sshd_config.d/ drop-in
  - unattended security upgrades (Debian family)
  - sysctl drop-in at /etc/sysctl.d/99-mk-labs.conf
  - journald retention caps
  - branded MOTD

Ubuntu/Debian is first-class; vars/RedHat.yml provides a placeholder
for future distros via the ansible_os_family pattern.

The legacy day0-baseline and common roles remain in place for now and
will be removed during the playbook cleanup sweep, alongside the
existing playbook naming inconsistencies.
2026-05-29 20:40:04 -05:00
Hermes Agent service account 1dfa7889ab chore(playbooks): switch day0_baseline.yml to new day0-baseline role 2026-05-28 22:06:49 -05:00
Hermes Agent service account 08d7da0c35 feat(baseline): add clean day0 baseline role (time, packages, hardening) 2026-05-28 21:54:34 -05:00
Hermes Agent service account 9ebeb42023 fix(semaphore): add SEMAPHORE_DB_SSLMODE=disable for postgres connection 2026-05-28 21:24:14 -05:00
Hermes Agent service account 075f34b1fb fix(semaphore): make network creation task more reliable 2026-05-28 15:22:45 -05:00
Hermes Agent service account 210c89c2c7 fix(semaphore): make network creation more robust and earlier in legacy block 2026-05-28 15:20:09 -05:00
Hermes Agent service account b93a6e50ab feat(semaphore): add optional aggressive storage cleanup for legacy mode 2026-05-28 15:17:54 -05:00
Hermes Agent service account 85cc1f8c6a fix(semaphore): improve container cleanup and volume reuse in legacy mode 2026-05-28 15:16:07 -05:00
Hermes Agent service account 8e781b0c54 fix(semaphore): add container cleanup at start of legacy postgres deployment 2026-05-28 15:14:34 -05:00
Hermes Agent service account d8ad35b8e9 fix(semaphore): add dedicated network for legacy postgres deployment 2026-05-28 15:13:16 -05:00
Hermes Agent service account a9973d1e0f feat(semaphore): add legacy postgres deployment path using podman_container 2026-05-28 15:07:47 -05:00
Hermes Agent service account 018782d986 fix(semaphore): remove User/Group from quadlet template 2026-05-28 14:49:37 -05:00
Hermes Agent service account 3681e8c03e fix(semaphore): make volume creation task more robust 2026-05-28 14:31:06 -05:00
Hermes Agent service account 8f377e4cf3 fix(semaphore): create named volumes before deploying quadlets 2026-05-28 14:25:18 -05:00
Hermes Agent service account 419acaa40d fix(semaphore): use systemctl start for quadlet services after daemon-reload 2026-05-28 14:08:53 -05:00
Hermes Agent service account 42b204bf8a fix(semaphore): make quadlet deployment self-contained with immediate daemon-reload + service start 2026-05-28 14:06:24 -05:00
Hermes Agent service account 1d7dcb7d82 fix(semaphore): load role defaults in cleanup playbook so variables are defined 2026-05-28 14:02:33 -05:00
Hermes Agent service account d63ca0b4f9 docs(semaphore): add cleanup playbook reference to deploy playbook 2026-05-28 14:01:00 -05:00
Hermes Agent service account e9440327aa feat(semaphore): add dedicated cleanup playbook
- Explicit playbook for removing old container, systemd services, and quadlets
- Optional semaphore_force_clean variable for data removal
- Safer than tags for destructive operations
2026-05-28 10:25:56 -05:00
Hermes Agent service account dcc7e282c7 feat(semaphore): complete quadlet deployment for PostgreSQL + Semaphore
- Add quadlet tasks and handlers for modern rootless Podman deployment
- Fix broken Jinja in semaphore.container.j2
- Add proper 0777 permissions handling for rootless
- Support semaphore_use_postgres toggle with fallback to legacy BoltDB path
2026-05-28 10:22:28 -05:00
Hermes Agent service account 6d5fc7c5c6 fix(semaphore): set data directory permissions to 0777 for rootless compatibility 2026-05-27 22:38:36 -05:00
Hermes Agent service account 4e0b4fa049 refactor(semaphore): remove unreliable admin user creation from role
Initial admin user must now be created manually after first deployment
2026-05-27 22:20:43 -05:00
Hermes Agent service account cf7c2a1436 fix(semaphore): clean up admin user creation tasks with proper fallback and force logic 2026-05-27 22:16:34 -05:00
Hermes Agent service account 3dc6555ad1 fix(semaphore): support vault_semaphore_admin_password as fallback variable name 2026-05-27 22:15:20 -05:00
Hermes Agent service account 27ff9286b4 feat(semaphore): add semaphore_force_admin_user option
- When set to true, deletes existing admin user before creating
- Useful for recovering from bad password or broken user state
2026-05-27 22:10:32 -05:00
Hermes Agent service account 6e50461999 docs: add comprehensive Semaphore deployment and setup guide
- Covers deployment, initial setup, DNS/Traefik, GitOps approach, and troubleshooting
2026-05-27 21:57:02 -05:00
Hermes Agent service account 3f1c3a40cf feat(semaphore): add idempotent initial admin user creation
- New variables for admin user (password from vault)
- Task checks if user exists before creating
- Uses podman exec + semaphore CLI
2026-05-27 21:56:36 -05:00
Hermes Agent service account 0116ec4cc3 fix(dns): improve hostname extraction in add_service_route.yml
- Use explicit Jinja2 loop for more reliable parsing of Host() rules
- Handles multi-host router definitions correctly
2026-05-27 21:39:51 -05:00
Hermes Agent service account f962d0a6d7 feat(dns): rewrite add_service_route.yml to support multi-host Traefik configs
- Parse Host() rules from router definitions
- Supports multiple hostnames per service file (e.g. semaphore + imagineering)
- More robust and future-proof
2026-05-27 21:31:41 -05:00
Hermes Agent service account dc3c0d7cb1 feat(traefik): add semaphore dynamic config with dual hostnames
- semaphore.local.mk-labs.cloud
- imagineering.local.mk-labs.cloud
- Backend: figment:3000
- Cloudflare certResolver + security-headers
2026-05-27 21:24:02 -05:00
Hermes Agent service account b05f9fad09 fix(semaphore): add :U volume flag for proper rootless UID mapping
Prevents permission issues with BoltDB data directory in rootless Podman
2026-05-27 21:17:40 -05:00
Hermes Agent service account 8650995926 fix(semaphore): adjust BoltDB Path in config.json to parent directory
Prevents 'database.boltdb/database.boltdb' path issue in v2.18.x
2026-05-27 21:09:12 -05:00
Hermes Agent service account e5469d2cb8 fix(semaphore): use semaphore_user for directory ownership + add config.json for BoltDB
- Replace hardcoded UID 1000 with {{ semaphore_user }} / {{ semaphore_group }}
- Add config.json creation task for non-interactive BoltDB startup
- Prevents interactive setup wizard and permission errors on v2.18.x
2026-05-27 21:07:30 -05:00
Hermes Agent service account 702698ddcd fix(semaphore): always regenerate systemd unit and use DB_DIALECT
- Switch podman_container env to SEMAPHORE_DB_DIALECT
- Remove creates: guard on podman generate systemd task
- Add changed_when so unit is always updated on role run
2026-05-27 20:56:47 -05:00
Hermes Agent service account d233d582d4 fix(semaphore): use SEMAPHORE_DB_DIALECT instead of legacy DB_TYPE
- Updated container quadlet template to use modern SEMAPHORE_DB_DIALECT variable
- Updated env.j2 template for consistency with current SemaphoreUI expectations
- Resolves BoltDB not being honoured on v2.18.x
2026-05-27 20:48:41 -05:00
Hermes Agent service account 3839fac162 fix: run directory ownership task with become: true 2026-05-26 23:04:23 -05:00
Hermes Agent service account b01dac85da fix: set correct ownership (1000:1000) on semaphore data directories 2026-05-26 23:02:04 -05:00
Hermes Agent service account 37a49824d0 fix: run podman generate systemd as root 2026-05-26 22:57:53 -05:00
Hermes Agent service account 0127016ab2 fix: run Semaphore container as root inside container 2026-05-26 22:57:10 -05:00
Hermes Agent service account e0b6fcb24a fix: use 'latest' as default Semaphore image tag 2026-05-26 22:55:12 -05:00
Hermes Agent service account e7d9a8fec5 fix: use containers.podman collection instead of community.general 2026-05-26 22:53:57 -05:00
Hermes Agent service account 1a9addc537 feat: use community.general collection + requirements.yml for semaphore 2026-05-26 22:51:59 -05:00
Hermes Agent service account 401f25b1c4 fix: use raw podman commands (no external collections required) 2026-05-26 22:50:55 -05:00
Hermes Agent service account ece522074e fix: switch semaphore role to podman_container + generate systemd (more reliable) 2026-05-26 22:50:18 -05:00
Hermes Agent service account c30c0074f1 fix: separate daemon-reload from service start in semaphore role 2026-05-26 22:49:38 -05:00
Hermes Agent service account fa51dc2c4d fix: correct Quadlet service startup in semaphore role 2026-05-26 22:48:55 -05:00
Hermes Agent service account c1810fde8a fix: replace semaphore role with new Podman + Quadlet version 2026-05-26 22:46:46 -05:00
Hermes Agent service account 92b2a9d609 refactor: consolidate all roles into ansible/roles/ and update ansible.cfg
- Move all roles from playbooks/roles/ to roles/
- Update roles_path in ansible.cfg
- Add cast user to common role
- Create standalone podman role
- Add semaphore role with Podman + Quadlet support
2026-05-26 22:22:08 -05:00
Hermes Agent service account 9250b0f193 chore: remove defunct hermes directory
The previous Hermes deployment configuration has been superseded.
This directory is no longer used and is being removed as part of
the transition to the new command centre on carousel-of-progress.
2026-05-26 14:42:45 -05:00