Giter VIP home page Giter VIP logo

serverless-registry-proxy's Introduction

Serverless Container Registry Proxy

This project offers a very simple reverse proxy that lets you expose your public or private Docker registries (such as Google Container Registry gcr.io, Google Artifact Registry (*.pkg.dev) or Docker Hub account) as a public registry on your own domain name.

You can also fork this project and customize as a middleware and deploy to Cloud Run or somewhere else since it’s a generic docker registry proxy.

Run on Google Cloud

For example, if you have a public registry, and offering images publicly with names such as:

docker pull gcr.io/ahmetb-public/foo
# or
docker pull us-central1-docker.pkg.dev/ahmetb-demo/ahmetb-demo/foo

you can use this proxy, and instead offer your images in a ✨fancier way✨ on a custom domain, such as:

docker pull r.ahmet.dev/foo

This project is a stateless reverse proxy, and can be deployed to a managed compute platform such as Cloud Run. It works by reverse proxying the Docker Registry API v2 requests to the underlying registry:

architecture diagram

It does not support "pushing"; however, as you push images to the underlying registry, you can serve them on your custom domain.

You are also free to fork this project and use it as a "customizable middleware" in front of your container image registry.

Building

Download the source code, and build as a container image:

docker build --tag gcr.io/[YOUR_PROJECT]/gcr-proxy .

Then, push to a registry like:

docker push gcr.io/[YOUR_PROJECT]/gcr-proxy

Deploying (to Google Cloud Run) for Google Container Registry (gcr.io)

You can easily deploy this as a serverless container to Google Cloud Run. This handles many of the heavy-lifting for you.

  1. Build and push docker images (previous step)
  2. Deploy to Cloud Run.
  3. Configure custom domain for Cloud Run service.
    1. Create domain mapping
    2. Verify domain ownership
    3. Update your DNS records
  4. Have fun!

To deploy this to Cloud Run, replace [GCP_PROJECT_ID] with the project ID of the GCR registry you want to expose publicly:

gcloud run deploy gcr-proxy \
    --allow-unauthenticated \
    --image "[IMAGE]" \
    --set-env-vars "REGISTRY_HOST=gcr.io" \
    --set-env-vars "REPO_PREFIX=[GCP_PROJECT_ID]"

This will deploy a publicly accessible registry for your gcr.io/[GCP_PROJECT_ID], which also needs to be public. If your GCR registry is private, see the section below on "Exposing private registries".

Then create a domain mapping by running (replace the --domain value):

gcloud run domain-mappings create \
    --service gcr-proxy \
    --domain [YOUR_DOMAIN]

This command will require verifying ownership of your domain name, and have you set DNS records for your domain to point to Cloud Run. Then, it will take some 15-20 minutes to actually provision TLS certificates for your domain name.

