diff --git a/argocd/README.md b/argocd/README.md index 3310730..7c78372 100644 --- a/argocd/README.md +++ b/argocd/README.md @@ -11,12 +11,13 @@ To deploy ArgoCD: helm repo add argo-cd https://argoproj.github.io/argo-helm kubectl create secret -n argocd generic argocd-secret # Initialize empty secret for sessions helm template -n argocd --release-name k6 argo-cd/argo-cd --include-crds -f values.yaml > argocd.yml -kubectl apply -f argocd.yml -n argocd +kubectl apply -f argocd.yml -f application-extras.yml -n argocd kubectl -n argocd rollout restart deployment/k6-argocd-redis kubectl -n argocd rollout restart deployment/k6-argocd-repo-server kubectl -n argocd rollout restart deployment/k6-argocd-server kubectl -n argocd rollout restart deployment/k6-argocd-notifications-controller kubectl -n argocd rollout restart statefulset/k6-argocd-application-controller +kubectl label -n argocd secret oidc-client-argocd-owner-secrets app.kubernetes.io/part-of=argocd ``` Note: Refer to Authelia README for OIDC secret setup diff --git a/argocd/application-extras.yml b/argocd/application-extras.yml new file mode 100644 index 0000000..da77b44 --- /dev/null +++ b/argocd/application-extras.yml @@ -0,0 +1,22 @@ +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWClient +metadata: + name: argocd +spec: + displayName: Argo CD + uri: https://argocd.k-space.ee + redirectUris: + - https://argocd.k-space.ee/auth/callback + allowedGroups: + - github.com:codemowers:admins + grantTypes: + - authorization_code + - refresh_token + responseTypes: + - code + availableScopes: + - openid + - profile + pkce: false + diff --git a/argocd/values.yaml b/argocd/values.yaml index 2bbed09..f0e70e1 100644 --- a/argocd/values.yaml +++ b/argocd/values.yaml @@ -1,7 +1,6 @@ global: logLevel: warn -# We use Authelia OIDC instead of Dex dex: enabled: false @@ -30,11 +29,11 @@ server: url: https://argocd.k-space.ee application.instanceLabelKey: argocd.argoproj.io/instance oidc.config: | - name: Authelia - issuer: https://auth.k-space.ee - clientID: argocd - cliClientID: argocd - clientSecret: $oidc.config.clientSecret + name: OpenID Connect + issuer: https://auth2.k-space.ee/ + clientID: $oidc-client-argocd-owner-secrets:OIDC_CLIENT_ID + cliClientID: $oidc-client-argocd-owner-secrets:OIDC_CLIENT_ID + clientSecret: $oidc-client-argocd-owner-secrets:OIDC_CLIENT_SECRET requestedIDTokenClaims: groups: essential: true @@ -50,10 +49,14 @@ server: hs = {} hs.status = "Healthy" return hs + apiextensions.k8s.io/CustomResourceDefinition: + ignoreDifferences: | + jsonPointers: + - "x-kubernetes-validations" # Members of ArgoCD Admins group in AD/Samba are allowed to administer Argo rbacConfig: - policy.default: role:readonly + policy.default: role:admin policy.csv: | # Map AD groups to ArgoCD roles g, Developers, role:developers diff --git a/authelia/.gitignore b/authelia/.gitignore deleted file mode 100644 index 8a0cde5..0000000 --- a/authelia/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -application-secrets.y*ml -oidc-secrets.y*ml diff --git a/authelia/README.md b/authelia/README.md deleted file mode 100644 index 0b51771..0000000 --- a/authelia/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# Authelia - -## Background - -Authelia works in conjunction with Traefik to provide SSO with -credentials stored in Samba (Active Directory compatible) directory tree. - -Samba resides outside Kubernetes cluster as it's difficuilt to containerize -while keeping it usable from outside the cluster due to Samba's networking. - -The MariaDB instance is used to store MFA tokens. -KeyDB is used to store session info. - - -## Deployment - -Inspect changes with `git diff` and proceed to deploy: - -``` -kubectl apply -n authelia -f application.yml -kubectl create secret generic -n authelia mysql-secrets \ - --from-literal=rootPassword=$(cat /dev/urandom | base64 | head -c 30) -kubectl create secret generic -n authelia mariadb-secrets \ - --from-literal=MYSQL_ROOT_PASSWORD=$(cat /dev/urandom | base64 | head -c 30) \ - --from-literal=MYSQL_PASSWORD=$(cat /dev/urandom | base64 | head -c 30) -kubectl -n authelia rollout restart deployment/authelia -``` - -To change secrets create `secret.yml`: - -``` ---- -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: application-secrets -data: - JWT_TOKEN: ... - SESSION_ENCRYPTION_KEY: ... - STORAGE_PASSWORD: ... - STORAGE_ENCRYPTION_KEY: ... - LDAP_PASSWORD: ... - STORAGE_PASSWORD: ... - SMTP_PASSWORD: ... -``` - -Apply with: - -``` -kubectl apply -n authelia -f application-secrets.yml -kubectl annotate -n authelia secret application-secrets reloader.stakater.com/match=true -``` - -## OIDC secrets - -OIDC secrets are separated from the main configuration until -Authelia will add CRD-s for these. - -Generally speaking for untrusted applications, that is stuff that is running -outside the Kubernetes cluster eg web browser based (JS) and -local command line clients one -should use `public: true` and omit `secret: ...`. - -Populate `oidc-secrets.yml` with approximately following: - -``` -identity_providers: - oidc: - clients: - - id: kubelogin - description: Kubernetes cluster - secret: ... - authorization_policy: two_factor - redirect_uris: - - http://localhost:27890 - scopes: - - openid - - groups - - email - - profile - - id: proxmox - description: Proxmox Virtual Environment - secret: ... - authorization_policy: two_factor - redirect_uris: - - https://pve.k-space.ee - scopes: - - openid - - groups - - email - - profile - - id: argocd - description: ArgoCD - secret: ... - authorization_policy: two_factor - redirect_uris: - - https://argocd.k-space.ee/auth/callback - scopes: - - openid - - groups - - email - - profile - - id: harbor - description: Harbor - secret: ... - authorization_policy: two_factor - redirect_uris: - - https://harbor.k-space.ee/c/oidc/callback - scopes: - - openid - - groups - - email - - profile - - id: gitea - description: Gitea - secret: ... - authorization_policy: one_factor - redirect_uris: - - https://git.k-space.ee/user/oauth2/authelia/callback - scopes: - - openid - - profile - - email - - groups - grant_types: - - refresh_token - - authorization_code - response_types: - - code - userinfo_signing_algorithm: none - - id: grafana - description: Grafana - secret: ... - authorization_policy: one_factor - redirect_uris: - - https://grafana.k-space.ee/login/generic_oauth - scopes: - - openid - - groups - - email - - profile -``` - -To upload the file to Kubernetes secrets: - -``` -kubectl -n authelia delete secret oidc-secrets -kubectl -n authelia create secret generic oidc-secrets \ - --from-file=oidc-secrets.yml=oidc-secrets.yml -kubectl annotate -n authelia secret oidc-secrets reloader.stakater.com/match=true -kubectl -n authelia rollout restart deployment/authelia -``` - -Synchronize OIDC secrets: - -``` -kubectl -n argocd delete secret argocd-secret -kubectl -n argocd create secret generic argocd-secret \ - --from-literal=server.secretkey=$(cat /dev/urandom | base64 | head -c 30) \ - --from-literal=oidc.config.clientSecret=$( \ - kubectl get secret -n authelia oidc-secrets -o json \ - | jq '.data."oidc-secrets.yml"' -r | base64 -d | yq -o json \ - | jq '.identity_providers.oidc.clients[] | select(.id == "argocd") | .secret' -r) -kubectl -n grafana delete secret oidc-secret -kubectl -n grafana create secret generic oidc-secret \ - --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=$( \ - kubectl get secret -n authelia oidc-secrets -o json \ - | jq '.data."oidc-secrets.yml"' -r | base64 -d | yq -o json \ - | jq '.identity_providers.oidc.clients[] | select(.id == "grafana") | .secret' -r) -``` diff --git a/authelia/application.yml b/authelia/application.yml deleted file mode 100644 index e12e11a..0000000 --- a/authelia/application.yml +++ /dev/null @@ -1,383 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: authelia-certificates - labels: - app.kubernetes.io/name: authelia -data: - ldaps.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZJekNDQXd1Z0F3SUJBZ0lVRzNaYnI0MGVVMlRHak1Lek5XaDhOTDJkRDRZd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0lURWZNQjBHQTFVRUF3d1dVMkZ0WW1FZ1lYUWdZV1F1YXkxemNHRmpaUzVsWlRBZUZ3MHlNVEV5TVRRdwpOekk0TlRGYUZ3MHlOakV5TVRNd056STROVEZhTUNFeEh6QWRCZ05WQkFNTUZsTmhiV0poSUdGMElHRmtMbXN0CmMzQmhZMlV1WldVd2dnSWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUURub3hEZFlicjAKVFJHOEErdk0xT0I1Rzg4Z05YL1pXeFNLb0VaM2p0ekF0NEc3blV0aUhoVzI1cUhEeXZGVGEzTzJiMUFVSEhzbwpVQXpVTWNVV1FRb3J2RjF4L1VsYitpcnk0QkxFTklaVTdYMVpxb2ZKYXgwZTcrbit1YVM3R015dnB4VXliNGlYCkd3djdZZEh5SmM4WjZROHd2MTdNV2F2ejNaOE5CWFdoeG1xc3ljTlphVkl2S1lNRVpGazNUTnA3T20vSTFpdkYKWDJuNVNtb2d2NmdBVmpVODhSeWc2NlRFVStiaGY5QWdiU0VxWjhMaVd6c20xdHc0WnJXMDVVK25JVjRzTHdlaQp2SXppblFMYmFMTkc2ZUl0cUtQZGVsWWhRNHlCeHM3QXpTOCtieVVBZk9jRktzUTI5alFVdUxNbE1pUmt6MjV5Cnc5UUZxSGVuRjNIYXJqU1JTL3ZZV3J3K0RNbmo2Tit3QVdtd21SR3NzVmxPMjFSLzAzNThBK0h5VzhyLzlsTm8KV1FoMmt3VGRPdjdxMzFwRmZQQUhHUkFITnZUN0dRKzVCeFFjdG83cG1GQ2t2OTdpbmhiZG50d2ViSmM1VWI3NQpBeHNWVC9uNk9aTjJSU09NV0RKY1pjVkpXYjQxdTNTL2lBVHlvbDBuOEZMRlRRZm9xdXdvVkQ1UnpwU0NsVm50Cjd1eENyaGNsYXhTYnhUUDhqa29ERXQzc1NycWoySm5PNlhtQ3R2VlZkMmQvWVZQQ21qQm54TWc1bld1WEwwZTgKNkh3MTd5TGtYeFgzVERkdjF2VThvYTdpTmZyNmc3Vlcrd2ZsUkJoVW5WRUluNXZEdm80STVSdWRXaEJxcHN6VQo3bGQrUDVjZE5GWEdjUlRQdFFlbXkxUllKMG5ZejkybGtRSURBUUFCbzFNd1VUQWRCZ05WSFE0RUZnUVVjZ1JrCnZ4U3V1QnNFaktzbXQvN3dpRHIxbHVRd0h3WURWUjBqQkJnd0ZvQVVjZ1JrdnhTdXVCc0VqS3NtdC83d2lEcjEKbHVRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQWdFQVNlNXM1aU04QjQ2agp6bXZMOUQ4dUJrQ0VIOW9mMnc1VFluL1NPZkFRVnhBOGxBYndORitlWmgyakdGSUN6citNYmlTMlhZdkxJNnVrClZ5cFJrN28vdExmdmY0alpqZnRpeEliWEM1MjYrUk1xOEcvV2xGbzJnWFZ0eW5BcXp5bXJVYjV1MVZJcG53QWYKNTBzNHlDOURFUXF1aGErYzJCWTBRQ3ZySnUvYy9KTUs3QTdYOFdRSzVDUy8wZkNPdzBPY2xkZzA0c3VWVlU2eQp0MEZmV0kvTlhURFFrU2JWVXN5OElmaXd4a0o5NmNsTjFNWVArQ015Mkh1eWF0aTZySnhVZFBEbS9tYzdRWXNPClNTSzQyNXJQOFFZMmduNlNXUXJXdUJic2dLSEpoVzRBYjdTTldkb0Q0QytwVDA2V1MzVXphMnhZd09TV1IvTWMKR1V5YXRwLzlxR05tOWM1d2RFQ3FtdkVQc2twQkp5ZWR6MUk2V2lxdjRuK0UvRk9qRGl0VVpFd3BFZXRUQktXZgoyRnZRa1pGRmpRU3VIdG5KT040cVRvWmlaNW4vbis4Z1k2Z1Y5Wnd0NHM5OGlpdnUwTFc4UlZGSTNkS0tiYm5lCkY1KzltNE9vMjF0SlU2QThXVGpqVXpLUnFKdEZSa1JpWGtOTGRoY2MrdTdMOFFlZTFOUjIyalg5N2NaVDNGUGoKYmpOUlpId3k5K1dhMG1zcC9EYUR5RnlaOStPUUhReUJJazdCSS9LdU0rT2dta3dlSHBNSE5CMUs1NHZQenZKawpHaFN1QUNIeTRybmdvQTBvMzNhZzJ6a3lEY3NocVRtK2Q3UXFWOWUzU2pONFpUUXlTeWNpa0I1bFJKVHAydVFkCk5jVjBtcG5nREl1aFVlSFRKWkJ0SVZCZnp4bHdHd2c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: authelia-config - labels: - app.kubernetes.io/name: authelia - annotations: - reloader.stakater.com/match: "true" -data: - authelia-config.yml: | - --- - log: - level: warn - certificates_directory: /certificates - theme: light - default_redirection_url: https://members.k-space.ee - totp: - issuer: K-SPACE - authentication_backend: - ldap: - implementation: activedirectory - url: ldaps://ad.k-space.ee - base_dn: dc=ad,dc=k-space,dc=ee - username_attribute: sAMAccountName - additional_users_dn: ou=Membership - users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user)) - additional_groups_dn: cn=Users - groups_filter: (&(member={dn})(objectclass=group)) - group_name_attribute: cn - mail_attribute: mail - display_name_attribute: displayName - user: cn=authelia,cn=Users,dc=ad,dc=k-space,dc=ee - session: - domain: k-space.ee - same_site: lax - expiration: 1M - inactivity: 120h - remember_me_duration: "0" - regulation: - ban_time: 5m - find_time: 2m - max_retries: 3 - storage: - mysql: - host: mariadb - database: authelia - username: authelia - notifier: - disable_startup_check: true - smtp: - host: mail.k-space.ee - port: 465 - username: authelia - sender: authelia@k-space.ee - subject: "[Authelia] {title}" - startup_check_address: lauri@k-space.ee - access_control: - default_policy: deny - rules: - # Longhorn dashboard - - domain: longhorn.k-space.ee - policy: two_factor - subject: group:Longhorn Admins - - domain: longhorn.k-space.ee - policy: deny - # Members site - - domain: members.k-space.ee - policy: bypass - resources: - - ^/?$ - - domain: members.k-space.ee - policy: two_factor - resources: - - ^/login/authelia/?$ - - domain: members.k-space.ee - policy: bypass - # Webmail - - domain: webmail.k-space.ee - policy: two_factor - # Etherpad - - domain: pad.k-space.ee - policy: two_factor - resources: - - ^/p/board- - subject: group:Board Members - - domain: pad.k-space.ee - policy: deny - resources: - - ^/p/board- - - domain: pad.k-space.ee - policy: two_factor - resources: - - ^/p/members- - - domain: pad.k-space.ee - policy: deny - resources: - - ^/p/members- - - domain: pad.k-space.ee - policy: bypass - # phpMyAdmin - - domain: phpmyadmin.k-space.ee - policy: two_factor - # Require login for everything else protected by traefik-sso middleware - - domain: '*.k-space.ee' - policy: one_factor - ... ---- -apiVersion: v1 -kind: Service -metadata: - name: authelia - labels: - app.kubernetes.io/name: authelia -spec: - type: ClusterIP - sessionAffinity: None - selector: - app.kubernetes.io/name: authelia - ports: - - name: http - protocol: TCP - port: 80 - targetPort: http ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: authelia - labels: - app.kubernetes.io/name: authelia - annotations: - reloader.stakater.com/search: "true" -spec: - selector: - matchLabels: - app.kubernetes.io/name: authelia - replicas: 2 - revisionHistoryLimit: 0 - template: - metadata: - labels: - app.kubernetes.io/name: authelia - spec: - enableServiceLinks: false - containers: - - name: authelia - image: authelia/authelia:4 - command: - - authelia - - --config=/config/authelia-config.yml - - --config=/config/oidc-secrets.yml - resources: - limits: - cpu: "4.00" - memory: 125Mi - requests: - cpu: "0.25" - memory: 50Mi - env: - - name: AUTHELIA_SERVER_DISABLE_HEALTHCHECK - value: "true" - - name: AUTHELIA_JWT_SECRET_FILE - value: /secrets/JWT_TOKEN - - name: AUTHELIA_SESSION_SECRET_FILE - value: /secrets/SESSION_ENCRYPTION_KEY - - name: AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE - value: /secrets/LDAP_PASSWORD - - name: AUTHELIA_SESSION_REDIS_PASSWORD - valueFrom: - secretKeyRef: - name: redis-ephemeral-owner-secrets - key: REDIS_PASSWORD - - name: AUTHELIA_SESSION_REDIS_HOST - valueFrom: - secretKeyRef: - name: redis-ephemeral-owner-secrets - key: REDIS_HOST - - name: AUTHELIA_SESSION_REDIS_PORT - valueFrom: - secretKeyRef: - name: redis-ephemeral-owner-secrets - key: REDIS_PORT - - name: AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE - value: /secrets/STORAGE_ENCRYPTION_KEY - - name: AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE - value: /mariadb-secrets/MYSQL_PASSWORD - - name: AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE - value: /secrets/OIDC_HMAC_SECRET - - name: AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE - value: /secrets/OIDC_PRIVATE_KEY - - name: AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE - value: /secrets/SMTP_PASSWORD - - name: TZ - value: Europe/Tallinn - startupProbe: - failureThreshold: 6 - httpGet: - path: /api/health - port: http - scheme: HTTP - initialDelaySeconds: 10 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - livenessProbe: - failureThreshold: 5 - httpGet: - path: /api/health - port: http - scheme: HTTP - initialDelaySeconds: 0 - periodSeconds: 30 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - failureThreshold: 5 - httpGet: - path: /api/health - port: http - scheme: HTTP - initialDelaySeconds: 0 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - ports: - - name: http - containerPort: 9091 - protocol: TCP - volumeMounts: - - mountPath: /config/authelia-config.yml - name: authelia-config - readOnly: true - subPath: authelia-config.yml - - mountPath: /config/oidc-secrets.yml - name: oidc-secrets - readOnly: true - subPath: oidc-secrets.yml - - mountPath: /secrets - name: secrets - readOnly: true - - mountPath: /certificates - name: certificates - readOnly: true - - mountPath: /mariadb-secrets - name: mariadb-secrets - readOnly: true - volumes: - - name: authelia-config - configMap: - name: authelia-config - - name: secrets - secret: - secretName: application-secrets - items: - - key: JWT_TOKEN - path: JWT_TOKEN - - key: SESSION_ENCRYPTION_KEY - path: SESSION_ENCRYPTION_KEY - - key: STORAGE_ENCRYPTION_KEY - path: STORAGE_ENCRYPTION_KEY - - key: STORAGE_PASSWORD - path: STORAGE_PASSWORD - - key: LDAP_PASSWORD - path: LDAP_PASSWORD - - key: OIDC_PRIVATE_KEY - path: OIDC_PRIVATE_KEY - - key: OIDC_HMAC_SECRET - path: OIDC_HMAC_SECRET - - key: SMTP_PASSWORD - path: SMTP_PASSWORD - - name: certificates - secret: - secretName: authelia-certificates - - name: mariadb-secrets - secret: - secretName: mariadb-secrets - - name: oidc-secrets - secret: - secretName: oidc-secrets - items: - - key: oidc-secrets.yml - path: oidc-secrets.yml ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: authelia - labels: - app.kubernetes.io/name: authelia - annotations: - external-dns.alpha.kubernetes.io/target: traefik.k-space.ee - kubernetes.io/tls-acme: "true" - traefik.ingress.kubernetes.io/router.entryPoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: authelia-chain-k6-authelia@kubernetescrd - traefik.ingress.kubernetes.io/router.tls: "true" -spec: - rules: - - host: auth.k-space.ee - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: authelia - port: - number: 80 - tls: - - hosts: - - "*.k-space.ee" ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: forwardauth-k6-authelia - labels: - app.kubernetes.io/name: authelia -spec: - forwardAuth: - address: http://authelia.authelia.svc.cluster.local/api/verify?rd=https://auth.k-space.ee/ - trustForwardHeader: true - authResponseHeaders: - - Remote-User - - Remote-Name - - Remote-Email - - Remote-Groups ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: headers-k6-authelia - labels: - app.kubernetes.io/name: authelia -spec: - headers: - browserXssFilter: true - customFrameOptionsValue: "SAMEORIGIN" - customResponseHeaders: - Cache-Control: "no-store" - Pragma: "no-cache" ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: chain-k6-authelia-auth - labels: - app.kubernetes.io/name: authelia -spec: - chain: - middlewares: - - name: forwardauth-k6-authelia - namespace: authelia ---- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: chain-k6-authelia - labels: - app.kubernetes.io/name: authelia -spec: - chain: - middlewares: - - name: headers-k6-authelia - namespace: authelia ---- -apiVersion: codemowers.io/v1alpha1 -kind: Redis -metadata: - name: ephemeral -spec: - class: ephemeral - capacity: 512Mi diff --git a/authelia/mariadb.yml b/authelia/mariadb.yml deleted file mode 120000 index e3e841e..0000000 --- a/authelia/mariadb.yml +++ /dev/null @@ -1 +0,0 @@ -../shared/mariadb.yml \ No newline at end of file diff --git a/camtiler/ingress.yml b/camtiler/ingress.yml index 56244b9..255dbda 100644 --- a/camtiler/ingress.yml +++ b/camtiler/ingress.yml @@ -1,4 +1,12 @@ --- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: sso +spec: + displayName: Cameras + uri: 'https://cams.k-space.ee/tiled' +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -6,7 +14,7 @@ metadata: annotations: kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd,camtiler-redirect@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: camtiler-sso@kubernetescrd,camtiler-redirect@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee spec: diff --git a/cluster-role-bindings.yml b/cluster-role-bindings.yml index c9dc1cd..262bf75 100644 --- a/cluster-role-bindings.yml +++ b/cluster-role-bindings.yml @@ -1,12 +1,11 @@ --- -# AD/Samba group "Kubernetes Admins" members have full access apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-admins subjects: - kind: Group - name: "Kubernetes Admins" + name: "k-space:kubernetes:admins" apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole diff --git a/grafana/README.md b/grafana/README.md index 06ca11e..b123686 100644 --- a/grafana/README.md +++ b/grafana/README.md @@ -5,10 +5,6 @@ kubectl create namespace grafana kubectl apply -n grafana -f application.yml ``` -## OIDC secret - -See Authelia README on provisioning and updating OIDC secrets for Grafana - ## Grafana post deployment steps * Configure Prometheus datasource with URL set to diff --git a/grafana/application.yml b/grafana/application.yml index 21b6d1b..0f27ff8 100644 --- a/grafana/application.yml +++ b/grafana/application.yml @@ -1,4 +1,25 @@ --- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWClient +metadata: + name: grafana +spec: + displayName: Grafana + uri: https://grafana.k-space.ee + redirectUris: + - https://grafana.k-space.ee/login/generic_oauth + allowedGroups: + - github.com:codemowers + grantTypes: + - authorization_code + - refresh_token + responseTypes: + - code + availableScopes: + - openid + - profile + tokenEndpointAuthMethod: none +--- apiVersion: v1 kind: ConfigMap metadata: @@ -14,14 +35,12 @@ data: name = OAuth icon = signin enabled = true - client_id = grafana - scopes = openid profile email groups empty_scopes = false - auth_url = https://auth.k-space.ee/api/oidc/authorize - token_url = https://auth.k-space.ee/api/oidc/token - api_url = https://auth.k-space.ee/api/oidc/userinfo allow_sign_up = true - role_attribute_path = contains(groups[*], 'Grafana Admins') && 'Admin' || 'Viewer' + use_pkce = true + role_attribute_path = contains(groups[*], 'github.com:codemowers') && 'Admin' || 'Viewer' + [security] + disable_initial_admin_creation = true --- apiVersion: apps/v1 kind: StatefulSet @@ -49,9 +68,42 @@ spec: readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 472 - envFrom: - - secretRef: - name: oidc-secret + env: + - name: GF_AUTH_GENERIC_OAUTH_SIGNOUT_REDIRECT_URL + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_GATEWAY_URI + - name: GF_AUTH_GENERIC_OAUTH_CLIENT_ID + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_CLIENT_ID + - name: GF_AUTH_GENERIC_OAUTH_SECRET + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_CLIENT_SECRET + - name: GF_AUTH_GENERIC_OAUTH_SCOPES + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_AVAILABLE_SCOPES + - name: GF_AUTH_GENERIC_OAUTH_AUTH_URL + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_GATEWAY_AUTH_URI + - name: GF_AUTH_GENERIC_OAUTH_TOKEN_URL + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_GATEWAY_TOKEN_URI + - name: GF_AUTH_GENERIC_OAUTH_API_URL + valueFrom: + secretKeyRef: + name: oidc-client-grafana-owner-secrets + key: OIDC_GATEWAY_USERINFO_URI ports: - containerPort: 3000 name: http-grafana diff --git a/logmower/application.yml b/logmower/application.yml index 6e5e3d2..5a4f056 100644 --- a/logmower/application.yml +++ b/logmower/application.yml @@ -1,18 +1,35 @@ --- apiVersion: codemowers.io/v1alpha1 -kind: GeneratedSecret +kind: OIDCGWMiddlewareClient +metadata: + name: frontend +spec: + displayName: Kubernetes pod log aggregator + uri: 'https://log.k-space.ee' + allowedGroups: + - k-space:kubernetes:admins + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username +--- +apiVersion: codemowers.cloud/v1beta1 +kind: SecretClaim metadata: name: logmower-readwrite-password spec: + size: 32 mapping: - key: password value: "%(password)s" --- -apiVersion: codemowers.io/v1alpha1 -kind: GeneratedSecret +apiVersion: codemowers.cloud/v1beta1 +kind: SecretClaim metadata: name: logmower-readonly-password spec: + size: 32 mapping: - key: password value: "%(password)s" @@ -335,7 +352,7 @@ metadata: kubernetes.io/ingress.class: traefik cert-manager.io/cluster-issuer: default traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: logmower-frontend@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee spec: diff --git a/longhorn-system/application-extras.yml b/longhorn-system/application-extras.yml index 4fb6f0d..d675e92 100644 --- a/longhorn-system/application-extras.yml +++ b/longhorn-system/application-extras.yml @@ -1,3 +1,19 @@ +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: ui +spec: + displayName: Longhorn + uri: 'https://longhorn.k-space.ee' + allowedGroups: + - k-space:kubernetes:admins + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -7,7 +23,7 @@ metadata: kubernetes.io/ingress.class: traefik external-dns.alpha.kubernetes.io/target: traefik.k-space.ee traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: longhorn-system-ui@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" spec: rules: diff --git a/mysql-clusters/README.md b/mysql-clusters/README.md index 04340f7..303f8b4 100644 --- a/mysql-clusters/README.md +++ b/mysql-clusters/README.md @@ -1,6 +1,6 @@ # MySQL clusters -This is namespace for MySQL clusters managed by operator-bundle +This is namespace for MySQL clusters managed by [operatorlib](https://github.com/codemowers/operatorlib/tree/main/samples/mysql-database-operator) ``` kubectl create namespace mysql-clusters diff --git a/mysql-clusters/application.yaml b/mysql-clusters/application.yaml index a128d12..4f3335b 100644 --- a/mysql-clusters/application.yaml +++ b/mysql-clusters/application.yaml @@ -1,35 +1,62 @@ --- -apiVersion: codemowers.io/v1alpha1 -kind: ClusterMysqlDatabaseClass -metadata: - name: external -spec: - targetCluster: external - targetNamespace: mysql-clusters - description: "MySQL instance running on the ZFS box" ---- -apiVersion: codemowers.io/v1alpha1 -kind: ClusterMysqlDatabaseClass +apiVersion: codemowers.cloud/v1beta1 +kind: MysqlDatabaseClass metadata: name: shared + annotations: + kubernetes.io/description: "Shared MySQL cluster" spec: - targetCluster: shared - targetNamespace: mysql-clusters - description: "Shared MySQL cluster" + reclaimPolicy: Retain + shared: true replicas: 3 routers: 2 storageClass: mysql + podSpec: + containers: + - name: mariadb + image: mariadb:10.9.7@sha256:198c7a5fea3d7285762042a628fe8f83f0a7ccef559605b4cc9502e65210880b + imagePullPolicy: IfNotPresent + nodeSelector: + dedicated: storage + tolerations: + - effect: NoSchedule + key: dedicated + operator: Equal + value: storage --- -apiVersion: codemowers.io/v1alpha1 -kind: ClusterMysqlDatabaseClass +apiVersion: codemowers.cloud/v1beta1 +kind: MysqlDatabaseClass metadata: name: dedicated + annotations: + kubernetes.io/description: "Dedicated MySQL cluster" spec: - targetNamespace: mysql-clusters - description: "Dedicated MySQL cluster" + reclaimPolicy: Retain replicas: 3 routers: 2 storageClass: mysql + podSpec: + containers: + - name: mariadb + image: mariadb:10.9.7@sha256:198c7a5fea3d7285762042a628fe8f83f0a7ccef559605b4cc9502e65210880b + imagePullPolicy: IfNotPresent + nodeSelector: + dedicated: storage + tolerations: + - effect: NoSchedule + key: dedicated + operator: Equal + value: storage +--- +apiVersion: codemowers.cloud/v1beta1 +kind: MysqlDatabaseClass +metadata: + name: external + annotations: + kubernetes.io/description: "External MySQL cluster" +spec: + reclaimPolicy: Retain + shared: true --- apiVersion: v1 kind: ConfigMap @@ -84,6 +111,19 @@ spec: configMap: name: phpmyadmin --- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: phpmyadmin +spec: + displayName: phpMyAdmin + uri: 'https://phpmyadmin.k-space.ee' + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -94,7 +134,7 @@ metadata: traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: mysql-clusters-phpmyadmin@kubernetescrd spec: rules: - host: phpmyadmin.k-space.ee diff --git a/oidc-gateway/README.md b/oidc-gateway/README.md new file mode 100644 index 0000000..fc3366b --- /dev/null +++ b/oidc-gateway/README.md @@ -0,0 +1,8 @@ +# OIDC Gateway + +To deploy + +``` +kubectl create namespace oidc-gateway +kubectl apply -n oidc-gateway -f crds.yml -f rbac.yml -f texts.yml -f deployment.yml -f kubelogin.yaml -f proxmox.yaml -f voron.yaml +``` diff --git a/oidc-gateway/deployment.yml b/oidc-gateway/deployment.yml index 78df08b..a83c560 100644 --- a/oidc-gateway/deployment.yml +++ b/oidc-gateway/deployment.yml @@ -95,7 +95,8 @@ spec: serviceAccountName: oidc-gateway containers: - name: oidc-gateway - image: codemowers/oidc-gateway + image: docker.io/codemowers/oidc-gateway + ports: - containerPort: 3000 env: @@ -108,13 +109,13 @@ spec: - name: GROUP_PREFIX value: 'k-space' - name: ADMIN_GROUP - value: 'github.com:codemowers:admins' + value: 'k-space:kubernetes:admins' # - name: REQUIRED_GROUP # allow everyone to authenticate, limit access to services on client level. # value: 'codemowers:users' - name: GITHUB_ORGANIZATION # if not set, gateway will add user groups from all organizations that (s)he granted access for. value: 'codemowers' - name: ENROLL_USERS # allow everyone to self-register - value: 'true' + value: 'false' - name: NAMESPACE_SELECTOR value: '*' - name: PREFERRED_EMAIL_DOMAIN # try to make primary email consistent diff --git a/oidc-gateway/kubelogin.yaml b/oidc-gateway/kubelogin.yaml new file mode 100644 index 0000000..71dc572 --- /dev/null +++ b/oidc-gateway/kubelogin.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWClient +metadata: + name: kubelogin +spec: + displayName: Kubernetes API + uri: https://git.k-space.ee/k-space/kube#cluster-access + redirectUris: + - http://localhost:27890 + allowedGroups: + - k-space:kubernetes:admins + grantTypes: + - authorization_code + - refresh_token + responseTypes: + - code + availableScopes: + - openid + - profile diff --git a/traefik/proxmox.yml b/oidc-gateway/proxmox.yaml similarity index 82% rename from traefik/proxmox.yml rename to oidc-gateway/proxmox.yaml index 28a6ba2..1ebb5ce 100644 --- a/traefik/proxmox.yml +++ b/oidc-gateway/proxmox.yaml @@ -1,3 +1,35 @@ +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: proxmox +spec: + displayName: Proxmox Virtual Environment (middleware) + uri: https://pve.k-space.ee/ + allowedGroups: + - k-space:proxmox:admins +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWClient +metadata: + name: proxmox +spec: + displayName: Proxmox Virtual Environment + uri: https://pve.k-space.ee/ + redirectUris: + - https://pve.k-space.ee/ + - https://pve.k-space.ee + allowedGroups: + - k-space:proxmox:admins + grantTypes: + - authorization_code + - refresh_token + responseTypes: + - code + availableScopes: + - openid + - profile +--- apiVersion: traefik.containo.us/v1alpha1 kind: ServersTransport metadata: @@ -61,7 +93,7 @@ kind: Service metadata: name: pve1 annotations: - traefik.ingress.kubernetes.io/service.serverstransport: traefik-proxmox-servers-transport@kubernetescrd + traefik.ingress.kubernetes.io/service.serverstransport: oidc-gateway-proxmox-servers-transport@kubernetescrd spec: type: ExternalName externalName: pve1.proxmox.infra.k-space.ee @@ -75,7 +107,7 @@ kind: Service metadata: name: pve8 annotations: - traefik.ingress.kubernetes.io/service.serverstransport: traefik-proxmox-servers-transport@kubernetescrd + traefik.ingress.kubernetes.io/service.serverstransport: oidc-gateway-proxmox-servers-transport@kubernetescrd spec: type: ExternalName externalName: pve8.proxmox.infra.k-space.ee @@ -89,7 +121,7 @@ kind: Service metadata: name: pve9 annotations: - traefik.ingress.kubernetes.io/service.serverstransport: traefik-proxmox-servers-transport@kubernetescrd + traefik.ingress.kubernetes.io/service.serverstransport: oidc-gateway-proxmox-servers-transport@kubernetescrd spec: type: ExternalName externalName: pve9.proxmox.infra.k-space.ee @@ -106,7 +138,7 @@ metadata: kubernetes.io/ingress.class: traefik external-dns.alpha.kubernetes.io/target: traefik.k-space.ee traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd,traefik-proxmox-redirect@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: oidc-gateway-proxmox@kubernetescrd,oidc-gateway-proxmox-redirect@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" spec: rules: diff --git a/traefik/voron.yml b/oidc-gateway/voron.yaml similarity index 64% rename from traefik/voron.yml rename to oidc-gateway/voron.yaml index 1c73275..1b8c9c1 100644 --- a/traefik/voron.yml +++ b/oidc-gateway/voron.yaml @@ -1,4 +1,19 @@ --- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: voron +spec: + displayName: Voron 3D printer + uri: 'https://voron.k-space.ee' + allowedGroups: + - k-space:floor + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username +--- apiVersion: v1 kind: Service metadata: @@ -18,7 +33,7 @@ metadata: annotations: kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: oidc-gateway-voron@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee spec: diff --git a/prometheus-operator/application.yml b/prometheus-operator/application.yml index 9a7462c..7c2a535 100644 --- a/prometheus-operator/application.yml +++ b/prometheus-operator/application.yml @@ -412,7 +412,7 @@ metadata: traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: prometheus-operator-prometheus@kubernetescrd spec: rules: - host: prom.k-space.ee @@ -437,7 +437,7 @@ metadata: traefik.ingress.kubernetes.io/router.entrypoints: websecure traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: prometheus-operator-alertmanager@kubernetescrd spec: rules: - host: am.k-space.ee @@ -514,3 +514,33 @@ spec: selector: matchLabels: app.kubernetes.io/name: kubelet +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: prometheus +spec: + displayName: Prometheus + uri: 'https://prom.k-space.ee' + allowedGroups: + - k-space:floor + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: alertmanager +spec: + displayName: AlertManager + uri: 'https://am.k-space.ee' + allowedGroups: + - k-space:kubernetes:admins + headerMapping: + email: Remote-Email + groups: Remote-Groups + name: Remote-Name + user: Remote-Username diff --git a/traefik/README.md b/traefik/README.md index 5bfedb0..2402495 100644 --- a/traefik/README.md +++ b/traefik/README.md @@ -3,5 +3,5 @@ Traefik Ingress Controller: ``` kubectl create namespace traefik helm template --include-crds -n traefik --release-name k6 traefik/traefik -f values.yml > application.yml -kubectl apply -n traefik -f application.yml -f application-extras.yml -f whoami.yml -f proxmox.yml -f voron.yml +kubectl apply -n traefik -f application.yml -f application-extras.yml -f whoami.yml ``` diff --git a/traefik/application-extras.yml b/traefik/application-extras.yml index dfa2422..e8a8645 100644 --- a/traefik/application-extras.yml +++ b/traefik/application-extras.yml @@ -1,14 +1,4 @@ --- -apiVersion: traefik.containo.us/v1alpha1 -kind: Middleware -metadata: - name: sso -spec: - chain: - middlewares: - - name: chain-k6-authelia-auth - namespace: authelia ---- apiVersion: v1 kind: Service metadata: @@ -16,8 +6,8 @@ metadata: namespace: traefik spec: selector: + app.kubernetes.io/instance: k6-traefik app.kubernetes.io/name: traefik - app.kubernetes.io/instance: k6 ports: - protocol: TCP port: 9000 @@ -30,13 +20,21 @@ metadata: namespace: traefik spec: selector: + app.kubernetes.io/instance: k6-traefik app.kubernetes.io/name: traefik - app.kubernetes.io/instance: k6 ports: - protocol: TCP port: 9100 targetPort: 9100 --- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: dashboard +spec: + displayName: Traefik dashboard + uri: 'https://traefik.k-space.ee' +--- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -44,11 +42,10 @@ metadata: namespace: traefik annotations: kubernetes.io/ingress.class: traefik - cert-manager.io/cluster-issuer: default # Keep IP address in sync with values.yaml external-dns.alpha.kubernetes.io/target: 193.40.103.36 traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd,traefik-dashboard-redirect@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: traefik-dashboard@kubernetescrd,traefik-dashboard-redirect@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" spec: rules: @@ -63,9 +60,8 @@ spec: port: number: 9000 tls: - - hosts: - - "*.k-space.ee" - secretName: wildcard-tls + - hosts: + - "*.k-space.ee" --- apiVersion: traefik.containo.us/v1alpha1 kind: TLSStore diff --git a/wildduck/application.yml b/wildduck/application.yml index 8e7a3e2..58a52ca 100644 --- a/wildduck/application.yml +++ b/wildduck/application.yml @@ -1,3 +1,14 @@ +--- +apiVersion: codemowers.io/v1alpha1 +kind: OIDCGWMiddlewareClient +metadata: + name: webmail +spec: + displayName: Wildduck Webmail + uri: 'https://webmail.k-space.ee' + headerMapping: + user: Remote-Username +--- apiVersion: v1 kind: ConfigMap metadata: @@ -15,8 +26,8 @@ data: domains=[] [service.sso.http] enabled = true - header = "Remote-User" - logoutRedirect = "https://auth.k-space.ee/logout" + header = "Remote-Username" + logoutRedirect = "https://auth2.k-space.ee/" [u2f] enabled=false [log] @@ -105,7 +116,7 @@ metadata: annotations: kubernetes.io/ingress.class: traefik traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd + traefik.ingress.kubernetes.io/router.middlewares: wildduck-webmail@kubernetescrd traefik.ingress.kubernetes.io/router.tls: "true" external-dns.alpha.kubernetes.io/target: traefik.k-space.ee spec: