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
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Keep signing public key with LF line endings across platforms.
security/ocm-signing-public-key.pub text eol=lf

# Keep all shell scripts LF-only for Linux CI compatibility.
*.sh text eol=lf
746 changes: 746 additions & 0 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Security Scan

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# Weekly scan on Mondays at 06:00 UTC
- cron: '0 6 * * 1'

permissions:
contents: read

jobs:
trivy:
name: CVE Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Scan manifests for misconfigurations
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: config
scan-ref: .
format: table
severity: HIGH,CRITICAL
exit-code: 0

- name: Scan Keycloak image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: quay.io/keycloak/keycloak:26.6.1@sha256:26ae26445475f7fac5f90ee138b1bdb64324f5815fb16133ffdbdb122d97c4d8
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1

- name: Scan PostgreSQL image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: ghcr.io/cloudnative-pg/postgresql:18.3@sha256:36f19b80864f5ae23bb6ce87166e413154bf8c1b705e893ec8360ebb4791e63e
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1

- name: Scan CNPG Operator image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: ghcr.io/cloudnative-pg/cloudnative-pg:1.29.0@sha256:68074486205a33ed41928761e22ad48278c690feebe8316727a1c6b3380f9e5e
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1

- name: Scan Keycloak Operator image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: ghcr.io/${{ github.repository_owner }}/keycloak-operator:latest
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1

- name: Scan Prometheus Operator image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: quay.io/prometheus-operator/prometheus-operator:v0.90.1@sha256:52a6a92d915ea2fa94314748d99db7a94922e3fe63274f6182fc033b9126b573
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1

- name: Scan Keycloak Config CLI image for CVEs
if: success() || failure()
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
with:
scan-type: image
image-ref: quay.io/adorsys/keycloak-config-cli:latest-26@sha256:2d2a0663cf324379d9ffab896db8d00293cd0326151968b319cf166f6eec8fca
format: table
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: 1
68 changes: 43 additions & 25 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Kubernetes
kubeconfig
*.kubeconfig

