Security

Ansible WSL: SSH Key Management with Docker and Semaphore

Ansible WSL is a minimal, Docker-based infrastructure I built for staged SSH key deployment across Ubuntu servers. The project solves a common problem: transitioning from password-based authentication to SSH keys across a fleet of servers without breaking access, while providing a path to Semaphore UI for ongoing automation. Everything runs through Docker , no local Ansible installation required.

The Problem: Ansible SSH Key Management at Scale

SSH key management sounds simple until you’re doing it across multiple servers with different users, ports, and existing configurations. One wrong move with PasswordAuthentication no and you’re locked out. I needed a system that generates unique key pairs per host and user, installs them idempotently, verifies access before disabling passwords, and keeps credentials in a local-only JSON file.

The solution: Ansible playbooks running in Docker containers, orchestrated by PowerShell scripts for Windows/WSL environments. After key deployment is verified, the entire workflow can be imported into Semaphore for a web-based management interface.

Architecture and Components

  • Ansible playbooks: ssh_key_setup.yml (generate and deploy keys), verify.yml (test key-based login + sudo), collect_host_keys.yml (gather known_hosts)
  • Docker execution: custom Dockerfile with Ansible, no local install needed. Volume-mounts the workspace for key persistence
  • PowerShell scripts: run-docker.ps1 (setup/verify/raw modes), semaphore-up.ps1 (Docker Compose stack), semaphore-import-all.ps1 (bulk template import with validation)
  • Semaphore integration: local Docker Compose stack (Postgres + UI), API-based template import with idempotency checks, orphan pruning, and dry-run mode
  • Security: unique key pairs per host/user, servers.json gitignored, host key verification enabled

Semaphore Auto-Import System

The semaphore-import-all.ps1 script is particularly sophisticated , it scans the playbooks directory, matches against existing Semaphore templates, creates or updates as needed, and supports validation-only mode, orphan pruning with confirmation, basename-only paths, and detailed exit codes (0 success, 2 partial API errors, 3 validation gaps, 4 missing bindings, 5 path mismatches). This level of automation is what separates a one-off setup from a maintainable infrastructure.

Practical Application

In my role as a Fractional CTO, I manage infrastructure across multiple environments. This toolkit was born from a real need: three Ubuntu servers that needed SSH hardening without downtime. The staged approach , deploy keys first, verify access, then disable passwords , eliminates the risk of lockout that plagues manual SSH key rotation.

The Docker-based execution means any team member with Docker installed can run the playbooks without configuring Python, Ansible, or SSH locally. Combined with Semaphore’s web UI, it provides a complete security management workflow accessible to operations teams.

Source Code

The repository is available upon request. Contact me for access. Includes Ansible roles, Docker configuration, PowerShell automation scripts, and Semaphore integration.

For infrastructure security consulting , I bring over 15 years managing server fleets across 38 countries with 6 patents in information security.

FAQ

Do I need Ansible installed locally?

No. Everything runs inside a Docker container built from the included Dockerfile. You only need Docker and PowerShell (for the helper scripts).

Is it safe to run on production servers?

Yes. The playbooks are idempotent , re-running doesn’t recreate existing keys. Password authentication stays enabled until you explicitly verify key-based access with the verify playbook and manually disable passwords.

What is Semaphore used for?

Semaphore provides a web UI for running Ansible playbooks. After verifying the playbooks work via Docker CLI, you can import them into Semaphore for ongoing management with scheduling, audit logs, and team access control.

How are credentials stored?

Server passwords are in a local servers.json file that is gitignored. SSH private keys are generated into a keys/ directory, also gitignored. Nothing sensitive is committed to the repository.

Ilya Arestov , Fractional CTO | Dubai Airport Free Zone (DAFZ), Dubai, UAE | Almaty, Zenkov Street 59, Kazakhstan | +971-585-930-600 | https://t.me/getmonolith
Rate article