Skip to content
Draft
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
42 changes: 42 additions & 0 deletions .github/workflows/experimental-signing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Experimental signing build isolation

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
signing:
runs-on: ubuntu-latest
strategy:
matrix:
tags: ["", experimental_signing]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Test signing integration and baseline
env:
BUILD_TAGS: ${{ matrix.tags }}
run: |
go test -tags "$BUILD_TAGS" ./pkg/config ./pkg/healthmanager ./pkg/objectcache/containerprofilecache ./pkg/rulebindingmanager/cache ./pkg/rulemanager/rulecreator ./pkg/rulemanager/ruleswatcher ./pkg/rulemanager/types/v1 ./pkg/storage/...
if [ -n "$BUILD_TAGS" ]; then
go test -tags "$BUILD_TAGS" ./pkg/signature/... ./pkg/compactyaml ./cmd/...
go build -tags "$BUILD_TAGS" -o /tmp/sign-object ./cmd/sign-object
fi
- name: Build agent and prove default isolation
env:
BUILD_TAGS: ${{ matrix.tags }}
run: |
go build -tags "$BUILD_TAGS" -o /tmp/node-agent ./cmd
if [ -z "$BUILD_TAGS" ]; then
go list -deps ./cmd > /tmp/default-deps
if grep -E 'github.com/kubescape/node-agent/pkg/signature|github.com/sigstore/(cosign|fulcio)' /tmp/default-deps; then
echo 'Experimental signing dependencies leaked into the default build'
exit 1
fi
fi
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
DOCKERFILE_PATH=./build/Dockerfile
BINARY_NAME=node-agent
# Opt in with BUILD_TAGS=experimental_signing. Empty by default.
BUILD_TAGS?=

IMAGE?=quay.io/kubescape/$(BINARY_NAME)
# GADGETS are pulled unmodified from upstream IG. trace_open is intentionally NOT
Expand All @@ -15,7 +17,7 @@ TAG?=test
# TAG?=v0.0.1

binary:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $(BINARY_NAME) ./cmd/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags "$(BUILD_TAGS)" -o $(BINARY_NAME) ./cmd

.PHONY: check-legacy-packages
check-legacy-packages:
Expand All @@ -28,10 +30,10 @@ check-ig-pin:
./scripts/check-inspektor-gadget-pin.sh go.mod

docker-build-only:
docker buildx build --platform linux/amd64 -t $(IMAGE):$(TAG) -f $(DOCKERFILE_PATH) --load .
docker buildx build --build-arg BUILD_TAGS="$(BUILD_TAGS)" --platform linux/amd64 -t $(IMAGE):$(TAG) -f $(DOCKERFILE_PATH) --load .

docker-build: gadgets
docker buildx build --platform linux/amd64 -t $(IMAGE):$(TAG) -f $(DOCKERFILE_PATH) --load .
docker buildx build --build-arg BUILD_TAGS="$(BUILD_TAGS)" --platform linux/amd64 -t $(IMAGE):$(TAG) -f $(DOCKERFILE_PATH) --load .

docker-push: docker-build
docker push $(IMAGE):$(TAG)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ git clone https://github.com/kubescape/node-agent.git
cd node-agent

# Build the binary
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o node-agent ./cmd/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o node-agent ./cmd

# Set required environment variables
export NODE_NAME=$(hostname)
Expand Down Expand Up @@ -520,7 +520,7 @@ go test ./tests/...
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/main.go",
"program": "${workspaceFolder}/cmd",
"env": {
"NODE_NAME": "<node-name>",
"KUBECONFIG": "<path-to-kubeconfig>"
Expand Down
3 changes: 2 additions & 1 deletion build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ FROM --platform=$BUILDPLATFORM golang:1.27-trixie AS builder
ENV GO111MODULE=on CGO_ENABLED=0
WORKDIR /work
ARG TARGETOS TARGETARCH
ARG BUILD_TAGS=""

RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
GOOS=$TARGETOS GOARCH=$TARGETARCH GOEXPERIMENT=greenteagc go build -o /out/node-agent -ldflags="-s -w" ./cmd/main.go
GOOS=$TARGETOS GOARCH=$TARGETARCH GOEXPERIMENT=greenteagc go build -tags "$BUILD_TAGS" -o /out/node-agent -ldflags="-s -w" ./cmd

RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
Expand Down
3 changes: 2 additions & 1 deletion build/Dockerfile.debug
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ FROM --platform=$BUILDPLATFORM golang:1.27-trixie AS builder
ENV GO111MODULE=on CGO_ENABLED=0
WORKDIR /work
ARG TARGETOS TARGETARCH
ARG BUILD_TAGS=""

RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN --mount=target=. \
--mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
GOOS=$TARGETOS GOARCH=$TARGETARCH GOEXPERIMENT=greenteagc go build -o /out/node-agent ./cmd/main.go
GOOS=$TARGETOS GOARCH=$TARGETARCH GOEXPERIMENT=greenteagc go build -tags "$BUILD_TAGS" -o /out/node-agent ./cmd

FROM gcr.io/distroless/static-debian13:debug

Expand Down
12 changes: 10 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,12 @@ func main() {
}
}