# Helm
*.tgz
charts/*/charts/

# OCM
*.ocm
component-archive/
ocm-output/
manifests.tar

# Secrets (never commit)
*.key
*.pem
*.priv
*secret*.yaml.local

# Generated security metadata
ocm-sbom.cdx.json

# Build artifacts
bin/
dist/
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
# Test artifacts
coverage.txt
*.test

# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env
reports/

# Editor/IDE
# .idea/
# .vscode/
# Temporary
tmp/
temp/
33 changes: 33 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: "2"

linters:
default: none
enable:
- errcheck
- govet
- ineffassign
- staticcheck
- unused
- bodyclose
- errname
- gosec
- misspell
- unconvert
- unparam
settings:
gosec:
excludes:
- G101 # Look for hardcoded credentials
- G204 # Audit use of command execution
- G306 # Poor file permissions used when writing to a file
staticcheck:
checks: ["all", "-ST1000", "-ST1003", "-ST1005", "-ST1016"]
exclusions:
generated: lax
presets: [comments, common-false-positives, legacy, std-error-handling]
warn-unused: true

formatters:
enable: [gofmt]
exclusions:
generated: lax
88 changes: 88 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

---

## [0.2.1-final]

### Security

- **Hardcoded default admin credentials removed** — `keycloak-secret.yaml` with static `admin/admin` credentials deleted; the deploy script now creates the `keycloak-admin` secret dynamically, using `KEYCLOAK_ADMIN_PASSWORD` if set or a generated random password otherwise.
- **Operator RBAC scoped to namespace** — the operator's `ClusterRole` replaced by a namespace-scoped `Role`, reducing blast radius to the deployment namespace.
- **NetworkPolicy added** — new `keycloak-networkpolicy.yaml` restricts ingress to Keycloak pods: HTTP/HTTPS limited to same-namespace pods and the operator namespace, the management port limited to Prometheus pods, and Infinispan clustering limited to peer Keycloak pods.
- **All OCI image references SHA-pinned** — Keycloak, PostgreSQL, CloudNativePG, BusyBox, and the operator base images are now pinned by digest in manifests, `component-constructor.yaml`, KRO RGD, and the operator `Dockerfile`.
- **GitHub Actions SHA-pinned** — all third-party actions pinned by commit SHA to prevent supply-chain substitution.

### Changed

- **Image versions updated** — Keycloak 26.5.5 → 26.6.1, CloudNativePG operator 1.28.1 → 1.29.0, Prometheus Operator v0.80.1 → v0.90.1.
- **Go and Kubernetes dependencies updated** — Go 1.23 → 1.26, `k8s.io/*` 0.31 → 0.35, `controller-runtime` 0.19 → 0.23.
- **golangci-lint configuration upgraded** to v2 format with additional linters (`gosec`, `bodyclose`, `errname`, `misspell`, `unconvert`, `unparam`) and `gofmt` formatting enforcement.
- **CI workflows consolidated** — standalone `golangci-lint.yml` and `operator-tests.yml` removed; lint and unit tests integrated into the main `ci.yml` pipeline.
- **Renovate configured for digest pinning** — `pinDigests: true` enabled globally; regex managers extended with `autoReplaceStringTemplate` to maintain digest pins on automated updates.
- **Secret update logic corrected** — the operator now mutates the existing config secret in-place (preserving `ResourceVersion`) rather than constructing a replacement object, avoiding conflict errors under concurrent reconciliation.
- **Deployment documentation updated** — air-gapped transfer instructions added; admin credential handling revised to reflect dynamic secret creation.

---

## [0.2.0-final]

### Added

- **Finalizer-based CR deletion** — the operator now sets a `keycloak.opendefense.cloud/cleanup` finalizer on every managed CR (Realm, Client, ClientScope, Group, User) so that deletions are intercepted and the corresponding resource is removed from Keycloak before the CR is released. Realm CRs are intentionally exempt: the finalizer is removed without touching the Keycloak realm, preserving data by design.
- **High Availability support** — Keycloak can now run as multiple replicas with distributed session sharing:
- `manifests/keycloak/keycloak-sa.yaml` — dedicated `ServiceAccount` (`keycloak`) for Keycloak pods.
- `manifests/keycloak/keycloak-rbac.yaml` — `Role` and `RoleBinding` granting the ServiceAccount `get`/`list`/`watch` on pods, required by Infinispan KUBE_PING for cluster discovery.
- `manifests/keycloak/keycloak-pdb.yaml` — `PodDisruptionBudget` (`minAvailable: 1`) preventing scale-to-zero during node maintenance.
- **KRO `ResourceGraphDefinition`** added to the OCM component archive (`component-constructor.yaml`) as a `blueprint`-type resource, enabling air-gapped single-CR instantiation of the full Keycloak stack.
- **HA resources in KRO RGD** — `keycloak-instance-rgd.yaml` now provisions the ServiceAccount, Role, RoleBinding, and PodDisruptionBudget as part of the KRO dependency graph, with the Keycloak Deployment depending on them.
- **OpenTelemetry tracing** support in Keycloak Deployment via Keycloak's native Quarkus OTEL integration (`KC_TRACING_*` env vars). Disabled by default; enable by setting `KC_TRACING_ENABLED=true` and pointing `KC_TRACING_ENDPOINT` at your cluster's OTEL Collector gRPC address.
- **Structured JSON logging** for Keycloak (`KC_LOG_FORMAT=json`, `KC_LOG_LEVEL=INFO`) to enable log aggregation with tools such as Loki or ELK.
- **Management port (9000)** exposed on the Keycloak Service to allow Prometheus scraping of the `/metrics` endpoint.
- `manifests/monitoring/keycloak-service-monitor.yaml` — `ServiceMonitor` resource for Prometheus Operator to scrape Keycloak metrics every 30 s.
- `manifests/monitoring/cnpg-pod-monitor.yaml` — `PodMonitor` resource for Prometheus Operator to scrape CloudNativePG cluster metrics every 30 s.
- `manifests/monitoring/keycloak-prometheus-rules.yaml` — `PrometheusRule` alerting definitions covering:
- Keycloak availability (pod down, not ready)
- Authentication anomalies (high login failure rate, brute force detection)
- Session count thresholds
- Database connection pool exhaustion
- Pod restart frequency
- CloudNativePG cluster unavailability and replication lag
- `renovate.json` — Renovate Bot configuration for automated dependency tracking and PR creation on new upstream releases of Keycloak, CloudNativePG, and PostgreSQL images, with digest pinning enabled.
- `docs/UPGRADE.md` — Upgrade runbook covering:
- Keycloak minor/patch rolling upgrades
- Keycloak major version upgrades (with DB migration guidance)
- PostgreSQL minor version upgrades via CloudNativePG rolling restarts
- PostgreSQL major version upgrades via CloudNativePG `pg_upgrade` cluster clone procedure
- Manual database backup procedure
- CloudNativePG operator upgrades
- Post-upgrade observability verification checklist

### Changed

- **Keycloak Deployment** — added `RollingUpdate` strategy (`maxUnavailable: 0`, `maxSurge: 1`), `serviceAccountName: keycloak`, and `KC_CACHE_STACK=kubernetes` to activate Infinispan KUBE_PING cluster mode for distributed session replication across replicas.
- **KRO RGD Keycloak Deployment resource** — aligned with standalone manifests: rolling update strategy, `serviceAccountName`, `KC_CACHE_STACK=kubernetes`, and health probes corrected to management port 9000 (was 8080).
- **`docs/ARCHITECTURE.md`** — added High Availability & Scalability section documenting multi-replica setup, PodDisruptionBudget, rolling updates, Infinispan KUBE_PING, and ServiceAccount/RBAC requirements.
- **`docs/USAGE.md`** — extended deletion behaviour documentation to cover all CR types; added CR Status and Conditions reference section.
- **`docs/DEPLOYMENT.md`** — added KRO-based deployment as the primary installation path; updated OCM resource table; fixed broken doc link.
- **`README.md`** — updated feature list and project structure to reflect all five CRDs and HA capabilities.

---

## [0.1.0-poc]

Initial proof-of-concept release.

### Added

- OCM component definition (`component-constructor.yaml`) bundling Keycloak 26.5.3, PostgreSQL 18.1, and CloudNativePG 1.28.1.
- Kubernetes manifests for Keycloak Deployment, Service, and admin Secret.
- CloudNativePG `Cluster` manifest for PostgreSQL with single-instance setup.
- Custom Keycloak Client Operator (Helm chart) with CRD-based declarative client configuration.
- KRO `ResourceGraphDefinition` for multi-instance provisioning.
- GitHub Actions CI/CD pipeline with OCM packaging, signing, and registry transfer.
- Deployment, smoke test, and utility scripts.
- Architecture, database, client, CI/CD, deployment, and usage-concept documentation.
96 changes: 96 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Contributing to keycloak-bundle

## Branching Strategy

| Branch pattern | Purpose |
|----------------|---------|
| `main` | Stable delivery branch. Direct commits are not permitted. |
| `feature/<REQ-ID>-<short-description>` | New feature or requirement implementation. |
| `fix/<short-description>` | Bug fix or hotfix. |
| `delivery/<milestone>` | Release preparation (docs, changelog, final integration). |

Branch off `main`, open a pull request back to `main`. Delete the branch after merge.

## Commit Message Conventions

Use the following prefixes:

```
feat(<scope>): short description
fix(<scope>): short description
docs: short description
ci: short description
refactor: short description
test: short description
```

**Rules:**
- Subject line: 72 characters maximum, imperative mood, no trailing period.
- Scope is optional but recommended for `feat` and `fix` (e.g. `operator`, `backup`, `deploy`).
- Body is optional; use it to explain *why*, not *what*.
- Do not reference ticket numbers in commit messages; use the branch name for traceability.

## Pull Request Review Process

1. Open the PR against `main` with a title following the commit conventions above.
2. The PR description must summarise the change and list any deferred or untested items.
3. At least one reviewer approval is required before merge.
4. All CI quality gates (see below) must pass before merge is permitted.
5. Squash merge is preferred to keep `main` history linear.
6. The author resolves review comments; the reviewer approves the resolution.

## CI Quality Gates

Every PR must pass the following checks before it can be merged. These run automatically
on push and pull request events via the `OCM Build & Deploy` workflow and `Security Scan`
workflow.

| Gate | Tool | Failure condition |
|------|------|-------------------|
| YAML Lint | yamllint | Any syntax error or rule violation in `manifests/`, `kro/`, `charts/`, `examples/` |
| Shell Lint | ShellCheck `-S warning` | Any warning or error in `scripts/**/*.sh` |
| Secrets Scan | Gitleaks | Any credential or secret pattern detected in the git history |
| Go Vet | `go vet ./...` | Any static analysis error in `operator/` |
| Non-root Check | `scripts/tests/test-security.sh` | Any Deployment manifest missing `allowPrivilegeEscalation: false`, `runAsNonRoot: true`, or `capabilities.drop: [ALL]` |
| CVE Scan | Trivy | Any unfixed HIGH or CRITICAL CVE in a container image shipped in the OCM archive |

**Bypass is not permitted.** If a gate fails, fix the underlying issue — do not skip or
disable the gate.

## Development Workflow

```bash
# 1. Create a branch
git checkout main && git pull
git checkout -b feature/REQ-XX-short-description

# 2. Make changes, commit, push
git add <files>
git commit -m "feat(scope): describe the change"
git push -u origin feature/REQ-XX-short-description

# 3. Open a pull request via GitHub
# 4. Address review comments, push additional commits
# 5. Merge after approval and all gates green
```

## Local Quality Checks

Run the same checks locally before pushing to avoid CI failures:

```bash
# YAML lint
yamllint -d '{extends: default, rules: {line-length: {max: 150}, truthy: {check-keys: false}, document-start: disable, comments-indentation: disable}}' manifests/ kro/ charts/ examples/

# ShellCheck
shellcheck -S warning scripts/**/*.sh

# Go vet
cd operator && go vet ./...

# Non-root security check
./scripts/tests/test-security.sh

# Secrets scan (requires gitleaks in PATH)
gitleaks git .
```
Loading
Loading