Série Firecracker:
- Parte 01: Firecracker
- Parte 02: Construindo um nano-Lambda
- Parte 03: Redes no Firecracker (você está aqui)
- Parte 04: Snapshots
- Parte 05: Firecracker em produção
No artigo anterior, construímos um nano-Lambda que executa funções Python em microVMs isoladas. Gerou QR Codes lindos. Mas tinha uma limitação: a VM era uma ilha. Sem rede, sem internet, sem comunicação com o mundo exterior.
Hoje vamos resolver isso. Vamos dar internet pro nosso nano-Lambda.
E pra provar que funciona de verdade, vamos construir algo útil: um validador de URLs. Você passa uma lista de sites, a microVM acorda, dispara requests em paralelo, e retorna um JSON com o status de cada um. Simples, prático, e impossível de fazer sem rede.
Por que rede em microVMs é diferente?
Quando você roda um container Docker, a rede “simplesmente funciona”. Docker cria bridges, configura NAT, gerencia IPs… tudo automático. Você nem pensa nisso.
Com Firecracker, você pensa. Muito.
Firecracker é minimalista até na rede. Ele suporta um único tipo de dispositivo de rede: virtio-net. E ele espera que você, configure tudo do lado do host. O Firecracker só conecta a VM a uma interface TAP que você criou.
Isso parece trabalhoso (e é), mas tem um lado bom: você entende exatamente o que tá acontecendo. Sem mágica, sem camadas escondidas. E isso é ótimo pra segurança — você controla cada pacote que entra e sai.
O que vamos construir
Nosso objetivo final:
- Criar uma interface TAP no host
- Configurar NAT pra VM acessar a internet
- Configurar DNS dentro da VM
- Atualizar o nano-Lambda pra suportar rede
- Criar um validador de URLs que funciona de verdade
Bônus: vamos bloquear acesso da VM à rede local, permitindo apenas internet pública. Isolamento de verdade.
Conceitos rápidos: TAP, Bridge e NAT
Antes de meter a mão na massa, um glossário rápido:
TAP: É uma interface de rede virtual. Pensa nela como um “cabo de rede imaginário”. De um lado, a microVM acha que tá conectada numa placa de rede real. Do outro lado, o host Linux vê uma interface de rede normal que pode rotear, filtrar, fazer NAT…
Bridge: Conecta várias interfaces de rede como se fossem um switch. Útil quando você quer que várias VMs conversem entre si.
NAT (Network Address Translation): Permite que a VM use o IP do host pra acessar a internet. A VM tem um IP privado (tipo 172.16.0.2), mas quando ela acessa google.com, o pacote sai com o IP público do host.
Pra nosso caso, vamos usar a configuração mais simples: TAP + NAT. Uma interface TAP conectada diretamente ao host, com masquerading pra internet.