clusterData, err := utilsmetadata.LoadConfig("/etc/config/clusterData.json")
clusterDataPath, cleanupClusterData, err := verifiedClusterDataPath()
if err != nil {
logger.L().Ctx(ctx).Fatal("verify clusterData error", helpers.Error(err))
}
defer cleanupClusterData()
clusterData, err := utilsmetadata.LoadConfig(clusterDataPath)
if err != nil {
logger.L().Ctx(ctx).Fatal("load clusterData error", helpers.Error(err))
}
Expand Down Expand Up @@ -209,6 +214,7 @@ func main() {

// Start the health manager
healthManager := healthmanager.NewHealthManager()
configureSigningHealth(healthManager)
healthManager.Start(ctx)

// Create clients
Expand Down Expand Up @@ -283,10 +289,11 @@ func main() {
}

var ruleBindingCache *rulebindingcachev1.RBCache
var rulesWatcher *ruleswatcher.RulesWatcherImpl
if cfg.EnableRuntimeDetection {
ruleCreator := rulecreator.NewRuleCreator()
ruleBindingCache = rulebindingcachev1.NewCache(cfg, k8sClient, ruleCreator)
rulesWatcher := ruleswatcher.NewRulesWatcher(k8sClient, ruleCreator, func() {
rulesWatcher = ruleswatcher.NewRulesWatcher(k8sClient, ruleCreator, func() {
ruleBindingCache.RefreshRuleBindingsRules()
})
dWatcher.AddAdaptor(rulesWatcher)
Expand Down Expand Up @@ -357,6 +364,7 @@ func main() {
ruleBindingCache.AddNotifier(&ruleBindingNotify)

cpc := containerprofilecache.NewContainerProfileCache(cfg, storageClient, k8sObjectCache, metricsProvider)
configureSigning(ctx, cfg, cpc, rulesWatcher, ruleBindingCache, exporter)
cpc.Start(ctx)
if cpm, ok := containerProfileManager.(*containerprofilemanagerv1.ContainerProfileManager); ok {
cpm.SetCompletionNotifier(cpc)
Expand Down
31 changes: 31 additions & 0 deletions cmd/sign-object/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM --platform=$BUILDPLATFORM golang:1.27-trixie AS builder

ENV GO111MODULE=on CGO_ENABLED=0
WORKDIR /src
ARG TARGETOS TARGETARCH

COPY go.mod go.sum ./
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
go mod download

COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -tags experimental_signing -trimpath -ldflags="-s -w" -o /sign-object ./cmd/sign-object

# Small alpine base + su-exec (pinned by digest). The entrypoint drops to the
# owner of the bind-mounted /work, so `docker run -v "$PWD:/work" …` writes
# user-owned files with NO --user flag; sign-object never runs as root when a
# non-root /work is mounted.
FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
RUN apk add --no-cache su-exec
LABEL org.opencontainers.image.title="sign-object" \
org.opencontainers.image.description="CLI to sign and verify Kubescape ApplicationProfile / NetworkNeighborhood / Rules objects (cosign, key-based or keyless)." \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.source="https://github.com/kubescape/node-agent"
COPY --from=builder /sign-object /usr/local/bin/sign-object
COPY cmd/sign-object/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
WORKDIR /work
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
14 changes: 14 additions & 0 deletions cmd/sign-object/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# Run sign-object as the owner of the bind-mounted /work, so files it writes
# (keys, signed profiles) are owned by the invoking user — no `--user` flag
# needed. Falls back to running directly if /work isn't a bind mount or is
# already root-owned.
set -e
if [ -d /work ]; then
uid="$(stat -c %u /work 2>/dev/null || echo 0)"
gid="$(stat -c %g /work 2>/dev/null || echo 0)"
if [ "$uid" != "0" ]; then
exec su-exec "${uid}:${gid}" sign-object "$@"
fi
fi
exec sign-object "$@"
Loading
Loading