From 3a3b425ddf6fcc77f93980ad48424192a4512f6f Mon Sep 17 00:00:00 2001 From: Piotr Dobrowolski Date: Sun, 24 Sep 2023 14:12:59 +0200 Subject: [PATCH] app/codehosting: forgejo deployment Change-Id: Icfe6e0b17932a3248e1bdb807f431c59c48430de Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1685 Reviewed-by: q3k --- app/codehosting/README.md | 26 +++ app/codehosting/app.ini.template | 98 +++++++++ app/codehosting/bootstrap-auth.sh | 19 ++ app/codehosting/create-oidc-binding.sql | 23 +++ app/codehosting/entrypoint.sh | 69 +++++++ app/codehosting/forgejo.libsonnet | 253 ++++++++++++++++++++++++ app/codehosting/prod.jsonnet | 20 ++ app/codehosting/signin_inner.tmpl | 77 ++++++++ cluster/kube/lib/nginx.libsonnet | 3 +- 9 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 app/codehosting/README.md create mode 100644 app/codehosting/app.ini.template create mode 100644 app/codehosting/bootstrap-auth.sh create mode 100644 app/codehosting/create-oidc-binding.sql create mode 100644 app/codehosting/entrypoint.sh create mode 100644 app/codehosting/forgejo.libsonnet create mode 100644 app/codehosting/prod.jsonnet create mode 100644 app/codehosting/signin_inner.tmpl diff --git a/app/codehosting/README.md b/app/codehosting/README.md new file mode 100644 index 00000000..197e4e1d --- /dev/null +++ b/app/codehosting/README.md @@ -0,0 +1,26 @@ +# Hackerspace Code Hosting deployment + +"Code Hosting service" below means Forgejo. + +Due to certain specific requirements our deployment is a little customized. + +While we prefer users to use SSO/OpenID Connect for authentication, we also +want code hosting service to be aware of all active users to correctly +synchronize account access and SSH keys. When running with both LDAP and OpenID +Connect integration enabled users are automatically created in a local database +based on LDAP source, however OpenID Connect identity is not automatically bound +to LDAP users. This causes code hosting service to still show a password-based +authentication form in order to join the two identities. + +Workaround for this in our case is a SQL trigger function that automatically +creates an OpenID Connect -> LDAP identity binding injected directly into code +hosting service's PostgreSQL database. This trigger can be reviewed in +`create-oidc-binding.sql` file here. For this to work correctly +auto-registration needs to be disabled for OpenID Connect integration, in case +some new user attempts to log in before code hosting service runs external +users synchronization job. + +LDAP users synchronization job has been adjusted to run every 10 minutes. (in +contrast to default 24h, see `app.ini.template`) + +Explore page has users listing disabled. Email and name display is disabled. diff --git a/app/codehosting/app.ini.template b/app/codehosting/app.ini.template new file mode 100644 index 00000000..b3b606ff --- /dev/null +++ b/app/codehosting/app.ini.template @@ -0,0 +1,98 @@ +APP_NAME = $APP_NAME +RUN_MODE = $RUN_MODE + +[repository] +ROOT = /data/git/repositories + +[repository.local] +LOCAL_COPY_PATH = /data/gitea/tmp/local-repo + +[repository.upload] +TEMP_PATH = /data/gitea/uploads + +[server] +APP_DATA_PATH = /data/gitea +DOMAIN = $DOMAIN +SSH_DOMAIN = $SSH_DOMAIN +HTTP_PORT = $HTTP_PORT +ROOT_URL = $ROOT_URL +START_SSH_SERVER = true +DISABLE_SSH = $DISABLE_SSH +SSH_PORT = $SSH_PORT +SSH_LISTEN_PORT = $SSH_LISTEN_PORT +LFS_START_SERVER = $LFS_START_SERVER +OFFLINE_MODE = $OFFLINE_MODE +LANDING_PAGE = explore + +[lfs] +PATH = /data/git/lfs + +[database] +PATH = /data/gitea/gitea.db +DB_TYPE = $DB_TYPE +HOST = $DB_HOST +NAME = $DB_NAME +USER = $DB_USER +PASSWD = $DB_PASSWD + +[storage] +STORAGE_TYPE = minio + +MINIO_ENDPOINT = $MINIO_ENDPOINT +MINIO_ACCESS_KEY_ID = $MINIO_ACCESS_KEY_ID +MINIO_SECRET_ACCESS_KEY = $MINIO_SECRET_ACCESS_KEY +MINIO_BUCKET = $MINIO_BUCKET + +[indexer] +ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve + +[session] +PROVIDER_CONFIG = /data/gitea/sessions + +[picture] +AVATAR_UPLOAD_PATH = /data/gitea/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars + +[attachment] +PATH = /data/gitea/attachments + +[log] +ROOT_PATH = /data/gitea/log + +[security] +INSTALL_LOCK = $INSTALL_LOCK +SECRET_KEY = $SECRET_KEY + +[service] +DISABLE_REGISTRATION = $DISABLE_REGISTRATION +REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW +ALLOW_ONLY_EXTERNAL_REGISTRATION = $ALLOW_ONLY_EXTERNAL_REGISTRATION + +[service.explore] +DISABLE_USERS_PAGE = true + +[ui] +SHOW_USER_EMAIL = false + +[oauth2_client] +REGISTER_EMAIL_CONFIRM = false +ENABLE_AUTO_REGISTRATION = false +USERNAME = userid +ACCOUNT_LINKING = auto + +[cron.sync_external_users] +SCHEDULE = @every 10m +RUN_AT_START = true + +[i18n] +LANGS = en-US,pl-PL +NAMES = English,Polski + +[mailer] +ENABLED = true +FROM = $MAILER_FROM +PROTOCOL = smtps +SMTP_ADDR = $MAILER_HOST +SMTP_PORT = $MAILER_PORT +USER = $MAILER_USER +PASSWD = $MAILER_PASSWORD diff --git a/app/codehosting/bootstrap-auth.sh b/app/codehosting/bootstrap-auth.sh new file mode 100644 index 00000000..29781b83 --- /dev/null +++ b/app/codehosting/bootstrap-auth.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# This script runs in an initContainer (once, using /data/.gitea_bootstrap_done +# as a witness file) and is responsible for setting up and configuring: +# * initial admin user +# * hswaw OpenID Connect provider +# * hswaw LDAP user database + +set -e -o pipefail + +if [[ -f '/data/.gitea_bootstrap_done' ]]; then + echo '/data/.gitea_bootstrap_done exists, not doing anything' + exit 0 +fi + +/app/gitea/gitea admin user create --username bofh --password ${ADMIN_PASSWORD} --email bofh@hackerspace.pl --admin --must-change-password=false +/app/gitea/gitea admin auth add-oauth --name hswaw-oidc --provider openidConnect --key ${SSO_CLIENT_ID} --secret ${SSO_CLIENT_SECRET} --auto-discover-url https://sso.hackerspace.pl/.well-known/openid-configuration +/app/gitea/gitea admin auth add-ldap --name hswaw-ldap --active --security-protocol ldaps --host ldap.hackerspace.pl --port 636 --bind-dn ${LDAP_BIND_DN} --bind-password ${LDAP_BIND_PASSWORD} --user-search-base "ou=People,dc=hackerspace,dc=pl" --user-filter "(&(objectclass=hsMember)(uid=%[1]s)(|(memberOf=cn=fatty,ou=Group,dc=hackerspace,dc=pl)(memberOf=cn=starving,ou=Group,dc=hackerspace,dc=pl)(memberOf=cn=potato,ou=Group,dc=hackerspace,dc=pl)))" --admin-filter "(memberOf=cn=staff,ou=Group,dc=hackerspace,dc=pl)" --username-attribute uid --email-attribute mail --public-ssh-key-attribute sshPublicKey --synchronize-users +touch /data/.gitea_bootstrap_done diff --git a/app/codehosting/create-oidc-binding.sql b/app/codehosting/create-oidc-binding.sql new file mode 100644 index 00000000..ef2250e9 --- /dev/null +++ b/app/codehosting/create-oidc-binding.sql @@ -0,0 +1,23 @@ +-- This trigger automatically ensures an openid connect identity is bound to an +-- equivalent LDAP account created by Gitea/Forgejo. + +BEGIN; + +CREATE OR REPLACE FUNCTION upsert_oidc_external_users () + RETURNS TRIGGER +AS $$ +BEGIN + -- 1 is OpenID Connect login source ID + -- 2 is LDAP login source ID + INSERT INTO external_login_user (external_id, user_id, login_source_id) SELECT name, id, 1 FROM "user" WHERE login_source = 2 ON CONFLICT DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE PLPGSQL; + +DROP TRIGGER IF EXISTS upsert_oidc_external_users ON "user"; +CREATE TRIGGER upsert_oidc_external_users AFTER INSERT OR UPDATE ON "user" EXECUTE PROCEDURE upsert_oidc_external_users(); + +-- Force trigger run +UPDATE "user" SET name = name WHERE FALSE; + +COMMIT; diff --git a/app/codehosting/entrypoint.sh b/app/codehosting/entrypoint.sh new file mode 100644 index 00000000..3002c633 --- /dev/null +++ b/app/codehosting/entrypoint.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +for FOLDER in /data/gitea/log /data/git /data/ssh; do + mkdir -p ${FOLDER} +done + +if [ ! -d /data/git/.ssh ]; then + mkdir -p /data/git/.ssh +fi + +if [ ! -f /data/git/.ssh/environment ]; then + echo "GITEA_CUSTOM=$GITEA_CUSTOM" >| /data/git/.ssh/environment + +elif ! grep -q "^GITEA_CUSTOM=$GITEA_CUSTOM$" /data/git/.ssh/environment; then + sed -i /^GITEA_CUSTOM=/d /data/git/.ssh/environment + echo "GITEA_CUSTOM=$GITEA_CUSTOM" >> /data/git/.ssh/environment +fi + +if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then + mkdir -p ${GITEA_CUSTOM}/conf + + # Set INSTALL_LOCK to true only if SECRET_KEY is not empty and + # INSTALL_LOCK is empty + if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then + INSTALL_LOCK=true + fi + + # Substitude the environment variables in the template + env -i \ + APP_NAME="${APP_NAME:-"Gitea: Git with a cup of tea"}" \ + RUN_MODE="${RUN_MODE:-"dev"}" \ + DOMAIN="${DOMAIN:-"localhost"}" \ + SSH_DOMAIN="${SSH_DOMAIN:-"localhost"}" \ + HTTP_PORT="${HTTP_PORT:-"3000"}" \ + ROOT_URL="${ROOT_URL:-""}" \ + DISABLE_SSH="${DISABLE_SSH:-"false"}" \ + SSH_PORT="${SSH_PORT:-"22"}" \ + SSH_LISTEN_PORT="${SSH_LISTEN_PORT:-"${SSH_PORT}"}" \ + LFS_START_SERVER="${LFS_START_SERVER:-"false"}" \ + DB_TYPE="${DB_TYPE:-"sqlite3"}" \ + DB_HOST="${DB_HOST:-"localhost:3306"}" \ + DB_NAME="${DB_NAME:-"gitea"}" \ + DB_USER="${DB_USER:-"root"}" \ + DB_PASSWD="${DB_PASSWD:-""}" \ + INSTALL_LOCK="${INSTALL_LOCK:-"false"}" \ + DISABLE_REGISTRATION="${DISABLE_REGISTRATION:-"false"}" \ + REQUIRE_SIGNIN_VIEW="${REQUIRE_SIGNIN_VIEW:-"false"}" \ + SECRET_KEY="${SECRET_KEY:-""}" \ + ALLOW_ONLY_EXTERNAL_REGISTRATION="${ALLOW_ONLY_EXTERNAL_REGISTRATION:-"false"}" \ + OFFLINE_MODE="${OFFLINE_MODE:-"true"}" \ + MINIO_ENDPOINT="${MINIO_ENDPOINT:-""}" \ + MINIO_ACCESS_KEY_ID="${MINIO_ACCESS_KEY_ID:-""}" \ + MINIO_SECRET_ACCESS_KEY="${MINIO_SECRET_ACCESS_KEY:-""}" \ + MINIO_BUCKET="${MINIO_BUCKET:-""}" \ + MAILER_FROM="${MAILER_FROM:-""}" \ + MAILER_HOST="${MAILER_HOST:-""}" \ + MAILER_PORT="${MAILER_PORT:-""}" \ + MAILER_USER="${MAILER_USER:-""}" \ + MAILER_PASSWORD="${MAILER_PASSWORD:-""}" \ + envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini + cat ${GITEA_CUSTOM}/conf/app.ini +fi + +if [ $# -gt 0 ]; then + exec "$@" +else + exec /app/gitea/gitea web +fi + diff --git a/app/codehosting/forgejo.libsonnet b/app/codehosting/forgejo.libsonnet new file mode 100644 index 00000000..9abbcf1a --- /dev/null +++ b/app/codehosting/forgejo.libsonnet @@ -0,0 +1,253 @@ +/* + + Deploy a Forgejo instance with PostgreSQL database and additional PV for git data. + Pre-provision the secrets with: + + kubectl -n $KUBE_NAMESPACE create secret generic forgejo \ + --from-literal=postgres_password=$(pwgen -s 24 1) \ + --from-literal=secret_key=$(pwgen -s 128 1) \ + --from-literal=admin_password=$(pwgen -s 128 1) \ + --from-literal=oauth2_client_id=$SSO_CLIENT_ID \ + --from-literal=oauth2_client_secret=$SSO_CLIENT_SECRET \ + --from-literal=ldap_bind_dn=$LDAP_BIND_DN \ + --from-literal=ldap_bind_password=$LDAP_BIND_PASSWORD \ + --from-literal=smtp_password=$SMTP_PASSWORD + + Import objectstore secret: + + ceph_ns=ceph-waw3; ceph_pool=waw-hdd-redundant-3 + kubectl -n $ceph_ns get secrets rook-ceph-object-user-${ceph_pool}-object-codehosting -o json | jq 'del(.metadata.namespace,.metadata.resourceVersion,.metadata.uid) | .metadata.creationTimestamp=null' | kubectl apply -f - -n $KUBE_NAMESPACE + + Import oidc auth trigger: + + kubectl -n $KUBE_NAMESPACE exec deploy/postgres -i -- psql -U forgejo forgejo < create-oidc-binding.sql + +*/ + +local kube = import "../../kube/kube.libsonnet"; +local postgres = import "../../kube/postgres.libsonnet"; + +{ + local forgejo = self, + local cfg = forgejo.cfg, + cfg:: { + namespace: error "namespace must be set", + prefix: "", + + image: "codeberg.org/forgejo/forgejo:1.20.5-0", + storageClassName: "waw-hdd-redundant-3", + storageSize: { git: "200Gi" }, + + admin_username: error "admin_username must be set", + admin_email: error "admin_email must be set", + + # Forgejo configuration, roughly representing the structure of app.ini + instanceName: error "instanceName (e.g. 'Warsaw Hackerspace Forgejo') must be set", + runMode: "prod", + server: { + domain: error "domain (e.g. git.hackerspace.pl) must be set", + sshDomain: cfg.server.domain, + rootURL: "https://" + cfg.server.domain + "/", + offlineMode: "true", + }, + security: { + installLock: "true", + }, + service: { + disableRegistration: "false", + allowOnlyExternalRegistration: "true", + }, + + s3: { + endpoint: "rook-ceph-rgw-waw-hdd-redundant-3-object.ceph-waw3.svc:80", #{ secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "Endpoint" } }, + accessKey: { secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "AccessKey" } }, + secretKey: { secretKeyRef: {name: "rook-ceph-object-user-waw-hdd-redundant-3-object-codehosting", key: "SecretKey" } }, + bucket: "codehosting", + }, + + mailer: { + from: "forgejo@hackerspace.pl", + host: "mail.hackerspace.pl", + port: 465, + user: "forgejo", + password: { secretKeyRef: { name: "forgejo", key: "smtp_password" } }, + }, + }, + + name(suffix):: cfg.prefix + suffix, + ns: kube.Namespace(cfg.namespace), + + postgres: postgres { + cfg+: { + namespace: cfg.namespace, + appName: "forgejo", + database: "forgejo", + username: "forgejo", + password: { secretKeyRef: { name: "forgejo", key: "postgres_password" } }, + storageClassName: cfg.storageClassName, + }, + }, + + configMap: forgejo.ns.Contain(kube.ConfigMap(forgejo.name("forgejo"))) { + data: { + "app.ini.template": importstr 'app.ini.template', + "entrypoint.sh": importstr 'entrypoint.sh', + "bootstrap-auth.sh": importstr 'bootstrap-auth.sh', + }, + }, + + dataVolume: forgejo.ns.Contain(kube.PersistentVolumeClaim(forgejo.name("forgejo"))) { + spec+: { + storageClassName: cfg.storageClassName, + accessModes: [ "ReadWriteOnce" ], + resources: { + requests: { + storage: cfg.storageSize.git, + }, + }, + }, + }, + + forgejoCustom: forgejo.ns.Contain(kube.ConfigMap(forgejo.name("forgejo-custom"))) { + data: { + "signin_inner.tmpl": importstr 'signin_inner.tmpl', + }, + }, + + statefulSet: forgejo.ns.Contain(kube.StatefulSet(forgejo.name("forgejo"))) { + spec+: { + replicas: 1, + template+: { + spec+: { + securityContext: { + runAsUser: 1000, + runAsGroup: 1000, + fsGroup: 1000, + }, + volumes_: { + configmap: kube.ConfigMapVolume(forgejo.configMap), + custom: kube.ConfigMapVolume(forgejo.forgejoCustom), + data: kube.PersistentVolumeClaimVolume(forgejo.dataVolume), + empty: kube.EmptyDirVolume(), + }, + containers_: { + server: kube.Container(forgejo.name("forgejo")) { + image: cfg.image, + command: [ "bash", "/usr/bin/entrypoint" ], + ports_: { + server: { containerPort: 3000 }, + ssh: { containerPort: 22 }, + }, + readinessProbe: { + tcpSocket: { + port: "server", + }, + initialDelaySeconds: 5, + periodSeconds: 5, + successThreshold: 1, + failureThreshold: 3 + }, + env_: { + APP_NAME: cfg.instanceName, + RUN_MODE: cfg.runMode, + INSTALL_LOCK: cfg.security.installLock, + SECRET_KEY: { secretKeyRef: { name: "forgejo", key: "secret_key" } }, + DB_TYPE: "postgres", + DB_HOST: "postgres:5432", + DB_USER: forgejo.postgres.cfg.username, + DB_PASSWD: forgejo.postgres.cfg.password, + DB_NAME: forgejo.postgres.cfg.appName, + DOMAIN: cfg.server.domain, + SSH_DOMAIN: cfg.server.sshDomain, + SSH_LISTEN_PORT: "2222", + ROOT_URL: forgejo.cfg.server.rootURL, + DISABLE_REGISTRATION: cfg.service.disableRegistration, + ALLOW_ONLY_EXTERNAL_REGISTRATION: cfg.service.allowOnlyExternalRegistration, + OFFLINE_MODE: cfg.server.offlineMode, + USER_UID: "1000", + USER_GID: "1000", + GITEA_CUSTOM: "/custom", + MINIO_ENDPOINT: cfg.s3.endpoint, + MINIO_BUCKET: cfg.s3.bucket, + MINIO_ACCESS_KEY_ID: cfg.s3.accessKey, + MINIO_SECRET_ACCESS_KEY: cfg.s3.secretKey, + MAILER_FROM: cfg.mailer.from, + MAILER_HOST: cfg.mailer.host, + MAILER_PORT: cfg.mailer.port, + MAILER_USER: cfg.mailer.user, + MAILER_PASSWORD: cfg.mailer.password, + }, + volumeMounts: [ + { name: "configmap", subPath: "entrypoint.sh", mountPath: "/usr/bin/entrypoint" }, + { name: "configmap", subPath: "app.ini.template", mountPath: "/etc/templates/app.ini" }, + { name: "data", mountPath: "/data" }, + { name: "empty", mountPath: "/custom" }, + { name: "custom", subPath: "signin_inner.tmpl", mountPath: "/custom/templates/user/auth/signin_inner.tmpl" }, + ], + }, + }, + initContainers: [ + kube.Container(forgejo.name("forgejo-dbmigrate")) { + image: forgejo.statefulSet.spec.template.spec.containers_.server.image, + command: [ "bash", "/usr/bin/entrypoint", "/app/gitea/gitea", "migrate" ], + env_: forgejo.statefulSet.spec.template.spec.containers_.server.env_, + volumeMounts: forgejo.statefulSet.spec.template.spec.containers_.server.volumeMounts, + }, + kube.Container(forgejo.name("forgejo-bootstrap-auth")) { + image: forgejo.statefulSet.spec.template.spec.containers_.server.image, + command: [ + "bash", "/bootstrap-auth.sh" + ], + env_: forgejo.statefulSet.spec.template.spec.containers_.server.env_ + { + ADMIN_PASSWORD: { secretKeyRef: { name: "forgejo", key: "admin_password" } }, + SSO_CLIENT_ID: { secretKeyRef: { name: "forgejo", key: "oauth2_client_id" } }, + SSO_CLIENT_SECRET: { secretKeyRef: { name: "forgejo", key: "oauth2_client_secret" } }, + LDAP_BIND_DN: { secretKeyRef: { name: "forgejo", key: "ldap_bind_dn" } }, + LDAP_BIND_PASSWORD: { secretKeyRef: { name: "forgejo", key: "ldap_bind_password" } }, + }, + volumeMounts: forgejo.statefulSet.spec.template.spec.containers_.server.volumeMounts + [ + { name: "configmap", subPath: "bootstrap-auth.sh", mountPath: "/bootstrap-auth.sh" }, + ] + }, + ], + }, + }, + }, + }, + + svc: forgejo.ns.Contain(kube.Service(forgejo.name("forgejo"))) { + target_pod:: forgejo.statefulSet.spec.template, + spec+: { + ports: [ + { name: "server", port: 80, targetPort: 3000, protocol: "TCP" }, + { name: "ssh", port: 22, targetPort: 2222, protocol: "TCP" }, + ], + }, + }, + + ingress: forgejo.ns.Contain(kube.Ingress(forgejo.name("forgejo"))) { + metadata+: { + annotations+: { + "kubernetes.io/tls-acme": "true", + "cert-manager.io/cluster-issuer": "letsencrypt-prod", + "nginx.ingress.kubernetes.io/proxy-body-size": "0", + }, + }, + spec+: { + tls: [ + { hosts: [cfg.server.domain], secretName: forgejo.name("acme") }, + ], + rules: [ + { + host: cfg.server.domain, + http: { + paths: [ + { path: "/", backend: forgejo.svc.name_port }, + ], + }, + } + ], + }, + }, + +} diff --git a/app/codehosting/prod.jsonnet b/app/codehosting/prod.jsonnet new file mode 100644 index 00000000..891463b7 --- /dev/null +++ b/app/codehosting/prod.jsonnet @@ -0,0 +1,20 @@ +local kube = import "../../kube/kube.libsonnet"; +local forgejo = import "forgejo.libsonnet"; +{ + #namespace: kube.Namespace("forgejo-prod"), + + forgejo: forgejo { + cfg+: { + namespace: "codehosting-prod", + prefix: "", + + admin_username: "bofh", + admin_email: "bofh@hackerspace.pl", + + instanceName: "Warsaw Hackerspace Codehosting", + server+: { + domain: "git.hackerspace.pl", + }, + }, + }, +} diff --git a/app/codehosting/signin_inner.tmpl b/app/codehosting/signin_inner.tmpl new file mode 100644 index 00000000..21cb0b56 --- /dev/null +++ b/app/codehosting/signin_inner.tmpl @@ -0,0 +1,77 @@ + + +{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}} +{{template "base/alert" .}} +{{end}} +

