2026-07-07 17:07:51 +00:00
|
|
|
# ───────────────────────────────────────────────────────────
|
|
|
|
|
# Alpine Docker Host — Minimal QEMU-Bootable Image
|
|
|
|
|
#
|
|
|
|
|
# Purpose: Temporary Docker host running on QEMU (pc-q35-10.0)
|
|
|
|
|
# atop Windows VMs in traditional deployments. Provides the
|
|
|
|
|
# Docker daemon that the Ansible control node container runs on.
|
|
|
|
|
#
|
|
|
|
|
# What it IS:
|
|
|
|
|
# • Alpine Linux 3.20 with OpenRC (no systemd)
|
|
|
|
|
# • Docker daemon + CLI
|
|
|
|
|
# • SSH server for remote management
|
|
|
|
|
# • QEMU-bootable via build-qemu.sh
|
|
|
|
|
#
|
|
|
|
|
# What it is NOT:
|
|
|
|
|
# • No Ansible (deployed as a separate container)
|
|
|
|
|
# • No GCC or build tools
|
|
|
|
|
# • No Python pip packages
|
|
|
|
|
# • No quality-of-life packages
|
|
|
|
|
#
|
|
|
|
|
# Target size: ~200MB Docker image → ~250MB qcow2
|
|
|
|
|
# ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
FROM alpine:3.20
|
|
|
|
|
|
|
|
|
|
LABEL org.opencontainers.image.title="Alpine Docker Host (QEMU)"
|
|
|
|
|
LABEL org.opencontainers.image.description="Minimal Alpine Linux with Docker daemon for QEMU pc-q35-10.0. Boots in ~6s."
|
|
|
|
|
|
|
|
|
|
# ── Core system (no bloat) ─────────────────────────────────
|
|
|
|
|
RUN apk add --no-cache \
|
|
|
|
|
alpine-base \
|
|
|
|
|
linux-virt \
|
|
|
|
|
e2fsprogs \
|
|
|
|
|
docker \
|
|
|
|
|
docker-openrc \
|
|
|
|
|
docker-cli-compose \
|
|
|
|
|
openssh-server \
|
|
|
|
|
openssh-client \
|
|
|
|
|
dhcpcd \
|
|
|
|
|
sudo \
|
|
|
|
|
curl \
|
|
|
|
|
ca-certificates \
|
2026-07-07 17:13:49 +00:00
|
|
|
util-linux \
|
|
|
|
|
python3
|
2026-07-07 17:07:51 +00:00
|
|
|
|
|
|
|
|
# ── OpenRC: enable just what's needed ──────────────────────
|
|
|
|
|
RUN rc-update add devfs sysinit && \
|
|
|
|
|
rc-update add dmesg sysinit && \
|
|
|
|
|
rc-update add mdev sysinit && \
|
|
|
|
|
rc-update add hwdrivers sysinit && \
|
|
|
|
|
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 && \
|
|
|
|
|
rc-update add sshd default && \
|
|
|
|
|
rc-update add dhcpcd default && \
|
|
|
|
|
rc-update add docker default
|
|
|
|
|
|
2026-07-07 17:13:49 +00:00
|
|
|
# ── TTY menu: auto-launch on serial console ────────────────
|
|
|
|
|
# Uses agetty -l to replace /bin/login with the menu script
|
|
|
|
|
COPY scripts/tty-menu.sh /usr/local/bin/tty-menu.sh
|
|
|
|
|
RUN chmod +x /usr/local/bin/tty-menu.sh && \
|
|
|
|
|
echo 'ttyS0::respawn:/sbin/agetty -L 115200 ttyS0 xterm-256color -l /usr/local/bin/tty-menu.sh' \
|
2026-07-07 17:07:51 +00:00
|
|
|
>> /etc/inittab
|
|
|
|
|
|
2026-07-07 17:13:49 +00:00
|
|
|
# ── Web UI ────────────────────────────────────────────────
|
|
|
|
|
COPY webui/app.py /usr/local/bin/webui.py
|
|
|
|
|
RUN chmod +x /usr/local/bin/webui.py
|
|
|
|
|
|
|
|
|
|
# OpenRC service for the web UI
|
|
|
|
|
RUN printf '#!/sbin/openrc-run\n\
|
|
|
|
|
name="webui"\n\
|
|
|
|
|
description="IEC 62443-3-3 Web UI"\n\
|
|
|
|
|
command="/usr/bin/python3"\n\
|
|
|
|
|
command_args="/usr/local/bin/webui.py"\n\
|
|
|
|
|
command_background=true\n\
|
|
|
|
|
pidfile="/run/webui.pid"\n\
|
|
|
|
|
depend() {\n\
|
|
|
|
|
need net docker\n\
|
|
|
|
|
}\n' \
|
|
|
|
|
> /etc/init.d/webui && \
|
|
|
|
|
chmod +x /etc/init.d/webui && \
|
|
|
|
|
rc-update add webui default
|
|
|
|
|
|
|
|
|
|
# ── Shared directories (host ↔ ansible container) ─────────
|
|
|
|
|
RUN mkdir -p /ansible/playbooks /ansible/reports /ansible/inventory && \
|
|
|
|
|
chown -R ansible:ansible /ansible
|
|
|
|
|
|
2026-07-07 17:07:51 +00:00
|
|
|
# ── Hostname ──────────────────────────────────────────────
|
|
|
|
|
RUN echo 'alpine-docker' > /etc/hostname
|
|
|
|
|
|
|
|
|
|
# ── SSH configuration ─────────────────────────────────────
|
|
|
|
|
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 ─────────────────────────────────────────────────
|
|
|
|
|
RUN echo 'root:ansible' | chpasswd && \
|
|
|
|
|
adduser -D ansible && \
|
|
|
|
|
echo 'ansible:ansible' | chpasswd && \
|
|
|
|
|
addgroup ansible wheel && \
|
|
|
|
|
addgroup ansible docker && \
|
|
|
|
|
echo '%wheel ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
|
|
|
|
|
|
|
|
|
# ── First-boot: expand rootfs, regenerate SSH host keys ────
|
|
|
|
|
RUN printf '#!/bin/sh\n\
|
|
|
|
|
ROOTDEV=$(findmnt -n -o SOURCE / 2>/dev/null || echo /dev/vda)\n\
|
|
|
|
|
resize2fs "$ROOTDEV" 2>/dev/null || true\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
|
|
|
|
|
|
|
|
|
|
# ── MOTD ──────────────────────────────────────────────────
|
|
|
|
|
RUN printf '\n\
|
|
|
|
|
\e[1;34m╔════════════════════════════════════════════════╗\e[0m\n\
|
|
|
|
|
\e[1;34m║ Alpine Docker Host — QEMU pc-q35-10.0 ║\e[0m\n\
|
|
|
|
|
\e[1;34m╠════════════════════════════════════════════════╣\e[0m\n\
|
2026-07-07 17:13:49 +00:00
|
|
|
\e[1;34m║ TTY: This console (auto-menu) ║\e[0m\n\
|
|
|
|
|
\e[1;34m║ Web UI: http://<ip>:8080 ║\e[0m\n\
|
|
|
|
|
\e[1;34m║ SSH: ssh ansible@<ip> -p 22 ║\e[0m\n\
|
|
|
|
|
\e[1;34m║ Pass: ansible ║\e[0m\n\
|
2026-07-07 17:07:51 +00:00
|
|
|
\e[1;34m╚════════════════════════════════════════════════╝\e[0m\n\
|
|
|
|
|
' > /etc/motd
|
|
|
|
|
|
|
|
|
|
WORKDIR /root
|
|
|
|
|
CMD ["/sbin/init"]
|