Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- addEventListener("fetch", (event) => {
- event.passThroughOnException();
- event.respondWith(handleRequest(event.request));
- });
- const upstream = "https://registry-1.docker.io";
- async function handleRequest(request) {
- const url = new URL(request.url);
- if (url.pathname == "/") {
- return new Response("", {
- status: 200,
- headers: {
- "content-type": "text/plain",
- }
- });
- }
- const authorization = request.headers.get("Authorization");
- if (url.pathname == "/v2/") {
- return handleFirstRequest(upstream, authorization, url.hostname);
- }
- // get token
- if (url.pathname == "/v2/auth") {
- return handleAuthRequest(upstream, url, authorization);
- }
- // redirect for DockerHub library images
- // Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest
- const pathParts = url.pathname.split("/");
- if (pathParts.length == 5) {
- pathParts.splice(2, 0, "library");
- const redirectUrl = new URL(url);
- redirectUrl.pathname = pathParts.join("/");
- return Response.redirect(redirectUrl.toString(), 301);
- }
- return handlePullRequest(upstream, request);
- }
- function parseAuthenticate(authenticateStr) {
- // sample: Bearer realm="https://auth.ipv6.docker.com/token",service="registry.docker.io"
- // match strings after =" and before "
- const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g;
- const matches = authenticateStr.match(re);
- if (matches == null || matches.length < 2) {
- throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`);
- }
- return {
- realm: matches[0],
- service: matches[1],
- };
- }
- async function fetchToken(wwwAuthenticate, scope, authorization) {
- const url = new URL(wwwAuthenticate.realm);
- if (wwwAuthenticate.service.length) {
- url.searchParams.set("service", wwwAuthenticate.service);
- }
- if (scope) {
- url.searchParams.set("scope", scope);
- }
- const headers = new Headers();
- if (authorization) {
- headers.set("Authorization", authorization);
- }
- return await fetch(url, { method: "GET", headers: headers });
- }
- async function handlePullRequest(upstream, request) {
- const url = new URL(request.url);
- const newUrl = new URL(upstream + url.pathname);
- // Clone the request headers
- const headers = new Headers(request.headers);
- // Add x-amz-content-sha256 header
- headers.set("x-amz-content-sha256", "UNSIGNED-PAYLOAD");
- // Add x-amz-date header
- const now = new Date();
- const amzDate = now.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
- headers.set("x-amz-date", amzDate);
- const newReq = new Request(newUrl, {
- method: request.method,
- headers: headers,
- redirect: "follow",
- });
- return await fetch(newReq);
- }
- async function handleFirstRequest(upstream, authorization, hostname) {
- const newUrl = new URL(upstream + "/v2/");
- const headers = new Headers();
- if (authorization) {
- headers.set("Authorization", authorization);
- }
- const resp = await fetch(newUrl.toString(), {
- method: "GET",
- headers: headers,
- redirect: "follow",
- });
- if (resp.status === 401) {
- headers.set(
- "Www-Authenticate",
- `Bearer realm="https://${hostname}/v2/auth",service="cloudflare-docker-proxy"`
- );
- return new Response(JSON.stringify({ message: "Unauthorized" }), {
- status: 401,
- headers: headers,
- });
- } else {
- return resp;
- }
- }
- async function handleAuthRequest(upstream, url, authorization) {
- const newUrl = new URL(upstream + "/v2/");
- const resp = await fetch(newUrl.toString(), {
- method: "GET",
- redirect: "follow",
- });
- if (resp.status !== 401) {
- return resp;
- }
- const authenticateStr = resp.headers.get("WWW-Authenticate");
- if (authenticateStr === null) {
- return resp;
- }
- const wwwAuthenticate = parseAuthenticate(authenticateStr);
- let scope = url.searchParams.get("scope");
- // autocomplete repo part into scope for DockerHub library images
- // Example: repository:busybox:pull => repository:library/busybox:pull
- if (scope) {
- let scopeParts = scope.split(":");
- if (scopeParts.length == 3 && !scopeParts[1].includes("/")) {
- scopeParts[1] = "library/" + scopeParts[1];
- scope = scopeParts.join(":");
- }
- }
- return await fetchToken(wwwAuthenticate, scope, authorization);
- }
Add Comment
Please, Sign In to add comment