diff --git a/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml new file mode 100644 index 0000000..4e5d616 --- /dev/null +++ b/clusters/atlas/flux-system/applications/jenkins/kustomization.yaml @@ -0,0 +1,18 @@ +# clusters/atlas/flux-system/applications/jenkins/kustomization.yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: jenkins + namespace: flux-system +spec: + interval: 10m + path: ./services/jenkins + prune: true + sourceRef: + kind: GitRepository + name: flux-system + targetNamespace: jenkins + dependsOn: + - name: helm + - name: traefik + wait: true diff --git a/clusters/atlas/flux-system/applications/kustomization.yaml b/clusters/atlas/flux-system/applications/kustomization.yaml index daf1c42..2ad7b14 100644 --- a/clusters/atlas/flux-system/applications/kustomization.yaml +++ b/clusters/atlas/flux-system/applications/kustomization.yaml @@ -16,3 +16,4 @@ resources: - keycloak/kustomization.yaml - oauth2-proxy/kustomization.yaml - mailu/kustomization.yaml + - jenkins/kustomization.yaml diff --git a/infrastructure/sources/helm/jenkins.yaml b/infrastructure/sources/helm/jenkins.yaml new file mode 100644 index 0000000..b746793 --- /dev/null +++ b/infrastructure/sources/helm/jenkins.yaml @@ -0,0 +1,9 @@ +# infrastructure/sources/helm/jenkins.yaml +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: jenkins + namespace: flux-system +spec: + interval: 1h + url: https://charts.jenkins.io diff --git a/infrastructure/sources/helm/kustomization.yaml b/infrastructure/sources/helm/kustomization.yaml index a0f55b0..cef76c2 100644 --- a/infrastructure/sources/helm/kustomization.yaml +++ b/infrastructure/sources/helm/kustomization.yaml @@ -5,6 +5,7 @@ resources: - grafana.yaml - hashicorp.yaml - jetstack.yaml + - jenkins.yaml - mailu.yaml - prometheus.yaml - victoria-metrics.yaml diff --git a/services/jenkins/helmrelease.yaml b/services/jenkins/helmrelease.yaml new file mode 100644 index 0000000..002b4b6 --- /dev/null +++ b/services/jenkins/helmrelease.yaml @@ -0,0 +1,139 @@ +# services/jenkins/helmrelease.yaml +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: jenkins + namespace: jenkins +spec: + interval: 30m + chart: + spec: + chart: jenkins + version: 5.8.114 + sourceRef: + kind: HelmRepository + name: jenkins + namespace: flux-system + install: + remediation: + retries: 3 + upgrade: + remediation: + retries: 3 + remediateLastFailure: true + cleanupOnFail: true + values: + controller: + jenkinsUrl: https://ci.bstein.dev + ingress: + enabled: true + hostName: ci.bstein.dev + ingressClassName: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt + traefik.ingress.kubernetes.io/router.entrypoints: websecure + tls: + - secretName: jenkins-tls + hosts: + - ci.bstein.dev + installPlugins: + - kubernetes:4334.v794a_7463eb_0c + - workflow-aggregator:596.v8c21c963d92d + - git:5.6.0 + - configuration-as-code:1835.v4296594051c5 + - oic-auth:1.30 + containerEnv: + - name: ENABLE_OIDC + value: "false" + - name: OIDC_CLIENT_ID + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: clientId + optional: true + - name: OIDC_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: clientSecret + optional: true + - name: OIDC_AUTH_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: authorizationUrl + optional: true + - name: OIDC_TOKEN_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: tokenUrl + optional: true + - name: OIDC_USERINFO_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: userInfoUrl + optional: true + - name: OIDC_LOGOUT_URL + valueFrom: + secretKeyRef: + name: jenkins-oidc + key: logoutUrl + optional: true + JCasC: + defaultConfig: true + configScripts: + 01-oidc.groovy: | + import jenkins.model.* + def env = System.getenv() + def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean() + if (!enable) { + println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm") + return + } + def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL'] + if (!required.every { env[it] }) { + println("OIDC enabled but missing one or more env vars: ${required}") + return + } + try { + def realm = new org.jenkinsci.plugins.oic.OicSecurityRealm( + env['OIDC_CLIENT_ID'], + env['OIDC_CLIENT_SECRET'], + env['OIDC_TOKEN_URL'], + env['OIDC_AUTH_URL'], + env['OIDC_USERINFO_URL'], + true, // logout from provider + env['OIDC_LOGOUT_URL'] ?: "", + "", // postLogoutRedirectUrl + "openid email profile", + "", // prompt + "preferred_username", + "name", + "email", + false, // disableSslVerification + true, // escapeHatchEnabled + "admin", + "", // escapeHatchSecret + "", // escapeHatchGroup + true, // loadUserInfo + true, // validateScopes + false, // allowUnsignedIdTokens + false, // enforceValidIssuers + "", // issuer + false // disableUserInfoFetch + ) + def instance = Jenkins.get() + instance.setSecurityRealm(realm) + instance.save() + println("Configured OIDC security realm from env") + } catch (Exception e) { + println("Failed to configure OIDC realm: ${e}") + } + persistence: + enabled: true + storageClass: astreae + size: 50Gi + serviceAccount: + create: true diff --git a/services/jenkins/kustomization.yaml b/services/jenkins/kustomization.yaml new file mode 100644 index 0000000..b20b1d3 --- /dev/null +++ b/services/jenkins/kustomization.yaml @@ -0,0 +1,7 @@ +# services/jenkins/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: jenkins +resources: + - namespace.yaml + - helmrelease.yaml diff --git a/services/jenkins/namespace.yaml b/services/jenkins/namespace.yaml new file mode 100644 index 0000000..36e999b --- /dev/null +++ b/services/jenkins/namespace.yaml @@ -0,0 +1,5 @@ +# services/jenkins/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: jenkins