Pricing Note: Cloud Run has a generous free tier. When serving from GCR using this proxy, the layer blobs will not be served through this proxy (as they're downloaded from a signed GCS URL). This saves you a lot of "billable time" and "egress networking" costs.

Deploying (to Google Cloud Run) for Google Artifact Registry (*.pkg.dev)

Same instructions as GCR listed above. You need to just configure these environment variables differently:

  • REGISTRY_HOST: your regional AR domain (e.g. us-central1-docker.pkg.dev)
  • REPO_PREFIX: project ID + AR Repository name (e.g. ahmetb-demo/prod-images)

Warning: When using Artifact Registry, the layer blobs are downloaded through this proxy, and therefore will incur more costs on Cloud Run such as networking egress and longer execution times leading to higher "billable time".

Deploying (elsewhere)

...is much harder. You need to deploy the application to an environment like Kubernetes, obtain a valid TLS certificate for your domain name, and make it publicly accessible.

Using with other Docker Registries

If you set REGISTRY_HOST and REGISTRY_PREFIX environment variables, you can also use this proxy for other docker registries.

For example, to proxy docker pull ahmet/example to Docker Hub, specify environment variables:

  • REGISTRY_HOST=index.docker.io
  • REPO_PREFIX=ahmet

Note: This is not tested with registries other than Docker Hub and GCR/AR. If you can make it work with Azure Container Registry or AWS Elastic Container Registry, contribute examples here.

Exposing private registries publicly (GCR.io)

⚠️ This will make images in your private GCR registries publicly accessible on the internet.

  1. Create an IAM Service Account.

  2. Give it permissions to access the GCR registry GCS Bucket. (Or simply, you can give it the project-wide Storage Object Viewer role.)

  3. Copy your service account JSON key into the root of the repository as key.json.

  4. (Not ideal, but whatever) Rebuild the docker image with your service account key JSON in it. This will require editing Dockerfile to add COPY and ENV directives like:

    COPY key.json /app/key.json
    ENV GOOGLE_APPLICATION_CREDENTIALS /app/key.json
    ENTRYPOINT [...]
    

    You need to rebuild and deploy the updated image.

Configuration

While deploying, you can set additional environment variables for customization:

Key Value
REGISTRY_HOST specify hostname for target registry, e.g. gcr.io.
DISABLE_BROWSER_REDIRECTS if you set this variable to any value, visiting example.com/image on this browser will not redirect to [REGISTRY_HOST]/[REPO_PREFIX]/image to allow your users to browse the image on GCR. If you're exposing private registries, you might want to set this variable.
AUTH_HEADER The Authentication: [...] header’s value to authenticate to the target registry
GOOGLE_APPLICATION_CREDENTIALS (For gcr.io) Path to the IAM service account JSON key file to expose the private GCR registries publicly.

This is not an official Google project. See LICENSE.

serverless-registry-proxy's People

Contributors

ahmetb avatar andrewhowdencom avatar atodorov avatar casperfrx avatar dependabot[bot] avatar dominik-lekse avatar matti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

serverless-registry-proxy's Issues

Question: how does AUTH_HEADER work ?

@ahmetb I've played around with this app and I can successfully configure it to proxy a public image from either Docker Hub or quay.io on my custom domain.

I've tried using the AUTH_HEADER variable to expose private images (hosted on quay.io) to a non-authorized account but cound't get this to work. (my end goal is to have custom authorization handler).

What is unclear to me is should I set a value of
Basic XYZ where XYZ is the base64 encoded value you can find in ~/.docker/config.json or to some other value?

If you can explain how the authentication handshake happens between the client and the registry that would probably also help me.

Thanks.

Does it always redirect for layers?

I tried deploying this in Cloud Run with a custom domain and it works great.
But when I try to pull images from a VM sitting behind an HTTP proxy (only allowing traffic to my custom domain), my client (crictl) throws this error:

FATA[0000] pulling image: rpc error:
code = Unknown
desc = failed to pull and unpack image "<mydomain>/images/<image>:<imagetag>":
failed to copy: httpReadSeeker: failed open: failed to do request:
Get "https://us-central1-docker.pkg.dev/artifacts-downloads/namespaces/<mygcpprojectid>/repositories/<myartifactregistryname>/downloads/<encodedtext>": Forbidden 

It looks like it's trying to pull the layers from the registry directly (pulling from us-central1-docker.pkg.dev rather than mydomain).

I tried adding/removing envvar DISABLE_BROWSER_REDIRECTS="true" to the CloudRun Service but the problem persists.

Perhaps I'm misunderstanding something about how serverless-registry-proxy works? I thought it was a pull-through proxy, but it feels like it's doing a redirect for the actual layers.

Edit: formatting

glb + docker 20.10.2 errors

invalid from flag value my-glb-host.name.com/libjpeg-turbo:2020-08-25-1: missing or empty Content-Length header

if I downgrade to 19.03 docker it works fine.

use service account identity in cloud run

Could this use service account in cloud run without using the envs and storing the key.json?

Like in the cloud run UI you can select the service account identity for the revision?

Bypass proxy for blob layer requests to upstream GCP artifact registry (and save money)

When the upstream registry is a GCP artifact registry, layer blobs pass through the proxy which causes billable networking egress and execution time for Cloud Run.

Similar to a GCR proxy, the registry client could be redirected directly to the upstream GCP artifact registry and thus by-pass the proxy for layer blob traffic.

Description

  • When the registry client requests a layer blob from AR, the AR responds with an 302 Found to a location of the form /artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/[1068 characters which appear to be a url base64 encoded signature and is assumed to authorize the get of the layer blob]

  • From the redirect target, a client can download the blob without the Authorization header

  • Therefore, the assumption is that the urlbase64 encoded character sequence is a signature (similar)

  • The serverless-registry-proxy can modify the response from the upstream AR and rewrite the Location before responding to the client

  • To bypass the proxy in the subsequence layer blob request, the proxy prepends the host of the upstream AR to the Location header

Example:

  • Location (by upstream AR): /artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/...
  • Location (to client): https://europe-west3-docker.pkg.dev/artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/...

The following (shortened) log has been observed from a crane blob -v to a *-docker.pkg.dev AR.

2020/12/21 18:14:31 --> GET https://europe-west3-docker.pkg.dev/v2/neuspaces/neuspaces/go-httpbin/blobs/sha256:24f0c933cbef83faee52f82c7f889c727b1ece5123b92d036c52fa865480f037 [body redacted: omitting binary blobs from logs]
2020/12/21 18:14:31 GET /v2/neuspaces/neuspaces/go-httpbin/blobs/sha256:24f0c933cbef83faee52f82c7f889c727b1ece5123b92d036c52fa865480f037 HTTP/1.1
Host: europe-west3-docker.pkg.dev
User-Agent: go-containerregistry
Authorization: <redacted>
Accept-Encoding: gzip

2020/12/21 18:14:31 <-- 302 https://europe-west3-docker.pkg.dev/v2/neuspaces/neuspaces/go-httpbin/blobs/sha256:24f0c933cbef83faee52f82c7f889c727b1ece5123b92d036c52fa865480f037 (171.810544ms) [body redacted: omitting binary blobs from logs]
2020/12/21 18:14:31 HTTP/2.0 302 Found
Content-Length: 1166
Alt-Svc: h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Content-Type: text/html; charset=utf-8
Date: Mon, 21 Dec 2020 17:14:31 GMT
Docker-Distribution-Api-Version: registry/2.0
Location: /artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/[1068 characters which appear to be a url base64 encoded signature and is assumed to authorize the get of the layer blob]
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 0

2020/12/21 18:14:31 --> GET https://europe-west3-docker.pkg.dev/artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/[1068 characters which appear to be a url base64 encoded signature and is assumed to authorize the get of the layer blob] [body redacted: omitting binary blobs from logs]
2020/12/21 18:14:31 GET /artifacts-downloads/namespaces/neuspaces/repositories/neuspaces/downloads/[1068 characters which appear to be a url base64 encoded signature and is assumed to authorize the get of the layer blob] HTTP/1.1
Host: europe-west3-docker.pkg.dev
User-Agent: go-containerregistry
Authorization: <redacted>
Referer: https://europe-west3-docker.pkg.dev/v2/neuspaces/neuspaces/go-httpbin/blobs/sha256:24f0c933cbef83faee52f82c7f889c727b1ece5123b92d036c52fa865480f037
Accept-Encoding: gzip

Proposed changes

  • Modify the response from the upstream AR as outlined in the description
  • Optional: Introduce a flag to toggle this behavior (GCP_AR_BLOB_BYPASS_PROXY)

unknown blob with artifact registry

This works great with GCR, but just go this with artifact registry

$ docker pull utils-test-1-aferlwuhda-lz.a.run.app/converter:latest
latest: Pulling from converter
801bfaa63ef2: Already exists
c0a8a1966def: Pulling fs layer
2878bfdcb065: Pulling fs layer
18b6c0a7ea1c: Pulling fs layer
3f43e5a79a1a: Waiting
cd53c83dcd1d: Waiting
ff3407ef5740: Waiting
ac898fb76262: Waiting
c8a0df839b8c: Waiting
d9fcedba8753: Waiting
621e556debb9: Waiting
5a4685b71455: Waiting
be4b2835c074: Waiting
d46985212c8d: Waiting
00ef4b6c7024: Waiting
03a79c73e374: Waiting
774e089d4e5b: Waiting
57669bd742d2: Waiting
c8bbe3869f36: Waiting
b222978708b0: Waiting
66049259904b: Waiting
f418639ab069: Waiting
32f7aea1f061: Waiting
dbdfa7f0ec0c: Waiting
c309eb68ec1f: Waiting
fda80ad33106: Waiting
d83ceacb26e9: Waiting
error pulling image configuration: unknown blob

Allow to configure proxy host

Allow to explicitly configure the host on which the proxy service binds.

Description

  • I am using the registry proxy in a deployment scenario in which it is relevant to set the host
  • For a Cloud Run deployment scenario, I agree that to configure the host is not relevant

Proposed changes

  • Read host from optional environment variable HOST

pkg.dev not work

 target registry's token endpoint could not be discovered: www-authenticate header not returned from https://europe-north1-npm.pkg.dev/v2/, cannot locate token endpoint

Damn, i can't find info about www-authenticate header
Get https://europe-north1-npm.pkg.dev/v2/ returned 404
But for europe-north1-docker.pkg.dev/v2/ header has been existing...

I understand it only works for docker images?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.