Servidor PXE all-in-one para instalação automatizada do Aurora Linux (base Fedora Kinoite) via rede, utilizando dnsmasq (DHCP + TFTP) e nginx (HTTP) em um único container Alpine.
┌─────────────────────────────────────────────────────────────────┐
│ Rede Física │
│ 192.168.200.0/24 │
│ │
│ ┌──────────────┐ ┌─────────────────────────────────────┐ │
│ │ Cliente │ │ Servidor PXE (host 192.168.200.115) │ │
│ │ PXE │ │ │ │
│ │ (BIOS/UEFI) │ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ │ │ dnsmasq │ │ nginx │ │ │
│ │ 1. DHCP │──────│ │ • DHCP │ │ • HTTP :8080 │ │ │
│ │ request │ │ │ • TFTP │ │ • boot.ipxe │ │ │
│ │ │ │ │ • undionly │ │ • vmlinuz │ │ │
│ │ 2. TFTP │◄─────│ │ .kpxe(BIOS) │ │ • initrd.img │ │ │
│ │ bootloader │ │ │ • shimx64.efi │ │ • ks.cfg │ │ │
│ │ │ │ │ (UEFI) │ └──────────────┘ │ │
│ │ 3. HTTP │◄─────│ └──────────────┘ │ │
│ │ menu iPXE │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │
│ │ 4. HTTP │ │ │
│ │ vmlinuz + │ │ 5. ostreecontainer pull │
│ │ initrd │ ▼ │
│ └──────────────┘ ┌────────────────────┐ │
│ │ ghcr.io/ublue-os/ │ │
│ │ aurora:latest │ │
│ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- DHCP Discovery — Cliente envia broadcast DHCP; dnsmasq responde com IP, gateway e servidor TFTP
- TFTP Bootloader — Arquitetura detectada via DHCP Option 93:
- BIOS →
undionly.kpxe - UEFI →
shimx64.efi
- BIOS →
- iPXE → HTTP — iPXE carrega
boot.ipxevia HTTP em:8080, exibindo menu interativo - Anaconda — vmlinuz + initrd.img (Fedora 42) baixados via HTTP; instalação lê
ks.cfg - Aurora — Kickstart executa
ostreecontainer --url ghcr.io/ublue-os/aurora:lateste instala o sistema
# Fedora / RHEL
sudo dnf install podman
# Ubuntu / Debian
sudo apt install podman
# Verificar versão
podman --version # >= 4.0 recomendado# Instalação via pip
pip install podman-compose
# Ou via pacote do sistema (Fedora)
sudo dnf install podman-compose
# Verificar
podman-compose --versionO servidor utiliza host networking — as portas abaixo devem estar liberadas no firewall do host:
| Porta | Protocolo | Serviço | Finalidade |
|---|---|---|---|
67/udp |
DHCP | dnsmasq | Atribuição de IPs |
69/udp |
TFTP | dnsmasq | Transferência do bootloader |
4011/udp |
PXE | dnsmasq | Proxy DHCP (se habilitado) |
8080/tcp |
HTTP | nginx | Menu iPXE, kernel, initrd, ks.cfg |
Exemplo com firewalld:
sudo firewall-cmd --add-port=67/udp --add-port=69/udp --add-port=8080/tcp
sudo firewall-cmd --runtime-to-permanentExemplo com iptables:
sudo iptables -A INPUT -p udp --dport 67 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 69 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPTSe SELinux está enforcing (padrão no Fedora), os volumes já utilizam a flag :z para auto-labeling. Nenhuma ação adicional é necessária.
podman-compose buildIsso baixa na build:
/tftpboot/undionly.kpxee/tftpboot/shimx64.efi(bootloaders iPXE)/tftpboot/vmlinuze/tftpboot/initrd.img(Fedora 42 pxeboot)
podman-compose up -dO container será iniciado em modo daemon e reiniciará automaticamente se cair (restart: unless-stopped).
podman logs -f pxe-serverOu via podman-compose:
podman-compose logs -fpodman-compose downRemove o container (a imagem permanece em cache para reinício rápido).
Edite os arquivos de configuração e recrie o container.
interface=enp3s0Altere para o nome da interface de rede do servidor PXE (ex: eth0, ens18).
dhcp-range=192.168.200.150,192.168.200.250,12hAjuste o range de IPs e lease time conforme sua rede.
dhcp-option=3,192.168.200.1
dhcp-option=6,192.168.200.1option 3: gateway padrãooption 6: servidor DNS
# config/dnsmasq.conf — linha 26
dhcp-boot=tag:ipxe,http://SEU_IP_AQUI:8080/boot.ipxe
# tftpboot/boot.ipxe — linha 11
kernel http://SEU_IP_AQUI:8080/vmlinuz ...Importante: O servidor utiliza
network_mode: host, portanto o IP deve ser o IP real do host na rede.
user --name=aurora --groups=wheel --password=SUA_SENHAAtenção: Esta senha é temporária. O primeiro boot do Aurora Linux exige definição de nova senha.
pxe/
├── Containerfile # Imagem Alpine + dependências + assets
├── compose.yaml # Orquestração podman-compose
├── entrypoint.sh # Entrypoint com graceful shutdown
├── config/
│ ├── dnsmasq.conf # DHCP + TFTP
│ └── nginx.conf # HTTP :8080
└── tftpboot/
├── boot.ipxe # Menu PXE
├── ks.cfg # Kickstart Aurora
├── undionly.kpxe # Bootloader BIOS (iPXE)
├── shimx64.efi # Bootloader UEFI (iPXE shim)
├── vmlinuz # Kernel Fedora 42
└── initrd.img # Initramfs Fedora 42
Após alterar qualquer arquivo, reconstrua e reinicie:
podman-compose build && podman-compose up -dpxe/
├── Containerfile # Alpine + dnsmasq + nginx + assets
├── compose.yaml # podman-compose: host net, NET_ADMIN, volumes
├── entrypoint.sh # nginx bg + dnsmasq fg + traps
├── README.md # Esta documentação
├── config/
│ ├── dnsmasq.conf # DHCP range, TFTP, boot files
│ └── nginx.conf # Servidor HTTP :8080
├── tftpboot/
│ ├── boot.ipxe # Menu iPXE
│ ├── ks.cfg # Kickstart Aurora Linux
│ ├── undionly.kpxe # Bootloader BIOS
│ ├── shimx64.efi # Bootloader UEFI
│ ├── vmlinuz # Kernel Fedora 42
│ └── initrd.img # Initramfs Fedora 42
└── test_*.sh # Testes de validação
Sintoma: O cliente PXE baixa o bootloader, carrega o menu iPXE, mas volta ao início do ciclo infinitamente.
Causa mais comum: O dnsmasq está servindo o bootloader iPXE novamente após o cliente já estar rodando iPXE — o cliente pede DHCP de novo e recebe undionly.kpxe/shimx64.efi em vez do próximo estágio HTTP.
Solução: A configuração já inclui detecção anti-loop via dhcp-userclass:
dhcp-userclass=set:ipxe,iPXE
dhcp-boot=tag:ipxe,http://192.168.200.115:8080/boot.ipxeO iPXE se identifica com a userclass iPXE, e o dnsmasq roteia para o boot HTTP em vez de re-entregar o bootloader TFTP.
Verificação:
# Confirme que a regra anti-loop está presente
grep -E 'dhcp-userclass|dhcp-boot=tag:ipxe' config/dnsmasq.confSintoma: Cliente recebe IP de outro servidor DHCP, ou IP inesperado.
Causa: Outro servidor DHCP (roteador, outro PXE) está ativo na mesma rede.
Solução:
- Identifique o DHCP conflitante:
sudo tcpdump -i enp3s0 port 67 or port 68 - Desative o DHCP do roteador na porta cabeada
- Ou mude o range DHCP em
config/dnsmasq.confpara evitar colisão
Nota: Este container faz DHCP completo (não Proxy DHCP). Ele deve ser o único servidor DHCP na rede.
Sintoma: Container não inicia, ou nginx/dnsmasq não conseguem ler arquivos montados.
Log: ausearch -m avc -ts recent
Solução:
As montagens no compose.yaml já incluem :z para auto-labeling SELinux:
volumes:
- ./config/dnsmasq.conf:/etc/dnsmasq.conf:ro,z
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro,z
- ./tftpboot/boot.ipxe:/tftpboot/boot.ipxe:ro,z
- ./tftpboot/ks.cfg:/tftpboot/ks.cfg:ro,zSe o problema persistir, verifique o contexto SELinux:
ls -Z config/dnsmasq.conf
# Deve mostrar: system_u:object_r:container_file_t:s0Se não estiver correto:
restorecon -Rv config/ tftpboot/Sintoma: Cliente não consegue fazer DHCP discovery, ou bootloader não carrega, ou menu HTTP não aparece.
Verificação rápida:
# Teste se a porta 8080 está acessível
curl -s http://localhost:8080/boot.ipxe
# Verifique portas abertas
sudo ss -tulpn | grep -E ':(67|69|8080)'Solução:
Libere as portas conforme seção Portas de Firewall.
Se estiver usando firewalld:
sudo firewall-cmd --list-portsSintoma: podman-compose up -d falha ou container cai imediatamente.
Diagnóstico:
# Logs completos
podman-compose logs
# Verifique se o container existe e seu status
podman ps -a
# Teste de sintaxe do entrypoint
bash -n entrypoint.sh
# Teste de sintaxe do dnsmasq (se instalado no host)
dnsmasq --test -C config/dnsmasq.confCausas comuns:
- Porta 67 já em uso por outro serviço DHCP (NetworkManager, outro dnsmasq)
- Interface de rede incorreta em
config/dnsmasq.conf(interface=enp3s0) - SELinux bloqueando (veja seção acima)
Sintoma: Anaconda inicia mas não encontra o repositório ou a imagem OCI.
Verificação:
# Teste se o mirror Fedora está acessível do container
podman exec pxe-server curl -s -o /dev/null -w "%{http_code}" \
https://dl.fedoraproject.org/pub/fedora/linux/releases/42/Everything/x86_64/os/
# Teste se a OCI image Aurora existe
podman exec pxe-server curl -s -o /dev/null -w "%{http_code}" \
https://ghcr.io/v2/ublue-os/aurora/manifests/latestSoluções:
- Verifique se o gateway (
192.168.200.1) tem acesso à internet - Confirme que DNS está funcionando (
dhcp-option=6) - A instalação requer internet para pull da OCI image do ghcr.io
Sintoma: Cliente PXE recebe IP mas trava em "TFTP timeout" ou "File not found".
Verificação:
# Confira se os bootloaders foram baixados na build
ls -la tftpboot/undionly.kpxe tftpboot/shimx64.efi
# Verifique permissões
ls -la tftpboot/Solução: Reconstrua a imagem para baixar novamente os assets:
podman-compose build --no-cache && podman-compose up -d