Conhecimento / Laboratório / LAB-004
Os arquivos Docker por trás deste site: build, publicação, atualização automática e túnel
O Dockerfile, o Compose e o workflow de CI que rodam este site em um servidor caseiro, mais a VM do lab Wazuh, higienizados para você copiar.
Este site roda em um container no meu próprio servidor. Dar push na main é o deploy inteiro: o GitHub gera a imagem, o servidor percebe e reinicia o site com ela, e um túnel o expõe sem abrir portas no roteador.
Abaixo estão os arquivos reais, trocando apenas os meus nomes por placeholders. Substitua <your-user> e <your-site> e você terá a mesma configuração.
O fluxo
git pushnamain.- O GitHub Actions gera a imagem e publica no GitHub Container Registry (GHCR).
- O Watchtower no servidor consulta o GHCR a cada 5 minutos e reinicia o site com a nova imagem.
- O cloudflared mantém um Cloudflare Tunnel de saída aberto, então o site fica público sem redirecionar nenhuma porta.
Dockerfile
Três estágios: instalar dependências, compilar e uma imagem final pequena que roda como usuário sem privilégios. Depende do output: "standalone" do Next.js.
# syntax=docker/dockerfile:1
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:24-alpine AS builder
WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=3000 \
HOSTNAME=0.0.0.0
RUN addgroup -S nodejs && adduser -S nextjs -G nodejs
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
# Posts are read from disk at runtime, so the MDX files ship with the image.
COPY --from=builder --chown=nextjs:nodejs /app/content ./content
USER nextjs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://127.0.0.1:3000/ >/dev/null || exit 1
CMD ["node", "server.js"]
.dockerignore
Mantém segredos e lixo de build fora da imagem.
node_modules
.next
.git
.gitignore
Dockerfile
.dockerignore
compose.yaml
README.md
npm-debug.log*
.env*
compose.yaml
É o único arquivo que o servidor precisa. O token do túnel fica em um .env ao lado dele, nunca no repositório.
services:
web:
image: ghcr.io/<your-user>/<your-site>:latest
container_name: web
restart: unless-stopped
# Reachable only from this machine; the public path is the tunnel below.
ports:
- "127.0.0.1:3000:3000"
labels:
- "com.centurylinklabs.watchtower.enable=true"
watchtower:
image: nickfedor/watchtower:1.23.2
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# Reuses the `docker login ghcr.io` credentials so Watchtower can pull
# the private image.
- ${HOME}/.docker/config.json:/config.json:ro
environment:
WATCHTOWER_LABEL_ENABLE: "true" # only touch containers with the label above
WATCHTOWER_POLL_INTERVAL: "300" # check for a new image every 5 minutes
WATCHTOWER_CLEANUP: "true" # delete old images after updating
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run
environment:
TUNNEL_TOKEN: ${TUNNEL_TOKEN:?Set TUNNEL_TOKEN in the .env file}
depends_on:
- web
labels:
- "com.centurylinklabs.watchtower.enable=true"
O arquivo .env:
TUNNEL_TOKEN=<token from the Cloudflare Zero Trust dashboard>
Workflow do GitHub Actions
Salve como .github/workflows/docker.yml. Todo push na main gera e publica a imagem, com as tags latest e o SHA do commit (útil para rollback). As actions estão fixadas por SHA de commit.
name: Publish Docker image
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest
type=sha
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Como configurar
- Crie um Cloudflare Tunnel, aponte o hostname público para
http://web:3000e copie o token. - No servidor, rode
docker login ghcr.iocom um token que possa ler pacotes. - Copie o
compose.yamle o.envpara o servidor e rodedocker compose up -d. - Dê push na
maine veja o site se atualizar sozinho.
O que vale saber
- O Watchtower monta o socket do Docker, o que na prática equivale a root no host. É um trade-off consciente para um home lab; fixe a versão da imagem (como acima) e não o exponha.
- A porta do site está presa a
127.0.0.1, então nada é acessível fora do túnel. - Nada secreto está na imagem ou no repositório: o token vem do
.env, e o.dockerignoreexclui.env*.
O lab do Wazuh: uma VM Windows em Docker
O SIEM Wazuh em si é a stack oficial single-node (4.14.6) do wazuh/wazuh-docker, pasta single-node, sem alterações. O que eu acrescentei foi o primeiro endpoint monitorado: uma VM Windows 10 LTSC que roda dentro do Docker (QEMU/KVM) com o dockur/windows e instala o agente do Wazuh sozinha.
Estrutura:
windows-ws/
├── compose.yaml
├── .env.example
├── oem/
│ └── install.bat
└── storage/ # disco da VM (não versionar)
compose.yaml
services:
windows:
image: dockurr/windows:6.06
container_name: windows-ws
environment:
VERSION: "10l" # Windows 10 LTSC
RAM_SIZE: "3G"
CPU_CORES: "2"
DISK_SIZE: "64G"
USERNAME: ${WIN_USER}
PASSWORD: ${WIN_PASS}
REGION: "pt-BR"
KEYBOARD: "pt-BR"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 127.0.0.1:8006:8006 # visualizador web
- ${VPN_IP}:8006:8006
- ${VPN_IP}:3389:3389/tcp # RDP
- ${VPN_IP}:3389:3389/udp
volumes:
- ./storage:/storage
- ./oem:/oem # install.bat roda no fim da instalação
restart: unless-stopped
stop_grace_period: 2m
.env.example
WIN_USER=labuser
WIN_PASS=troque-por-uma-senha-forte
VPN_IP=100.x.y.z # IP da interface VPN/Tailscale do host
Copie o .env.example para .env e edite os valores. O .env real nunca é publicado.
oem/install.bat
@echo off
REM Instala o Wazuh agent e registra no manager
set MANAGER=WAZUH_MANAGER_IP
set LOG=C:\OEM\wazuh-install.log
echo [%date% %time%] Downloading wazuh-agent 4.14.6 >> %LOG%
powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol='Tls12'; Invoke-WebRequest -UseBasicParsing -Uri 'https://packages.wazuh.com/4.x/windows/wazuh-agent-4.14.6-1.msi' -OutFile \"$env:TEMP\wazuh-agent.msi\"" >> %LOG% 2>&1
echo [%date% %time%] Installing >> %LOG%
msiexec.exe /i "%TEMP%\wazuh-agent.msi" /q WAZUH_MANAGER="%MANAGER%" WAZUH_AGENT_NAME="windows-ws" /l*v C:\OEM\wazuh-msi.log
echo [%date% %time%] msiexec exit %ERRORLEVEL% >> %LOG%
NET START WazuhSvc >> %LOG% 2>&1
echo [%date% %time%] Done >> %LOG%
O oem/install.bat roda no fim da instalação do Windows. Ele baixa o agente e o registra no manager. O arquivo precisa de quebras de linha do Windows (CRLF).
.gitignore
.env
storage/
Como subir
cp .env.example .env # editar valores
docker compose up -d
docker logs -f windows-ws
# acompanhar em http://HOST:8006
# agentes registrados no manager
docker exec single-node-wazuh.manager-1 /var/ossec/bin/agent_control -l
Observações
- As portas são publicadas apenas em
127.0.0.1e na interface da VPN (Tailscale); nada fica exposto na LAN ou na internet. - A versão do agente deve ser igual ou menor que a do manager.
- A RAM fica apertada em hardware modesto (o Wazuh usa cerca de 2,5 GB e a VM 3 GB). Se o servidor ficar lento, reduza a VM ou o heap do indexer.
- Se o agente não conectar, libere as portas 1514 (eventos) e 1515 (enrollment) no firewall e veja o
C:\OEM\wazuh-install.logdentro da VM.