Skip to content

Commit

Permalink
Merge branch 'ciiiii:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
npcxiao authored Jul 13, 2024
2 parents 7319b84 + aa61ad5 commit b1e560a
Show file tree
Hide file tree
Showing 8 changed files with 760 additions and 42 deletions.
14 changes: 5 additions & 9 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,11 @@ jobs:
runs-on: ubuntu-latest
name: Build & Deploy
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: "12.x"
- run: npm install
- uses: actions/checkout@v4
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
env:
CF_ACCOUNT_ID: ${{secrets.CF_ACCOUNT_ID}}
accountId: ${{secrets.CF_ACCOUNT_ID}}
command: deploy --env production --minify src/index.js
environment: production
23 changes: 23 additions & 0 deletions .github/workflows/stage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Deploy to Cloudflare Workers(Staging)

on:
pull_request:
branches:
- master
paths-ignore:
- '**.md'
repository_dispatch:

jobs:
build-and-deploy:
runs-on: ubuntu-latest
name: Build & Deploy
steps:
- uses: actions/checkout@v4
- name: Publish
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{secrets.CF_ACCOUNT_ID}}
command: deploy --env staging --minify src/index.js
environment: staging
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### cloudflare worker ###
worker
.wrangler

# Created by https://www.toptal.com/developers/gitignore/api/osx
# Edit at https://www.toptal.com/developers/gitignore?templates=osx
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

