From 5668b6bbfd22fa64ba073c1e2d13967e695247b8 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 2 Jul 2024 20:54:45 +0200 Subject: [PATCH] feat(web02): Deploy a CAS server on cas-eleves.dgnum.eu --- machines/web02/_configuration.nix | 11 +- machines/web02/cas-eleves/default.nix | 105 ++++++++++++++++++ .../django-cas-server/01-pytest.patch | 20 ++++ .../packages/django-cas-server/default.nix | 64 +++++++++++ .../packages/loadcredential/default.nix | 34 ++++++ .../web02/secrets/cas_eleves-secret_key_file | 29 +++++ machines/web02/secrets/secrets.nix | 2 +- npins/sources.json | 11 ++ 8 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 machines/web02/cas-eleves/default.nix create mode 100644 machines/web02/cas-eleves/packages/django-cas-server/01-pytest.patch create mode 100644 machines/web02/cas-eleves/packages/django-cas-server/default.nix create mode 100644 machines/web02/cas-eleves/packages/loadcredential/default.nix create mode 100644 machines/web02/secrets/cas_eleves-secret_key_file diff --git a/machines/web02/_configuration.nix b/machines/web02/_configuration.nix index 7ff7dc1..7efe6d4 100644 --- a/machines/web02/_configuration.nix +++ b/machines/web02/_configuration.nix @@ -1,13 +1,15 @@ -{ lib, ... }: +{ lib, pkgs, ... }: lib.extra.mkConfig { enabledModules = [ # List of modules to enable "dgn-fail2ban" + "dgn-web" ]; enabledServices = [ # List of services to enable + "cas-eleves" ]; extraConfig = { @@ -21,6 +23,13 @@ lib.extra.mkConfig { # Disable monitoring dgn-node-monitoring.enable = false; + + # Enable Postgres databases + services.postgresql = { + enable = true; + + package = pkgs.postgresql_16; + }; }; root = ./.; diff --git a/machines/web02/cas-eleves/default.nix b/machines/web02/cas-eleves/default.nix new file mode 100644 index 0000000..e656009 --- /dev/null +++ b/machines/web02/cas-eleves/default.nix @@ -0,0 +1,105 @@ +{ + config, + lib, + pkgs, + sources, + ... +}: + +let + inherit (lib) mapAttrsToList; + + port = 9889; + + python3 = pkgs.python312; + pythonEnv = python3.withPackages (ps: [ + ps.django + ps.ldap3 + ps.gunicorn + ps.psycopg + + # Local packages + (ps.callPackage ./packages/django-cas-server { }) + (ps.callPackage ./packages/loadcredential { }) + ]); + + staticDrv = pkgs.stdenv.mkDerivation { + name = "cas_eleves-static"; + + src = sources.cas-eleves; + + nativeBuildInputs = [ pythonEnv ]; + + configurePhase = '' + export CE_STATIC_ROOT=$out/static + export CE_DEBUG=true + export CREDENTIALS_DIRECTORY=$(pwd)/.credentials + ''; + + buildPhase = '' + mkdir -p $out/static + ''; + + installPhase = '' + python3 manage.py collectstatic + ''; + }; +in + +{ + systemd.services = { + "django-cas-eleves" = { + description = "ENS CAS server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + DynamicUser = true; + LoadCredential = mapAttrsToList (name: value: "${name}:${value}") { + SECRET_KEY = config.age.secrets."cas_eleves-secret_key_file".path; + }; + StateDirectory = "django-cas-eleves"; + User = "cas_server"; + WorkingDirectory = sources.cas-eleves; + }; + + environment = { + CE_ALLOWED_HOSTS = builtins.toJSON [ + "cas-eleves.dgnum.eu" + "cas.eleves.ens.fr" + ]; + CE_STATIC_ROOT = staticDrv; + }; + + path = [ pythonEnv ]; + + script = '' + python3 manage.py migrate + gunicorn app.wsgi --pythonpath ${sources.cas-eleves} -b 127.0.0.1:${builtins.toString port} --workers=2 --threads=4 + ''; + }; + }; + + services = { + postgresql = { + ensureDatabases = [ "cas_server" ]; + ensureUsers = [ + { + name = "cas_server"; + ensureDBOwnership = true; + } + ]; + }; + + nginx.virtualHosts."cas-eleves.dgnum.eu" = { + enableACME = true; + forceSSL = true; + + locations = { + "/".proxyPass = "http://127.0.0.1:${builtins.toString port}"; + "/static/".root = staticDrv; + "= /robots.txt".root = "${staticDrv}/static"; + }; + }; + }; +} diff --git a/machines/web02/cas-eleves/packages/django-cas-server/01-pytest.patch b/machines/web02/cas-eleves/packages/django-cas-server/01-pytest.patch new file mode 100644 index 0000000..7a31d2b --- /dev/null +++ b/machines/web02/cas-eleves/packages/django-cas-server/01-pytest.patch @@ -0,0 +1,20 @@ +diff --git a/cas_server/tests/test_utils.py b/cas_server/tests/test_utils.py +index d690724..73ee761 100644 +--- a/cas_server/tests/test_utils.py ++++ b/cas_server/tests/test_utils.py +@@ -17,6 +17,7 @@ from django.db import connection + import six + import warnings + import datetime ++import pytest + + from cas_server import utils + +@@ -61,6 +62,7 @@ class CheckPasswordCase(TestCase): + ) + ) + ++ @pytest.mark.skip(reason="crypt is broken somehow") + def test_crypt(self): + """test the crypt auth method""" + salts = ["$6$UVVAQvrMyXMF3FF3", "aa"] diff --git a/machines/web02/cas-eleves/packages/django-cas-server/default.nix b/machines/web02/cas-eleves/packages/django-cas-server/default.nix new file mode 100644 index 0000000..3b9bbd4 --- /dev/null +++ b/machines/web02/cas-eleves/packages/django-cas-server/default.nix @@ -0,0 +1,64 @@ +{ + lib, + buildPythonPackage, + pytestCheckHook, + fetchFromGitHub, + setuptools, + wheel, + django, + lxml, + requests, + requests-futures, + six, + pytest-django, + pytest-env, + pytest-runner, + mock, +}: + +buildPythonPackage rec { + pname = "django-cas-server"; + version = "unstable-2024-04-13"; + format = "pyproject"; + + src = fetchFromGitHub { + owner = "nitmir"; + repo = "django-cas-server"; + rev = "a04477d34eedba4fcc91f00a22689defd3f22a7f"; + hash = "sha256-K6SKnYBiA1TrSdDSodYJoz1Bk20PsNo2g0dvs4XdmY0="; + }; + + patches = [ ./01-pytest.patch ]; + + nativeBuildInputs = [ + setuptools + wheel + ]; + + propagatedBuildInputs = [ + django + lxml + requests + requests-futures + setuptools + six + ]; + + nativeCheckInputs = [ + mock + pytestCheckHook + pytest-django + pytest-env + pytest-runner + ]; + + pythonImportsCheck = [ "cas_server" ]; + + meta = with lib; { + description = "A Django Central Authentication Service server implementing the CAS Protocol 3.0 Specification"; + homepage = "https://github.com/nitmir/django-cas-server"; + changelog = "https://github.com/nitmir/django-cas-server/blob/${src.rev}/CHANGELOG.rst"; + license = licenses.gpl3Only; + maintainers = [ ]; + }; +} diff --git a/machines/web02/cas-eleves/packages/loadcredential/default.nix b/machines/web02/cas-eleves/packages/loadcredential/default.nix new file mode 100644 index 0000000..114fb52 --- /dev/null +++ b/machines/web02/cas-eleves/packages/loadcredential/default.nix @@ -0,0 +1,34 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + wheel, +}: + +buildPythonPackage rec { + pname = "loadcredential"; + version = "1.1"; + pyproject = true; + + src = fetchFromGitHub { + owner = "Tom-Hubrecht"; + repo = "loadcredential"; + rev = "v${version}"; + hash = "sha256-GXpMqGLDmDnTGa9cBYe0CP3Evm5sQ3AK9u6k3mLAW34="; + }; + + build-system = [ + setuptools + wheel + ]; + + pythonImportsCheck = [ "loadcredential" ]; + + meta = { + description = "A simple python package to read credentials passed through systemd's LoadCredential, with a fallback on env variables "; + homepage = "https://github.com/Tom-Hubrecht/loadcredential"; + license = lib.licenses.mit; + maintainers = [ ]; # with lib.maintainers; [ thubrecht ]; + }; +} diff --git a/machines/web02/secrets/cas_eleves-secret_key_file b/machines/web02/secrets/cas_eleves-secret_key_file new file mode 100644 index 0000000..99f01df --- /dev/null +++ b/machines/web02/secrets/cas_eleves-secret_key_file @@ -0,0 +1,29 @@ +age-encryption.org/v1 +-> ssh-ed25519 jIXfPA IgVUoMVMiwd1D/DozApR4frFKdikH9an7c9RgA3Z7jU +HPy8NxwXH/4SBRrm+IBCpUF9fbfRvkZ0OU1XlHQkTHw +-> ssh-ed25519 QlRB9Q 2+9r3Np2QeAP8AyngkwBfpFzyGKROVV2f5ndYzNkEyc +RNqx1X4828mIIGMZP03ONpqccbcUq3ewRYQQ+RrVJhU +-> ssh-ed25519 r+nK/Q 495UQFnwATORjh4kQYbRwaMhOk2jTOxQSn0NAOVqjhI +NtBmalEkV7B5W6Z6Yh+RoLbm+cvePhcZsUwo1+cBbPM +-> ssh-rsa krWCLQ +dfbJ2yyFSi9xWMsxfQOYrCByCPHjKfYbGx8UxtVS0NRAtTl288MUkgs0XDiDZFzj +khjk5bMjoFEGEgzHQuw96jhnL8MWEhfRe0hGQ8xisdLS/HCjZmbhsdx13tKaNKDb +Wc7IM1pnoC+HqfVodH/DfV7IDHHZAideiCMGf00gkEdwgzYh5Ce+ZddbnnL/PgoY +fwUsulBJ4U/3kFwzQjpHKspWgigZqTfIu3KgwLnlTUFUN68oNe4WtwKpzMGcdmCz +9FP5ZlVEqMQdOQ8KbIbEAZfgRINDurtmTzv/UJ0HLGLB3zBIpVtMNE9OQjU+bDK3 +QDI0IhHqakQIrGkinQMvWA +-> ssh-ed25519 /vwQcQ VjDT3QquajzEqC2pSaZAFf5QpoVHz2jYO6RoeaaMdDo +c0hmNVbQZ+P5q49giVvR2jjD09GtbRg/8kbdyTrPCa0 +-> ssh-ed25519 0R97PA JbBVHq9ec8pCvGpoAMMXnk/61crUPDAeLj/c70wLDAM +vMfLW7PapJomKy7UK2ciWj65gNN2QceXz0NQ7pniYLM +-> ssh-ed25519 JGx7Ng OREWPSG2jfjrn1iBZKQqzEFgMBgp2MpJuO6sbWlO0DU +UWhQ8a+RcStTc+bw5sH3CxXvJJvwqBoGG3B/y68W+hA +-> ssh-ed25519 5SY7Kg snSFu4/7qEdPFui7n5QAwXtiABxt7KGxhaFKk8IDZxw ++mYT8pEvMl7BTH9CFYlk1cobj+C0sZZwT1nAnDxuWN8 +-> ssh-ed25519 p/Mg4Q zCnGwjID8wlbRvxqyZ75Bdq2/ayKTMrsUsdM2EWLhjI +rGFLoX4R02Dym30U4wwgRcF0iZ92siDuvlqu2NEmaaA +-> ssh-ed25519 IY5FSQ EbXHvG1ArgA8p8yirhJW+hFwZNrvfTVJYy91F8cjxSE +Qirf2rDLCMBMJf/O/ANN19/lnmWr6tJ894bBRxNQ78s +--- gfdWQjkVEeqZH4rZWpFDMjEv9f/P4MMRw0jVfyduxYY +9: +taɟIP'gKfKysɧi[z+bTעPB>q7cr] zͧy O8 \ No newline at end of file diff --git a/machines/web02/secrets/secrets.nix b/machines/web02/secrets/secrets.nix index 3c037bf..a8e5afe 100644 --- a/machines/web02/secrets/secrets.nix +++ b/machines/web02/secrets/secrets.nix @@ -2,4 +2,4 @@ let lib = import ../../../lib { }; publicKeys = lib.getNodeKeys "web02"; in -lib.setDefault { inherit publicKeys; } [ ] +lib.setDefault { inherit publicKeys; } [ "cas_eleves-secret_key_file" ] diff --git a/npins/sources.json b/npins/sources.json index d5d43b0..22d961b 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -39,6 +39,17 @@ "url": "https://github.com/zhaofengli/attic/archive/4dbdbee45728d8ce5788db6461aaaa89d98081f0.tar.gz", "hash": "1iri77pbf0gvas93zra29qy1c3l61n97z84xblqxmmhsxvljzvnh" }, + "cas-eleves": { + "type": "Git", + "repository": { + "type": "Git", + "url": "https://git.dgnum.eu/DGNum/cas-eleves.git" + }, + "branch": "main", + "revision": "b47165f64cffcccac7e8c5b24f8993176d16db64", + "url": null, + "hash": "00f4n6mpc7sn4rlbn22bzbd7l0mn35s0llf3ad5ff5ln626iscbz" + }, "disko": { "type": "GitRelease", "repository": {