diff --git a/lib/builds/README.md b/lib/builds/README.md index 4aaa7de..1ca63b5 100644 --- a/lib/builds/README.md +++ b/lib/builds/README.md @@ -273,13 +273,13 @@ Builder images must include: See [`images/README.md`](./images/README.md) for detailed build instructions. ```bash -# Build and push the builder image -docker build \ - -t yourregistry/builder:latest \ +# Build and push the builder image (must use OCI mediatypes) +docker buildx build \ + --platform linux/amd64 \ + --output type=image,oci-mediatypes=true,push=true \ + --tag yourregistry/builder:latest \ -f lib/builds/images/generic/Dockerfile \ . - -docker push yourregistry/builder:latest ``` ### Environment Variables @@ -366,7 +366,7 @@ go test ./lib/builds/registry_token_test.go ./lib/builds/registry_token.go -v curl -X POST http://localhost:8083/images \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ - -d '{"name": "hirokernel/builder-generic:latest"}' + -d '{"name": "onkernel/builder-generic:latest"}' ``` 3. **Create test source with Dockerfile**: diff --git a/lib/builds/builder_agent/main.go b/lib/builds/builder_agent/main.go index 102fa87..0c645c6 100644 --- a/lib/builds/builder_agent/main.go +++ b/lib/builds/builder_agent/main.go @@ -572,7 +572,10 @@ func runBuild(ctx context.Context, config *BuildConfig, logWriter io.Writer) (st cmd.Stdout = io.MultiWriter(logWriter, &buildLogs) cmd.Stderr = io.MultiWriter(logWriter, &buildLogs) // Use BUILDKITD_FLAGS from environment (set in Dockerfile) or empty for default - cmd.Env = os.Environ() + // Explicitly set DOCKER_CONFIG to ensure buildkit finds the auth config + env := os.Environ() + env = append(env, "DOCKER_CONFIG=/home/builder/.docker") + cmd.Env = env if err := cmd.Run(); err != nil { return "", buildLogs.String(), fmt.Errorf("buildctl failed: %w", err) diff --git a/lib/builds/images/README.md b/lib/builds/images/README.md index 9304557..161fde7 100644 --- a/lib/builds/images/README.md +++ b/lib/builds/images/README.md @@ -44,12 +44,12 @@ images/ ## Building the Generic Builder Image -Hypeman supports both Docker v2 and OCI image formats. You can use standard `docker build` -or `docker buildx` - both work. +Hypeman requires images to use **OCI mediatypes**. Use `docker buildx` with `oci-mediatypes=true` +to ensure compatibility. ### Prerequisites -1. **Docker** installed +1. **Docker** with buildx installed 2. **Docker Hub login** (or your registry): ```bash docker login @@ -58,22 +58,11 @@ or `docker buildx` - both work. ### 1. Build and Push ```bash -# From repository root -docker build \ - -t hirokernel/builder-generic:latest \ - -f lib/builds/images/generic/Dockerfile \ - . - -docker push hirokernel/builder-generic:latest -``` - -Or with buildx for multi-platform support: - -```bash +# From repository root - use buildx with OCI mediatypes docker buildx build \ --platform linux/amd64 \ - --push \ - --tag hirokernel/builder-generic:latest \ + --output type=image,oci-mediatypes=true,push=true \ + --tag onkernel/builder-generic:latest \ -f lib/builds/images/generic/Dockerfile \ . ``` @@ -88,10 +77,10 @@ TOKEN=$(make gen-jwt | tail -1) curl -X POST http://localhost:8083/images \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ - -d '{"name": "hirokernel/builder-generic:latest"}' + -d '{"name": "onkernel/builder-generic:latest"}' # Wait for import to complete -curl http://localhost:8083/images/docker.io%2Fhirokernel%2Fbuilder-generic:latest \ +curl http://localhost:8083/images/docker.io%2Fonkernel%2Fbuilder-generic:latest \ -H "Authorization: Bearer $TOKEN" ``` @@ -100,7 +89,7 @@ curl http://localhost:8083/images/docker.io%2Fhirokernel%2Fbuilder-generic:lates Set the builder image in your `.env`: ```bash -BUILDER_IMAGE=hirokernel/builder-generic:latest +BUILDER_IMAGE=onkernel/builder-generic:latest ``` ### Building for Local Testing (without pushing) @@ -245,7 +234,7 @@ When the builder runs inside a Hypeman microVM: ```bash # Check image status -cat ~/hypeman_data_dir/images/docker.io/hirokernel/builder-generic/*/metadata.json | jq . +cat ~/hypeman_data_dir/images/docker.io/onkernel/builder-generic/*/metadata.json | jq . # Check OCI cache index cat ~/hypeman_data_dir/system/oci-cache/index.json | jq '.manifests[-1]' diff --git a/lib/images/oci.go b/lib/images/oci.go index f98b89f..31962d8 100644 --- a/lib/images/oci.go +++ b/lib/images/oci.go @@ -346,20 +346,21 @@ func (c *ociClient) unpackLayers(ctx context.Context, layoutTag, targetDir strin // convertToOCIManifest converts a go-containerregistry manifest to OCI v1.Manifest // This allows us to use go-containerregistry (which handles both Docker v2 and OCI v1) // for manifest parsing, while still using umoci for layer unpacking. +// Docker v2 mediatypes are converted to OCI equivalents since umoci expects OCI format. func convertToOCIManifest(gcrManifest *gcr.Manifest) v1.Manifest { - // Convert config descriptor + // Convert config descriptor with mediatype conversion configDesc := v1.Descriptor{ - MediaType: string(gcrManifest.Config.MediaType), + MediaType: convertToOCIMediaType(string(gcrManifest.Config.MediaType)), Digest: gcrDigestToOCI(gcrManifest.Config.Digest), Size: gcrManifest.Config.Size, Annotations: gcrManifest.Config.Annotations, } - // Convert layer descriptors + // Convert layer descriptors with mediatype conversion layers := make([]v1.Descriptor, len(gcrManifest.Layers)) for i, layer := range gcrManifest.Layers { layers[i] = v1.Descriptor{ - MediaType: string(layer.MediaType), + MediaType: convertToOCIMediaType(string(layer.MediaType)), Digest: gcrDigestToOCI(layer.Digest), Size: layer.Size, Annotations: layer.Annotations, @@ -370,13 +371,32 @@ func convertToOCIManifest(gcrManifest *gcr.Manifest) v1.Manifest { Versioned: specs.Versioned{ SchemaVersion: int(gcrManifest.SchemaVersion), }, - MediaType: string(gcrManifest.MediaType), + MediaType: convertToOCIMediaType(string(gcrManifest.MediaType)), Config: configDesc, Layers: layers, Annotations: gcrManifest.Annotations, } } +// convertToOCIMediaType converts Docker v2 media types to OCI equivalents. +// Images from Docker Hub often use Docker-specific mediatypes, but umoci +// requires OCI-standard mediatypes for layer unpacking. +func convertToOCIMediaType(mediaType string) string { + switch mediaType { + case "application/vnd.docker.distribution.manifest.v2+json": + return v1.MediaTypeImageManifest + case "application/vnd.docker.container.image.v1+json": + return v1.MediaTypeImageConfig + case "application/vnd.docker.image.rootfs.diff.tar.gzip": + return v1.MediaTypeImageLayerGzip + case "application/vnd.docker.image.rootfs.diff.tar": + return v1.MediaTypeImageLayer + default: + // If already OCI or unknown, return as-is + return mediaType + } +} + // gcrDigestToOCI converts a go-containerregistry digest to OCI digest func gcrDigestToOCI(d gcr.Hash) digest.Digest { return digest.NewDigestFromEncoded(digest.Algorithm(d.Algorithm), d.Hex) diff --git a/lib/middleware/oapi_auth.go b/lib/middleware/oapi_auth.go index 00624a7..98c9c6c 100644 --- a/lib/middleware/oapi_auth.go +++ b/lib/middleware/oapi_auth.go @@ -181,7 +181,7 @@ func isRegistryPath(path string) bool { return strings.HasPrefix(path, "/v2/") } -// isInternalVMRequest checks if the request is from an internal VM network (10.102.x.x) +// isInternalVMRequest checks if the request is from an internal VM network // This is used as a fallback for builder VMs that don't have token auth yet. // // SECURITY: We only trust RemoteAddr, not X-Real-IP or X-Forwarded-For headers, @@ -195,8 +195,8 @@ func isInternalVMRequest(r *http.Request) bool { ip = ip[:idx] } - // Check if it's from the VM network (10.102.x.x) - return strings.HasPrefix(ip, "10.102.") + // Check if it's from the VM network (10.100.x.x or 10.102.x.x) + return strings.HasPrefix(ip, "10.100.") || strings.HasPrefix(ip, "10.102.") } // extractRepoFromPath extracts the repository name from a registry path.