Skip to content

Commit c4ffeed

Browse files
authored
Merge branch 'main' into peterj/substrate
2 parents 40f6209 + 32ce1fb commit c4ffeed

29 files changed

Lines changed: 1542 additions & 336 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ build-golang-adk-full: buildx-create
279279
.PHONY: build-skills-init
280280
build-skills-init: ## Build and push the skills-init image
281281
build-skills-init: buildx-create
282-
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) -t $(SKILLS_INIT_IMG) -f docker/skills-init/Dockerfile docker/skills-init
282+
$(DOCKER_BUILDER) $(DOCKER_BUILD_ARGS) -t $(SKILLS_INIT_IMG) -f docker/skills-init/Dockerfile ./go
283283
$(DOCKER_PUSH) $(SKILLS_INIT_IMG)
284284

285285
.PHONY: push

docker/skills-init/Dockerfile

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
1-
### Stage 0: build krane
2-
FROM golang:1.26-alpine AS krane-builder
3-
4-
ENV KRANE_VERSION=v0.21.2
5-
WORKDIR /build
6-
7-
RUN apk add --no-cache git && \
8-
git clone --depth 1 --branch $KRANE_VERSION \
9-
https://github.com/google/go-containerregistry.git
10-
11-
WORKDIR /build/go-containerregistry/cmd/krane
12-
13-
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /build/krane .
14-
1+
### Stage 0: build the skills-init Go binary
2+
ARG BASE_IMAGE_REGISTRY=cgr.dev
3+
ARG BUILDPLATFORM
4+
FROM --platform=$BUILDPLATFORM $BASE_IMAGE_REGISTRY/chainguard/go:latest AS builder
5+
ARG TARGETARCH
6+
ARG TARGETOS
7+
8+
WORKDIR /workspace
9+
10+
COPY go.mod go.sum ./
11+
RUN --mount=type=cache,target=/root/go/pkg/mod,rw \
12+
--mount=type=cache,target=/root/.cache/go-build,rw \
13+
go mod download
14+
15+
COPY api/ api/
16+
COPY core/ core/
17+
COPY adk/ adk/
18+
19+
ARG LDFLAGS
20+
RUN --mount=type=cache,target=/root/go/pkg/mod,rw \
21+
--mount=type=cache,target=/root/.cache/go-build,rw \
22+
CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \
23+
go build -a -trimpath -ldflags "$LDFLAGS" -o /skills-init ./core/cmd/skills-init
24+
25+
### Stage 1: runtime
1526
FROM alpine:3.23
1627

1728
ARG PYTHON_UID=1001
1829
ARG PYTHON_GID=1001
1930

20-
RUN apk upgrade --no-cache && apk add --no-cache git jq
21-
COPY --from=krane-builder /build/krane /usr/local/bin/krane
31+
# git is invoked by skills-init via exec.Command with an argv vector — never
32+
# through a shell — so the only attack surface here is git itself. OCI fetch
33+
# uses the in-process go-containerregistry library, so krane and jq are gone.
34+
RUN apk upgrade --no-cache && apk add --no-cache git openssh-client ca-certificates
35+
36+
COPY --from=builder /skills-init /usr/local/bin/skills-init
2237

2338
# Run as the same UID/GID as the main agent container (python user) so that
2439
# files written to the shared /skills volume are readable by the main container.
@@ -28,3 +43,5 @@ RUN addgroup -g ${PYTHON_GID} pythongroup && \
2843
adduser -u ${PYTHON_UID} -G pythongroup -s /bin/sh -D python
2944

3045
USER ${PYTHON_UID}:${PYTHON_GID}
46+
47+
ENTRYPOINT ["/usr/local/bin/skills-init"]

go/api/config/crd/bases/kagent.dev_agents.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13284,8 +13284,10 @@ spec:
1328413284
URL path segment, without .git).
1328513285
type: string
1328613286
path:
13287-
description: Subdirectory within the repo to use as the
13288-
skill root.
13287+
description: |-
13288+
Subdirectory within the repo to use as the skill root. The API validates
13289+
this input path, but treats repository contents as trusted: symlinks under
13290+
this path are dereferenced when materializing the skill.
1328913291
type: string
1329013292
ref:
1329113293
default: main

go/api/config/crd/bases/kagent.dev_sandboxagents.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10942,8 +10942,10 @@ spec:
1094210942
URL path segment, without .git).
1094310943
type: string
1094410944
path:
10945-
description: Subdirectory within the repo to use as the
10946-
skill root.
10945+
description: |-
10946+
Subdirectory within the repo to use as the skill root. The API validates
10947+
this input path, but treats repository contents as trusted: symlinks under
10948+
this path are dereferenced when materializing the skill.
1094710949
type: string
1094810950
ref:
1094910951
default: main

go/api/v1alpha2/agent_types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ type GitRepo struct {
147147
// +kubebuilder:default="main"
148148
Ref string `json:"ref,omitempty"`
149149

150-
// Subdirectory within the repo to use as the skill root.
150+
// Subdirectory within the repo to use as the skill root. The API validates
151+
// this input path, but treats repository contents as trusted: symlinks under
152+
// this path are dereferenced when materializing the skill.
151153
// +optional
152154
Path string `json:"path,omitempty"`
153155

go/core/cmd/skills-init/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Command skills-init is the init container binary that fetches an Agent's
2+
// skills from git repositories and OCI images before the main agent container
3+
// starts.
4+
//
5+
// It reads its configuration from a ConfigMap-mounted JSON file (see the
6+
// skillsinit package for the wire format) and performs all subprocess
7+
// invocations with argv vectors — no user input is ever interpolated into a
8+
// shell, which is the original design defect that motivated this rewrite.
9+
package main
10+
11+
import (
12+
"log"
13+
"os/user"
14+
15+
"github.com/kagent-dev/kagent/go/core/internal/skillsinit"
16+
)
17+
18+
func main() {
19+
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
20+
21+
cfg, err := skillsinit.LoadConfig()
22+
if err != nil {
23+
log.Fatalf("skills-init: %v", err)
24+
}
25+
26+
// Resolve home from /etc/passwd via the current uid rather than $HOME so
27+
// the binary behaves consistently regardless of how the container was
28+
// invoked. The image's user is created with a fixed home dir, so this is
29+
// deterministic in production and overridable in tests via the user db.
30+
u, err := user.Current()
31+
if err != nil {
32+
log.Fatalf("skills-init: lookup current user: %v", err)
33+
}
34+
home := u.HomeDir
35+
if home == "" {
36+
log.Fatalf("skills-init: current user %q has no home directory", u.Username)
37+
}
38+
39+
if err := skillsinit.Run(cfg, home); err != nil {
40+
log.Fatalf("skills-init: %v", err)
41+
}
42+
}

0 commit comments

Comments
 (0)