惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
美团技术团队
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
月光博客
月光博客
T
Tor Project blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
I
Intezer
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
罗磊的独立博客
V
V2EX
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
Scott Helme
Scott Helme
Vercel News
Vercel News
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
C
CERT Recently Published Vulnerability Notes
G
Google Developers Blog
B
Blog
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
IT之家
IT之家
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
小众软件
小众软件
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
AWS News Blog
AWS News Blog

繁星点点

最新轻量级龙虾部署教程 通过netboot.xyz重装系统 giffgaff卡保号教程 Llinux系统中全新透明代理方案(可代理所有出站流量) tailscale开启子网路由常见的坑 OpenWrt固件编译 解决UFW和Docker的问题 使用PGP的非对称算法加密文件 NSSM工具把任意exe程序注册为Windows后台服务 轻量级简约端口转发rinetd 安卓IDE的安装和使用 IP信息查询网站汇总 热点共享代理教程 SSH使用密钥认证登录详细解释 Debian系统公网IP出口切换指南,适用多IP服务器 Windows系统安装Scoop包管理器 安卓Flutter开发学习笔记 使用手机搭建节点服务端 关于github提交记录泄露真实邮箱的处理办法 anytls协议部署教程 caddy给域名用上ECH加密 通过gitlab流水线自动备份仓库 通过CF-Workers代理访问网站 低配机器使用alpine系统跑docker 端口敲门教程 3x-ui面板通过api管理示例 nmap端口扫描 数据库常用命令 snap和winget包管理器 Git推送GitHub常用命令 😩😩😩学不完,根本学不完😩😩😩 使用CDN后获取访客真实IP 一些用于网络测试的下载测速文件 纯v6网络访问v4站点最简单的方法 服务器配置SSH密钥登录 windows重装系统教程 Docker部署哪吒监控 Github-Actions默认用户变量GITHUB_TOKEN的提权语法 给站点套CFT的一些坑 使用py3快速启动一个文件上传和下载的接口 ios系统最新免拔卡使用tiktok方法 clash手搓配置最全协议示例详解 为IP地址申请免费证书并开启HTTPS 实用网站收藏 纯CSS实现剧透效果 在CloudflareWAF中屏蔽国内浏览器 网络安全之百度URL欺骗漏洞分析和复现 超级强大的ansible批量管理主机运维工具使用教程 收集整理的最新的常用VPS脚本工具 无法注册TG账号和国内号码接收不到验证码详细解决方案 国内环境安装xui面板 免费接码网站合集 完整的网络状态码列表 关于xui面板报错无法监听127.0.0.1:62789的问题 Xboard面板常见问题 项目编译构建文档 ios最新美区ID注册方法 V2board前后端分离 Markdown语法教程 Github加速 Docker镜像加速和离线安装 Liunx配置wrap 代理工具和SSH工具推荐 常用代码记录
解决CF反代Docker失效的问题
2024-11-16 · via 繁星点点

最近国内拉取镜像时发现之前的worker反代的docker加速用不了了,报错为auth.docker.io/token这个域名的问题。

经过抓包发现,现在拉取镜像会请求三个域名,顺序是先请求了registry-1.docker.io得到了 401http 状态码后转去访问了auth.docker.io得到了 Authorization字段以后重新请求 registry-1.docker.io,获取源数据后被 307 转发到了 production.cloudflare.docker.com 上。


由此看出auth.docker.io是用于验证鉴权的域名,production.cloudflare.docker.com 是用于下载镜像文件的域名,这些域名我测了一下全都被墙了。

之前的CF代码只反代了registry-1.docker.io,所以导致失效,既然找到问题所在就比较好解决了。

下面贴出我修改的worker.js代码:

import HTML from './docker.html';

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const host = request.headers.get("host");
    
    const registryHost = "registry-1.docker.io";
    const authHost = "auth.docker.io";
    const productionHost = "production.cloudflare.docker.com";

    // 处理认证请求
    if (url.pathname.startsWith('/token')) {
      const headers = new Headers(request.headers);
      headers.set('host', authHost);
      
      const authUrl = `https://${authHost}${url.pathname}${url.search}`;
      const authRequest = new Request(authUrl, {
        method: request.method,
        headers: headers,
        body: request.body,
        redirect: "follow",
      });

      const response = await fetch(authRequest);
      const responseHeaders = new Headers(response.headers);
      responseHeaders.set('access-control-allow-origin', host);
      responseHeaders.set('access-control-allow-headers', 'Authorization');
      
      return new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: responseHeaders,
      });
    }
    
    // 处理 registry v2 请求
    if (url.pathname.startsWith('/v2/')) {
      const headers = new Headers(request.headers);
      headers.set('host', registryHost);
      
      const registryUrl = `https://${registryHost}${url.pathname}${url.search}`;
      const registryRequest = new Request(registryUrl, {
        method: request.method,
        headers: headers,
        body: request.body,
        redirect: "follow",
      });

      const response = await fetch(registryRequest);
      const responseHeaders = new Headers(response.headers);
      responseHeaders.set('access-control-allow-origin', host);
      responseHeaders.set('access-control-allow-headers', 'Authorization');

      // 修改认证头,将认证请求指向主域名
      const wwwAuth = responseHeaders.get('www-authenticate');
      if (wwwAuth) {
        const newWwwAuth = wwwAuth
          .replace('https://auth.docker.io', `https://${host}`)
          .replace('https://auth.hub.docker.com', `https://${host}`);
        responseHeaders.set('www-authenticate', newWwwAuth);
      }

      // 修改重定向地址
      const location = responseHeaders.get('location');
      if (location) {
        const newLocation = location
          .replace('https://production.cloudflare.docker.com', `https://${host}`);
        responseHeaders.set('location', newLocation);
      }

      return new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: responseHeaders,
      });
    }

    // 处理默认请求
    return new Response(HTML.replace(/{{host}}/g, host), {
      status: 200,
      headers: {
        "content-type": "text/html"
      }
    });
  }
}