O diagrama mostra exatamente o que vamos construir: a App Python dentro da MicroVM se comunica via eth0 (172.16.0.2), que conecta na interface tap0 do host. O firewall faz NAT pra internet pública, mas bloqueia acesso à rede local (192.168.x.x). Isolamento de verdade.
Nota sobre firewalls: Neste artigo uso
firewalld(padrão no Fedora). Se você usa Ubuntu, o sistema usaiptables. Os scripts do repositório detectam automaticamente qual firewall está ativo e usam os comandos corretos. Se preferir seguir com iptables, os conceitos são os mesmos — apenas a sintaxe muda.
Passo 1: Criando a interface TAP
Primeiro, precisamos criar uma interface TAP. Isso é feito com o comando ip tuntap:
# Cria a interface TAP
sudo ip tuntap add dev tap0 mode tap
# Atribui um IP pro lado do host
sudo ip addr add 172.16.0.1/24 dev tap0
# Sobe a interface
sudo ip link set tap0 up
Pronto. Agora temos uma interface tap0 com IP 172.16.0.1. A VM vai usar 172.16.0.2.
Pra verificar:
ip addr show tap0
Deve mostrar algo como:
tap0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 ...
inet 172.16.0.1/24 scope global tap0
Passo 2: Configurando NAT no host
Agora precisamos que o host faça NAT — traduza os pacotes da VM pro IP do host quando eles saem pra internet.
# Habilita IP forwarding (permite que o host roteie pacotes)
sudo sysctl -w net.ipv4.ip_forward=1
# Adiciona a interface TAP à zona trusted (permite todo tráfego dela)
sudo firewall-cmd --zone=trusted --add-interface=tap0
# Habilita masquerading (NAT) pra saída
sudo firewall-cmd --add-masquerade
Simples assim. O firewalld cuida do resto. A zona trusted permite todo tráfego da TAP, e o masquerade faz o NAT automático.
Dica: Pra descobrir sua interface de saída, use
ip route | grep default. A interface aparece depois de “dev”.
Bloqueando acesso à rede local (segurança)
Aqui tá o ouro do isolamento. Queremos que a VM acesse a internet pública, mas NÃO a sua rede local. Imagina se alguém mandar um código malicioso que tenta escanear sua rede interna?
# Bloqueia acesso a redes privadas usando rich rules
sudo firewall-cmd --zone=trusted --add-rich-rule='rule family=ipv4 source address=172.16.0.0/24 destination address=10.0.0.0/8 drop'
sudo firewall-cmd --zone=trusted --add-rich-rule='rule family=ipv4 source address=172.16.0.0/24 destination address=192.168.0.0/16 drop'
Agora a VM pode acessar google.com, mas não pode acessar 192.168.1.1 (seu roteador) ou qualquer máquina na sua rede.
Versão com iptables (Ubuntu)
Se você usa Ubuntu ou prefere iptables: “`bash # Habilita IP forwarding sudo sysctl -w net.ipv4.ip_forward=1 # Configura masquerading (NAT) – substitua eth0 pela sua interface sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # Permite tráfego da TAP pro mundo sudo iptables -A FORWARD -i tap0 -o eth0 -j ACCEPT sudo iptables -A FORWARD -i eth0 -o tap0 -m state –state RELATED,ESTABLISHED -j ACCEPT # Bloqueia acesso a redes privadas sudo iptables -I FORWARD -i tap0 -d 10.0.0.0/8 -j DROP sudo iptables -I FORWARD -i tap0 -d 172.16.0.0/12 -j DROP sudo iptables -I FORWARD -i tap0 -d 192.168.0.0/16 -j DROP # Mas permite a própria subnet da VM sudo iptables -I FORWARD -i tap0 -d 172.16.0.0/24 -j ACCEPT “`Passo 3: Configurando o Firecracker pra usar a TAP
Quando iniciar o Firecracker, precisamos dizer pra ele usar a interface TAP. Isso é feito via API:
curl --unix-socket /tmp/firecracker.socket -X PUT \
"http://localhost/network-interfaces/eth0" \
-H "Content-Type: application/json" \
-d '{
"iface_id": "eth0",
"guest_mac": "AA:FC:00:00:00:01",
"host_dev_name": "tap0"
}'
Isso conecta a interface eth0 dentro da VM à interface tap0 do host.
Passo 4: Configurando a rede dentro da VM
Agora a parte que pega muita gente: configurar a rede DENTRO da microVM. O Firecracker só conecta o “cabo”. Quem configura IP, gateway e DNS é o sistema dentro da VM.
Precisamos atualizar nosso rootfs pra configurar a rede no boot. Primeiro, vamos montar a imagem:
# Cria um diretório temporário para montar
sudo mkdir -p /mnt/rootfs
# Monta a imagem ext4
sudo mount rootfs-python.ext4 /mnt/rootfs
# Agora você pode editar os arquivos dentro de /mnt/rootfs
Com o rootfs montado, vamos adicionar as configurações de rede:
# Configura interface de rede estática
sudo cat > /mnt/rootfs/etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 172.16.0.2
netmask 255.255.255.0
gateway 172.16.0.1
EOF
# DNS - crucial pra resolver nomes
sudo cat > /mnt/rootfs/etc/resolv.conf << 'EOF'
nameserver 8.8.8.8
nameserver 1.1.1.1
EOF
O truque aqui é o /etc/resolv.conf. Sem ele, ping google.com falha mesmo com a rede funcionando. A VM precisa saber onde perguntar “qual o IP de google.com?”.
Atualizando o script de boot
Também precisamos garantir que a interface suba antes de rodar nossa função. Atualize o /mnt/rootfs/run-function.sh:
#!/bin/sh
echo "Configurando rede"
# Sobe a interface de rede
ip link set eth0 up
ip addr add 172.16.0.2/24 dev eth0
ip route add default via 172.16.0.1
echo "Testando conectividade..."
ping -c 1 8.8.8.8 > /dev/null 2>&1 && echo "Internet OK" || echo "Sem internet"
echo ""
echo "nano-Lambda executando..."
echo ""
if [ -f /functions/handler.py ]; then
cd /functions
python3 handler.py
RETVAL=$?
echo ""
echo "Execução finalizada (exit: $RETVAL)"
else
echo "ERRO: handler.py não encontrado"
fi
echo ""
sync
sleep 1
poweroff -f
Depois de fazer todas as alterações, desmonte o rootfs:
# Desmonta o rootfs
sudo umount /mnt/rootfs
Passo 5: Atualizando o build-rootfs.sh
Vamos atualizar o script de build do rootfs pra incluir as configurações de rede:
# Adiciona pacotes de rede
apk add --root /rootfs --no-cache \
python3 \
py3-requests \
py3-urllib3 \
ca-certificates
O ca-certificates é importante pra HTTPS funcionar. Sem ele, requests.get('https://google.com') vai reclamar de certificado inválido.
Importante: Se você já tem um
rootfs-python.ext4do artigo anterior, apague-o antes de rodar o build novamente. O rootfs antigo não tem as dependências de rede (requests,ca-certificates). Sem isso, a função vai falhar comModuleNotFoundError.rm -f rootfs-python.ext4 sudo ./build-rootfs-network.sh
Passo 6: O Validador de URLs
Agora a função que vai provar que tudo funciona. Salve como exemplo-validador/handler.py:
#!/usr/bin/env python3
"""
Função nano-Lambda: Validador de URLs
Lê uma lista de URLs de /functions/input.txt e retorna o status HTTP de cada uma.
Usa requests em paralelo pra performance.
"""
import json
import sys
import ssl
import socket
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
try:
import requests
HAS_REQUESTS = True
except ImportError:
import urllib.request
import urllib.error
HAS_REQUESTS = False
def check_url_requests(url):
"""Verifica URL usando requests."""
try:
resp = requests.head(url, timeout=10, allow_redirects=True)
return {
"url": url,
"status": resp.status_code,
"ok": resp.ok
}
except requests.exceptions.SSLError as e:
return {"url": url, "status": "SSL_ERROR", "ok": False, "error": str(e)}
except requests.exceptions.ConnectionError:
return {"url": url, "status": "CONNECTION_ERROR", "ok": False}
except requests.exceptions.Timeout:
return {"url": url, "status": "TIMEOUT", "ok": False}
except Exception as e:
return {"url": url, "status": "ERROR", "ok": False, "error": str(e)}
def check_url_urllib(url):
"""Verifica URL usando urllib (fallback)."""
try:
req = urllib.request.Request(url, method='HEAD')
with urllib.request.urlopen(req, timeout=10) as resp:
return {
"url": url,
"status": resp.getcode(),
"ok": 200 <= resp.getcode() < 400
}
except urllib.error.HTTPError as e:
return {"url": url, "status": e.code, "ok": False}
except urllib.error.URLError as e:
return {"url": url, "status": "URL_ERROR", "ok": False, "error": str(e.reason)}
except Exception as e:
return {"url": url, "status": "ERROR", "ok": False, "error": str(e)}
def check_ssl_expiry(url):
"""Verifica se o certificado SSL vai expirar em 30 dias."""
try:
hostname = url.replace("https://", "").replace("http://", "").split("/")[0]
context = ssl.create_default_context()
with socket.create_connection((hostname, 443), timeout=10) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
not_after = cert['notAfter']
expire_date = datetime.strptime(not_after, '%b %d %H:%M:%S %Y %Z')
days_left = (expire_date - datetime.now()).days
return {
"expires": not_after,
"days_left": days_left,
"warning": days_left < 30
}
except Exception as e:
return {"error": str(e)}
def main():
try:
with open('/functions/input.txt', 'r') as f:
content = f.read().strip()
except FileNotFoundError:
print("ERRO: /functions/input.txt não encontrado")
sys.exit(1)
# Parseia URLs (uma por linha ou separadas por vírgula)
urls = [u.strip() for u in content.replace(',', '\n').split('\n') if u.strip()]
if not urls:
print("ERRO: Nenhuma URL fornecida")
sys.exit(1)
print(f"Validando {len(urls)} URLs...")
# Escolhe função de verificação
check_func = check_url_requests if HAS_REQUESTS else check_url_urllib
results = []
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(check_func, url): url for url in urls}
for future in as_completed(futures):
result = future.result()
if result["url"].startswith("https://") and result.get("ok"):
result["ssl"] = check_ssl_expiry(result["url"])
results.append(result)
print(f" {result['url']}: {result['status']}")
report = {
"timestamp": datetime.now().isoformat(),
"total": len(urls),
"ok": sum(1 for r in results if r.get("ok")),
"failed": sum(1 for r in results if not r.get("ok")),
"results": results
}
print("")
print("JSON_RESULT_START")
print(json.dumps(report, indent=2))
print("JSON_RESULT_END")
if __name__ == '__main__':
main()
Essa função:
- Lê URLs do input (uma por linha ou separadas por vírgula)
- Verifica cada uma em paralelo usando
concurrent.futures - Pra URLs HTTPS, também verifica se o certificado SSL expira em menos de 30 dias
- Retorna um JSON estruturado com os resultados
Passo 7: Atualizando o nano-lambda.py
Precisamos atualizar o nano-Lambda pra configurar a rede antes de iniciar a VM.
Cuidado com duplicação de regras: Se você rodar o script 10 vezes, não queremos 10 regras idênticas poluindo o firewall. A solução é verificar se a interface já existe antes de configurar:
def setup_network(self):
"""Configura a interface TAP e NAT."""
# Evita duplicar regras se já estiver configurado
if os.path.exists("/sys/class/net/tap0"):
print("[*] Rede já configurada, pulando setup...")
return
print("[*] Configurando rede...")
subprocess.run(
["ip", "tuntap", "add", "dev", "tap0", "mode", "tap"],
check=True
)
subprocess.run(
["ip", "addr", "add", "172.16.0.1/24", "dev", "tap0"],
check=True
)
subprocess.run(["ip", "link", "set", "tap0", "up"], check=True)
subprocess.run(
["sysctl", "-w", "net.ipv4.ip_forward=1"],
check=True, capture_output=True
)
# Configura firewall (firewalld no Fedora)
subprocess.run([
"firewall-cmd", "--zone=trusted", "--add-interface=tap0"
], check=False)
subprocess.run([
"firewall-cmd", "--add-masquerade"
], check=False)
def configure_vm(self):
"""Configura a microVM via API."""
print("[*] Configurando rede da VM...")
self._call_api("PUT", "/network-interfaces/eth0", {
"iface_id": "eth0",
"guest_mac": "AA:FC:00:00:00:01",
"host_dev_name": "tap0"
})
Nota: O script completo no repositório detecta automaticamente se o sistema usa
firewalldouiptablese configura corretamente. Se você usa Ubuntu, o script vai usar iptables sem precisar mudar nada.
Testando!
Com tudo configurado:
# Cria arquivo de input com URLs pra testar
cat > urls.txt << 'EOF'
https://google.com
https://fogonacaixadagua.com.br
https://github.com
https://site-que-nao-existe.xyz
EOF
# Executa o validador
sudo python3 nano-lambda.py exemplo-validador/handler.py "$(cat urls.txt)"
Se tudo funcionar, você verá:
==================================================
nano-Lambda: Executando função em microVM isolada
==================================================
Função: exemplo-validador/handler.py
Input: https://google.com
https://fogonacaixadagua.com.br
...
[*] Configurando rede...
[*] Copiando rootfs template...
[*] Iniciando Firecracker...
[*] Configurando kernel...
[*] Configurando rootfs...
[*] Configurando rede da VM...
[*] Iniciando microVM...
[*] Aguardando execução...
Validando 4 URLs...
https://google.com: 200
https://fogonacaixadagua.com.br: 200
https://github.com: 200
https://site-que-nao-existe.xyz: CONNECTION_ERROR
==================================================
Resultado:
==================================================
{
"timestamp": "2024-12-08T10:30:00",
"total": 4,
"ok": 3,
"failed": 1,
"results": [...]
}
O que aprendemos
Configurar rede no Firecracker é mais trabalhoso que no Docker, mas você ganha:
- Controle total: Você sabe exatamente o que entra e sai
- Isolamento real: A VM pode acessar internet mas não sua rede local
- Segurança: Código malicioso não consegue escanear sua rede interna
Esse é o tipo de isolamento que faz sentido pra executar código não confiável. A VM pode baixar o que quiser da internet, mas tá completamente bloqueada de acessar qualquer coisa na sua rede.
Troubleshooting
VM não consegue pingar nada:
- Verifique se
net.ipv4.ip_forward=1está ativo:sysctl net.ipv4.ip_forward - Verifique se masquerading está ativo:
firewall-cmd --query-masquerade(ouiptables -t nat -L -nno Ubuntu) - Verifique se a TAP está na zona trusted:
firewall-cmd --zone=trusted --list-interfaces
DNS não funciona (ping IP funciona, ping domínio não):
- Verifique
/etc/resolv.confdentro da VM - Tente com DNS diferente:
nameserver 8.8.8.8 - Em redes corporativas ou VPNs, a porta 53 pode ser bloqueada pra fora. Use o DNS da sua rede local
HTTPS falha com erro de certificado:
- Verifique se
ca-certificatesestá instalado no rootfs - Tente
update-ca-certificatesdentro da VM
Próximos passos
Agora seu nano-Lambda tem acesso à internet e pode fazer coisas úteis de verdade. Algumas ideias:
- Web scraper isolado: Baixa páginas, extrai dados, retorna JSON
- Verificador de APIs: Testa endpoints e mede latência
- Downloader seguro: Baixa arquivos suspeitos em ambiente isolado
No próximo artigo, vamos explorar snapshots e restore — a tecnologia que permite o Lambda da AWS iniciar em milissegundos. Spoiler: envolve tirar “fotos” da memória da VM.
Até lá!
Este artigo faz parte da série “Firecracker: MicroVMs na Prática”. Todo o código está disponível em GitHub.





Deixe um comentário