Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ae9405a
chore: CONTAINER SEC CONTEXT
RealAnna Feb 23, 2026
97b72ec
ALL
RealAnna Feb 24, 2026
4e494c4
test github action instead
RealAnna Feb 24, 2026
e43e7bc
test with bash
RealAnna Feb 25, 2026
39229be
Try go code
RealAnna Mar 2, 2026
a7179a0
Try go code
RealAnna Mar 2, 2026
e20cf57
Try go code
RealAnna Mar 2, 2026
3a21468
Separate run
RealAnna Mar 2, 2026
09f8fb0
Separate run
RealAnna Mar 2, 2026
693cb8a
chore: retrigger CI
RealAnna Mar 2, 2026
0a4f66f
Separate run
RealAnna Mar 2, 2026
9732c7a
Separate run
RealAnna Mar 2, 2026
318a06c
repoRoot
RealAnna Mar 2, 2026
5846ddf
Apply suggestion from @mowies
RealAnna Mar 2, 2026
61b42c8
move to internal
RealAnna Mar 4, 2026
0b55f76
move to internal
RealAnna Mar 4, 2026
7c89764
Add README
RealAnna Mar 4, 2026
a192d0a
Add README
RealAnna Mar 4, 2026
7d2361a
Add version with goreleaser and Makefile
RealAnna Mar 5, 2026
aa7c04e
Add version with goreleaser and Makefile
RealAnna Mar 5, 2026
0200989
Add version with goreleaser and Makefile
RealAnna Mar 5, 2026
a2cdc08
Add version with goreleaser and Makefile
RealAnna Mar 5, 2026
8288b3e
Add version with goreleaser and Makefile
RealAnna Mar 5, 2026
a439190
READMES
RealAnna Mar 5, 2026
a8270d0
ratelimited excluded
RealAnna Mar 5, 2026
fa3fee5
ratelimited excluded
RealAnna Mar 5, 2026
8d8b27f
rebase
RealAnna Mar 11, 2026
014b9e4
rebase
RealAnna Mar 11, 2026
0cbef35
review
RealAnna Mar 12, 2026
11a7cf2
Update .github/workflows/kyverno/README.md
RealAnna Mar 12, 2026
6fde7db
get rid of gomplate
RealAnna Mar 16, 2026
d45bd33
get rid of gomplate
RealAnna Mar 16, 2026
6f0a487
get rid of gomplate
RealAnna Mar 16, 2026
ebed800
Update config_examples/collector-helm-values.yaml
RealAnna Mar 16, 2026
bfc3b4f
Update Makefile
RealAnna Mar 16, 2026
362c40c
Update README.md
RealAnna Mar 16, 2026
fdbca1c
remove journald example
RealAnna Mar 16, 2026
0c24381
added new policy for hostmetrics
RealAnna Mar 17, 2026
4d91e9b
Update .github/workflows/kyverno/README.md
RealAnna Mar 18, 2026
eeb62d9
Update Makefile
RealAnna Mar 18, 2026
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
39 changes: 39 additions & 0 deletions .github/workflows/kyverno/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
### Validate rendered workloads with Kyverno

Install the Kyverno CLI: https://kyverno.io/docs/kyverno-cli/

Then run the Kyverno checks against the rendered workloads:

```bash
make kyverno-workloads
```

### CI / automation

The same render + validate steps run in the **YAML Policy Check** workflow "[yaml-policy-check.yml](../yaml-policy-check.yml)"

## Kyverno policies

Policies live in: [`.github/workflows/kyverno/policies/`](./policies)
The policy for the hardened Collector `securityContext`is [here](./policies/collector-securitycontext.yaml)
It enforces the following container security settings:

- `securityContext.capabilities.drop: ["ALL"]`
- `securityContext.readOnlyRootFilesystem: true`
- `securityContext.allowPrivilegeEscalation: false`
- `securityContext.runAsNonRoot: true`
- `securityContext.runAsUser: 10001`
- `securityContext.runAsGroup: 10001`
- `securityContext.privileged: false`
- `securityContext.seccompProfile.type: RuntimeDefault`

These are widely recommended Kubernetes hardening defaults. For background, see:
- Kubernetes Security Context docs: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
- Kubernetes Pod Security Standards: https://kubernetes.io/docs/concepts/security/pod-security-standards/

## Notes / scope

- This is an **internal CI tool** (not part of the shipped Collector artifacts).
- The Kyverno validation applies to the **workloads/scenarios rendered and exercised by this repository’s CI**. It is
intended as a compatibility/regression check and a guardrail for new additions — not a blanket guarantee that every
possible configuration of every component will work under all hardened Kubernetes policies.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: collector-workloads-securitycontext
spec:
validationFailureAction: Enforce
background: false
rules:
- name: require-hardened-securitycontext-on-collector-workloads
match:
any:
- resources:
kinds:
- Deployment
- DaemonSet
- StatefulSet
# Exempt all matching resources in this namespace
exclude:
resources:
namespaces:
- e2ehostmetrics
preconditions:
all:
- key: "{{ request.object.spec.template.metadata.labels.\"app.kubernetes.io/name\" }}"
operator: Equals
value: opentelemetry-collector

validate:
message: "Collector workloads must run with hardened container securityContext."
pattern:
spec:
template:
spec:
containers:
- name: "?*"
securityContext:
privileged: false
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: collector-hostmetrics-securitycontext
spec:
validationFailureAction: Enforce
background: false
rules:
- name: enforce-hostmetrics-daemonset-securitycontext
match:
resources:
kinds:
- DaemonSet
namespaces:
- e2ehostmetrics

