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
2 changes: 1 addition & 1 deletion .github/workflows/e2e-minikube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
APP_IMAGE: quay.io/redhat-user-workloads/trusted-content-tenant/rhtpa-product-0-6-z:v3.1.1-rc
APP_IMAGE: quay.io/redhat-user-workloads/trusted-content-tenant/rhtpa-product-0-6-z:v3.2.0-rc
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down
9 changes: 9 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ linters:
linters:
- dupl
- lll
# The TLS configurator deals in OpenSSL/IANA cipher-suite names, which
# recur as literals in lookup tables and test fixtures. Naming each one as
# a constant makes those tables harder to read, not easier.
- path: "pkg/tlsconfigurator/*"
linters:
- goconst
- path: "test/tlsconfigurator/*"
linters:
- goconst

formatters:
enable:
Expand Down
2 changes: 1 addition & 1 deletion .tekton/operator-3-y-z-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ metadata:
spec:
params:
- name: version
value: "v3.1.1"
value: "v3.2.0"
- name: version-postfix-qe
value: "-rc"
- name: git-url
Expand Down
2 changes: 1 addition & 1 deletion .tekton/operator-bundle-3-y-z-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ metadata:
spec:
params:
- name: version
value: "v3.1.1"
value: "v3.2.0"
- name: version-postfix-qe
value: "-rc"
- name: git-url
Expand Down
171 changes: 171 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,177 @@ The TrustedProfileAnalyzer CR spec uses `x-kubernetes-preserve-unknown-fields: t
- `metrics.enabled`: Enable metrics collection
- `tracing.enabled`: Enable distributed tracing

## TLS Configurator & Post-Quantum Cryptography (PQC)

### How the TLS Configurator is wired in

Detailed documentation lives in `docs/tls-configurator/`; start with
`docs/tls-configurator/FINAL_PROJECT_STATUS.md`.

The chart ships an optional `tlsConfigurator` module (disabled by default).

**The configurator now lives in this repo and ships in the operator image.** It
was previously an external component built from the sibling
`tls-openshift-configurator` repo and published as its own image. Its source is
now:

```
cmd/tls-configurator/ CLI entry point, flag parsing, action dispatch
pkg/tlsconfigurator/
client/ IngressController, APIServer, ClusterVersion, Deployment clients
config/ Config building / kubeconfig handling
controller/ One-shot TLS controller orchestration
crypto/ OpenShift TLSSecurityProfile -> crypto/tls.Config (+ PQC)
reconcile/ Long-running runtime reconciler (watch -> roll workloads)
test/tlsconfigurator/ Ginkgo/Gomega integration suite
```

Both `Dockerfile` and `Dockerfile.rhtpa-operator.rh` build a second binary
alongside `manager` and install it at `/usr/local/bin/tls-configurator`. The
image `ENTRYPOINT` remains `/manager`, so the configurator Deployment selects it
with an explicit `command:`. `make build-tls-configurator` builds it standalone.

- Toggle: `modules.tlsConfigurator.enabled` (`values.yaml`, default `false`)
- Image: `modules.tlsConfigurator.image.fullName` — the **operator's own image**.
`watches.yaml` sets `overrideValues` to expand
`$RELATED_IMAGE_TLS_CONFIGURATOR` into this key, so the configurator always
runs the exact digest of the operator that rendered the chart.
`RELATED_IMAGE_TLS_CONFIGURATOR` is set on the manager container
(`config/manager/manager.yaml`, and the generated CSV) and mirrored into the
CSV `relatedImages` under the name `tls-configurator` so disconnected installs
pull it. **When the operator image digest changes, all three must move
together**: the manager `image:`, the env var, and the `relatedImages` entry.
- Rendered resources live under
`helm-charts/redhat-trusted-profile-analyzer/templates/init/tls-configure/`:
ServiceAccount (`010`), ClusterRole (`015`), namespaced Role (`016`),
RoleBinding (`017`), ClusterRoleBinding (`018`), and a **Deployment** (`020`).

Sharing an image does **not** mean sharing a pod: the configurator still runs as
its own Deployment with its own ServiceAccount and cluster RBAC.

**Architecture change (runtime reconciliation).** The module used to be a Helm
`pre-install,pre-upgrade` hook **Job** that ran `--action=update` once. It is now
a long-running **Deployment** that runs `--action=reconcile`: it reconciles once
on startup (covering the old install-time behaviour) and then watches the
cluster-wide TLS profile so a change **at runtime** rolls the affected
workloads. The RBAC resources are therefore plain (non-hook) objects that live
for the lifetime of the release, in `.Release.Namespace`.

The Deployment invokes:

```
--action=reconcile
--enable-pqc={{ .Values.modules.tlsConfigurator.pqc.enabled }}
--target-namespace={{ .Release.Namespace }}
--target-deployments={{ join "," .Values.modules.tlsConfigurator.targetDeployments }}
--resync-period={{ .Values.modules.tlsConfigurator.resyncPeriod }}
```

### Runtime update flow (TLS change → workload rollout)

1. The reconciler watches the cluster `APIServer` CR (`cluster`)
`.spec.tlsSecurityProfile` — the authoritative cluster-wide TLS config.
2. On any change it computes a hash of the effective (optionally post-quantum)
TLS config and compares it to each target Deployment's
`rhtpa.io/tls-config-hash` pod-template annotation.
3. Deployments whose hash differs are patched, changing the pod template and
triggering a **rolling restart** so pods re-read the new TLS settings. This
reuses the same idea as the chart's existing `configHash/auth` annotation on
the server Deployment. Kubernetes does not restart pods on ConfigMap/Secret
change by itself, so this explicit hash bump is required.
4. `rhtpa.io/tls-config-hash` is intentionally **not** in the Helm templates so
the operator's periodic re-render does not fight the reconciler.

`targetDeployments` (default `[server]`) must match the rendered Deployment
names of the TLS-serving workloads (the server Deployment renders as `server`).

### When the module must be on, and when it must be off

The module is OpenShift-only, and the chart *enforces* the matrix rather than
just documenting it:

| Platform | `modules.tlsConfigurator.enabled` | Enforced by |
| --- | --- | --- |
| OpenShift >= 4.22 | **required `true`** | install fails unless `allowDisabled: true` |
| OpenShift < 4.22 | optional | nothing — `reconcile` has no version gate, so it runs fine, it is just not mandatory |
| plain Kubernetes | **required `false`** (the default) | install fails if enabled |

Enforcement lives in
`helm-charts/redhat-trusted-profile-analyzer/templates/init/tls-configure/000-validate.yaml`.
It renders no resources and is deliberately **not** gated on
`.enabled` — it has to run in the disabled case too. Details:

- Platform detection reuses the chart's existing
`trustification.openshift.detect` helper (`route.openshift.io/v1` +
`openshift.enabled`). Escape hatch for wrong detection:
`openshift.enabled: true`.
- The 4.22 check uses `lookup "config.openshift.io/v1" "ClusterVersion" ""
"version"` and parses `.status.desired.version`. `lookup` returns nothing
under `helm template` / `--dry-run`, so the check is **skipped** there rather
than failing on an unreadable version. Do not "fix" that by failing closed —
it would break every dry run and the operator's own rendering paths.
- `modules.tlsConfigurator.allowDisabled` (default `false`) is consulted only
when `enabled` is `false`. It is an explicit acknowledgement that a runtime
TLS profile change will not roll the workloads.

User-facing versions of this live in the chart `README.md` and in
`values.yaml` comments; `values.schema.json` is the schema Helm actually
enforces, so `allowDisabled` had to be added there (the `.yaml` schema is the
source but is not what Helm reads).

### Enabling Post-Quantum Cryptography

PQC in TLS 1.3 is delivered through the hybrid **key-exchange group**
`X25519MLKEM768` (X25519 + ML-KEM-768, NIST FIPS 203) — **not** through the
cipher suites, which stay the same. Hybrid PQC key exchange requires TLS 1.3.

PQC support (a `--enable-pqc` flag, `CurvePreferences=[X25519MLKEM768, X25519]`,
forced TLS 1.3, a `validate` action) and the runtime `reconcile` mode are built
into the configurator. Because it now ships in the operator image, there is no
separate image to rebuild or republish — an operator build carries it.

**Router-level PQC is now unblocked but not yet implemented.** The old
limitation was that the pinned `openshift/api` exposed only `minTLSVersion` and
`ciphers` on `TLSSecurityProfile`, so `update` could not push a key-exchange
group onto the IngressController. The version this repo now depends on
(`v0.0.0-20260924195948`) adds `TLSProfileSpec.Groups []TLSGroup`, including
`TLSGroupX25519MLKEM768`, behind the `TLSGroupPreferences` feature gate.
Nothing in `pkg/tlsconfigurator` writes that field yet: PQC still applies only
to Go services' `crypto/tls.Config` and to the rollout hash. Wiring `Groups`
into the `update` path (and gating on `TLSGroupPreferences` being enabled on the
cluster) is the remaining work.

To enable from the operator side:

1. Set `modules.tlsConfigurator.enabled: true`. The image is the operator's own;
no value needs pointing at a separate registry.
2. Set `modules.tlsConfigurator.pqc.enabled: true`.
3. Confirm `modules.tlsConfigurator.targetDeployments` lists the TLS-serving
Deployments to roll on change.

### RBAC

The reconciler needs: `watch` on `config.openshift.io/apiservers`, `get,list` on
`clusterversions`, `get,list,watch,update,patch` on
`operator.openshift.io/ingresscontrollers` (ClusterRole `015`), and
`get,list,watch,update,patch` on `apps/deployments` in the release namespace
(Role `016`). These are provided by the chart.

Because this is a Helm operator, it can only *grant* permissions it holds
itself (RBAC escalation prevention). `config/rbac/role_cluster_rbac_manager.yaml`
(`rhtpa-rbac-manager`) was therefore widened to also hold `apiservers` (watch),
`ingresscontrollers`, `apps/deployments`, and namespaced `roles`/`rolebindings`
so the operator can create the reconciler's RBAC and Deployment.

**Follow-up / known issue:** `config/rbac/role_cluster_tlsconfigurator.yaml` +
`role_binding_tlsconfigurator.yaml` + the `tls-configurator` entry in
`config/rbac/service_account.yaml` are static bundle copies from the old hook-Job
design. They now duplicate (by name) the ClusterRole/ClusterRoleBinding/SA the
Helm chart creates, and the static binding still targets
`openshift-ingress-operator` while the reconciler SA now lives in the release
namespace. Decide whether to remove the static copies (let the chart own them) or
keep them as the bundle grant — this is a packaging call left open on purpose.

## Linting

The project uses golangci-lint with configuration in `.golangci.yml`. Enabled linters include:
Expand Down
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ RUN go mod download

# Copy the go source
COPY main.go main.go
COPY cmd/ cmd/
COPY pkg/ pkg/
#TODO uncomment after adding golang api
#COPY api/ api/
#COPY controllers/ controllers/
Expand All @@ -24,6 +26,11 @@ COPY main.go main.go
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go

# The TLS configurator ships in the same image as a second entrypoint.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a \
-ldflags="-w -s" \
-o tls-configurator ./cmd/tls-configurator

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM registry.access.redhat.com/ubi10/ubi-minimal:1790075800
Expand Down Expand Up @@ -56,6 +63,9 @@ COPY --chown=${USER_UID}:0 helm-charts ${HOME}/helm-charts
# Copy manager binary
COPY --from=builder /workspace/manager .

# TLS configurator binary, selected via an explicit `command:` in its Deployment.
COPY --from=builder /workspace/tls-configurator /usr/local/bin/tls-configurator

USER ${USER_UID}

WORKDIR ${HOME}
Expand Down
21 changes: 16 additions & 5 deletions Dockerfile.rhtpa-operator.rh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary 1.26.7
FROM registry.redhat.io/ubi10/go-toolset:1.26.7-1790270385 AS builder
FROM registry.access.redhat.com/hi/go:1.26.7 AS builder
ARG TARGETOS
ARG TARGETARCH

Expand All @@ -13,6 +13,8 @@ RUN go mod download

# Copy the go source
COPY main.go main.go
COPY cmd/ cmd/
COPY pkg/ pkg/
#TODO uncomment after adding golang api
#COPY api/ api/
#COPY controllers/ controllers/
Expand All @@ -27,8 +29,13 @@ RUN go generate -mod=readonly ./...
# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -mod=readonly -a -o manager main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
# The TLS configurator ships in the same image as a second entrypoint. It runs
# as its own Deployment (see the tls-configure chart templates), so it only
# shares the build artifact with the manager, not the pod.
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -mod=readonly -a \
-ldflags="-w -s" \
-o tls-configurator ./cmd/tls-configurator

FROM registry.access.redhat.com/ubi10/ubi-minimal:1790075800

LABEL com.redhat.component="rhtpa-operator"
Expand All @@ -39,8 +46,8 @@ LABEL io.openshift.tags="RHTPA, rhtpa-operator, Red Hat Trusted Profile Analyzer
LABEL name="rhtpa/rhtpa-rhel10-operator"
LABEL org.opencontainers.image.source="https://github.com/trustification/trusted-profile-analyzer-operator"
LABEL summary="RHTPA Operator"
LABEL version="3.1.1"
LABEL release=3.1.1
LABEL version="3.2.0"
LABEL release=3.2.0
LABEL maintainer="Red Hat"
LABEL cpe="cpe:/a:redhat:trusted_profile_analyzer:3.1::el10"

Expand Down Expand Up @@ -84,6 +91,10 @@ COPY --chown=${USER_UID}:0 helm-charts ${HOME}/helm-charts
# Copy manager binary
COPY --from=builder /workspace/manager .

# Copy TLS configurator binary. Selected via an explicit `command:` in the
# tls-configure Deployment, since ENTRYPOINT here belongs to the manager.
COPY --from=builder /workspace/tls-configurator /usr/local/bin/tls-configurator

USER ${USER_UID}

WORKDIR ${HOME}
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# To re-generate a bundle for another specific version without changing the standard setup, you can:
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
VERSION ?= 3.1.1
IMAGE_TAG ?= 3.1.1
REDUCED_VERSION ?= 3.1.1-snapshot
VERSION ?= 3.2.0
IMAGE_TAG ?= 3.2.0
REDUCED_VERSION ?= 3.2.0-snapshot
CONTROLLER_TOOLS_VERSION ?= v0.18.0

# CHANNELS define the bundle channels used in the bundle.
Expand Down Expand Up @@ -131,6 +131,10 @@ e2e-minikube: ## Deploy operator on Minikube for e2e testing (requires running M
run: helm-operator ## Run against the configured Kubernetes cluster in ~/.kube/config
$(HELM_OPERATOR) run

.PHONY: build-tls-configurator
build-tls-configurator: ## Build the TLS configurator binary (also shipped in the operator image).
CGO_ENABLED=0 go build -ldflags="-w -s" -o bin/tls-configurator ./cmd/tls-configurator

.PHONY: docker-build
docker-build: ## Build docker image with the manager.
docker build -t ${IMG} .
Expand Down
4 changes: 2 additions & 2 deletions bundle.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ LABEL maintainer="Red Hat"
LABEL vendor="Red Hat, Inc."
LABEL distribution-scope="public"
LABEL url="https://www.redhat.com"
LABEL version="3.1.1"
LABEL release=3.1.1
LABEL version="3.2.0"
LABEL release=3.2.0
LABEL cpe="cpe:/a:redhat:trusted_profile_analyzer:3.1::el10"

LABEL features.operators.openshift.io/cni="false"
Expand Down
Loading
Loading