IBM Verify Identity Access mit ArgoCD auf Kubernetes deployen
IBM Verify Identity Access mit ArgoCD auf Kubernetes deployen
Die Verwaltung von IBM Verify Identity Access (IVIA) Deployments über mehrere Umgebungen hinweg kann eine große Herausforderung sein. Manuelle Konfiguration führt auf Dauer zu Drift und Inkonsistenzen in den Einstellungen und führt somit langfristig zu Instabilität der Umgebungen. Gemeinsam mit GitOps-Prozessen zur Konfiguration ermöglicht ArgoCD ein vollständig deklaratives Deployment und vereinfacht das Management der IVIA Umgebungen enorm.
In diesem Post zeige ich wie ich ein Deployment von IBM Verify Identity Access 11.0.3.0 auf Kubernetes mithilfe von ArgoCD umgesetzt habe. Die beschriebenen Schritte decken die Web Reverse Proxy Instanzen, IVIA Runtime, Distributed Session Cache, OIDC Provider, LDAP Directory und PostgreSQL Datenbank ab und ermöglichen damit ein vollständiges Deployment.
Für das Templating der Kubernetes Manifeste bieten sich Helm Charts oder Kustomize an. In diesem Blogpost werde ich Kustomize verwenden, da es direkt mit nativen YAML-Dateien arbeitet und durch ein schlichtes Overlay-System ohne zusätzliche Abstraktionsschicht auskommt.
Der Post ist Teil einer Reihe von Posts zum Thema CI/CD Deployment. In einem weiteren Post werde ich auch die Möglichkeiten einer GitOps basierten Konfiguration beschreiben.
Ein Beispielprojekt steht unter https://github.com/pklueter/ivia-argocd-blogpost zur Verfügung.
Voraussetzungen
Dieser Post geht davon aus, dass die folgenden Punkte bereits bereitstehen. Für die Konfiguration gibt es viele Tutorials online zu finden, sodass ich diese ausklammer.
- Kubernetes Cluster (K3s, Openshift, etc.)
- Git Remote Host (z.B. Github, Gitlab, etc.)
- Domain mit konfiguriertem DNS
- cert-manager für TLS Zertifikate
Deployment Prozess mit ArgoCD

Der GitOps-Workflow mit ArgoCD läuft in drei Phasen ab:
- Änderung pushen — Der Entwickler führt ein
git pushaus und speichert seine Änderungen im Remote Repository. Secrets, die nicht im Git gespeichert werden sollen, werden separat direkt über die Kubernetes API erstellt. In einer produktiven Umgebung sollte dies durch ein Secrets Management Tool wie z.B. Hashicorp Vault übernommen werden. - Synchronisation — ArgoCD überwacht das Repository kontinuierlich. Bei einer Änderung clont/pullt es den aktuellen Stand und baut mit Kustomize den stage-spezifischen Desired State. Im Standard geschieht dies alle 3 Minuten. Der Stage spezifische Pfad wird aus der ArgoCD App entnommen. Siehe
ArgoCD Application Definitionen. - Abgleich und Anwendung — ArgoCD vergleicht den Desired State mit dem aktuell laufenden Zustand im Cluster (Live State). Abweichungen werden automatisch über den Kubernetes API Server ausgeglichen: Deployments, Services, Ingress-Regeln werden erstellt oder aktualisiert, woraufhin Kubernetes die zugehörigen Pods startet.
Git Projekt Setup
Vor dem Deployment muss zunächst das Git Repository eingerichtet werden.
# Repository Struktur erstellen
mkdir -p verify-argocd/{kubernetes/{ks,apps,verify-deployment/{verify,ldap,database}},iviaop/config}
cd verify-argocd
# Git initialisieren
git init
git add .
git commit -m "Initial repository structure"
# Zu Git Hosting Service pushen
git remote add origin https://github.com/${your-org}/verify-argocd.git
git push -u origin main
Die Befehle haben folgende Ordner erstellt
kubernetes/
In diesem Verzeichnis sind sämtliche Kubernetes-Artefakte enthalten, darunter Deployments, Persistent Volume Claims (PVCs), ConfigMaps, Services und weitere Ressourcen, die für den Betrieb der Anwendung erforderlich sind.
iviaop/config/
Im Gegensatz zu klassischen Containern unterstützt der moderne OIDC-Provider-Container eine vollständig YAML-basierte Konfiguration. In diesem Verzeichnis werden die entsprechenden Konfigurationsdateien verwaltet und anschließend über Argo CD automatisiert ausgerollt.
Komponenten
Ziel dieses Blog Post ist ein beispielhaftes Deployment aller IVIA Komponenten:
- Web Reverse Proxy (WRP): Mehrere Instanzen (wrp01, wrp02)
- IVIA Runtime: Advanced Access Control und Federation
- Distributed Session Cache (DSC): Session Management
- OIDC Provider: OpenID Connect Authentication
- LDAP (IVD): Nutzer-Directory
- PostgreSQL: Datenbank für den Runtime Container
Setup
ArgoCD installieren
Installation:
kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Als Ingress verwende ich Traefik, daher erstelle ich eine IngressRoute. Die Dokumentation beinhaltet die notwendige Konfiguration für viele andere Ingress Systeme: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress
kubernetes/ks/ingress.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-cmd-params-cm
app.kubernetes.io/part-of: argocd
data:
server.insecure: "true"
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: argocd-tls
namespace: argocd
spec:
secretName: argocd-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- argocd.yourdomain.com
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: argocd-server
namespace: argocd
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`argocd.yourdomain.com`)
priority: 10
services:
- name: argocd-server
port: 80
- kind: Rule
match: Host(`argocd.yourdomain.com`) && Header(`Content-Type`, `application/grpc`)
priority: 11
services:
- name: argocd-server
port: 80
scheme: h2c
tls:
secretName: argocd-tls
kubectl -n argocd apply -f kubernetes/ks/ingress.yaml
ArgoCD erstellt ein initiales Admin Passwort und speichert es als Secret. Dieses kann mit kubectl abgerufen werden:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d && echo
Repository Struktur
Das Git Repository ist der Kern eines jeden ArgoCD Projekts. Wie in der Einleitung beschrieben nutze ich Kustomize (https://kustomize.io/) um umgebungsspezifische Konfigurationen anzulegen und gleichzeitige unnötige Duplikate zu umgehen.
Der Inhalt der einzelnen Dateien kann in meinem Beispiel Repository angeschaut werden: https://github.com/pklueter/ivia-argocd-blogpost
kubernetes/
├── apps.yaml # Root ArgoCD Application
├── apps/
│ ├── verify.yml # IBM Verify Identity Access Application
│ ├── ldap.yml # LDAP Application
│ └── database.yml # Database Application
└── verify-deployment/
├── verify/
│ ├── base/
│ │ ├── kustomization.yaml
│ │ ├── service-account.yaml
│ │ ├── config/ # IVIA Config Container
│ │ │ ├── deployment.yaml
│ │ │ ├── services.yaml
│ │ │ └── pvc.yaml
│ │ ├── wrp/ # Web Reverse Proxy
│ │ │ ├── deployment.yaml
│ │ │ └── services.yaml
│ │ ├── runtime/ # IVIA Runtime
│ │ │ ├── deployment.yaml
│ │ │ └── services.yaml
│ │ ├── dsc/ # Distributed Session Cache
│ │ │ ├── deployment.yaml
│ │ │ └── services.yaml
│ │ └── iviaop/ # OIDC Provider
│ │ ├── deployment.yaml
│ │ ├── services.yaml
│ │ └── config/
│ └── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ ├── ingress.yaml
│ │ └── iviaop/
│ │ └── config/
│ ├── staging/
│ └── prod/
├── ldap/
│ ├── base/
│ │ ├── kustomization.yaml
│ │ ├── deployment.yaml
│ │ ├── service-account.yaml
│ │ ├── isvd-config.yaml
│ │ ├── schema.yaml
│ │ └── volume-claim.yaml
│ └── overlays/
│ ├── dev/
│ ├── staging/
│ └── prod/
└── database/
├── base/
│ ├── kustomization.yaml
│ ├── psql.yaml
│ └── pvc.yaml
└── overlays/
├── dev/
├── staging/
└── prod/
Konfigurations Deep Dive
Kustomize Base Konfiguration
Die base Ordner enthält gemeinsame Konfiguration für IBM Verify Identity Access Komponenten welche in alle Umgebungen verwendet werden.
In kustomization.yaml werden dafür die einzelnen Resource Dateien referenziert.
kubernetes/verify-deployment/verify/base/kustomization.yaml:
resources:
- service-account.yaml
# Config Container
- config/pvc.yaml
- config/services.yaml
- config/deployment.yaml
# WRP
- wrp/services.yaml
- wrp/deployment.yaml
# Runtime
- runtime/services.yaml
- runtime/deployment.yaml
# DSC
- dsc/services.yaml
- dsc/deployment.yaml
# IVIAOP
- iviaop/services.yaml
- iviaop/deployment.yaml
- iviaop/config/op-config.yaml
- iviaop/config/op-mr.yaml
- iviaop/config/op-ap.yaml
ConfigMaps Generierung
OIDC Provider Konfiguration lebt in ConfigMaps. Zum einfachen Management, erstelle ich die Konfiguration im Ordner iviaop/config. Über kubectl kann ich daraus die ConfigMaps generieren lassen.
# ----------------------------
# Generic IVIA Configuration
# ----------------------------
base_output_dir="kubernetes/verify-deployment/verify/base/iviaop/config"
# Base Konfiguration
kubectl create configmap op-config \
--from-file=./iviaop/config/*.yml \
--dry-run=client -o yaml > ${base_output_dir}/op-config.yaml
# Mapping Rules
kubectl create configmap op-mapping-rules \
--from-file=./iviaop/config/mappingRules \
--dry-run=client -o yaml > ${base_output_dir}/op-mr.yaml
# Access Policies
kubectl create configmap op-access-policies \
--from-file=./iviaop/config/accessPolicies \
--dry-run=client -o yaml > ${base_output_dir}/op-ap.yaml
Environment Overlays
Mit Kustomize Overlays können umgebungsspezifische Unterschiede eingerichtet werden. Klassischer weise sind dies Hostnamen & OIDC Client.
Hierfür wird unter kubernetes/verify-deployment/verify/overlays/{umgebung}/ eine eigene kustomization.yaml erstellt, welche die umgebungsspezifischen Ressourcen referenziert.
kubernetes/verify-deployment/verify/overlays/{umgebung}/kustomization.yaml:
resources:
- ../../base
- ingress.yaml
- iviaop/config/op.yaml
- iviaop/config/clients.yaml
- iviaop/config/op-clients.yaml
- iviaop/config/op-mr-stage-config.yaml
- iviaop/config/op-ap-stage-config.yaml
Analog zum Base Setup, können auch die umgebungsspezifischen OIDC Provider Config Maps per kubectl erstellt werden.
# ----------------------------
# Stage-Specific Configuration
# ----------------------------
stage_output_dir="kubernetes/verify-deployment/verify/overlays/${STAGE}/iviaop/config"
# Static manifests
cp "./iviaop/stage_config/${STAGE}/op.yml" "${stage_output_dir}/op.yaml"
cp "./iviaop/stage_config/${STAGE}/clients.yml" "${stage_output_dir}/clients.yaml"
# ConfigMap: Client definitions
kubectl create configmap op-clients \
--from-file="./iviaop/stage_config/${STAGE}/clients" \
--dry-run=client -o yaml > "${stage_output_dir}/op-clients.yaml"
# ConfigMap: Stage-specific mapping rules
kubectl create configmap mr-stage-config \
--from-file="./iviaop/stage_config/${STAGE}/mapping_rules" \
--dry-run=client -o yaml > "${stage_output_dir}/op-mr-stage-config.yaml"
# ConfigMap: Stage-specific access policies
kubectl create configmap ap-stage-config \
--from-file="./iviaop/stage_config/${STAGE}/access_policies" \
--dry-run=client -o yaml > "${stage_output_dir}/op-ap-stage-config.yaml"
ArgoCD Application Definitionen
ArgoCD Applications sind die zentrale Ressource, die ArgoCD mitteilt welches Git Repository, welchen Pfad und welchen Ziel-Cluster es für ein Deployment verwenden soll. Über das App-of-Apps Pattern wird eine Root Application angelegt, die ihrerseits alle weiteren Applications im kubernetes/apps/ Verzeichnis verwaltet. Änderungen im Repository werden dadurch automatisch erkannt und synchronisiert.
Root Application (kubernetes/apps.yaml):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: verify-apps
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/${your-org}/verify-gitops
path: kubernetes/apps
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
IBM Verify Identity Access Application (kubernetes/apps/verify.yml):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ibm-verify
spec:
project: default
revisionHistoryLimit: 2
source:
repoURL: https://github.com/${your-org}/verify-gitops
path: kubernetes/verify-deployment/verify/overlays/{umgebung}
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: verify
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
LDAP Application (kubernetes/apps/ldap.yml):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ldap
spec:
project: default
source:
repoURL: https://github.com/${your-org}/verify-gitops
path: kubernetes/verify-deployment/ldap/overlays/{umgebung}
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: verify
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Database Application (kubernetes/apps/database.yml):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: database
spec:
project: default
revisionHistoryLimit: 2
source:
repoURL: https://github.com/${your-org}/verify-gitops
path: kubernetes/verify-deployment/database/overlays/{umgebung}
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: verify
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Secrets
Aus Sicherheitsgründen dürfen Passwörter, Zertifikate oder sonstige Secrets nie in einem Git Repository gespeichert werden. Daher müssen diese vor dem ersten Deployment manuell im Cluster angelegt werden.
Tools wie Hashicorp Vault ermöglichen die automatische Verwaltung dieser Secrets, die Integration werde ich jedoch erst in einem späteren Blogpost beschreiben.
Scripts im Beispiel Repository
Im Beispiel Repository befinden sich unter scripts/ drei Shell-Skripte, die alle notwendigen Schritte automatisiert durchführen. Als Grundlage dient eine .env Datei, in der Passwörter, Domains und weitere Parameter zentral definiert werden:
# Vorlage kopieren und Werte eintragen
cp scripts/env.example .env
Danach die Schritte der Reihe nach ausführen:
# 1. TLS-Zertifikate und Schlüssel generieren
bash scripts/01-generate-certs.sh
# 2. Kubernetes Secrets im Cluster anlegen
bash scripts/02-create-secrets.sh
# 3. OIDC Provider ConfigMaps aus den Quelldateien generieren und committen
bash scripts/03-generate-configmaps.sh
git add kubernetes/ && git commit -m "chore: generate configmaps" && git push
Die folgenden Abschnitte beschreiben die einzelnen Schritte im Detail, falls eine manuelle Ausführung oder Anpassung gewünscht ist.
Zunächst muss der verify Namespace angelegt werden, da die Secrets darin abgelegt werden:
kubectl create namespace verify
IVIA Admin Passwort
Der Config Container (iviaconfig) benötigt das IVIA Admin-Passwort, mit dem die LMI-Oberfläche gesichert wird:
kubectl create secret generic iviaadmin \
--from-literal=adminpw=<ivia-admin-passwort> \
-n verify
IVIA Config Service
Der Config Container stellt den anderen IVIA-Containern (WRP, Runtime, DSC) die Konfiguration über einen internen Config Service bereit. Das dafür benötigte Passwort wird als separates Secret hinterlegt:
kubectl create secret generic configreader \
--from-literal=cfgsvcpw=<config-service-passwort> \
-n verify
PostgreSQL
Der Datenbank-Container benötigt das Passwort sowie ein selbstsigniertes TLS-Zertifikat für verschlüsselte Verbindungen:
# Selbstsigniertes TLS-Zertifikat für PostgreSQL generieren
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout ./postgresql/keys/server.key \
-out ./postgresql/keys/server.pem \
-days 365 \
-subj "/CN=db.verify.svc.cluster.local/O=verify"
# Als PEM kombinieren (Key + Zertifikat)
cat ./postgresql/keys/server.key ./postgresql/keys/server.pem > ./postgresql/keys/server.crt
kubectl create secret generic postgresql-keys \
--from-literal=password=<postgresql-passwort> \
--from-file=server.crt=./postgresql/keys/server.crt \
-n verify
OIDC Provider Keystores
Der OIDC Provider benötigt TLS-Zertifikate und den JWT-Signing-Key als Keystores. Diese werden als Secret op-keystores abgelegt.
Zunächst die benötigten Schlüssel und Zertifikate generieren:
# JWT Signing Key (RSA 2048)
openssl genrsa -out ./iviaop/secrets/jwtsigningkey.pem 2048
# TLS Schlüssel und selbstsigniertes Zertifikat für den HTTPS Endpunkt
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout ./iviaop/secrets/https/personal/server_key.pem \
-out ./iviaop/secrets/https/signer/server_cert.pem \
-days 365 \
-subj "/CN=iviaop/O=verify"
# Keystores als ZIP zusammenfassen (wie vom Container erwartet)
cd ./iviaop/secrets && zip https.zip https/personal/server_key.pem https/signer/server_cert.pem && cd -
Das PostgreSQL CA-Zertifikat für die verschlüsselte DB-Verbindung wird aus den PostgreSQL-Schlüsseln abgeleitet, die im nächsten Schritt erstellt werden. Den Pfad nach Erstellung entsprechend eintragen:
cp ./postgresql/keys/server.crt ./iviaop/secrets/server.pem
Danach das Secret anlegen:
kubectl create secret generic op-keystores \
--from-file=jwtsigningkey.pem=./iviaop/secrets/jwtsigningkey.pem \
--from-file=https.zip=./iviaop/secrets/https.zip \
--from-file=server.pem=./iviaop/secrets/server.pem \
-n verify
OIDC Provider Passwörter
Datenbankpasswort und LDAP-Bind-Passwort für den OIDC Provider:
kubectl create secret generic op \
--from-literal=db_password=<postgresql-passwort> \
--from-literal=ldap_password=<ldap-bind-passwort> \
-n verify
LDAP (IVD)
Der IVD Container benötigt das Admin-Passwort, einen Server-Key für LDAPS sowie die Lizenzdatei.
Der Lizenzschlüssel kann über IBM Passport Advantage heruntergeladen werden.
TLS-Zertifikat für LDAPS generieren:
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout ./ivd/server_key.pem \
-out ./ivd/server_cert.pem \
-days 365 \
-subj "/CN=ivd.verify.svc.cluster.local/O=verify"
# Als PEM kombinieren (Key + Zertifikat)
cat ./ivd/server_key.pem ./ivd/server_cert.pem > ./ivd/server.pem
Secrets anlegen:
# Admin-Passwort
kubectl create secret generic ivd-passwords \
--from-literal=admin=<ivd-admin-passwort> \
-n verify
# Server-Zertifikat (Key + Cert als kombinierte PEM-Datei)
kubectl create secret generic ivd-server-keys \
--from-file=server.pem=./ivd/server.pem \
-n verify
# Lizenzdatei (Download via IBM Passport Advantage)
kubectl create secret generic ivd-license-key \
--from-file=ivd-10.0.0_license_key_limited.txt=./ivd/ivd-10.0.0_license_key_limited.txt \
-n verify
Deployment
Jetzt kann die Kubernetes Anwendung deployed werden. Durch das app-of-apps Konzept werden automatisch die IVIA, LDAP und Datenbank App angelegt und gestartet.
kubectl apply -f kubernetes/apps.yaml
Sobald die Root Application angelegt ist, übernimmt ArgoCD den Rest. Es erkennt die Applications im kubernetes/apps/ Verzeichnis und startet das Deployment aller Komponenten in der richtigen Reihenfolge:
- Der
verifyNamespace wird angelegt, sofern er noch nicht existiert. - Die PostgreSQL Datenbank wird mit persistentem Storage gestartet, da sie von Runtime und OIDC Provider als Abhängigkeit benötigt wird.
- Der LDAP Directory Server wird deployed und stellt die Nutzerverwaltung bereit.
- Die IBM Verify Identity Access Komponenten (WRP Instanzen, Runtime, DSC, OIDC Provider) werden ausgerollt und verbinden sich mit Datenbank und LDAP.
- Services und Ingress Ressourcen werden konfiguriert und der Traffic auf die entsprechenden Komponenten geleitet.
Verifikation
Nach dem Deployment lässt sich der Status aller Applications über die ArgoCD CLI abfragen. argocd app list gibt einen schnellen Überblick, argocd app get liefert Details zu einer einzelnen Application:
argocd app list
argocd app get ibm-verify
Infos zur Installation der CLI: https://argo-cd.readthedocs.io/en/stable/cli_installation/
Den Pod-Status im verify Namespace direkt über kubectl prüfen:
kubectl get pods -n verify
Wenn alle Komponenten erfolgreich gestartet sind, sollte die Ausgabe in etwa so aussehen:
NAME READY STATUS RESTARTS AGE
iviaconfig-xxx 1/1 Running 0 5m
wrp-rp01-xxx 1/1 Running 0 5m
wrp-api-xxx 1/1 Running 0 5m
wrp-oidcrp-xxx 1/1 Running 0 5m
iviaruntime-xxx 1/1 Running 0 5m
iviadsc-xxx 1/1 Running 0 5m
iviaop-xxx 1/1 Running 0 5m
isvd-xxx 1/1 Running 0 5m
postgresql-xxx 1/1 Running 0 5m
Hinweis: Die IVA-Container starten zwar ihre Services stehen jedoch erst dann vollständig zur Verfügung, wenn sie über die LMI des Config Containers konfiguriet wurden und ein Deploy durchgeführt wurde. Erst danach beziehen die Container ihre Konfiguration und sind betriebsbereit. Die LMI ist unter
lmi.yourdomain.comerreichbar.
Zugriff auf Services
DNS konfigurieren, um auf den Load Balancer des Clusters zu zeigen:
lmi.yourdomain.com- IVIA Config LMIverify.yourdomain.com- WRP Instanz wrp-rp01api.yourdomain.com- WRP Instanz wrp-apirp.yourdomain.com- WRP Instanz wrp-oidcrpargocd.yourdomain.com- ArgoCD UI