新建一个docker.html前端文件,填入以下代码,部署后记得绑定自定义域,因为默认的worker域名是被墙的,其他的默认即可。

docker.html文件的代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Docker Hub 镜像加速</title>
        <style>
        html, body {
            height: 100%;
        }
        body {
            font-family: "Roboto", "Helvetica", "Arial", sans-serif;
            font-size: 16px;
            color: #333;
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
        }
        .container {
            margin: 0 auto;
            max-width: 600px;
            flex-grow: 1;
            display: flex;
            flex-direction: column;
        }
        .header {
            background-color: #438cf8;
            color: white;
            padding: 10px;
            display: flex;
            align-items: center;
        }
        h1 {
            font-size: 24px;
            margin: 0;
            padding: 0;
        }
        .content {
            padding: 32px;
            flex-grow: 1;
        }
        pre {
            background-color: #f4f4f4;
            padding: 16px;
            border-radius: 4px;
            position: relative;
            overflow: auto;
            margin-bottom: 16px;
        }
        code {
            display: block;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .copy-button {
            position: absolute;
            top: 8px;
            right: 8px;
            padding: 4px 8px;
            background-color: #438cf8;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        }
        .footer {
            padding: 5px;
            text-align: center;
            font-size: 15px;
        }
        .footer a {
            color: #438cf8;
            text-decoration: none;
        }
        </style>
        <script>
            function copyCode(button) {
                const code = button.previousElementSibling.innerText;
                navigator.clipboard.writeText(code).then(function() {
                    button.innerText = "已复制";
                    setTimeout(function() {
                        button.innerText = "复制";
                    }, 2000);
                });
            }
        </script>
    </head>
    <body>
        <div class="header">
            <h1>Docker Hub 镜像加速</h1>
        </div>
        <div class="container">
            <div class="content">
                <h2>Docker Hub 镜像加速</h2>
                <p>为了加速镜像拉取,你可以使用以下命令设置 registry mirror</p>
                <pre><code>sudo mkdir -p /etc/docker</code><button class="copy-button" onclick="copyCode(this)">复制</button></pre>
                <pre><code>sudo tee /etc/docker/daemon.json &lt;&lt;EOF
{
    "registry-mirrors": ["https://{{host}}"]
}
EOF</code><button class="copy-button" onclick="copyCode(this)">复制</button></pre>
                <pre><code>sudo systemctl daemon-reload</code><button class="copy-button" onclick="copyCode(this)">复制</button></pre>
                <pre><code>sudo systemctl restart docker</code><button class="copy-button" onclick="copyCode(this)">复制</button></pre>
                <br>
                <p>不用设置环境也可以直接使用,用法示例:</p>
                <pre><code>docker pull {{host}}/library/mysql:5.7</code><button class="copy-button" onclick="copyCode(this)">复制</button></pre>
                <p>说明:library是一个特殊的命名空间,它代表的是官方镜像。如果是某个用户的镜像就把library替换为镜像的用户名</p>
            </div>
        </div>
        <div class="footer">
            <p><a href="https://blog.52013120.xyz/post/29.html" target="_blank">代码地址</a></p>
        </div>
    </body>
</html>

  • 基于这个思路我还写了caddy反代的配置,caddy自动配置SSL证书可太香了,并且不依赖CF,可以自己部署在服务器上。
hub.example.com {
    handle /v2/* {
        reverse_proxy https://registry-1.docker.io {
            header_up Host {http.reverse_proxy.upstream.hostport}
            header_down WWW-Authenticate "https://auth.docker.io" "https://{http.request.host}"
            header_down Location "https://production.cloudflare.docker.com" "https://{http.request.host}"
        }
    }

    handle /token* {
        reverse_proxy https://auth.docker.io {
            header_up Host {http.reverse_proxy.upstream.hostport}
        }
    }

    handle /* {
        reverse_proxy https://production.cloudflare.docker.com {
            header_up Host {http.reverse_proxy.upstream.hostport}
        }
    }
}

记得第一行的hub.example.com替换为你的域名

  • caddy反代ghcr.io
example.com {
    handle /v2/* {
        reverse_proxy https://ghcr.io {
            header_up Host {http.reverse_proxy.upstream.hostport}
            header_down WWW-Authenticate "https://ghcr.io" "https://{http.request.host}"
            header_down Location "https://pkg-containers.githubusercontent.com" "https://{http.request.host}"
        }
    }

    handle /token* {
        reverse_proxy https://ghcr.io {
            header_up Host {http.reverse_proxy.upstream.hostport}
        }
    }

    handle /* {
        reverse_proxy https://pkg-containers.githubusercontent.com {
            header_up Host {http.reverse_proxy.upstream.hostport}
        }
    }
}