Ansible Automation For Ghosts - Semaphore UI

Mission of the Ghost

Ansible is a sharp blade.

  • open source
  • command line
  • fast and efficient
  • Ghost approved

For operators craving API-driven command, Semaphore rises from Ansible's silent core.

  • open source
  • fast, Go language based
  • UI
  • API

DeadSwitch maps it.

Requirements

  • Debian 12 (with hardened kernel preferred)
  • Podman installed and functional (v4 or later)
  • Environment variables set securely or managed via .env
  • Terminal access with elevated privileges or configured rootless Podman

Step 1: Create the podman volume

Persistent volume for the MySQL data:

podman volume create semaphore_mysql

Step 2: Deploy containers with Podman Compose

With Podman Compose you can whisper to Podman and deploy the MySQL and Semaphore containers together.

In production you must use environment files:

 env_file:
  - .env
 version:  "3.9"

 services:
   mysql:
     image: docker.io/mysql:lts
     hostname: mysql
     volumes:
      - semaphore_mysql:/var/lib/mysql
     environment:
       MYSQL_RANDOM_ROOT_PASSWORD:  'yes'
       MYSQL_DATABASE: semaphore
       MYSQL_USER: semaphore
       MYSQL_PASSWORD: semaphore

   semaphore:
     image: docker.io/semaphoreui/semaphore:latest
     ports:
      - 3000:3000
     environment:
       SEMAPHORE_DB_USER: semaphore
       SEMAPHORE_DB_PASS: semaphore
       SEMAPHORE_DB_HOST: mysql
       SEMAPHORE_DB_PORT: 3306
       SEMAPHORE_DB_DIALECT: mysql
       SEMAPHORE_DB: semaphore
       SEMAPHORE_PLAYBOOK_PATH: /tmp/semaphore/
       SEMAPHORE_ADMIN_PASSWORD: changeme
       SEMAPHORE_ADMIN_NAME: admin
       SEMAPHORE_ADMIN_EMAIL: admin@localhost
       SEMAPHORE_ADMIN: admin
       SEMAPHORE_ACCESS_KEY_ENCRYPTION: gs72mPntFATGJs9qK0pQ0rKtfidlexiMjYCH9gWKhTU=
       SEMAPHORE_LDAP_ACTIVATED:  'no'
       SEMAPHORE_LDAP_HOST: dc01.local.example.com
       SEMAPHORE_LDAP_PORT:  '636'
       SEMAPHORE_LDAP_NEEDTLS:  'yes'
       SEMAPHORE_LDAP_DN_BIND:  'uid=bind_user,cn=users,cn=accounts,dc=local,dc=shiftsystems,dc=net'
       SEMAPHORE_LDAP_PASSWORD:  'ldap_bind_account_password'
       SEMAPHORE_LDAP_DN_SEARCH:  'dc=local,dc=example,dc=com'
       SEMAPHORE_LDAP_SEARCH_FILTER:  "(\u0026(uid=%s)(memberOf=cn=ipausers,cn=groups,cn=accounts,dc=local,dc=example,dc=com))"
       TZ: UTC
     depends_on:
      - mysql

 volumes:
   semaphore_mysql:

Note: Podman Compose supports the same syntax as Docker Compose but is a separate tool. Install with:

pip install podman-compose

Deploy using:

podman-compose -f podman-compose.yaml up

Step 3: First contact

Initiate secure access using a hardened browser:

http://localhost:3000/

Credentials: whatever you defined in the environment above.

Step 4: Harden

This is only the echo.

  • Wrap behind a reverse proxy (Caddy, Traefik, or Nginx).
  • Enforce HTTPS (Let's Encrypt or internal CA).
  • Rotate admin password. Strip LDAP if unused.
  • Lock containers to loopback if accessed only locally.
  • Use systemd units generated from Podman.

Your playbook is now mapped.


Write to DeadSwitch on Matrix: @deadswitch:matrix.org
Maybe he whispers back…

DeadSwitch | The Cyber Ghost
"In silence, we rise. In the switch, we fade."