preconditions:
all:
- key: "{{ request.object.spec.template.metadata.labels.\"app.kubernetes.io/name\" }}"
operator: Equals
value: opentelemetry-collector

validate:
message: "Host-metrics DaemonSet must use the approved securityContext for journal/host access."
pattern:
spec:
template:
spec:
containers:
- name: "?*"
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
40 changes: 40 additions & 0 deletions .github/workflows/yaml-policy-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: YAML Policy Check

permissions:
contents: read

on:
pull_request:
branches: [main]
push:
branches: [main]

defaults:
run:
shell: bash

env:
# renovate: datasource=golang-version depName=go
GO_VERSION: "1.25.7"
OUT_BASE: "/tmp/rendered-collectors-workloads"

jobs:
kyverno-yaml-check:
runs-on: ubuntu-24.04
steps:
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: |
internal/renderworkloads/go.sum

- name: Install Kyverno CLI
uses: kyverno/action-install-cli@fcee92fca5c883169ef9927acf543e0b5fc58289 # v0.2.0

- name: Render workloads and validate with Kyverno
run: |
make kyverno-workloads OUT_BASE="${OUT_BASE}"
25 changes: 24 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ snapshot: .goreleaser.yaml $(GORELEASER)
$(GORELEASER) release --snapshot --clean --skip archive,sbom --fail-fast

$(TOOLS_BIN_NAMES): $(TOOLS_MOD_DIR)/go.mod | $(TOOLS_BIN_DIR)
cd $(TOOLS_MOD_DIR) && go build -o $@ -trimpath $(filter %/$(notdir $@),$(TOOLS_PKG_NAMES))
cd $(TOOLS_MOD_DIR) && $(GOCMD) build -trimpath -o $(abspath $@) \
$(filter %/$(notdir $@),$(TOOLS_PKG_NAMES))

$(BIN): .goreleaser.yaml $(GORELEASER) $(MAIN) $(SOURCES)
$(GORELEASER) build --single-target --snapshot --clean -o $(BIN)
Expand Down Expand Up @@ -102,3 +103,25 @@ for-all-target: $(INTERNAL_MODS)
.PHONY: gomoddownload
gomoddownload:
$(MAKE) --no-print-directory for-all-target TARGET="moddownload"

OUT_BASE ?= /tmp/rendered-collectors-workloads

RENDERWORKLOADS_MOD_DIR := internal/renderworkloads

.PHONY: render-workloads kyverno-workloads

render-workloads:
@echo "Rendering workloads to $(OUT_BASE)"
@cd "$(SRC_ROOT)/$(RENDERWORKLOADS_MOD_DIR)" && go run . \
-repo-root "$(SRC_ROOT)" \
-in-root internal/testbed/integration \
-out-base "$(OUT_BASE)" \
-vars-file internal/renderworkloads/render-vars.json \


kyverno-workloads: render-workloads
@echo "Running Kyverno against rendered workloads from $(OUT_BASE)/workloads.txt"
@cd "$(SRC_ROOT)" && \
{ test -s "$(OUT_BASE)/workloads.txt" || { echo "ERROR: workloads.txt is empty"; exit 1; }; } && \
sed 's|^|-r |' "$(OUT_BASE)/workloads.txt" \
| xargs -n 1000 kyverno apply .github/workflows/kyverno/policies/*.yaml
12 changes: 12 additions & 0 deletions config_examples/collector-helm-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ image:
tag: $TAG
command:
name: dynatrace-otel-collector
containerSecurityContext:
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
privileged: false
extraEnvs:
- name: DT_API_TOKEN
valueFrom:
Expand Down
41 changes: 41 additions & 0 deletions internal/renderworkloads/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

# renderworkloads

`renderworkloads` is an internal helper used by CI to **render the Kubernetes collector workload definitions in this repository**
into fully-materialized Kubernetes YAML.

The rendered output is then checked with **Kyverno** to enforce a baseline container `securityContext`. This provides:
- a regression test that the **components included in the Dynatrace OTel Collector distribution** remain compatible with hardened settings
- a guardrail when adding/changing components or manifests

## How to use (local)

```bash
make render-workloads
```

This produces:
- Rendered workload YAMLs under `"$OUT_BASE"` (paths preserved relative to repo root)
- A file list at `"$OUT_BASE/workloads.txt"` (one rendered YAML path per line)

### Run Kyverno checks

This runs the Kyverno policies against the rendered workloads listed in `workloads.txt`.

```bash
OUT_BASE="/tmp/rendered-collectors-workloads"
make kyverno-workloads OUT_BASE="$OUT_BASE"
```

Expected output looks like:

```text
Applying 1 policy rule(s) to N resource(s)...
pass: N, fail: 0, warn: 0, error: 0, skip: 0
```

## Notes

- `kyverno-workloads` depends on `render-workloads` and will re-render before running Kyverno.
- If `workloads.txt` is empty, the Kyverno target will fail (to avoid silently doing nothing).
- You need `kyverno` available in your `PATH`.
25 changes: 25 additions & 0 deletions internal/renderworkloads/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module github.com/Dynatrace/dynatrace-otel-collector/internal/renderworkloads

go 1.25.7

require k8s.io/apimachinery v0.35.2

require (
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/x448/float16 v0.8.4 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/text v0.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
58 changes: 58 additions & 0 deletions internal/renderworkloads/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
Loading
Loading