ci: enable oidc for jenkins/gitops/gitea
This commit is contained in:
parent
04602a2914
commit
a46226bb0a
@ -55,6 +55,12 @@ spec:
|
|||||||
value: "master"
|
value: "master"
|
||||||
- name: ROOT_URL
|
- name: ROOT_URL
|
||||||
value: "https://scm.bstein.dev"
|
value: "https://scm.bstein.dev"
|
||||||
|
- name: GITEA__service__ENABLE_OPENID_SIGNIN
|
||||||
|
value: "true"
|
||||||
|
- name: GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION
|
||||||
|
value: "true"
|
||||||
|
- name: GITEA__service__DISABLE_REGISTRATION
|
||||||
|
value: "false"
|
||||||
- name: DB_TYPE
|
- name: DB_TYPE
|
||||||
value: "postgres"
|
value: "postgres"
|
||||||
- name: DB_HOST
|
- name: DB_HOST
|
||||||
|
|||||||
@ -7,3 +7,4 @@ resources:
|
|||||||
- service.yaml
|
- service.yaml
|
||||||
- pvc.yaml
|
- pvc.yaml
|
||||||
- ingress.yaml
|
- ingress.yaml
|
||||||
|
- oidc-job.yaml
|
||||||
|
|||||||
85
services/gitea/oidc-job.yaml
Normal file
85
services/gitea/oidc-job.yaml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# services/gitea/oidc-job.yaml
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: gitea-oidc-bootstrap
|
||||||
|
namespace: gitea
|
||||||
|
spec:
|
||||||
|
ttlSecondsAfterFinished: 1800
|
||||||
|
backoffLimit: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gitea
|
||||||
|
job: gitea-oidc-bootstrap
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
affinity:
|
||||||
|
podAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
volumes:
|
||||||
|
- name: gitea-data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: gitea-data
|
||||||
|
containers:
|
||||||
|
- name: gitea-oidc-bootstrap
|
||||||
|
image: gitea/gitea:1.23
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
volumeMounts:
|
||||||
|
- name: gitea-data
|
||||||
|
mountPath: /data
|
||||||
|
env:
|
||||||
|
- name: CLIENT_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-oidc
|
||||||
|
key: client_id
|
||||||
|
- name: CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-oidc
|
||||||
|
key: client_secret
|
||||||
|
- name: DISCOVERY_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-oidc
|
||||||
|
key: openid_auto_discovery_url
|
||||||
|
command:
|
||||||
|
- /bin/bash
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
set -euo pipefail
|
||||||
|
APPINI=/data/gitea/conf/app.ini
|
||||||
|
BIN=/usr/local/bin/gitea
|
||||||
|
list="$($BIN -c "$APPINI" admin auth list)"
|
||||||
|
id=$(echo "$list" | awk '$2=="keycloak"{print $1}')
|
||||||
|
if [ -n "$id" ]; then
|
||||||
|
echo "Updating existing auth source id=$id"
|
||||||
|
$BIN -c "$APPINI" admin auth update-oauth \
|
||||||
|
--id "$id" \
|
||||||
|
--name keycloak \
|
||||||
|
--provider openidConnect \
|
||||||
|
--key "$CLIENT_ID" \
|
||||||
|
--secret "$CLIENT_SECRET" \
|
||||||
|
--auto-discover-url "$DISCOVERY_URL" \
|
||||||
|
--scopes "openid profile email" \
|
||||||
|
--group-claim-name groups
|
||||||
|
else
|
||||||
|
echo "Creating keycloak auth source"
|
||||||
|
$BIN -c "$APPINI" admin auth add-oauth \
|
||||||
|
--name keycloak \
|
||||||
|
--provider openidConnect \
|
||||||
|
--key "$CLIENT_ID" \
|
||||||
|
--secret "$CLIENT_SECRET" \
|
||||||
|
--auto-discover-url "$DISCOVERY_URL" \
|
||||||
|
--scopes "openid profile email" \
|
||||||
|
--group-claim-name groups
|
||||||
|
fi
|
||||||
@ -23,13 +23,10 @@ spec:
|
|||||||
remediateLastFailure: true
|
remediateLastFailure: true
|
||||||
cleanupOnFail: true
|
cleanupOnFail: true
|
||||||
values:
|
values:
|
||||||
|
additionalArgs:
|
||||||
|
- --auth-methods=oidc
|
||||||
adminUser:
|
adminUser:
|
||||||
create: true
|
create: false
|
||||||
createClusterRole: true
|
|
||||||
createSecret: true
|
|
||||||
username: admin
|
|
||||||
# bcrypt hash for temporary password "G1tOps!2025" (rotate after login)
|
|
||||||
passwordHash: "$2y$12$wDEOzR1Gc2dbvNSJ3ZXNdOBVFEjC6YASIxnZmHIbO.W1m0fie/QVi"
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: true
|
||||||
className: traefik
|
className: traefik
|
||||||
@ -45,5 +42,7 @@ spec:
|
|||||||
- secretName: gitops-ui-tls
|
- secretName: gitops-ui-tls
|
||||||
hosts:
|
hosts:
|
||||||
- cd.bstein.dev
|
- cd.bstein.dev
|
||||||
|
oidcSecret:
|
||||||
|
create: false
|
||||||
metrics:
|
metrics:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@ -7,3 +7,4 @@ resources:
|
|||||||
- helmrelease.yaml
|
- helmrelease.yaml
|
||||||
- certificate.yaml
|
- certificate.yaml
|
||||||
- networkpolicy-acme.yaml
|
- networkpolicy-acme.yaml
|
||||||
|
- rbac.yaml
|
||||||
|
|||||||
15
services/gitops-ui/rbac.yaml
Normal file
15
services/gitops-ui/rbac.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# services/gitops-ui/rbac.yaml
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: gitops-admins
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: weave-gitops
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: cluster-admin
|
||||||
|
subjects:
|
||||||
|
- kind: Group
|
||||||
|
name: admin
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@ -44,7 +44,7 @@ spec:
|
|||||||
- oic-auth
|
- oic-auth
|
||||||
containerEnv:
|
containerEnv:
|
||||||
- name: ENABLE_OIDC
|
- name: ENABLE_OIDC
|
||||||
value: "false"
|
value: "true"
|
||||||
- name: OIDC_ISSUER
|
- name: OIDC_ISSUER
|
||||||
value: "https://sso.bstein.dev/realms/atlas"
|
value: "https://sso.bstein.dev/realms/atlas"
|
||||||
- name: OIDC_CLIENT_ID
|
- name: OIDC_CLIENT_ID
|
||||||
@ -85,50 +85,45 @@ spec:
|
|||||||
optional: true
|
optional: true
|
||||||
initScripts:
|
initScripts:
|
||||||
oidc.groovy: |
|
oidc.groovy: |
|
||||||
|
import hudson.util.Secret
|
||||||
|
import jenkins.model.IdStrategy
|
||||||
import jenkins.model.Jenkins
|
import jenkins.model.Jenkins
|
||||||
import org.jenkinsci.plugins.oic.OicSecurityRealm
|
import org.jenkinsci.plugins.oic.OicSecurityRealm
|
||||||
|
import org.jenkinsci.plugins.oic.OicServerWellKnownConfiguration
|
||||||
def env = System.getenv()
|
def env = System.getenv()
|
||||||
def enable = (env['ENABLE_OIDC'] ?: 'false').toBoolean()
|
if (!(env['ENABLE_OIDC'] ?: 'false').toBoolean()) {
|
||||||
if (!enable) {
|
|
||||||
println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm")
|
println("OIDC disabled (ENABLE_OIDC=false); keeping default security realm")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_AUTH_URL','OIDC_TOKEN_URL','OIDC_USERINFO_URL']
|
def required = ['OIDC_CLIENT_ID','OIDC_CLIENT_SECRET','OIDC_ISSUER']
|
||||||
if (!required.every { env[it] }) {
|
if (!required.every { env[it] }) {
|
||||||
println("OIDC enabled but missing vars: ${required.findAll { !env[it] }}")
|
println("OIDC enabled but missing vars: ${required.findAll { !env[it] }}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
def wellKnown = "${env['OIDC_ISSUER']}/.well-known/openid-configuration"
|
||||||
|
def serverCfg = new OicServerWellKnownConfiguration(wellKnown)
|
||||||
|
serverCfg.setScopesOverride('openid profile email')
|
||||||
def realm = new OicSecurityRealm(
|
def realm = new OicSecurityRealm(
|
||||||
env['OIDC_CLIENT_ID'],
|
env['OIDC_CLIENT_ID'],
|
||||||
env['OIDC_CLIENT_SECRET'],
|
Secret.fromString(env['OIDC_CLIENT_SECRET']),
|
||||||
env['OIDC_TOKEN_URL'],
|
serverCfg,
|
||||||
env['OIDC_AUTH_URL'],
|
false,
|
||||||
env['OIDC_USERINFO_URL'],
|
IdStrategy.CASE_INSENSITIVE,
|
||||||
true, // logout from provider
|
IdStrategy.CASE_INSENSITIVE
|
||||||
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
|
|
||||||
env['OIDC_ISSUER'] ?: "",
|
|
||||||
false // disableUserInfoFetch
|
|
||||||
)
|
)
|
||||||
|
realm.setLogoutFromOpenidProvider(true)
|
||||||
|
realm.setPostLogoutRedirectUrl('https://ci.bstein.dev')
|
||||||
|
realm.setUserNameField('preferred_username')
|
||||||
|
realm.setFullNameFieldName('name')
|
||||||
|
realm.setEmailFieldName('email')
|
||||||
|
realm.setGroupsFieldName('groups')
|
||||||
|
realm.setRootURLFromRequest(true)
|
||||||
|
realm.setSendScopesInTokenRequest(true)
|
||||||
def j = Jenkins.get()
|
def j = Jenkins.get()
|
||||||
j.setSecurityRealm(realm)
|
j.setSecurityRealm(realm)
|
||||||
j.save()
|
j.save()
|
||||||
println("Configured OIDC realm from init script")
|
println("Configured OIDC realm from init script (well-known)")
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
println("Failed to configure OIDC realm: ${e}")
|
println("Failed to configure OIDC realm: ${e}")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user