+ {{if .LinkAccountMode}} + {{.locale.Tr "auth.oauth_signin_title"}} + {{else}} + {{.locale.Tr "auth.login_userpass"}} + {{end}} +

+
+
+ {{.CsrfTokenHtml}} +
+
+ + +
+ {{if or (not .DisablePassword) .LinkAccountMode}} +
+ + +
+ {{end}} + {{if not .LinkAccountMode}} +
+ +
+ + +
+
+ {{end}} + + {{template "user/auth/captcha" .}} + +
+ + + {{.locale.Tr "auth.forgot_password"}} +
+ + {{if .ShowRegistrationButton}} + + {{end}} +
+ {{if and .OrderedOAuth2Names .OAuth2Providers}} +
+ {{.locale.Tr "sign_in_or"}} +
+
+
+
+ {{range $key := .OrderedOAuth2Names}} + {{$provider := index $.OAuth2Providers $key}} + + {{end}} +
+
+
+ {{end}} +
+
diff --git a/cluster/kube/lib/nginx.libsonnet b/cluster/kube/lib/nginx.libsonnet index acb7b44b..4ac46d69 100644 --- a/cluster/kube/lib/nginx.libsonnet +++ b/cluster/kube/lib/nginx.libsonnet @@ -84,6 +84,7 @@ local policies = import "../../../kube/policies.libsonnet"; tcp: env.maps.make("tcp-services") { data: { "22": "gerrit/gerrit:22", + "222": "codehosting-prod/forgejo:22", } }, udp: env.maps.make("udp-services"), @@ -235,7 +236,7 @@ local policies = import "../../../kube/policies.libsonnet"; }, }, - serviceGitea: kube.Service("ingress-nginx-gitea") { + serviceCodehosting: kube.Service("ingress-nginx-codehosting") { metadata+: env.metadata, target:: env.deployment, spec+: {