diff --git a/assets/logo_blue_regular.png b/assets/logo_blue_regular.png new file mode 100644 index 0000000..56c8ff7 Binary files /dev/null and b/assets/logo_blue_regular.png differ diff --git a/assets/logo_blue_regular.svg b/assets/logo_blue_regular.svg new file mode 100644 index 0000000..9ff0dea --- /dev/null +++ b/assets/logo_blue_regular.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hosts/bekkalokk/configuration.nix b/hosts/bekkalokk/configuration.nix index 84e54dc..33b1564 100644 --- a/hosts/bekkalokk/configuration.nix +++ b/hosts/bekkalokk/configuration.nix @@ -9,10 +9,10 @@ #./services/keycloak.nix # TODO: set up authentication for the following: - # ./services/website/website.nix - ./services/website/nginx.nix - # ./services/website/gitea.nix - ./services/website/mediawiki.nix + # ./services/website.nix + ./services/nginx.nix + ./services/gitea/default.nix + # ./services/mediawiki.nix ]; sops.defaultSopsFile = ../../secrets/bekkalokk/bekkalokk.yaml; diff --git a/hosts/bekkalokk/services/gitea/default.nix b/hosts/bekkalokk/services/gitea/default.nix new file mode 100644 index 0000000..92143ee --- /dev/null +++ b/hosts/bekkalokk/services/gitea/default.nix @@ -0,0 +1,102 @@ +{ config, values, pkgs, ... }: +let + cfg = config.services.gitea; + domain = "git.pvv.ntnu.no"; + sshPort = 2222; +in { + sops.secrets = { + "gitea/database" = { + owner = "gitea"; + group = "gitea"; + }; + "gitea/passwd-ssh-key" = { }; + "gitea/ssh-known-hosts" = { }; + "gitea/import-user-env" = { }; + }; + + services.gitea = { + enable = true; + stateDir = "/data/gitea"; + appName = "PVV Git"; + + database = { + type = "postgres"; + host = "postgres.pvv.ntnu.no"; + port = config.services.postgresql.port; + passwordFile = config.sops.secrets."gitea/database".path; + createDatabase = false; + }; + + settings = { + server = { + DOMAIN = domain; + ROOT_URL = "https://${domain}/"; + PROTOCOL = "http+unix"; + SSH_PORT = sshPort; + START_SSH_SERVER = true; + }; + indexer = { + REPO_INDEXER_ENABLED = true; + }; + service.DISABLE_REGISTRATION = true; + session.COOKIE_SECURE = true; + database.LOG_SQL = false; + picture = { + DISABLE_GRAVATAR = true; + ENABLE_FEDERATED_AVATAR = false; + }; + "ui.meta".DESCRIPTION = "Bokstavelig talt programvareverkstedet"; + }; + }; + + environment.systemPackages = [ cfg.package ]; + + services.nginx.virtualHosts."${domain}" = { + forceSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://unix:${cfg.settings.server.HTTP_ADDR}"; + recommendedProxySettings = true; + extraConfig = '' + client_max_body_size 512M; + ''; + }; + }; + + networking.firewall.allowedTCPPorts = [ sshPort ]; + + # Automatically import users + systemd.services.gitea-import-users = { + enable = true; + preStart=''${pkgs.rsync}/bin/rsync -e "${pkgs.openssh}/bin/ssh -o UserKnownHostsFile=$CREDENTIALS_DIRECTORY/ssh-known-hosts -i $CREDENTIALS_DIRECTORY/sshkey" -a pvv@smtp.pvv.ntnu.no:/etc/passwd /tmp/passwd-import''; + serviceConfig = { + ExecStart = pkgs.writers.writePython3 "gitea-import-users" { libraries = [ pkgs.python3Packages.requests ]; } (builtins.readFile ./gitea-import-users.py); + LoadCredential=[ + "sshkey:${config.sops.secrets."gitea/passwd-ssh-key".path}" + "ssh-known-hosts:${config.sops.secrets."gitea/ssh-known-hosts".path}" + ]; + DynamicUser="yes"; + EnvironmentFile=config.sops.secrets."gitea/import-user-env".path; + }; + }; + + systemd.timers.gitea-import-users = { + enable = true; + requires = [ "gitea.service" ]; + after = [ "gitea.service" ]; + timerConfig = { + OnCalendar = "*-*-* 02:00:00"; + Persistent = true; + Unit = "gitea-import-users.service"; + }; + }; + + system.activationScripts.linkGiteaLogo.text = let + logo-svg = ../../../../assets/logo_blue_regular.svg; + logo-png = ../../../../assets/logo_blue_regular.png; + in '' + install -Dm444 ${logo-svg} ${cfg.stateDir}/custom/public/img/logo.svg + install -Dm444 ${logo-png} ${cfg.stateDir}/custom/public/img/logo.png + install -Dm444 ${./loading.apng} ${cfg.stateDir}/custom/public/img/loading.png + ''; +} diff --git a/hosts/bekkalokk/services/gitea/gitea-import-users.py b/hosts/bekkalokk/services/gitea/gitea-import-users.py new file mode 100644 index 0000000..29fa9c4 --- /dev/null +++ b/hosts/bekkalokk/services/gitea/gitea-import-users.py @@ -0,0 +1,94 @@ +import requests +import secrets +import os + +EMAIL_DOMAIN = os.getenv('EMAIL_DOMAIN') +if EMAIL_DOMAIN is None: + EMAIL_DOMAIN = 'pvv.ntnu.no' + +API_TOKEN = os.getenv('API_TOKEN') +if API_TOKEN is None: + raise Exception('API_TOKEN not set') + +GITEA_API_URL = os.getenv('GITEA_API_URL') +if GITEA_API_URL is None: + GITEA_API_URL = 'https://git.pvv.ntnu.no/api/v1' + +BANNED_SHELLS = [ + "/usr/bin/nologin", + "/usr/sbin/nologin", + "/sbin/nologin", + "/bin/false", + "/bin/msgsh", +] + +existing_users = {} + + +# This function should only ever be called when adding users +# from the passwd file +def add_user(username, name): + user = { + "full_name": name, + "username": username, + "login_name": username, + "visibility": "public", + "source_id": 1, # 1 = SMTP + } + + if username not in existing_users: + user["password"] = secrets.token_urlsafe(32) + user["must_change_password"] = False + user["visibility"] = "private" + user["email"] = username + '@' + EMAIL_DOMAIN + + r = requests.post(GITEA_API_URL + '/admin/users', json=user, + headers={'Authorization': 'token ' + API_TOKEN}) + if r.status_code != 201: + print('ERR: Failed to create user ' + username + ': ' + r.text) + return + + print('Created user ' + username) + existing_users[username] = user + + else: + r = requests.patch(GITEA_API_URL + f'/admin/users/{username}', + json=user, + headers={'Authorization': 'token ' + API_TOKEN}) + if r.status_code != 200: + print('ERR: Failed to update user ' + username + ': ' + r.text) + return + + print('Updated user ' + username) + + +def main(): + # Fetch existing users + r = requests.get(GITEA_API_URL + '/admin/users', + headers={'Authorization': 'token ' + API_TOKEN}) + + if r.status_code != 200: + raise Exception('Failed to get users: ' + r.text) + + for user in r.json(): + existing_users[user['login']] = user + + # Read the file, add each user + with open("/tmp/passwd-import", 'r') as f: + for line in f.readlines(): + uid = int(line.split(':')[2]) + if uid < 1000: + continue + + shell = line.split(':')[-1] + if shell in BANNED_SHELLS: + continue + + username = line.split(':')[0] + name = line.split(':')[4].split(',')[0] + + add_user(username, name) + + +if __name__ == '__main__': + main() diff --git a/hosts/bekkalokk/services/gitea/loading.apng b/hosts/bekkalokk/services/gitea/loading.apng new file mode 100644 index 0000000..1e6ed7e Binary files /dev/null and b/hosts/bekkalokk/services/gitea/loading.apng differ diff --git a/hosts/bekkalokk/services/website/mediawiki.nix b/hosts/bekkalokk/services/mediawiki.nix similarity index 99% rename from hosts/bekkalokk/services/website/mediawiki.nix rename to hosts/bekkalokk/services/mediawiki.nix index 94036f9..1caea97 100644 --- a/hosts/bekkalokk/services/website/mediawiki.nix +++ b/hosts/bekkalokk/services/mediawiki.nix @@ -28,7 +28,7 @@ in { database = { type = "postgres"; - host = values.hosts.postgres.ipv4; + host = "postgres.pvv.ntnu.no"; port = config.services.postgresql.port; passwordFile = config.sops.secrets."keys/postgres/mediawiki".path; createLocally = false; diff --git a/hosts/bekkalokk/services/nginx.nix b/hosts/bekkalokk/services/nginx.nix new file mode 100644 index 0000000..183f49e --- /dev/null +++ b/hosts/bekkalokk/services/nginx.nix @@ -0,0 +1,18 @@ +{ pkgs, config, ... }: +{ + security.acme = { + acceptTerms = true; + defaults.email = "drift@pvv.ntnu.no"; + }; + + services.nginx = { + enable = true; + + recommendedTlsSettings = true; + recommendedProxySettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/hosts/bekkalokk/services/website/website.nix b/hosts/bekkalokk/services/website.nix similarity index 100% rename from hosts/bekkalokk/services/website/website.nix rename to hosts/bekkalokk/services/website.nix diff --git a/hosts/bekkalokk/services/website/gitea.nix b/hosts/bekkalokk/services/website/gitea.nix deleted file mode 100644 index 908bc22..0000000 --- a/hosts/bekkalokk/services/website/gitea.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ config, values, ... }: -{ - sops.secrets."postgres/gitea/password" = { }; - - services.gitea = { - enable = true; - rootUrl = "https://git2.pvv.ntnu.no/"; - stateDir = "/data/gitea"; - appName = "PVV Git"; - - enableUnixSocket = true; - - database = { - type = "postgres"; - host = values.bicep.ipv4; - port = config.services.postgresql.port; - passwordFile = config.sops.secrets."postgres/gitea/password".path; - createDatabase = false; - }; - - settings = { - service.DISABLE_REGISTRATION = true; - session.COOKIE_SECURE = true; - }; - }; -} diff --git a/hosts/bekkalokk/services/website/nginx.nix b/hosts/bekkalokk/services/website/nginx.nix deleted file mode 100644 index c2ecbac..0000000 --- a/hosts/bekkalokk/services/website/nginx.nix +++ /dev/null @@ -1,42 +0,0 @@ -{ pkgs, config, ... }: -{ - security.acme = { - acceptTerms = true; - defaults.email = "drift@pvv.ntnu.no"; - }; - - services.nginx = { - enable = true; - - recommendedTlsSettings = true; - recommendedProxySettings = true; - recommendedOptimisation = true; - recommendedGzipSettings = true; - - virtualHosts = { - "bekkalokk.pvv.ntnu.no" = { - forceSSL = true; - enableACME = true; - root = "${config.services.mediawiki.finalPackage}/share/mediawiki"; - locations = { - "/" = { - extraConfig = '' - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_index index.php; - fastcgi_pass unix:${config.services.phpfpm.pools.mediawiki.socket}; - include ${pkgs.nginx}/conf/fastcgi_params; - include ${pkgs.nginx}/conf/fastcgi.conf; - ''; - }; - - "/images".root = config.services.mediawiki.uploadsDir; - - # "/git" = { - # proxyPass = "http://unix:${config.services.gitea.settings.server.HTTP_ADDR}"; - # proxyWebsockets = true; - # }; - }; - }; - }; - }; -} diff --git a/secrets/bekkalokk/bekkalokk.yaml b/secrets/bekkalokk/bekkalokk.yaml index 7ae1ed6..677bd16 100644 --- a/secrets/bekkalokk/bekkalokk.yaml +++ b/secrets/bekkalokk/bekkalokk.yaml @@ -1,12 +1,14 @@ gitea: password: ENC[AES256_GCM,data:hlNzdU1ope0t50/3aztyLeXjMHd2vFPpwURX+Iu8f49DOqgSnEMtV+KtLA==,iv:qljRnSnchL5cFmaUAfCH9GQYQxcy5cyWejgk1x6bFgI=,tag:tIhboFU5kZsj5oAQR3hLbw==,type:str] + database: ENC[AES256_GCM,data:UlS33IdCEyeSvT6ngpmnkBWHuSEqsB//DT+3b7C+UwbD8UXWJlsLf1X8/w==,iv:mPRW5ldyZaHP+y/0vC2JGSLZmlkhgmkvXPk4LazkSDs=,tag:gGk6Z/nbPvzE1zG+tJC8Sw==,type:str] + passwd-ssh-key: ENC[AES256_GCM,data:L0lF0wvpayss1NU9m3A45cH0bCMQzODTFVrq6EPd1JHx54wIcoaRBYLmxXKXASzBlCg9zlwXMUIk3OQcS3kdzMKL0iqcSL2iicAcKjFIHyrWLqXgwV5pRSP/tRPcVw8KW8gz0bh33EgESs5ReddZ3VZ0Cy1s2YupMRQvBXr89k1+Hv70OWB6P06hvxhv/zKcMGI1N/dWLroMgrQuT9imw4+/Q1RqwzTYeEU+eUn24AM9GjcBg4qf3OI+6g0nXUat/upIYE28iF5J3lbUSmDSmirBLc8xgHLdOyyJPTObWYWYxlSL78T7IqiMm9lI3rtBlpJDDcn/YxZpVqN5bg2154GISNK+uR0TVSLdJ+drdGHIfIX3G78XSxf2L9rbJyRn8MQlgStfdBIQicLavQKVMrmj+XQfvEMez23WbPLjH4oViBQFI+GrOHOGy/f16cz8Sn4n+69OcsOeTxs3tKYdfq6r1XLYSJ/fe/zvxBpaZiyGXljsuyEdIyBL2A8D6uSXe3Nd3/DAdBtceFfIdN1olCdutixzVWgxaJnrel161z5A/4w=,iv:Uy46yY3jFYSvpxrgCHxRMUksnWfhf5DViLMvCXVMMl4=,tag:wFEJ5+icFrOKkc56gY0A5g==,type:str] + ssh-known-hosts: ENC[AES256_GCM,data:zlRLoelQeumMxGqPmgMTB69X1RVWXIs2jWwc67lk0wrdNOHUs5UzV5TUA1JnQ43RslBU92+js7DkyvE5enGzw7zZE5F1ZYdGv/eCgvkTMC9BoLfzHzP6OzayPLYEt3xJ5PRocN8JUAD55cuu4LgsuebuydHPi2oWOfpbSUBKSeCh6dvk5Pp1XRDprPS5SzGLW8Xjq98QlzmfGv50meI9CDJZVF9Wq/72gkyfgtb3YVdr,iv:AF06TBitHegfWk6w07CdkHklh4ripQCmA45vswDQgss=,tag:zKh7WVXMJN2o9ZIwIkby3Q==,type:str] + import-user-env: ENC[AES256_GCM,data:vfaqjGEnUM9VtOPvBurz7nFwzGZt3L2EqijrQej4wiOcGCrRA4tN6kBV6NmhHqlFPsw=,iv:viPGkyOOacCWcgTu25da4qH7DC4wz2qdeC1W2WcMUdI=,tag:BllNqGQoaxqUo3lTz9LGnw==,type:str] mediawiki: password: ENC[AES256_GCM,data:HsBuA1E7187roGnKuFPfPDYxA16GFjAUucgUtrdUFmcOzmTNiFH+NWY2ZQ==,iv:vDYUmmZftcrkDtJxNYKAJSx9j+AQcmQarC62QRHR4IM=,tag:3TKjNrGRivFWoK3djC748g==,type:str] -keys: - postgres: - gitea: ENC[AES256_GCM,data:lG4P8kzp7Zq94WftN7p1RJqM65esPuTFZ2JJWkFFXTzlid2DRZPsG2FGIA==,iv:JvHQUgwwb7wJTNMxjLjOUw5sKKWlyMJafVaUOLUu9Sk=,tag:qE0+gDFU/YtghqCv/d2Qgw==,type:str] - mediawiki: ENC[AES256_GCM,data:p+s/uQ3ywQY9RpImFWTxjt1orzl905i9kTQPzsAIs6hAK5t3B00XVzKZgQ==,iv:xp3PRrjCGFxCsRZOlJGIonBOKWJ+3/1CByc4q7O3vDw=,tag:bfKlU2Pcoq0cQjbhp+UXag==,type:str] - keycloak: ENC[AES256_GCM,data:A3cbJTfP97yT35ov/yuWaD+b3wD2I8H+2GkW1ONp3YiNEsmKFjROx2rpwA==,iv:kMbuPtvy/49soEH9jxdY/X0BFDoiK7EyZ56xMkwjMUg=,tag:Ttp8BbJqfPWaeH5iaOwcQQ==,type:str] + database: ENC[AES256_GCM,data:EvVK3Mo6cZiIZS+gTxixU4r9SXN41VqwaWOtortZRNH+WPJ4xcYvzYMJNg==,iv:JtFTRLn3fzKIfgAPRqRgQjct7EdkEHtiyQKPy8/sZ2Q=,tag:nqzseG6BC0X5UNI/3kZZ3A==,type:str] +keycloak: + database: ENC[AES256_GCM,data:76+AZnNR5EiturTP7BdOCKE90bFFkfGlRtviSP5NHxPbb3RfFPJEMlwtzA==,iv:nS7VTossHdlrHjPeethhX+Ysp9ukrb5JD7kjG28OFpY=,tag:OMpiEv9nQA7v6lWJfNxEEw==,type:str] sops: kms: [] gcp_kms: [] @@ -40,8 +42,8 @@ sops: akVjeTNTeGorZjJQOVlMeCtPRUVYL3MK+VMvGxrbzGz4Q3sdaDDWjal+OiK+JYKX GHiMXVHQJZu/RrlxMjHKN6V3iaqxZpuvLAEJ2Lzy5EOHPtuiiRyeHQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-05-06T21:36:22Z" - mac: ENC[AES256_GCM,data:F9XujlDa5o0N07UfA4QTjApiJQyaT/l6jVSmekwx8exLWGKfMIVs3KKt8ZIT8MmmCg1+GPYHV1MzC+OCImj1q0uYDkqG/Of5KAKYrizz2GwmVa8pSyV/b+tFdBNKxlVjH+YWwxkMltCoZNzaYJDALAfUv07Xp8mnKaXdkS7SQBQ=,iv:LAmhmXDui8gkYKjL8gk9HPRFlcKAviQ9g9prp7yDptQ=,tag:GNffyDqt+mm3umUtnTU9hw==,type:str] + lastmodified: "2023-09-03T19:12:38Z" + mac: ENC[AES256_GCM,data:Zo6WD3n33nX7bUun9YqaidvqZjFmbIx7QTzOTGOanSbeDmrejRRdBgGMohWG07byxrdlYO6mQwBkz2xic7+Rh3k1UJ65FDNyM7EOrwuc/X7HJy2Tk9WQO0DDbwDh+OfCeLOhrpBWTlsVt9HpN6xU8xBDABVxBQzd47pm1GRs3Ig=,iv:ECl4h15AnDJPcR3eXZ/wXSTUP8QnAuYiWRWx+Ouazd4=,tag:ZkZ/kSrx/5HCDPQhCGuxLw==,type:str] pgp: - created_at: "2023-05-21T00:28:40Z" enc: |