Guest User

Untitled

a guest
Feb 14th, 2025
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. addEventListener("fetch", (event) => {
  2.   event.passThroughOnException();
  3.   event.respondWith(handleRequest(event.request));
  4. });
  5.  
  6. const upstream = "https://registry-1.docker.io";
  7.  
  8. async function handleRequest(request) {
  9.  
  10.   const url = new URL(request.url);
  11.  
  12.   if (url.pathname == "/") {
  13.     return new Response("", {
  14.       status: 200,
  15.       headers: {
  16.         "content-type": "text/plain",
  17.       }
  18.     });
  19.   }
  20.  
  21.   const authorization = request.headers.get("Authorization");
  22.   if (url.pathname == "/v2/") {
  23.     return handleFirstRequest(upstream, authorization, url.hostname);
  24.   }
  25.   // get token
  26.   if (url.pathname == "/v2/auth") {
  27.     return handleAuthRequest(upstream, url, authorization);
  28.   }
  29.  
  30.   // redirect for DockerHub library images
  31.   // Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest
  32.   const pathParts = url.pathname.split("/");
  33.   if (pathParts.length == 5) {
  34.     pathParts.splice(2, 0, "library");
  35.     const redirectUrl = new URL(url);
  36.     redirectUrl.pathname = pathParts.join("/");
  37.     return Response.redirect(redirectUrl.toString(), 301);
  38.   }
  39.  
  40.   return handlePullRequest(upstream, request);
  41. }
  42.  
  43. function parseAuthenticate(authenticateStr) {
  44.   // sample: Bearer realm="https://auth.ipv6.docker.com/token",service="registry.docker.io"
  45.   // match strings after =" and before "
  46.   const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g;
  47.   const matches = authenticateStr.match(re);
  48.   if (matches == null || matches.length < 2) {
  49.     throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`);
  50.   }
  51.   return {
  52.     realm: matches[0],
  53.     service: matches[1],
  54.   };
  55. }
  56.  
  57. async function fetchToken(wwwAuthenticate, scope, authorization) {
  58.   const url = new URL(wwwAuthenticate.realm);
  59.   if (wwwAuthenticate.service.length) {
  60.     url.searchParams.set("service", wwwAuthenticate.service);
  61.   }
  62.   if (scope) {
  63.     url.searchParams.set("scope", scope);
  64.   }
  65.   const headers = new Headers();
  66.   if (authorization) {
  67.     headers.set("Authorization", authorization);
  68.   }
  69.   return await fetch(url, { method: "GET", headers: headers });
  70. }
  71.  
  72. async function handlePullRequest(upstream, request) {
  73.   const url = new URL(request.url);
  74.   const newUrl = new URL(upstream + url.pathname);
  75.  
  76.   // Clone the request headers
  77.   const headers = new Headers(request.headers);
  78.  
  79.   // Add x-amz-content-sha256 header
  80.   headers.set("x-amz-content-sha256", "UNSIGNED-PAYLOAD");
  81.  
  82.   // Add x-amz-date header
  83.   const now = new Date();
  84.   const amzDate = now.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
  85.   headers.set("x-amz-date", amzDate);
  86.  
  87.   const newReq = new Request(newUrl, {
  88.     method: request.method,
  89.     headers: headers,
  90.     redirect: "follow",
  91.   });
  92.  
  93.   return await fetch(newReq);
  94. }
  95.  
  96. async function handleFirstRequest(upstream, authorization, hostname) {
  97.   const newUrl = new URL(upstream + "/v2/");
  98.   const headers = new Headers();
  99.   if (authorization) {
  100.     headers.set("Authorization", authorization);
  101.   }
  102.   const resp = await fetch(newUrl.toString(), {
  103.     method: "GET",
  104.     headers: headers,
  105.     redirect: "follow",
  106.   });
  107.   if (resp.status === 401) {
  108.     headers.set(
  109.       "Www-Authenticate",
  110.       `Bearer realm="https://${hostname}/v2/auth",service="cloudflare-docker-proxy"`
  111.     );
  112.     return new Response(JSON.stringify({ message: "Unauthorized" }), {
  113.       status: 401,
  114.       headers: headers,
  115.     });
  116.   } else {
  117.     return resp;
  118.   }
  119. }
  120.  
  121. async function handleAuthRequest(upstream, url, authorization) {
  122.   const newUrl = new URL(upstream + "/v2/");
  123.   const resp = await fetch(newUrl.toString(), {
  124.     method: "GET",
  125.     redirect: "follow",
  126.   });
  127.   if (resp.status !== 401) {
  128.     return resp;
  129.   }
  130.   const authenticateStr = resp.headers.get("WWW-Authenticate");
  131.   if (authenticateStr === null) {
  132.     return resp;
  133.   }
  134.   const wwwAuthenticate = parseAuthenticate(authenticateStr);
  135.   let scope = url.searchParams.get("scope");
  136.   // autocomplete repo part into scope for DockerHub library images
  137.   // Example: repository:busybox:pull => repository:library/busybox:pull
  138.   if (scope) {
  139.     let scopeParts = scope.split(":");
  140.     if (scopeParts.length == 3 && !scopeParts[1].includes("/")) {
  141.       scopeParts[1] = "library/" + scopeParts[1];
  142.       scope = scopeParts.join(":");
  143.     }
  144.   }
  145.   return await fetchToken(wwwAuthenticate, scope, authorization);
  146. }
Add Comment
Please, Sign In to add comment