Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/builds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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**:
Expand Down
5 changes: 4 additions & 1 deletion lib/builds/builder_agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 10 additions & 21 deletions lib/builds/images/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 \
.
```
Expand All @@ -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"
```

Expand All @@ -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)
Expand Down Expand Up @@ -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]'
Expand Down
30 changes: 25 additions & 5 deletions lib/images/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions lib/middleware/oapi_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels a little fragile ... should it depend on config (e.g. config.go has subnet / bridge config that determines this prefix)

}

// extractRepoFromPath extracts the repository name from a registry path.
Expand Down