# ─────────────────────────────────────────────────────────── # Ansible Control Node — Alpine-based, QEMU-bootable # # Dual-purpose image: # 1. Run directly: docker run --rm -it ansible-node ansible-playbook ... # 2. Convert to QEMU disk: scripts/build-qemu.sh extracts this rootfs # into a bootable qcow2 for pc-q35-10.0 # # Integrations: # • Windows — pywinrm + kerberos → WinRM # • Cisco ASA — cisco.asa + paramiko → SSH/CLI # • Cisco Catalyst— cisco.ios + paramiko → SSH/CLI # • Cisco NX-OS — cisco.nxos + paramiko → SSH/NX-API # • Linux — native SSH (built-in ansible) # • VMware — pyvmomi → vCenter/ESXi SOAP API # • MSSQL — pymssql → SQL Server TDS # ─────────────────────────────────────────────────────────── FROM alpine:3.20 # ── System labels ────────────────────────────────────────── LABEL org.opencontainers.image.title="Ansible Control Node" LABEL org.opencontainers.image.description="Minimal Ansible control node for Windows, Cisco, VMware, MSSQL, and Linux targets. QEMU-bootable via build-qemu.sh." LABEL org.opencontainers.image.authors="ansible_testing" # ── Kernel + base system ────────────────────────────── # alpine-base = openrc + busybox + mdev + init scripts (no systemd) # linux-virt = kernel optimized for VMs (virtio, no firmware) # docker = container runtime for nested workloads RUN apk add --no-cache \ alpine-base \ linux-virt \ e2fsprogs \ docker \ docker-openrc \ # ── Networking ──────────────────────────────────────── dhcpcd \ openssh-client \ openssh-server \ curl \ wget \ ca-certificates \ bind-tools \ # ── Kerberos (WinRM Kerberos auth to Windows) ──────── krb5 \ krb5-server \ # ── FreeTDS (pymssql → MSSQL TDS protocol) ─────────── freetds \ freetds-dev \ # ── Ansible + Python + SSH pw provider ─────────────── ansible \ sshpass \ py3-pip \ python3 \ python3-dev \ # ── Build deps for pip packages (purged after) ─────── gcc \ musl-dev \ openssl-dev \ krb5-dev \ libffi-dev \ # ── Quality-of-life ────────────────────────────────── bash \ bash-completion \ vim \ tmux \ git \ jq \ less \ sudo # ── Python packages for target integrations ────────────── RUN pip3 install --no-cache-dir --break-system-packages \ # Windows: WinRM + Kerberos 'pywinrm[kerberos]>=0.4' \ requests-kerberos \ requests-ntlm \ # Cisco: SSH/NETCONF/RESTCONF paramiko>=2.7 \ ncclient>=0.6 \ netmiko>=4.0 \ scp \ # VMware: vCenter/ESXi SOAP API pyvmomi>=8.0 \ requests \ # MSSQL: TDS protocol pymssql>=2.2 \ # General utilities (required by many Ansible modules) jmespath>=1.0 \ xmltodict>=0.13 \ pyyaml>=6.0 \ cryptography>=41.0 \ packaging # ── Ansible collections for all target types ────────────── RUN ansible-galaxy collection install \ # Windows management ansible.windows \ # Network common (required by Cisco collections) ansible.netcommon \ ansible.utils \ # Cisco ASA firewalls cisco.asa \ # Cisco Catalyst / IOS / IOS-XE switches & routers cisco.ios \ # Cisco Nexus / NX-OS data center switches cisco.nxos \ # VMware vCenter / ESXi community.vmware \ # MSSQL Server community.general \ community.mysql \ microsoft.sql \ # Crypto/certificates (needed for various modules) community.crypto \ # Generic collection with many useful modules community.general # ── OpenRC: enable boot services ────────────────────────── # sysinit — devices, logging, hardware RUN rc-update add devfs sysinit && \ rc-update add dmesg sysinit && \ rc-update add mdev sysinit && \ rc-update add hwdrivers sysinit # boot — modules, networking, hostname (urandom handled by bootmisc) RUN rc-update add modules boot && \ rc-update add sysctl boot && \ rc-update add bootmisc boot && \ rc-update add hostname boot && \ rc-update add networking boot # default — SSH, DHCP, Docker daemon RUN rc-update add sshd default && \ rc-update add dhcpcd default && \ rc-update add docker default # ── Serial console (ttyS0) for QEMU -nographic ──────────── RUN echo 'ttyS0::respawn:/sbin/agetty -L 115200 ttyS0 xterm-256color' \ >> /etc/inittab # ── Hostname (hosts managed by init/dhcpcd at boot) ──────── RUN echo 'ansible-node' > /etc/hostname # ── SSH: allow root + ansible user ──────────────────────── RUN ssh-keygen -A && \ sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' \ /etc/ssh/sshd_config && \ sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' \ /etc/ssh/sshd_config && \ echo 'UseDNS no' >> /etc/ssh/sshd_config # ── Users ───────────────────────────────────────────────── # Root password (change on first boot!) RUN echo 'root:ansible' | chpasswd # Ansible service user RUN adduser -D ansible && \ echo 'ansible:ansible' | chpasswd && \ addgroup ansible wheel && \ echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers # ── Docker: enable ansible user to run docker ───────────── RUN addgroup ansible docker # ── Working directory ───────────────────────────────────── RUN mkdir -p /ansible/playbooks /ansible/inventory && \ chown -R ansible:ansible /ansible # ── Ansible config ──────────────────────────────────────── RUN mkdir -p /etc/ansible && \ printf '[defaults]\n\ host_key_checking = False\n\ stdout_callback = yaml\n\ callback_whitelist = profile_tasks\n\ retry_files_enabled = False\n\ \n\ [ssh_connection]\n\ pipelining = True\n\ control_path = /tmp/ansible-%%h-%%p-%%r' \ > /etc/ansible/ansible.cfg # ── Default inventory (placeholder) ─────────────────────── RUN printf '[windows]\n\ # win-host.example.com\n\ \n\ [cisco_asa]\n\ # asa-firewall.example.com\n\ \n\ [cisco_ios]\n\ # catalyst-switch.example.com\n\ \n\ [cisco_nxos]\n\ # nexus-switch.example.com\n\ \n\ [vmware]\n\ # vcenter.example.com\n\ \n\ [mssql]\n\ # sql-server.example.com\n\ \n\ [linux]\n\ # debian-host.example.com\n\ \n\ [all:vars]\n\ ansible_user=ansible\n' \ > /etc/ansible/hosts # ── MOTD (friendly boot message) ────────────────────────── RUN printf '\n\ \e[1;32m╔════════════════════════════════════════════════╗\e[0m\n\ \e[1;32m║ Ansible Control Node — Alpine Linux ║\e[0m\n\ \e[1;32m╠════════════════════════════════════════════════╣\e[0m\n\ \e[1;32m║ Windows • Cisco • VMware • MSSQL • Linux ║\e[0m\n\ \e[1;32m╠════════════════════════════════════════════════╣\e[0m\n\ \e[1;32m║ SSH: ssh ansible@ -p 22 ║\e[0m\n\ \e[1;32m║ Dir: /ansible ║\e[0m\n\ \e[1;32m╚════════════════════════════════════════════════╝\e[0m\n\ ' > /etc/motd # ── First-boot: expand rootfs to fill disk, gen SSH keys ─ RUN printf '#!/bin/sh\n\ # Expand root filesystem to fill the underlying disk\n\ ROOTDEV=$(findmnt -n -o SOURCE / 2>/dev/null || echo /dev/vda)\n\ resize2fs "$ROOTDEV" 2>/dev/null || true\n\ # Re-generate SSH host keys if running from a cloned image\n\ if [ ! -f /etc/ssh/.host-keys-generated ]; then\n\ ssh-keygen -A && touch /etc/ssh/.host-keys-generated\n\ fi\n' \ > /etc/local.d/00-first-boot.start && \ chmod +x /etc/local.d/00-first-boot.start && \ rc-update add local default WORKDIR /ansible CMD ["/sbin/init"]