![deploy](https://github.com/ciiiii/cloudflare-docker-proxy/actions/workflows/deploy.yaml/badge.svg)

[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/ciiiii/cloudflare-docker-proxy)

> If you're looking for proxy for helm, maybe you can try [cloudflare-helm-proxy](https://github.com/ciiiii/cloudflare-helm-proxy).
## Deploy
[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/ciiiii/cloudflare-docker-proxy)

1. fork this project
2. modify the link of the above button to your fork url
3. click the button, you will be redirected to the deploy page
1. click the "Deploy With Workers" button
2. follow the instructions to fork and deploy
3. update routes as you requirement

[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/ciiiii/cloudflare-docker-proxy)

## Config tutorial
## Routes configuration tutorial

1. use cloudflare worker host: only support proxy one registry
```javascript
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
"devDependencies": {
"prettier": "^2.4.1",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1"
"webpack-cli": "^4.9.1",
"wrangler": "^3.36.0"
},
"scripts": {
"format": "prettier --write '**/*.{js,css,json,md}'",
"build": "webpack"
"build": "webpack",
"dev": "wrangler dev src/index.js --env dev"
},
"license": "MIT",
"main": "src/index.js"
Expand Down
61 changes: 49 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

const dockerHub = "https://registry-1.docker.io";

const routes = {
"docker.libcuda.so": "https://registry-1.docker.io",
// production
"docker.libcuda.so": dockerHub,
"quay.libcuda.so": "https://quay.io",
"gcr.libcuda.so": "https://gcr.io",
"k8s-gcr.libcuda.so": "https://k8s.gcr.io",
"k8s.libcuda.so": "https://registry.k8s.io",
"ghcr.libcuda.so": "https://ghcr.io",
"cloudsmith.libcuda.so": "https://docker.cloudsmith.io",
"ecr.libcuda.so": "https://public.ecr.aws",

// staging
"docker-staging.libcuda.so": dockerHub,
};

function routeByHosts(host) {
Expand All @@ -36,20 +43,25 @@ async function handleRequest(request) {
}
);
}
// check if need to authenticate
const isDockerHub = upstream == dockerHub;
const authorization = request.headers.get("Authorization");
if (url.pathname == "/v2/") {
const newUrl = new URL(upstream + "/v2/");
const headers = new Headers();
if (authorization) {
headers.set("Authorization", authorization);
}
// check if need to authenticate
const resp = await fetch(newUrl.toString(), {
method: "GET",
headers: headers,
redirect: "follow",
});
if (resp.status === 200) {
} else if (resp.status === 401) {
const headers = new Headers();
if (resp.status === 401) {
if (MODE == "debug") {
headers.set(
"Www-Authenticate",
`Bearer realm="${LOCAL_ADDRESS}/v2/auth",service="cloudflare-docker-proxy"`
`Bearer realm="http://${url.host}/v2/auth",service="cloudflare-docker-proxy"`
);
} else {
headers.set(
Expand Down Expand Up @@ -80,7 +92,28 @@ async function handleRequest(request) {
return resp;
}
const wwwAuthenticate = parseAuthenticate(authenticateStr);
return await fetchToken(wwwAuthenticate, url.searchParams);
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 && isDockerHub) {
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);
}
// redirect for DockerHub library images
// Example: /v2/busybox/manifests/latest => /v2/library/busybox/manifests/latest
if (isDockerHub) {
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, 301);
}
}
// foward requests
const newUrl = new URL(upstream + url.pathname);
Expand All @@ -97,7 +130,7 @@ function parseAuthenticate(authenticateStr) {
// match strings after =" and before "
const re = /(?<=\=")(?:\\.|[^"\\])*(?=")/g;
const matches = authenticateStr.match(re);
if (matches === null || matches.length < 2) {
if (matches == null || matches.length < 2) {
throw new Error(`invalid Www-Authenticate Header: ${authenticateStr}`);
}
return {
Expand All @@ -106,13 +139,17 @@ function parseAuthenticate(authenticateStr) {
};
}

async function fetchToken(wwwAuthenticate, searchParams) {
async function fetchToken(wwwAuthenticate, scope, authorization) {
const url = new URL(wwwAuthenticate.realm);
if (wwwAuthenticate.service.length) {
url.searchParams.set("service", wwwAuthenticate.service);
}
if (searchParams.get("scope")) {
url.searchParams.set("scope", searchParams.get("scope"));
if (scope) {
url.searchParams.set("scope", scope);
}
headers = new Headers();
if (authorization) {
headers.set("Authorization", authorization);
}
return await fetch(url, { method: "GET", headers: {} });
return await fetch(url, { method: "GET", headers: headers });
}
41 changes: 28 additions & 13 deletions wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
name = "cloudflare-docker-proxy"
workers_dev = true
main = "src/index.js"
compatibility_date = "2021-12-07"
compatibility_date = "2023-12-01"

[dev]
ip = "0.0.0.0"
port = 8787
local_protocol="http"
upstream_protocol="https"

[vars]
MODE="production"
LOCAL_ADDRESS=""
TARGET_UPSTREAM=""
local_protocol = "http"

[env.dev.vars]
MODE="debug"
LOCAL_ADDRESS="http://192.168.10.102:8787"
TARGET_UPSTREAM="https://registry-1.docker.io"
MODE = "debug"
TARGET_UPSTREAM = "https://registry-1.docker.io"

[env.production]
name = "cloudflare-docker-proxy"
# routes = [
# { pattern = "docker.libcuda.so", custom_domain = true },
# { pattern = "quay.libcuda.so", custom_domain = true },
# { pattern = "gcr.libcuda.so", custom_domain = true },
# { pattern = "k8s-gcr.libcuda.so", custom_domain = true },
# { pattern = "k8s.libcuda.so", custom_domain = true },
# { pattern = "ghcr.libcuda.so", custom_domain = true },
# { pattern = "cloudsmith.libcuda.so", custom_domain = true },
# ]

[env.production.vars]
MODE = "production"
TARGET_UPSTREAM = ""

[env.staging]
name = "cloudflare-docker-proxy-staging"
# route = { pattern = "docker-staging.libcuda.so", custom_domain = true }

[env.staging.vars]
MODE = "staging"
TARGET_UPSTREAM = ""
Loading

0 comments on commit b1e560a

Please sign in to comment.