Solução para Acesso Restrito ao Docker Hub na China

Solução para Acesso Restrito ao Docker Hub na China

  1. Configuração do Proxy com Cloudflare Workers

Pré-requisitos:

  • Uma conta no Cloudflare (registro em dash.cloudflare.com).
  • Um domínio gerenciado pelo Cloudflare.
  • Observação: Contas gratuitas têm limites de requisição (100.000/dia, 1.000/minuto).

1.1 Criação do Worker

No painel do Cloudflare, navegue até Workers e Páginas > Criar aplicativo > Criar Worker. Substitua o código do Worker pelo seguinte script. O script atua como um proxy reverso, redirecionando requisições de domínios de registro de contêineres (como docker.io, ghcr.io) para os servidores upstream corretos.

// Arquivo: _worker.js
const REGISTRY_ORIGINS = {
  "quay": "quay.io",
  "gcr": "gcr.io",
  "k8s-gcr": "k8s.gcr.io",
  "k8s": "registry.k8s.io",
  "ghcr": "ghcr.io",
  "cloudsmith": "docker.cloudsmith.io",
  "nvcr": "nvcr.io",
  "test": "registry-1.docker.io"
};
const AUTH_ENDPOINT = "https://auth.docker.io";
const DEFAULT_REGISTRY = "registry-1.docker.io";
const BLOCKED_UAS = ["netcraft"];

function resolveRegistry(hostnamePrefix) {
  if (REGISTRY_ORIGINS.hasOwnProperty(hostnamePrefix)) {
    return [REGISTRY_ORIGINS[hostnamePrefix], false];
  }
  return [DEFAULT_REGISTRY, true];
}

function isValidUUID(str) {
  const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return regex.test(str);
}

function generateResponse(body, status = 200, headers = {}) {
  headers['access-control-allow-origin'] = '*';
  return new Response(body, { status, headers });
}

function sanitizeInput(rawInput) {
  return rawInput.replace(/[\s"'|\r\n]+/g, ',').replace(/^,|,$/g, '').split(',').filter(Boolean);
}

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const ua = request.headers.get("User-Agent")?.toLowerCase() || "";
    const blockedUas = env.UA ? [...BLOCKED_UAS, ...sanitizeInput(env.UA)] : BLOCKED_UAS;
    const proxyHost = `https://${url.hostname}`;
    const path = url.pathname;
    const queryNs = url.searchParams.get("ns");
    const queryHubHost = url.searchParams.get("hubhost") || url.hostname;
    const hostPrefix = queryHubHost.split('.')[0];

    let [targetRegistry, useFallbackPage] = resolveRegistry(hostPrefix);
    if (queryNs) {
      targetRegistry = (queryNs === 'docker.io') ? DEFAULT_REGISTRY : queryNs;
    }

    // Bloquear User-Agents específicos com uma página falsa
    if (blockedUas.some(blocked => ua.includes(blocked))) {
      return new Response(fakeNginxPage(), { headers: { 'Content-Type': 'text/html' } });
    }

    // Lógica principal de proxy e reescrita de URLs
    // ... (código de manipulação de rotas, tokens, e encaminhamento para o registry de destino) ...
    // Este código reescreve headers, redireciona chamadas de autenticação e ajusta caminhos.
    
    // Exemplo de chamada final:
    const upstreamReq = new Request(targetRegistry + url.pathname + url.search, {
      method: request.method,
      headers: new Headers(request.headers),
      body: request.body,
      redirect: "follow"
    });
    upstreamReq.headers.set("Host", new URL(upstreamReq.url).hostname);
    
    const originalResponse = await fetch(upstreamReq);
    const newResponse = new Response(originalResponse.body, originalResponse);
    newResponse.headers.set("access-control-allow-origin", "*");
    // Reescrever header de autenticação se necessário
    if (newResponse.headers.has("Www-Authenticate")) {
      newResponse.headers.set("Www-Authenticate",
        newResponse.headers.get("Www-Authenticate").replace(new RegExp(AUTH_ENDPOINT, 'g'), proxyHost)
      );
    }
    return newResponse;
  }
};

function fakeNginxPage() {
  return `<title>Welcome to nginx!</title><h1>Welcome to nginx!</h1>`;
}

1.2 Uso do Proxy Configurado

Supondo que o domínio do seu Worker seja docker.seudominio.com, existem duas formas principais de utilizá-lo:

a) Especfiicando o caminho completo da imagem:

docker pull docker.seudominio.com/stilleshan/frpc:latest
docker pull docker.seudominio.com/library/nginx:stable-alpine

b) Configurando o Docker para usar o proxy como espelho padrão:

Crie ou edite o arquivo /etc/docker/daemon.json:

{
  "registry-mirrors": ["https://docker.seudominio.com"]
}

Em seguida, reinicie o daemon do Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

1.3 Configuração para Outros Runtimes de Contêinerr

Containerd: Edite /etc/containerd/config.toml para definir espelhos.

    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://docker.seudominio.com"]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
      endpoint = ["https://docker.seudominio.com"]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
      endpoint = ["https://docker.seudominio.com"]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."ghcr.io"]
      endpoint = ["https://docker.seudominio.com"]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
      endpoint = ["https://docker.seudominio.com"]

Podman: Edite /etc/containers/registries.conf.

unqualified-search-registries = ["docker.io", "k8s.gcr.io", "gcr.io", "ghcr.io", "quay.io"]

[[registry]]
prefix = "docker.io"
location = "registry-1.docker.io"

[[registry.mirror]]
location = "https://docker.seudominio.com"

[[registry]]
prefix = "k8s.gcr.io"
location = "k8s.gcr.io"

[[registry.mirror]]
location = "https://docker.seudominio.com"
// ... configurações similares para gcr.io, ghcr.io, quay.io

  1. Fontes Públicas de Aceleração na China

Uma alternativa mais simples é utilizar espelhos públicos mantidos por empresas chinesas. Configure o arquivo /etc/docker/daemon.json com a lista abaixo:

{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://hub-mirror.c.163.com",
    "https://mirror.ccs.tencentyun.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://atomhub.openatom.cn"
  ]
}

Após a configuração, reinicie o Docker para que as alterações tenham efeito.

Tags: Docker cloudflare-workers registry-mirrors containerd podman

Publicado em 6-5 18:11 por Thomas