Solução para Acesso Restrito ao Docker Hub na China
- 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
- 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.