Skip to content

fix(docker): restrict exposed database and phpMyAdmin ports - #4

Open
Guajir0-code wants to merge 1 commit into
sourcevortex:mainfrom
Guajir0-code:fix/docker-exposed-services
Open

fix(docker): restrict exposed database and phpMyAdmin ports#4
Guajir0-code wants to merge 1 commit into
sourcevortex:mainfrom
Guajir0-code:fix/docker-exposed-services

Conversation

@Guajir0-code

Copy link
Copy Markdown

Problema

O docker-compose.yml publica o MySQL e o phpMyAdmin em 0.0.0.0 enquanto o MySQL aceita root de qualquer host com senha vazia. Quem alcançar a máquina abre o phpMyAdmin na porta 3333 e entra como root no banco do WordPress.

Evidência

Três configurações que, isoladas, seriam aceitáveis, e juntas formam um caminho aberto:

mysql:
    ports:
        - '${FORWARD_DB_PORT:-3306}:3306'      # 0.0.0.0
    environment:
        MYSQL_ROOT_HOST: '%'                   # root de qualquer host
        MYSQL_ALLOW_EMPTY_PASSWORD: 1          # sem senha

phpmyadmin:
    restart: always                            # sobe sozinho, sempre
    ports:
        - "3333:80"                            # 0.0.0.0

MYSQL_ALLOW_EMPTY_PASSWORD sobrepõe a proteção que MYSQL_ROOT_PASSWORD daria. Como o restart: always religa o phpMyAdmin a cada boot da máquina, o serviço fica exposto mesmo sem ninguém ter rodado docker compose up naquele dia.

O risco é proporcional a onde isso sobe: numa rede doméstica é ruim; numa VPS com IP público é acesso irrestrito ao banco do site.

Solução

mysql:
    ports:
        # Bound to the loopback interface: reachable from this machine and
        # from the compose network, not from the rest of the LAN.
        - '127.0.0.1:${FORWARD_DB_PORT:-3306}:3306'
    environment:
        MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
        MYSQL_ROOT_HOST: '%'
        # MYSQL_ALLOW_EMPTY_PASSWORD removido

phpmyadmin:
    profiles:
        - tools
    restart: unless-stopped
    ports:
        - "127.0.0.1:3333:80"

Quatro mudanças:

  1. MySQL e phpMyAdmin ligados a 127.0.0.1. Continuam acessíveis da própria máquina e de dentro da rede do compose; deixam de ser acessíveis do resto da rede.
  2. MYSQL_ALLOW_EMPTY_PASSWORD removido, de forma que DB_PASSWORD passa a ser obrigatório de verdade.
  3. phpMyAdmin atrás do profile tools, ou seja, deixa de subir junto com o up normal.
  4. restart: alwaysunless-stopped, para o container não voltar sozinho depois de parado de propósito.

Por que MYSQL_ROOT_HOST: '%' continua

O phpMyAdmin conecta ao MySQL de dentro da rede do compose, com um IP que não é localhost do ponto de vista do container. Restringir esse valor quebraria o login como root. Com a porta ligada ao loopback e a senha obrigatória, o % passa a significar "qualquer host da rede do compose", que é o comportamento pretendido.

Como validar

O phpMyAdmin agora é opt-in:

docker compose --profile tools up -d

Sem a flag, sobem apenas laravel.test e mysql.

Conferindo que as portas não escutam mais em todas as interfaces:

docker compose ps --format 'table {{.Service}}\t{{.Ports}}'

Espera-se 127.0.0.1:3306->3306/tcp, não 0.0.0.0:3306->3306/tcp.

Não consegui executar isso. Não tenho Docker no ambiente onde preparei o PR, então validei o arquivo com um parser de YAML e conferi a estrutura resultante campo a campo. A verificação acima precisa ser feita por alguém com o ambiente rodando.

Impacto

  • Fluxo de trabalho: quem usa o phpMyAdmin precisa passar a subir com --profile tools. É a única mudança de rotina.
  • Senha do banco: ambientes que dependiam da senha vazia passam a exigir DB_PASSWORD no .env. Volumes já existentes mantêm as credenciais com que foram inicializados, então isso afeta principalmente instalações novas.
  • Aplicação: nenhuma. Só docker-compose.yml muda.
  • Rollback: reverter o commit.

Fora de escopo

docker-compose.override.yml não foi tocado, de propósito. Ele define user: root, cap_add: NET_BIND_SERVICE e volumes com o sufixo :z — o conjunto é um contorno conhecido de SELinux, típico de Fedora/RHEL. Remover o user: root seria defensável em tese, mas eu não tenho como reproduzir esse ambiente e a mudança tem chance real de quebrar as permissões de arquivo da máquina de quem mantém o projeto. Fica registrado como observação, para decisão de quem conhece o host.

The compose file published MySQL and phpMyAdmin on 0.0.0.0 while MySQL also
accepted root from any host with an empty password, so anyone able to reach
the machine could open phpMyAdmin on :3333 and log in as root against the
WordPress database.

- bind MySQL and phpMyAdmin to 127.0.0.1
- drop MYSQL_ALLOW_EMPTY_PASSWORD, so DB_PASSWORD is required
- move phpMyAdmin behind the tools compose profile
- restart: always -> unless-stopped

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant