apiVersion: v1 kind: Namespace metadata: name: konflux-ui --- apiVersion: v1 kind: ServiceAccount metadata: labels: app: dex name: dex namespace: konflux-ui --- apiVersion: v1 kind: ServiceAccount metadata: name: proxy namespace: konflux-ui --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: dex rules: - apiGroups: - dex.coreos.com resources: - '*' verbs: - '*' - apiGroups: - apiextensions.k8s.io resources: - customresourcedefinitions verbs: - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: konflux-proxy rules: - apiGroups: - "" resources: - users - groups verbs: - impersonate - apiGroups: - authorization.k8s.io resources: - localsubjectaccessreviews verbs: - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: konflux-proxy-namespace-lister rules: - apiGroups: - "" resources: - namespaces verbs: - list - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: dex roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: dex subjects: - kind: ServiceAccount name: dex namespace: konflux-ui --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: konflux-proxy roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: konflux-proxy subjects: - kind: ServiceAccount name: proxy namespace: konflux-ui --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: konflux-proxy-namespace-lister roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: konflux-proxy-namespace-lister subjects: - kind: ServiceAccount name: proxy namespace: konflux-ui --- apiVersion: v1 data: nginx.conf: | worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { log_format upstreamlog '[$time_local] $remote_addr - $remote_user - $server_name $host to: $proxy_host $upstream_addr: $request $status upstream_response_time $upstream_response_time msec $msec request_time $request_time'; access_log /dev/stderr upstreamlog; error_log /dev/stderr; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; default_type application/octet-stream; include /etc/nginx/mime.types; map $http_upgrade $connection_upgrade { default upgrade; '' close; } map $request_method $ns_target { GET @namespacelister; default @kubeapi; } server { listen 9443 ssl; ssl_certificate /mnt/tls.crt; ssl_certificate_key /mnt/tls.key; server_name _; root /opt/app-root/src; location / { alias /opt/app-root/src/static-content/; try_files $uri /index.html; } location = /404.html { } location /segment { default_type text/plain; } location = /oauth2/auth { internal; proxy_pass http://127.0.0.1:6000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; # nginx auth_request includes headers but not body proxy_set_header Content-Length ""; proxy_pass_request_body off; } location /oauth2/ { proxy_pass http://127.0.0.1:6000/oauth2/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } location /api/k8s/ { # Kube-API auth_request /oauth2/auth; proxy_pass https://kubernetes.default.svc/; proxy_read_timeout 30m; include /mnt/nginx-generated-config/auth.conf; } location /wss/k8s/ { auth_request /oauth2/auth; proxy_pass https://kubernetes.default.svc/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 30m; include /mnt/nginx-generated-config/auth.conf; } location /health { # Used for liveness probes return 200; } # GET requests to /api/k8s/api/v1/namespaces and /api/k8s/api/v1/namespaces/ # are handled from the namespace-lister. # Requests with other methods are handled by the Kube-API location = /api/k8s/api/v1/namespaces { try_files /dev/null $ns_target; } location = /api/k8s/api/v1/namespaces/ { try_files /dev/null $ns_target; } location @namespacelister { auth_request_set $email $upstream_http_x_auth_request_email; auth_request /oauth2/auth; proxy_read_timeout 30m; proxy_set_header X-User $email; proxy_set_header X-Group system:authenticated; proxy_hide_header X-Correlation-ID; rewrite ^.*$ /api/v1/namespaces break; proxy_pass https://namespace-lister.namespace-lister.svc.cluster.local:8080; } location @kubeapi { auth_request /oauth2/auth; proxy_read_timeout 30m; rewrite ^/api/k8s/(.*)/$ /$1 break; proxy_pass https://kubernetes.default.svc; include /mnt/nginx-generated-config/auth.conf; } location /idp/ { # Identity Provider proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-Port 9443; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass https://dex.konflux-ui.svc.cluster.local:9443; } include /mnt/nginx-generated-location-config/*.conf; include /mnt/nginx-additional-location-configs/*.conf; } } kind: ConfigMap metadata: name: proxy-5f87fc6759 namespace: konflux-ui --- apiVersion: v1 data: proxy-nginx-generate-loop-probe.sh: | #!/bin/bash set -euo pipefail for cmd in date stat; do command -v "${cmd}" >/dev/null 2>&1 || { echo "required command not found: ${cmd}"; exit 1; } done AUTH_CONF_FILE=/mnt/nginx-generated-config/auth.conf AUTH_CONF_NEW_FILE=/mnt/nginx-generated-config/auth.conf.new { [ -f "${AUTH_CONF_NEW_FILE}" ] && [ $(( $(date +%s) - $(stat -c %Y "${AUTH_CONF_NEW_FILE}") )) -lt 60 ]; } || \ { [ -f "${AUTH_CONF_FILE}" ] && [ $(( $(date +%s) - $(stat -c %Y "${AUTH_CONF_FILE}") )) -lt 60 ]; } proxy-nginx-generate-loop.sh: | #!/bin/bash set -euo pipefail RETRY_INTERVAL=10 TOKEN_FILEPATH=/var/run/secrets/konflux-ci.dev/serviceaccount/token AUTH_CONF_FILE=/mnt/nginx-generated-config/auth.conf.new AUTH_CONF_TMP_FILE=/mnt/nginx-generated-config/auth.conf.new.tmp AUTH_CONF_TEMPLATE_FILE=/mnt/nginx-templates/auth.conf for cmd in cat sed chmod mv sleep date; do command -v "${cmd}" >/dev/null 2>&1 || { echo "required command not found: ${cmd}"; exit 1; } done log() { echo "$(date -Iseconds) generate-loop: $*"; } log "starting" produceToken() ( # Copy the auth.conf template and replace the bearer token token=$(cat "${TOKEN_FILEPATH}") # Produce a tmp file sed "s/__BEARER_TOKEN__/${token}/" "${AUTH_CONF_TEMPLATE_FILE}" > "${AUTH_CONF_TMP_FILE}" chmod 640 "${AUTH_CONF_TMP_FILE}" # Rename (atomic) the file to avoid sync issues mv "${AUTH_CONF_TMP_FILE}" "${AUTH_CONF_FILE}" ) produceTokenWithRetry() ( produceToken || \ { sleep 3; produceToken; } || \ { sleep 5; produceToken; } ) while produceTokenWithRetry; do sleep "${RETRY_INTERVAL}"; done echo "loop broke, crashing" exit 1 kind: ConfigMap metadata: name: proxy-nginx-generate-loop-8745b5hf5k namespace: konflux-ui --- apiVersion: v1 data: proxy-nginx-run.sh: | #!/bin/bash set -euo pipefail NGINX_AUTH_CONF_FILE='/mnt/nginx-generated-config/auth.conf' NGINX_NEW_AUTH_CONF_FILE='/mnt/nginx-generated-config/auth.conf.new' NGINX_CONF_FILE='/etc/nginx/nginx.conf' RETRY_INTERVAL=10 for cmd in nginx cksum mv sleep date; do command -v "${cmd}" >/dev/null 2>&1 || { echo "required command not found: ${cmd}"; exit 1; } done log() { echo "$(date -Iseconds) proxy-nginx-run: $*"; } log "starting" # test configuration nginx -g "daemon off;" -c "${NGINX_CONF_FILE}" -t # run the hot-reload loop in background ( # wait for nginx to start before first reload attempt while [ ! -f /run/nginx.pid ]; do sleep 1; done log "hot-reload loop started" # retries hot reloading the nginx configuration multiple # times with increasing timeouts before returning the error reloadWithRetry() { if [ -f "${NGINX_NEW_AUTH_CONF_FILE}" ] && \ [ "$(cksum < "${NGINX_NEW_AUTH_CONF_FILE}")" != "$(cksum < "${NGINX_AUTH_CONF_FILE}")" ]; then log "config changed, reloading nginx" # Move (atomic) the new configuration and reload it in NGINX mv "${NGINX_NEW_AUTH_CONF_FILE}" "${NGINX_AUTH_CONF_FILE}" && \ { nginx -s reload || \ { sleep 3; nginx -s reload; } || \ { sleep 5; nginx -s reload; } } fi } # hot reload infinite loop while reloadWithRetry; do sleep "${RETRY_INTERVAL}"; done log "loop broke, crashing" exit 1 ) & RELOAD_PID=$! # run the nginx server in background ( nginx -g "daemon off;" -c "${NGINX_CONF_FILE}" log "nginx crashed" exit 1 ) & # forward SIGTERM/SIGINT for graceful shutdown trap 'log "received signal, shutting down"; nginx -s quit 2>/dev/null; kill "${RELOAD_PID}" 2>/dev/null; wait; exit 0' TERM INT # wait for any of the background tasks to crash wait -n EXIT_CODE=$? log "child exited with code ${EXIT_CODE}, terminating" exit "${EXIT_CODE}" kind: ConfigMap metadata: name: proxy-nginx-run-k675kmbfc5 namespace: konflux-ui --- apiVersion: v1 data: auth.conf: | # Auth configuration with impersonate headers auth_request_set $user $upstream_http_x_auth_request_email; proxy_set_header Impersonate-User $user; proxy_set_header Impersonate-Group system:authenticated; proxy_set_header Authorization "Bearer __BEARER_TOKEN__"; tekton-results.conf: | location /api/k8s/plugins/tekton-results/ { auth_request /oauth2/auth; rewrite /api/k8s/plugins/tekton-results/(.+) /$1 break; proxy_read_timeout 30m; proxy_pass https://__TEKTON_RESULTS_HOSTNAME__:8080; include /mnt/nginx-generated-config/auth.conf; } kind: ConfigMap metadata: name: proxy-nginx-templates-dt292585th namespace: konflux-ui --- apiVersion: v1 kind: Service metadata: name: dex namespace: konflux-ui spec: ports: - name: dex port: 9443 protocol: TCP targetPort: 9443 selector: app: dex type: ClusterIP --- apiVersion: v1 kind: Service metadata: labels: app: proxy name: proxy namespace: konflux-ui spec: internalTrafficPolicy: Cluster ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - name: web port: 8888 protocol: TCP targetPort: web - name: web-tls port: 9443 protocol: TCP targetPort: web-tls selector: app: proxy type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: dex name: dex namespace: konflux-ui spec: progressDeadlineSeconds: 2147483647 replicas: 1 selector: matchLabels: app: dex template: metadata: labels: app: dex spec: containers: - command: - /usr/local/bin/dex - serve - /etc/dex/cfg/config.yaml env: - name: CLIENT_SECRET valueFrom: secretKeyRef: key: client-secret name: oauth2-proxy-client-secret image: quay.io/konflux-ci/dex@sha256:ef2f9c2ed6177d0a9c4759a7ada65c9196efc76353d870f8b6eca0039dd8b983 name: dex ports: - containerPort: 9443 name: https - containerPort: 5558 name: telemetry protocol: TCP readinessProbe: httpGet: path: /healthz/ready port: telemetry resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true volumeMounts: - mountPath: /etc/dex/cfg name: dex - mountPath: /etc/dex/tls name: tls serviceAccountName: dex volumes: - configMap: defaultMode: 420 items: - key: config.yaml path: config.yaml name: dex name: dex - name: tls secret: secretName: dex-cert --- apiVersion: apps/v1 kind: Deployment metadata: annotations: ignore-check.kube-linter.io/no-anti-affinity: Using topologySpreadConstraints labels: app: proxy name: proxy namespace: konflux-ui spec: minReadySeconds: 30 progressDeadlineSeconds: 2147483647 replicas: 1 selector: matchLabels: app: proxy strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 0 template: metadata: labels: app: proxy spec: containers: - args: - /var/run/scripts/konflux-ci.dev/proxy-nginx-generate-loop.sh command: - bash image: quay.io/konflux-ci/konflux-ui@sha256:f3cd59eab1dcff0fb2fdce943871e52260f7b256c3f3d801200cc2d429ff5714 livenessProbe: exec: command: - bash - /var/run/scripts/konflux-ci.dev/proxy-nginx-generate-loop-probe.sh failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 name: generate-nginx-configs-loop resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true volumeMounts: - mountPath: /var/run/scripts/konflux-ci.dev/ name: proxy-generate-loop-script readOnly: true - mountPath: /var/run/secrets/konflux-ci.dev/serviceaccount name: kube-api-token readOnly: true - mountPath: /mnt/nginx-generated-config name: nginx-generated-config - mountPath: /mnt/nginx-templates name: nginx-templates - args: - /var/run/scripts/konflux-ci.dev/proxy-nginx-run.sh command: - bash image: registry.access.redhat.com/ubi9/nginx-124@sha256:20ae3d2ced3df6a622d11334288f2fd31f568ae49858d1e0eac43eca332a1322 livenessProbe: failureThreshold: 3 httpGet: path: /health port: 9443 scheme: HTTPS initialDelaySeconds: 30 periodSeconds: 60 successThreshold: 1 timeoutSeconds: 1 name: nginx ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 9443 name: web-tls protocol: TCP readinessProbe: failureThreshold: 3 httpGet: path: /health port: 9443 scheme: HTTPS initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 1 resources: limits: cpu: 300m memory: 256Mi requests: cpu: 30m memory: 128Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true volumeMounts: - mountPath: /var/run/scripts/konflux-ci.dev/ name: proxy-nginx-run-script readOnly: true - mountPath: /etc/nginx/nginx.conf name: proxy readOnly: true subPath: nginx.conf - mountPath: /var/log/nginx name: logs - mountPath: /var/lib/nginx/tmp name: nginx-tmp - mountPath: /run name: run - mountPath: /mnt name: serving-cert - mountPath: /mnt/nginx-generated-config name: nginx-generated-config - mountPath: /mnt/nginx-generated-location-config name: nginx-generated-location-config - mountPath: /opt/app-root/src/static-content name: static-content - mountPath: /opt/app-root/src/segment name: segment-bridge-config - env: - name: OAUTH2_PROXY_CLIENT_SECRET valueFrom: secretKeyRef: key: client-secret name: oauth2-proxy-client-secret - name: OAUTH2_PROXY_COOKIE_SECRET valueFrom: secretKeyRef: key: cookie-secret name: oauth2-proxy-cookie-secret image: quay.io/konflux-ci/oauth2-proxy@sha256:1f4a37529de4f750099af18e113556bd995fe6922030c614b7d6d23eaa0bd318 name: oauth2-proxy ports: - containerPort: 6000 name: web protocol: TCP resources: limits: cpu: 300m memory: 256Mi requests: cpu: 30m memory: 128Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true initContainers: - command: - cp - -R - /opt/app-root/src/. - /mnt/static-content/ image: quay.io/konflux-ci/konflux-ui@sha256:f3cd59eab1dcff0fb2fdce943871e52260f7b256c3f3d801200cc2d429ff5714 name: copy-static-content resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true volumeMounts: - mountPath: /mnt/static-content name: static-content - command: - sh - -c - | set -e # Copy the auth.conf template and replace the bearer token token=$(cat /var/run/secrets/konflux-ci.dev/serviceaccount/token) sed "s/__BEARER_TOKEN__/$token/" /mnt/nginx-templates/auth.conf > /mnt/nginx-generated-config/auth.conf chmod 640 /mnt/nginx-generated-config/auth.conf # Resolve tekton-results hostname results_hostname_k8s=tekton-results-api-service.tekton-pipelines.svc.cluster.local results_hostname_openshift=tekton-results-api-service.openshift-pipelines.svc.cluster.local if nslookup $results_hostname_k8s > /dev/null 2>&1; then results_hostname=$results_hostname_k8s elif nslookup $results_hostname_openshift > /dev/null 2>&1; then results_hostname=$results_hostname_openshift else echo "Error: Could not resolve tekton-results hostname" exit 1 fi sed "s/__TEKTON_RESULTS_HOSTNAME__/$results_hostname/" /mnt/nginx-templates/tekton-results.conf > /mnt/nginx-generated-location-config/tekton-results.conf chmod 640 /mnt/nginx-generated-location-config/tekton-results.conf image: quay.io/konflux-ci/konflux-ui@sha256:f3cd59eab1dcff0fb2fdce943871e52260f7b256c3f3d801200cc2d429ff5714 name: generate-nginx-configs resources: limits: cpu: 50m memory: 128Mi requests: cpu: 10m memory: 64Mi securityContext: readOnlyRootFilesystem: true runAsNonRoot: true volumeMounts: - mountPath: /var/run/secrets/konflux-ci.dev/serviceaccount name: kube-api-token readOnly: true - mountPath: /mnt/nginx-generated-config name: nginx-generated-config - mountPath: /mnt/nginx-templates name: nginx-templates - mountPath: /mnt/nginx-generated-location-config name: nginx-generated-location-config serviceAccountName: proxy topologySpreadConstraints: - labelSelector: matchLabels: app: proxy maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway volumes: - configMap: defaultMode: 488 items: - key: proxy-nginx-generate-loop.sh path: proxy-nginx-generate-loop.sh - key: proxy-nginx-generate-loop-probe.sh path: proxy-nginx-generate-loop-probe.sh name: proxy-nginx-generate-loop-8745b5hf5k name: proxy-generate-loop-script - configMap: defaultMode: 488 items: - key: proxy-nginx-run.sh path: proxy-nginx-run.sh name: proxy-nginx-run-k675kmbfc5 name: proxy-nginx-run-script - name: kube-api-token projected: defaultMode: 420 sources: - serviceAccountToken: expirationSeconds: 600 path: token - configMap: defaultMode: 420 items: - key: nginx.conf path: nginx.conf name: proxy-5f87fc6759 name: proxy - configMap: defaultMode: 420 name: proxy-nginx-templates-dt292585th name: nginx-templates - emptyDir: {} name: logs - emptyDir: {} name: nginx-tmp - emptyDir: {} name: run - name: serving-cert secret: secretName: serving-cert - emptyDir: {} name: nginx-generated-config - emptyDir: {} name: nginx-generated-location-config - emptyDir: {} name: static-content - name: segment-bridge-config secret: items: - key: key path: key - key: url path: url optional: true secretName: segment-bridge-config --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: dex-cert namespace: konflux-ui spec: dnsNames: - localhost - dex.konflux-ui - dex.konflux-ui.svc.cluster.local isCA: true issuerRef: kind: Issuer name: ui-ca-issuer secretName: dex-cert subject: organizations: - konflux --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: oauth2-proxy-cert namespace: konflux-ui spec: dnsNames: - localhost issuerRef: kind: Issuer name: ui-ca-issuer secretName: oauth2-proxy-cert --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: serving-cert namespace: konflux-ui spec: dnsNames: - localhost - proxy.konflux-ui.svc - proxy.konflux-ui.svc.cluster.local issuerRef: kind: Issuer name: ui-ca-issuer secretName: serving-cert --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: ui-ca namespace: konflux-ui spec: commonName: ui-ca isCA: true issuerRef: group: cert-manager.io kind: ClusterIssuer name: self-signed-cluster-issuer privateKey: algorithm: ECDSA size: 256 secretName: ui-ca --- apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: ui-ca-issuer namespace: konflux-ui spec: ca: secretName: ui-ca