diff --git a/Dockerfile b/Dockerfile index 702dc20f..3ddff64b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details FROM gcr.io/distroless/static:nonroot +LABEL source_repository="https://github.com/ironcore-dev/boot-operator" WORKDIR / COPY --from=builder /workspace/manager . COPY --from=builder /workspace/templates/ templates/ diff --git a/server/imageproxyserver.go b/server/imageproxyserver.go index 6436cf56..71850646 100644 --- a/server/imageproxyserver.go +++ b/server/imageproxyserver.go @@ -18,6 +18,7 @@ import ( const ( ghcrIOKey = "ghcr.io/" + keppelKey = "keppel.global.cloud.sap/" imageKey = "imageName" layerDigestKey = "layerDigest" versionKey = "version" @@ -45,6 +46,8 @@ func RunImageProxyServer(imageProxyServerAddr string, k8sClient client.Client, l if strings.HasPrefix(imageDetails.OCIImageName, ghcrIOKey) { handleGHCR(w, r, &imageDetails, log) + } else if strings.HasPrefix(imageDetails.OCIImageName, keppelKey) { + handleKeppel(w, r, &imageDetails, log) } else { http.Error(w, "Bad Request", http.StatusBadRequest) log.Info("Unsupported registry") @@ -84,6 +87,24 @@ func handleGHCR(w http.ResponseWriter, r *http.Request, imageDetails *ImageDetai proxy.ServeHTTP(w, r) } +func handleKeppel(w http.ResponseWriter, r *http.Request, imageDetails *ImageDetails, log logr.Logger) { + log.Info("Processing Image Proxy request for Keppel", "method", r.Method, "path", r.URL.Path, "clientIP", r.RemoteAddr) + + digest := imageDetails.LayerDigest + targetURL := fmt.Sprintf("https://%sv2/%s/blobs/%s", keppelKey, imageDetails.RepositoryName, digest) + proxyURL, _ := url.Parse(targetURL) + + proxy := &httputil.ReverseProxy{ + Director: imageDetails.modifyDirector(proxyURL, "", digest), + } + + r.URL.Host = proxyURL.Host + r.URL.Scheme = proxyURL.Scheme + r.Host = proxyURL.Host + + proxy.ServeHTTP(w, r) +} + func (imageDetails ImageDetails) getBearerToken() (string, error) { url := fmt.Sprintf("https://ghcr.io/token?scope=repository:%s:pull", imageDetails.RepositoryName) resp, err := http.Get(url) @@ -157,12 +178,20 @@ func parseImageURL(queries url.Values) (imageDetails ImageDetails, err error) { ociImageName := queries.Get(imageKey) layerDigest := queries.Get(layerDigestKey) version := queries.Get(versionKey) - repositoryName := strings.TrimPrefix(ociImageName, ghcrIOKey) if ociImageName == "" || layerDigest == "" || version == "" { return ImageDetails{}, fmt.Errorf("missing required query parameters 'image' or 'layer' or 'version'") } + var repositoryName string + if strings.HasPrefix(ociImageName, ghcrIOKey) { + repositoryName = strings.TrimPrefix(ociImageName, ghcrIOKey) + } else if strings.HasPrefix(ociImageName, keppelKey) { + repositoryName = strings.TrimPrefix(ociImageName, keppelKey) + } else { + return ImageDetails{}, fmt.Errorf("unsupported registry key") + } + return ImageDetails{ OCIImageName: ociImageName, RepositoryName: repositoryName, @@ -176,6 +205,8 @@ func (ImageDetails ImageDetails) modifyDirector(proxyURL *url.URL, bearerToken s req.URL.Scheme = proxyURL.Scheme req.URL.Host = proxyURL.Host req.URL.Path = fmt.Sprintf("/v2/%s/blobs/%s", ImageDetails.RepositoryName, digest) - req.Header.Set("Authorization", "Bearer "+bearerToken) + if bearerToken != "" { + req.Header.Set("Authorization", "Bearer "+bearerToken) + } } }