Proxy em Containers

Saudações.

Vou apresentar a vocês algumas experiências com o Squid Proxy em ambiente de containers.

Pré-requisitos:

  • VM/VPS/Host com Docker (standalone ou Swarm);
  • Internet no servidor (sua VPS ou host);

1 – Conceito de Proxy e o software Squid

O Squid (https://www.squid-cache.org/) é um software escrito em C++ cujo objetivo é ser um framework supremo de Proxy.

Foi criado em 1996 e possui décadas de maturidade.

Ele consegue atuar de todas as formas possíveis dentro do escopo dos protocolos HTTP, HTTPs, FTP, ICP e alguns outros.

Principais recursos:

  • Proxy direto: Atende clientes HTTP fornecendo Internet indiretamente;
  • Proxy reverso: Recebe conexões em direção a um site e entrega ao servidor interno correto;
    • Conexão de entrada HTTPs, conexão interna HTTP;
    • Cache ajuda a aliviar a carga no fornecimento de arquivos e assets;
  • Proxy transparente: Captura tráfego de rede e se passa pelo site acessado;
  • Cache: Armazena arquivos e recursos em RAM e/ou disco para acelerar a Internet e economizar banda;
    • Esse era sua principal função antes da migração em massa para HTTPs;
    • Num mundo HTTPs o cache perdeu seu lugar e descontinou o Squid nos provedores;
  • Autenticação: Permite discriminar usuários por login e senha e controlar quais recursos o usuário pode acessar e com quais caractarísticas técnicas (IP de origem, QoS, horário, etc);
  • ACL: Permite discriminar IPs, domínios, usuários, cabeçalhos para decidir permitir, negar, redirecionar ou modificar os detalhes do acesso;

No Docker standalone (servidor solitário) ou no cluster Docker Swarm o Squid encontra a oportunidade de nos ajudar no ingress (conexões que vem da Internet em direção aos nossos serviços) atuando como Proxy Reverso ou no egress, fornecendo acesso controlado e auditado do que nossos containers estão indo buscar na Internet.

2 – Estudo do caso

Observe meu caso:

  • Cluster de servidores Docker Swarm com workers distribuídos em vários datacenters;
  • Containers que precisam acessar a Internet (bots, pesquisas, scrapping, tools de agentes);
  • Cada container navegava com um IP diferente pelo NAT do Linux de cada servidor;
  • Containers workers rodam:
    • Web Scrapping: Transformam os sites acessados em documentos JSON e MarkDown;
    • Web Search: Meus agentes de IA precisam navegar (Agent Tools) em vários sites para realizar pesquisas;
    • API: Acesso a API de provedores de IA, APIs corporativas, scrapping remoto (firecrawl), etc;
Servidores Workers do Docker Swarm em vários datacenters com IPs de saída difernetes.

Problemas que enfrentei:

  • Problema 1 – leve: Sites modernos implementam limite de requisições por IP (RPM – Request per Minute) e limite de banda por IP (Traffic Shapping);
  • Problema 2 – moderado: Sites protegidos por WAF (Web Application Firewall), o WAF aprende o padrão de repetição “like a bot” e bloqueiam o IP de origem;
  • Problema 3 – grave: Alguns containers rodam em países diferentes, o bloqueio por GEOIP impede o funcionamento nesses servidores;
  • Problema 4 – gravíssimo: Para cada IP bloqueado os containers que ainda conseguem navegar passam a fazer muitas requisições e os problemas anteriores se amplificam.

A solução parece óbvia: Precisamos contratar um proxy externo PAGO para balancear essa carga de saída entre milhares de IPs diferentes para que os bloqueios e limites sejam contornados.

Containers usando proxy comercial para balancear e escolher os IPs que os sites enxergam para contornar bloqueios.

Como muitas das minhas ferramentas precisam apenas de controle de uso mais do que volume, optei por usar o Squid como gateway dos meus containers, e no Squid eu implemento toda a lógica do negócio, usando IPs próprios balanceados ou entregando para provedores de Proxy quando necessário.

Fase 1 – No diagrama abaixo o problema parece se agravar, os containers que antes navegavam com os IPs de seus servidores agora navegam com o IP do Squid.

Servidor Squid atenderá os containers e os conduzirá à Internet usando o IP do Squid, centrando os acessos.

Fase 2 – O servidor Squid agora pode ser configurado com a lógica do nosso negócio, indo diretamente na Internet para os serviços menos burocráticos e delegando para provedores Proxy o acesso sensível a IPs de origem, observe:

Servidor Squid atua como intermediário do acesso, permitindo apenas o acesso autorizado (Auth e ACL) e fazendo o direcionamento (Routing) para o serviço adequado que garanta o IP de origem e a técnica adequada de obter o site ou API.

Fase 3 – Quando a rede escala e você precisa balancear seus softwares para navegarem na Internet fazendo centenas a milhares de requisições por segundo:

Containers configurados para usar proxy balanceado por DNS, cada cluster em uma região para maior redundância, clusters de squids balanceiam a saída até os sites finais, com ou sem provedores de proxies acima deles.

3 – Clientes HTTP e Variáveis de Ambiente

Para que você não tenha que configurar cada software e cada navegador para usar o proxy, a industria facilitou com o uso de alguns variáveis de ambiente que se presentes, desviam o tráfego para o proxy.

3.1 – Variáveis de Ambiente

As principais variáveis são:

  • http_proxy: Endereço do servidor proxy para requisições do schema “http://“;
  • https_proxy: Endereço do servidor proxy para requisições do schema “https://“;
  • ftp_proxy: Endereço do servidor proxy para requisições do schema “ftp://“;
  • socks_proxy: Endereço do servidor proxy para conexões SOCKS4/SOCKS5;
  • no_proxy: Registro de excessões separadas por virgula, as excessões não usam proxy e vão direto para o site de destino;
  • all_proxy: Funciona como fallback genérico. Se não houver uma variável específica para o schema do protocolo (HTTP, HTTPS, FTP, etc.), o cliente usa esse endereço;

Você quer cobrir a maioria dos cenários, configurar http_proxy, https_proxy, no_proxy e all_proxy.

3.2 – Declarações com case-sensitive

Infelizmente diferentes softwares e bibliotecas HTTP fazem a leitura nas versões upper-case (maiúsculas) dessas variáveis, assim, recomendo declarar nas duas formas, mesmo a lower-case (minúscula) seja a oficial.

Variável padrãoVariável uper-caseFinalidade
http_proxyHTTP_PROXYProxy para http://
https_proxyHTTPS_PROXYProxy para https://
ftp_proxyFTP_PROXYProxy para ftp://
socks_proxySOCKS_PROXYProxy para socks
no_proxyNO_PROXYDeclaração de destinos sem proxy
all_proxyALL_PROXYProxy fallback para todos os protocolos

3.3 – Bibliotecas de cliente HTTP

Você precisará estudar o manual da biblioteca HTTP que seus programas utilizam para afinar a configuração de proxy. As principais bibliotecas são:

  • Python: requests (a mais popular), urllib3 (baixo nível, avançada), httpx (moderna, HTTP/2), aiohttp (async nativa), pycurl (performance máxima, multi-protocolo), httplib2 (mais antiga, cache, HTTP/2), urllib (stdlib, nativa da linguagem);
  • JavaScript: fetch API (nativa), axios (mais popular), node-fetch (versões antigas do Node), got (rica em funcionalidades), ky (wrapper leve), undici (alta performance), superagent (encadeável, tem plugins), needle (leve, suporta streaming);
  • Go: net/http (stdlib, geral), resty (wrapper da net/http), heimdall (avançada), gentleman (baseado em plugins), grequests (inspirada na requests do Python), go-retryablehttp (HashiCorp, net/http com retries automáticos e backoff).

3.4 – Declarando e usando variáveis de proxy

Exemplos de como declarar variáveis de ambiente e seus efeitos.

Usando endereço de proxy universal para todos os protocolos:

Bash
# Usando all_proxy:
    export all_proxy="http://proxy.empresa.com:3128"
    unset  ftp_proxy;
    unset  http_proxy;
    unset  https_proxy;
    
# Todas as requisicoes abaixo vao via HTTP até o proxy (sem criptografia)
    curl  http://api.ipify.org;  echo;  # Usa o proxy (http => proxy => http)
    curl  https://api.ipify.org; echo;  # Usa o proxy (http => proxy => https)
    curl  ftp://api.ipify.org;   echo;  # Usa o proxy (http => proxy => ftp)

Proxy apenas para HTTP ou apenas para HTTPs:

Bash
# Apenas HTTP
    # - Declarar proxy HTTP
    export http_proxy="http://proxy0.empresa.com:3128";
    # - Sem proxy HTTPs
    export https_proxy=""; # declarar vazio, ou
    unset  https_proxy;    # remover variavel de ambiente

    # Requisicao HTTP => Proxy HTTP
    curl  http://api.ipify.org;  echo; # Usar proxy na porta 3128 do proxy0

    # Requisicao HTTPs => direto na Internet
    curl  https://api.ipify.org; echo; # Nao passa pelo proxy

# Apenas HTTPs
    # - Sem proxy HTTP
    export http_proxy=""; # declarar vazio, ou
    unset  http_proxy;    # remover variavel de ambiente
    # - Declarar proxy HTTPs
    export https_proxy="https://proxy0.empresa.com:8443";

    # Requisicao HTTP => direto na Internet
    curl  http://api.ipify.org;  echo; # Nao passa pelo proxy

    # Requisicao HTTPs => Proxy HTTPs
    curl  https://api.ipify.org; echo; # Usar proxy na porta 8443 do proxy0

CURL com proxy HTTP e HTTPs em endereços diferentes:

Bash
# Declarar enderecos dos servidores proxy:
    # - Proxy HTTP
    export http_proxy="http://proxy1.empresa.com:3128"
    # - Proxy HTTPs
    export https_proxy="http://proxy2.empresa.com:3129"

# Acessar sites:
    curl  http://api.ipify.org;  echo;  # Usa proxy na porta 3128 do proxy1
    curl  https://api.ipify.org; echo;  # Usa proxy na porta 3129 do proxy2

# Ignora o uso de proxy:
    curl --no-proxy  http://api.ipify.org;  echo; # Acesso direto na Internet
    curl --no-proxy  https://api.ipify.org; echo; # Acesso direto na Internet

CURL com proxy geral de fallback:

Bash
# Declarar enderecos dos servidores proxy:
    # - Proxy HTTP, HTTPs, Sockets, FTP, ...
    export all_proxy="http://proxy3.empresa.com:3128"

# Acessar sites:
    curl  http://api.ipify.org;  echo;  # Usa proxy na porta 3128 do proxy3
    curl  https://api.ipify.org; echo;  # Usa proxy na porta 3128 do proxy3

CURL com proxy geral de fallback e HTTPs (mais específico vence):

Bash
# Declarar proxy de HTTPs:
    export https_proxy="http://proxy4.empresa.com:8443";

# Declarar proxy geral:
    export all_proxy="http://proxy5.empresa.com:3128";

# Acessar sites:
    curl  http://api.ipify.org;  echo;  # Usa proxy na porta 3128 do proxy5
    curl  https://api.ipify.org; echo;  # Usa proxy na porta 8443 do proxy4

CURL especificando proxy na linha de comando (sem variável de ambiente):

Bash
# Proxy explicito, canal HTTP acessando site HTTP:
    curl  -m 5  -x http://proxy6.empresa.com:1080  http://api.ipify.org; echo;

# Proxy explicito, canal HTTP (insecuro) acessando site HTTPs:
    curl  -m 5  -x http://proxy7.empresa.com:3128  https://api.ipify.org; echo;

4 – Canais entre cliente e proxy

Existem 3 canais (protocolos) para comunicação com um proxy:

  • HTTP (RFC 2616): Transporta o protocolo em puro texto sobre TCP (padrão) ou UDP;
  • HTTPs (RFC 2818): Transporta o protocolo HTTP dentro de uma conexão TCP protegida por TLS (padrão) ou SSL;
    • Envolve o uso de certificados x509;
    • Certificados auto-assinados não possuem reconhecimento dos clientes;
    • Certificados assinados tem preço, os gratuitos possuem validade curta (3 meses);
  • ICP (RFC 2186): Utiliza o Internet Cache Protocol como canal de comunicação, normalmente somente entre servidores proxy (essa adjacência se chama cache peer).

O cliente pode se comunicar com o Proxy usando as seguintes combinações de protocolos cliente=>proxy=>site:

4.1 – Proxy com HTTPs fim-a-fim

Essa é a configuração perfeita.

O cliente se conecta via HTTPs no Proxy. Nessa etapa há a validação do certificado do Proxy.

Se a conexão TLS com o Proxy for bem sucedida o cliente solicita método CONNECT e é conduzido de forma transparente (TCP opaco) até IP e porta do site.

  • 1 – Cliente faz conexão TLS (TCP#1 + TLS) até o proxy;
    • Cliente analisa certificado x509 do proxy;
  • 2 – Cliente envia ao proxy: CONNECT exemplo.com:443 HTTP/1.1;
  • 3 – Proxy irá se conectar ao site solicitado:
    • Resolver o DNS, pode obter IPv4 e/ou IPv6;
    • Conexão TCP simples até o IP e porta do site;
  • 4 – Proxy retorna o status da operação CONNECT ao cliente:
    • Proxy => Cliente: HTTP/1.1 200 Connection established;
    • Qualquer erro nesse ponto impede a continuação, erros comuns:
      • Erro de proxy sem rede;
      • Site bloqueou o IP do proxy;
      • Site fora do ar;
    • Quando o proxy respondeu OK (status 200 na operação CONNECT) a conexão passou para o modo transparente (tunnel/relay), tudo que o cliente enviar a partir desse ponto vai direto para o site final;
  • 5 – Cliente inicia uma conexão TLS dentro da conexão TCP+TLS já estabelecida com o proxy;
    • Cliente analisa o certificado x509 do site;
    • Cliente envia requisição (GET, POST, HEAD, …) para o site;

Apenas o IP é alterado (o site enxerga o IP do Proxy apenas).

Não é possível fazer CACHE de objetos (arquivos) no proxy devido a criptografia ficar intacta fim-a-fim.

4.2 – Proxy HTTP para acessos HTTPs

Cliente se conecta via HTTP com o Proxy, não há criptografia nesse canal inicial.

  • 1 – Cliente faz conexão TCP sem criptografia até o proxy;
  • 2 – Cliente envia ao proxy: CONNECT exemplo.com:443 HTTP/1.1;
  • 3 – Proxy irá se conectar ao site solicitado:
    • Resolver o DNS, pode obter IPv4 e/ou IPv6;
    • Conexão TCP simples até o IP e porta do site;
  • 4 – Proxy retorna o status da operação CONNECT ao cliente (status 200);
  • 5 – Cliente inicia uma conexão TLS dentro da conexão TCP já estabelecida com o proxy;
    • Cliente analisa o certificado x509 do site;
    • Cliente envia requisição (GET, POST, HEAD, …) para o site;

Observe que a segurança é garantida pela negociação fim-a-fim do cliente até o site, mas comprometida na origem:

  • O cliente não pode validar se o Proxy é real ou um ataque de homem no meio;
  • Captura de tráfego torna visivel quais sites o cliente está acessando.

A vantagem desse método é:

  • Não precisa fazer setup de certificado no Proxy;
  • O proxy pode ser um container ou servidor implementado rapidamente.

Ambiente de containers usando Squid como gateway HTTP de saída:

4.3 – Proxy HTTP para acessos HTTP

Esse caso é o mais antigo de todos, não existe criptografia em nenhum dos canais. Atualmente esse modo é restrito a ambientes intranet e de soluções muito específicas.

A morte desse modo na Internet aconteceu pela implementação do HTTPs como padrão de navegação.

É o único que permite o CACHE de objetos (arquivos) no proxy para economizar banda e acelerar a entrega.

O cliente trata o proxy como o próprio site com algumas pequenas diferenças. Exemplos de cabeçalhos envolvidos na transação com o proxy HTTP-HTTP:

  • O método inclui a URL inteira:
    • Com proxy (Proxy-* não é repassado ao site):
      • GET http://example.com/pagina.html HTTP/1.1
      • Host: example.com
      • Proxy-Authorization: Basic cHJ…w== (login no proxy)
      • Proxy-Authorization: keep-alive
    • Sem proxy:
      • GET /pagina.html HTTP/1.1
      • Host: example.com
  • Cabeçalhos inseridos pelo cliente que instruem o proxy:
    • Proxy-Connection: keep-alive
  • Cabeçalhos inseridos pelo proxy na requisição para o site:
    • Via: 1.1 squid-host (squid/7.5)
    • X-Forwarded-For: 192.168.1.10
  • Cabeçalhos inseridos pelo proxy na resposta ao cliente:
    • X-Cache: MISS (ou HIT)
    • X-Cache-Lookup: HIT from squid-host:3128

Casos de uso:

  • Controle de acesso em intranet de APIs protegidas;
  • Roteamento de URLs em ambientes de micro-serviços.

4.4 – Proxy HTTPs para acessos HTTP

Essa combinação é comum em ambientes de proxy reverso, onde o cliente pode ser qualquer cliente na Internet ou Intranet e o destino é um serviço interno (container) que possui apenas HTTP como meio de comunicação.

O Traefik, Caddy e Squid são exemplos de implementações comuns.

O certificado x509 deve ser obtido via compra (1 ano de validade, renovavel) ou via provedor ACME como o LetsEncrypt.

O Squid não possui uma integração nativa com LetsEncrypt, esse recurso pode ser obtido por scripts externos (certbot) que obtem e renovam o certificado e mandam um “reconfigure” para o Squid ler novamente os arquivos.

Para obter o mesmo efeito do Traefik seria necessário criar um software agente que monitore via events os labels dos containers e construa a configuração do Squid para o caso. Requer acesso ao socket Unix do Docker.

5 – Certificados auto-assinados

Agora que ficou claro o uso de HTTPs e os canais onde ele aparece, preciso esclarecer o que acontece em ambientes seguros.

O ideal é que todos os seus serviços se comuniquem por HTTPs, deixando tudo muito seguro por criptografia.

O problema é que isso adiciona a camada TLS, que envolve:

  • Gestão da Unidade Certificadora (CA);
  • Geração de chaves privadas nos micro-serviços;
  • Geração de certificados;
  • Overhead de criptografia:
    • Latência levemente maior no inicio de toda conexão;
    • Consumo de CPU no processo de criptografia dos dados;

Você tem dois caminhos:

  • 1 – Gerar certificados assinados corretamente e reconhecidos globalmente, cada micro-serviço possuindo o seu certificado – dispendioso e caro;
  • 2 – Gerar uma CA local para assinar seus próprios certificados, problemas:
    • Os clientes HTTP (CURL, navegadores) não vão reconhecer sua CA como globalmente confiável e se negarão a abrir as URLs;
    • Você precisará instalar seu certificado de CA em todos os micro-serviços gerados em sua infra;

Bom… e tem o atalho! Ordenar que os clientes HTTP ignorem o certificado auto-assinado e confiem neles.

Novamente você terá que ler o manual das bibliotecas envolvidas.

No CURL:

Bash
# Usar proxy com certificado auto-assinado e site reconhecido globalmente:
    # - Ignora a validacao do certificado auto-assinado (proxy10.empresa.com:8443)
    # - Não ignora a validacao do site final (https://api.ipify.org)
    curl \
        --proxy-insecure \
        -x http://proxy10.empresa.com:8443 \
        \
        https://api.ipify.org; echo;


# Usar proxy com certificado reconhecido globalmente com site auto-assinado:
    # - Não ignora validacao do certificado do proxy (proxy11.empresa.com:8443)
    # - Ignora a validacao do site final (https://api.ipify.org)
    curl \
        -x http://proxy11.empresa.com:8443 \
        \
        --insecure \
        \
        https://api.ipify.org; echo;


# Usar proxy com certificado auto-assinado e site com certificado auto-assinado:
    # - Ignora a validacao do certificado auto-assinado (proxy12.empresa.com:8443)
    # - Ignora a validacao do site final (https://api.ipify.org)
    curl \
        --proxy-insecure \
        -x http://proxy12.empresa.com:8443 \
        \
        --insecure \
        \
        https://api.ipify.org; echo;

6 – Cache Peer

O recurso mais incrível do Squid é a flexibilidade de conectar proxies entre si. Esse recurso se chama “cache-peer” e pode ser implementado usando protocolo HTTP/HTTPs ou ICP.

O resultado é uma rede hierárquica ou mesh entre muitos servidores de proxy que, combinados com a flexibilidade de replicação e distribuição das plataformas de containers criam uma solução pronta pra todo tipo de engenharia.

O balanceamento por DNS, IPVS e round-robin entrega uma nuvem ultra-resiliente de acesso a serviços HTTP.

7 – Logs e observabilidade

O Squid conta com vários canais para logs, por padrão utiliza-se log em arquivos mas você pode alterar para TCP, UDP, Syslog e STDOUT (saída padrão).

Canais de logs:

  • Arquivo: Grava diretamente em arquivos (volume);
  • Daemon: Envia os logs para um processo vizinho, esse processo pode gravar em arquivos (padrão) ou transmitir no formato desejado;
  • Syslog local: Enviar para syslog local (/dev/log);
  • Syslog remoto: Enviar via TCP ou UDP para um servidor de logs remoto;

Usando Syslog via TCP (mais seguro):

Recursos da geração de logs:

  • Formato: Permite formatar e personalizar a linha de log;
  • Filtro: Permite filtrar por ACL quais logs são enviados para determinados canais;

Para observabilidade e métricas, precisamos de um exportador paraleo.

O serviço “squid-exporter” (https://github.com/boynux/squid-exporter) pode ser integrado para fazer a transmissão da telemetria em tempo real ou cronometrada.

8 – Squid no Docker

Para usar o Squid o processo é muito simples, você pode construir um container do zero. Você poderá incluir sua configuração personalizada e embutir sua regra de negócios nele.

Nesse capítulo vou demonstrar uma POC (prof of concept) de como rodar. Esse projetinho não é exatamente completo e perfeito mas serve para a didática da técnica.

Crie uma pasta para esse projeto, vou chamar de “squid-basic“. Na pasta coloque o arquivo Dockerfile:

Dockerfile – squid-basic/Dockerfile
# Container do Squid com base Alpine
FROM        alpine:3.23.3

# Variaveis globais
ENV         TZ=America/Sao_Paulo
ENV         PS1='\u@\h:\w\$ '
ENV         LANG=en_US.UTF-8
ENV         LANGUAGE=en_US.UTF-8

# Atualizar base
RUN         apk update && apk upgrade

# Instalar pacotes
RUN         apk add squid curl openssl

# Envs de proxy, ontainer usando o proprio squid como proxy
ENV         http_proxy=http://localhost:1080/

# https requer projetar uso de certificados
#ENV         https_proxy=https://localhost:443/

# Portas internas do Squid
EXPOSE      443/tcp
EXPOSE      1080/tcp
EXPOSE      8080/udp
EXPOSE      8443/tcp

# Processo do container
CMD         [ "/usr/sbin/squid", \
              "-n", \
              "proxy", \
              "-f", \
              "/etc/squid/squid.conf", \
              "--foreground" \
            ]

Dentro da pasta do projeto, construa a imagem “squid-basic” com tag padrão “latest“:

Bash
# Criar imagem squid-basic
    docker build . \
        --pull \
        --no-cache \
        -f Dockerfile \
        -t squid-basic:latest;

A imagem está pronta para uso, mas infelizmente, usando a configuração padrão do squid em /etc/squid/squid.conf (caminho dentro do container).

Vamos criar um arquivo squid.conf na pasta do projeto para mapear como volume de arquivo dentro do container:

squid.conf
# Portas HTTP
http_port 1080
http_port 3128
http_port 8080

# Portas HTTPs, precisa projetar a forma de gerar e usar certificados
#https_port 8443 cert=/certs/fullchain.pem key=/certs/privkey.pem
#https_port 443 cert=/certs/fullchain.pem key=/certs/privkey.pem

# Lista de prefixos para controle de acesso
# - Localhost
acl LOCALHOSTv4 src 127.0.0.1/32
acl LOCALHOSTv6 src ::1/128
acl LOCALHOST src 127.0.0.1/32
acl LOCALHOST src ::1/128

# - Redes locais conhecidas
acl LOCALNETSv4 src 10.117.0.0/16
acl LOCALNETSv6 src 2001:db8:10:117::/64
acl LOCALNETSv6 src fe80::/64

# - Redes privadas (todas)
# - IPv4:
acl PRIVATESv4 src 10.0.0.0/8
acl PRIVATESv4 src 169.254.0.0/16
acl PRIVATESv4 src 172.16.0.0/12
acl PRIVATESv4 src 192.0.0.0/24
acl PRIVATESv4 src 192.0.2.0/24
acl PRIVATESv4 src 192.88.99.0/24
acl PRIVATESv4 src 192.168.0.0/16
acl PRIVATESv4 src 198.18.0.0/15
acl PRIVATESv4 src 198.51.100.0/24
acl PRIVATESv4 src 203.0.113.0/24
acl PRIVATESv4 src 224.0.0.0/4
acl PRIVATESv4 src 240.0.0.0/4
# - IPv6:
acl PRIVATESv6 src ::/3
acl PRIVATESv6 src 2001:db8::/32
acl PRIVATESv6 src 4000::/2
acl PRIVATESv6 src 8000::/1

# - Redes publicas confiaveis (seus servidores, VPs, VM)
# - IPv4
acl ALLOWED_NETWORKSv4 src 172.233.34.15
acl ALLOWED_NETWORKSv4 src 45.255.128.0/22
# - IPv4
acl ALLOWED_NETWORKSv6 src 2600:3c0d::f04d:a4ef:fc13:7cb8/128
acl ALLOWED_NETWORKSv6 src 2804:cafe::/32

# Controle de acesso
# - Sempre permitir localhost
http_access allow LOCALHOSTv4
http_access allow LOCALHOSTv6

# - Sempre permitir redes locais (docker networks)
http_access allow LOCALNETSv4
http_access allow LOCALNETSv6

# - Sempre permitir redes privadas
http_access allow PRIVATESv4
http_access allow PRIVATESv6

# - Sempre permitir IPs publicos conhecidos
http_access allow ALLOWED_NETWORKSv4
http_access allow ALLOWED_NETWORKSv6

# - NEGAR TODO O RESTO
http_access deny all

# Opcoes gerais
pid_filename        /run/squid.pid
icon_directory      /run
err_page_stylesheet none

# Ajustes de parametros
forward_max_tries  25
connect_timeout     2 minutes
read_timeout       15 minutes
request_timeout     5 minutes
shutdown_lifetime  30 seconds

# Arquivos de logs
access_log      daemon:/data/logs/squid-access.log squid !LOCALHOST
cache_log       /dev/null
cache_store_log none
logfile_rotate 10

# Cache
cache_replacement_policy heap GDSF
cache_mem 0 MB
maximum_object_size_in_memory 8 MB
minimum_object_size 0 KB
maximum_object_size 4 MB

# Modo oculto, nao encaminhar cabecalhos que revelam a existencia do proxy
# - Desativar cabecalhos nativos
via off
forwarded_for delete
follow_x_forwarded_for deny all
httpd_suppress_version_string on
visible_hostname localhost
strip_query_terms off
uri_whitespace strip
# - Retirar cabecalhos do Squid
reply_header_access X-Cache deny all
reply_header_access X-Cache-Lookup deny all
reply_header_access X-Squid-Error deny all
request_header_access X-xproxy-role deny all
request_header_access X-Forwarded-For deny all
request_header_access Forwarded deny all
request_header_access Proxy-Connection deny all
request_header_access Proxy-Authorization deny all

Agora vamos rodar o container. Na pasta do projeto onde está o squid.conf, execute:

Bash
# Rede de containers squid (ipv4 only)
    docker network create proxynet \
        -d bridge \
        -o com.docker.network.bridge.name=br-proxynet \
        -o com.docker.network.driver.mtu=1500 \
        -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected;

# Pasta do volume
    # - Pasta principal
    mkdir -p /storage/squid-basic;

    # - Pasta para logs
    mkdir -p /storage/squid-basic/logs;
    chmod 777 /storage/squid-basic/logs;

# Renovar/rodar container squid-basic:
    # Remover atual
    docker rm -f squid-basic 2>/dev/null;

    # Criar e rodar
    docker run -d \
        --restart=always \
        --name=squid-basic \
        --hostname squid-basic.intranet.br \
        \
        --user=root --cap-add=ALL --privileged \
        \
        --cpus=1 \
        --memory 512m --memory-swap 512m --shm-size 512m \
        \
        --tmpfs /run:rw,size=8m \
        -v /storage/squid-basic:/data \
        -v ./squid.conf:/etc/squid/squid.conf \
        \
        --network proxynet \
        \
        -p 1080:1080 \
        -p 3128:3128 \
        \
        squid-basic;

Container rodando, vamos entrar no shell do container:

Bash
# Entrar no shell ash do container:
docker exec -it squid-basic ash;

Testando proxy:

Bash
# Testar com proxy HTTP via variavel http_proxy
curl -v http://api.ipify.org; echo;

    # * Uses proxy env variable http_proxy == 'http://localhost:1080/'
    # * Host localhost:1080 was resolved.
    # * IPv6: ::1
    # * IPv4: 127.0.0.1
    # *   Trying [::1]:1080...
    # * Established connection to localhost (::1 port 1080) from ::1 port 36930 
    # * using HTTP/1.x
    # > GET http://api.ipify.org/ HTTP/1.1
    # > Host: api.ipify.org
    # > User-Agent: curl/8.17.0
    # > Accept: */*
    # > Proxy-Connection: Keep-Alive
    # > 
    # * Request completely sent off
    # < HTTP/1.1 200 OK
    # < Date: Sun, 12 Apr 2026 15:15:07 GMT
    # < Content-Type: text/plain
    # < Content-Length: 14
    # < Server: cloudflare
    # < Vary: Origin
    # < cf-cache-status: DYNAMIC
    # < CF-RAY: 9eb32f424ab1c6ce-GRU
    # < Cache-Status: localhost;detail=mismatch
    # < Connection: keep-alive
    # < 
    # * Connection #0 to host localhost:1080 left intact
    # 45.255.128.158

Observe a primeira linha “http_proxy == ‘http://localhost:1080/’“.

Temos um proxy muito básico e funcional que infelizmente não atende HTTPs.

9 – Projeto Squid Gateway

Criei um projeto chamado “squid-gateway” capaz de prover um Squid pronto para produção e replicação e com suporte HTTPs com certificados do Traefik.

Recursos do container:

  • Controle de portas internas:
    • HTTP_PORT: Padrão 8080;
    • HTTPS_PORT: Padrão 8443;
    • As portas 1080 e 443 sempre serão abertas localmente no container;
  • ACL automática para permitir redes locais (rede do container), redes privadas gerais e alguns IPs públicos da sua escolha;
    • ACL_ENABLE: Padrão “yes”, sem ACL libera tudo (“no”), se ativo:
      • ACL_PERMIT_PRIVATES: Padrão “yes”,
      • ACL_PERMIT_LOCAL: Padrão “yes”, permite redes locais do container;
      • ACL_PERMIT_NETWORKS: Especifique os prefixos confiáveis;
  • Controle de logs para ativar ou desativar;
    • LOG_ENABLE: Padrão “yes”;
    • LOG_FILE_ACCESS : Caminho do access-log no container;
    • LOG_FILE_CACHE: Caminho do cache.log no container;
    • LOG_FILE_STORE: Caminho do store.log no container;
    • Se não especificar o caminho o log não é gerado (padrão “none”);
  • Upstream facilitado para encadeamento de servidores proxy:
    • UPSTREAM_ENABLE: Ativar proxy do proxy para encaminhamento de tudo, padrão “no”;
    • UPSTREAM_ADDRESS: Endereço IP ou nome de DNS do proxy superior;
    • UPSTREAM_PORT: Porta do proxy superior;
    • UPSTREAM_AUTH: Usuário para autenticação, opcional;
    • UPSTREAM_PASS: Senha para autenticação;
  • Personalização do caminho do certificado para a porta HTTPs do proxy:
    • CERTDIR: Pasta dentro do container para os certificados auto-assinados;
    • TLS_KEY_PATH: Caminho do certificado específico (arquivo fullchain) desejado;
    • TLS_KEY_PATH: Caminho da chave privada específica;

9.1 – Rede Docker

É necessário criar a rede Docker para definir como seu proxy irá lidar com o protocolo IP.

Rede somente IPv4 – Proxy não fará acessos em IPv6

Bash
# Rede de containers Squid - Somente IPv4
    docker network create squidnet -d bridge \
        -o com.docker.network.bridge.name=br-squidnet \
        -o com.docker.network.driver.mtu=1500 \
        -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected;

Rede dual-stack – IPv4 e IPv6, o proxy escolherá por conveniência do DNS e rapidez

Bash
# Rede de containers Squid - Dual-stack IPv4 e IPv6
    docker network create squidnet -d bridge --ipv6 \
        -o com.docker.network.bridge.name=br-squidnet \
        -o com.docker.network.driver.mtu=1500 \
        -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected;

9.2 – Certificado

O container gera certificado auto-assinado para a porta HTTPs e permite que você informe o caminho de um certificado obtido pelo Traefik (usar traefik-certs) ou por compra (Wildcard ou específico).

Se o arquivo do certificado for alterado o serviço detecta automaticamente e recarrega os novos arquivos.

9.3 – Proxy simples para containers

O propósito desse container é servir de proxy HTTP para auditar o acesso de containers e conduzi-los para outros serviços.

Bash
# variaveis
    IMAGE="tmsoftbrasil/squid-gateway:latest";
    NAME="squid-proxy";

    # Controle de acesso do proxy
    ACL_ENABLE=yes;
    ACL_PERMIT_PRIVATES=yes;
    ACL_PERMIT_LOCAL=yes;
    ACL_PERMIT_NETWORKS="45.255.128.0/22,2804:cafe::/32";

    # Opcoes gerais
    # - Portas internas
    HTTP_PORT="1080";
    HTTPS_PORT="1443";

    # - Logs
    LOG_ENABLE=yes;
    LOG_FILE_ACCESS="/data/logs/squid-access.log";
    LOG_FILE_CACHE="none";
    LOG_FILE_STORE="none";

# Volume
    # - pasta de dados privados persistentes da VPS
    DATADIR=/storage/$NAME;
    mkdir -p $DATADIR;

# Rodar container
    echo "# Iniciando container...";
    docker rm -f $NAME 2>/dev/null;
    docker run \
        -d --restart=always \
        --name=$NAME --hostname $NAME.intranet.br \
        --user=root --cap-add=ALL --privileged \
        --read-only \
        --tmpfs /run:rw,size=8m \
        \
        --network squidnet \
        \
        -p 8001:$HTTP_PORT \
        -p 8002:$HTTPS_PORT \
        \
        -v $DATADIR:/data \
        \
        -e ACL_ENABLE="$ACL_ENABLE" \
        -e ACL_PERMIT_PRIVATES="$ACL_PERMIT_PRIVATES" \
        -e ACL_PERMIT_LOCAL="$ACL_PERMIT_LOCAL" \
        -e ACL_PERMIT_NETWORKS="$ACL_PERMIT_NETWORKS" \
        \
        -e HTTP_PORT="$HTTP_PORT" \
        -e HTTPS_PORT="$HTTPS_PORT" \
        -e CERTDIR="$CERTDIR" \
        \
        -e LOG_ENABLE="$LOG_ENABLE" \
        -e LOG_FILE_ACCESS="$LOG_FILE_ACCESS" \
        -e LOG_FILE_CACHE="$LOG_FILE_CACHE" \
        -e LOG_FILE_STORE="$LOG_FILE_STORE" \
        \
        \
        $IMAGE;

Observe que as portas publicadas externamente são 8001 (HTTP) e 8002 (HTTPs com certificado auto-assinado).

Internamente e na rede LAN do container a porta 1080 (HTTP) e 443 (HTTPs) sempre abertas via EXPOSE.

Testando no mesmo container do proxy:

Bash
# Rodar CURL dentro do container do squid-proxy:
    docker exec -it squid-proxy curl -v 'http://api.ipify.org'; echo;

9.4 – Usando proxy em containers vizinhos

Crie qualquer container declarando as variáveis de ambiente para clientes HTTP e aponte para o container de proxy.

Rodando container temporário para teste na rede “squidnet“:

Bash
# Rodar container temporario vizinho ao container de proxy
    docker run --rm --read-only --network squidnet \
        \
        -e http_proxy=http://squid-proxy:1080 \
        -e https_proxy=http://squid-proxy:1080 \
        \
        alpine/curl \
            \
            curl -v 'http://api.ipify.org'; echo;

Observar o acesso no Log do container “squid-proxy“:

Bash
# Observar log do squid:
    # - Mostrar tudo
    cat /storage/squid-proxy/logs/squid-access.log;

    # - Acompanhar em tempo real
    tail -f /storage/squid-proxy/logs/squid-access.log;

.

Todos os meus movimentos
foram friamente calculados.
Chapolim Colorado

Terminamos por hoje!

Patrick Brandão, patrickbrandao@gmail.com