summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2022-11-07 20:51:39 +0100
committerGregor Kleen <gkleen@yggdrasil.li>2022-11-07 20:51:39 +0100
commit0e9f1e85cd8c6f9d546ef88e971043b909017170 (patch)
tree5cb4d14df7594ef123f20d82cb2ec423b6bca744
parentf563ddece04adfd8d80d4e984405f5c70a6c94f3 (diff)
downloadnixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar
nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.gz
nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.bz2
nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.xz
nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.zip
...
-rw-r--r--flake.lock13
-rw-r--r--flake.nix11
-rw-r--r--hosts/surtr/prometheus/tls.crt19
-rw-r--r--hosts/vidhar/borg/default.nix91
-rw-r--r--hosts/vidhar/borg/pyprctl-packages.nix21
-rw-r--r--hosts/vidhar/prometheus/ca/.gitignore3
-rw-r--r--hosts/vidhar/prometheus/ca/ca.crt20
-rw-r--r--hosts/vidhar/prometheus/ca/ca.key21
-rw-r--r--hosts/vidhar/prometheus/ca/ca.key.sops21
-rw-r--r--hosts/vidhar/prometheus/ca/certs/01.pem39
-rw-r--r--hosts/vidhar/prometheus/ca/certs/02.pem38
-rw-r--r--hosts/vidhar/prometheus/ca/index.txt2
-rw-r--r--hosts/vidhar/prometheus/ca/index.txt.attr1
-rw-r--r--hosts/vidhar/prometheus/ca/serial1
-rw-r--r--hosts/vidhar/prometheus/tls.crt17
-rwxr-xr-xmodules/borgcopy/copy/copy_borg/__main__.py (renamed from hosts/vidhar/borg/copy/copy_borg/__main__.py)0
-rw-r--r--modules/borgcopy/copy/setup.py (renamed from hosts/vidhar/borg/copy/setup.py)0
-rw-r--r--modules/borgcopy/default.nix120
-rw-r--r--modules/borgsnap/default.nix16
-rw-r--r--modules/zfssnap/zfssnap/zfssnap/__main__.py2
-rw-r--r--overlays/matrix-synapse/1.70.1/default.nix111
-rw-r--r--overlays/matrix-synapse/1.70.1/plugins/default.nix8
-rw-r--r--overlays/matrix-synapse/1.70.1/plugins/ldap3.nix17
-rw-r--r--overlays/matrix-synapse/1.70.1/plugins/mjolnir-antispam.nix32
-rw-r--r--overlays/matrix-synapse/1.70.1/plugins/pam.nix15
-rw-r--r--overlays/matrix-synapse/1.70.1/plugins/shared-secret-auth.nix26
-rw-r--r--overlays/matrix-synapse/1.70.1/tools/default.nix6
-rw-r--r--overlays/matrix-synapse/1.70.1/tools/rust-synapse-compress-state.nix30
-rw-r--r--overlays/matrix-synapse/1.70.1/tools/synadm.nix47
-rw-r--r--overlays/matrix-synapse/default.nix3
-rw-r--r--shell.nix35
-rw-r--r--tools/ca/ca/__main__.py568
-rw-r--r--tools/ca/setup.py10
33 files changed, 1144 insertions, 220 deletions
diff --git a/flake.lock b/flake.lock
index 89e68eab..54334c39 100644
--- a/flake.lock
+++ b/flake.lock
@@ -94,6 +94,18 @@
94 "type": "github" 94 "type": "github"
95 } 95 }
96 }, 96 },
97 "leapseconds": {
98 "flake": false,
99 "locked": {
100 "narHash": "sha256-VCE0xQRXz833Qann3dgKU2wHPRZDdSN4/M+0nd3yYl4=",
101 "type": "file",
102 "url": "https://www.ietf.org/timezones/data/leap-seconds.list"
103 },
104 "original": {
105 "type": "file",
106 "url": "https://www.ietf.org/timezones/data/leap-seconds.list"
107 }
108 },
97 "mach-nix": { 109 "mach-nix": {
98 "inputs": { 110 "inputs": {
99 "flake-utils": "flake-utils", 111 "flake-utils": "flake-utils",
@@ -197,6 +209,7 @@
197 "deploy-rs": "deploy-rs", 209 "deploy-rs": "deploy-rs",
198 "flake-compat": "flake-compat", 210 "flake-compat": "flake-compat",
199 "home-manager": "home-manager", 211 "home-manager": "home-manager",
212 "leapseconds": "leapseconds",
200 "mach-nix": "mach-nix", 213 "mach-nix": "mach-nix",
201 "nixpkgs": "nixpkgs", 214 "nixpkgs": "nixpkgs",
202 "nvfetcher": "nvfetcher", 215 "nvfetcher": "nvfetcher",
diff --git a/flake.nix b/flake.nix
index 8f52e8d0..2b22d7c5 100644
--- a/flake.nix
+++ b/flake.nix
@@ -66,6 +66,11 @@
66 pypi-deps-db.follows = "pypi-deps-db"; 66 pypi-deps-db.follows = "pypi-deps-db";
67 }; 67 };
68 }; 68 };
69
70 leapseconds = {
71 url = "https://www.ietf.org/timezones/data/leap-seconds.list";
72 flake = false;
73 };
69 }; 74 };
70 75
71 outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, ... }@inputs: 76 outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, ... }@inputs:
@@ -210,11 +215,7 @@
210 215
211 apps = foldr recursiveUpdate {} [activateNixosConfigurations activateHomeManagerConfigurations]; 216 apps = foldr recursiveUpdate {} [activateNixosConfigurations activateHomeManagerConfigurations];
212 217
213 devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix { 218 devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); });
214 pkgs = self.legacyPackages.${system};
215 deploy-rs = deploy-rs.packages.${system}.deploy-rs;
216 nvfetcher = nvfetcher.defaultPackage.${system};
217 };});
218 219
219 templates.default = { 220 templates.default = {
220 path = ./.; 221 path = ./.;
diff --git a/hosts/surtr/prometheus/tls.crt b/hosts/surtr/prometheus/tls.crt
index ba958f40..d81f429f 100644
--- a/hosts/surtr/prometheus/tls.crt
+++ b/hosts/surtr/prometheus/tls.crt
@@ -1,10 +1,13 @@
1-----BEGIN CERTIFICATE----- 1-----BEGIN CERTIFICATE-----
2MIIBXzCCARGgAwIBAgIBATAFBgMrZXAwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55 2MIIB5TCCAWWgAwIBAgIPQAAAAGNpYE436fsCRvVfMAUGAytlcTAfMR0wGwYDVQQD
3Z2dkcmFzaWwwIBcNMjIwNDA4MjAwMzU1WhgPMjA5MDA0MjYyMDAzNTVaMBoxGDAW 3DBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAeFw0yMjExMDcxOTM5NDFaFw0zMjExMDcx
4BgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd8I32X/z9J0cO2Oz+ 4OTQ0NDFaMBoxGDAWBgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd
54KAoIJq0igdMdbLBA+8WO+vgo3UwczAMBgNVHRMBAf8EAjAAMEQGA1UdEQQ9MDuC 58I32X/z9J0cO2Oz+4KAoIJq0igdMdbLBA+8WO+vgo4G8MIG5MB8GA1UdIwQYMBaA
6GnByb21ldGhldXMuc3VydHIueWdnZHJhc2lsgh1wcm9tZXRoZXVzLnN1cnRyLnln 6FObrhCUDCZk6/JeeDMNWl8WeLr+MMB0GA1UdDgQWBBQ3na09y/kUWmnN4nHYCJeT
7Z2RyYXNpbC5saTAdBgNVHQ4EFgQUN52tPcv5FFppzeJx2AiXk6UgPDgwBQYDK2Vw 7pSA8ODAOBgNVHQ8BAf8EBAMCBeAwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggr
8A0EAPN9zhaeBB2C1TursdARH0jVBz9g0dRhP7sO5ZG0K+xp24paLXiTF1rYub24p 8BgEFBQcDAjBEBgNVHREEPTA7ghpwcm9tZXRoZXVzLnN1cnRyLnlnZ2RyYXNpbIId
9/yZw71p7M0BAE+hJqYBzYo5YBQ== 9cHJvbWV0aGV1cy5zdXJ0ci55Z2dkcmFzaWwubGkwBQYDK2VxA3MAYHd3I/Mg/t34
10zdcxrIKOAKJ9ZVVoP0msk/viKrZ4b+Q9rKSNEnkyk0y56Z7FlLDxGLScaemqQ3uA
115hjhdTci/xd4xYX/edLw1AWGRs2kBe3vs2WOmrdcKa849vdMH27G/P/+bgbdofCN
12fukxYHzpESYA
10-----END CERTIFICATE----- 13-----END CERTIFICATE-----
diff --git a/hosts/vidhar/borg/default.nix b/hosts/vidhar/borg/default.nix
index 7672de18..80ce9c7e 100644
--- a/hosts/vidhar/borg/default.nix
+++ b/hosts/vidhar/borg/default.nix
@@ -1,4 +1,4 @@
1{ config, pkgs, lib, flakeInputs, ... }: 1{ config, pkgs, lib, flakeInputs, utils, ... }:
2 2
3with lib; 3with lib;
4 4
@@ -21,60 +21,26 @@ let
21 ServerAliveCountMax 30 21 ServerAliveCountMax 30
22 ''; 22 '';
23 23
24 copyService = { repo, repoEscaped }: let 24 checkBorgUnit = {
25 serviceName = "copy-borg@${repoEscaped}";
26 in nameValuePair serviceName {
27 serviceConfig = { 25 serviceConfig = {
28 Type = "oneshot"; 26 Type = "oneshot";
29 ExecStart = "${copyBorg}/bin/copy_borg --verbosity 3 ${escapeShellArg repo} yggdrasil.borgbase:repo"; 27 ExecStart = "${pkgs.borgbackup}/bin/borg ${utils.escapeSystemdExecArgs [
30 TimeoutStartSec = "8h"; 28 "--lock-wait" "3600"
31 # User = "borg"; 29 "--progress"
32 # Group = "borg"; 30 "check"
33 # StateDirectory = "borg"; 31 "--verify-data"
34 RuntimeDirectory = "copy-borg"; 32 ]} %I";
35 Environment = [ 33 Environment = [
36 "BORG_RSH=\"${pkgs.openssh}/bin/ssh -F ${pkgs.writeText "config" sshConfig}\""
37 "BORG_BASE_DIR=/var/lib/borg" 34 "BORG_BASE_DIR=/var/lib/borg"
38 "BORG_CONFIG_DIR=/var/lib/borg/config" 35 "BORG_CONFIG_DIR=/var/lib/borg/config"
39 "BORG_CACHE_DIR=/var/lib/borg/cache" 36 "BORG_CACHE_DIR=/var/lib/borg/cache"
40 "BORG_SECURITY_DIR=/var/lib/borg/security" 37 "BORG_SECURITY_DIR=/var/lib/borg/security"
41 "BORG_KEYS_DIR=/var/lib/borg/keys" 38 "BORG_KEYS_DIR=/var/lib/borg/keys"
42 "BORG_KEY_FILE=${config.sops.secrets."yggdrasil.borgkey".path}"
43 "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes"
44 "BORG_HOSTNAME_IS_UNIQUE=yes" 39 "BORG_HOSTNAME_IS_UNIQUE=yes"
40 "BORG_RSH=\"${pkgs.openssh}/bin/ssh -F ${pkgs.writeText "config" sshConfig}\""
45 ]; 41 ];
46
47 LogRateLimitIntervalSec = 0;
48 }; 42 };
49 }; 43 };
50
51 copyBorg = flakeInputs.mach-nix.lib.${config.nixpkgs.system}.buildPythonPackage rec {
52 pname = "copy-borg";
53 src = ./copy;
54 version = "0.0.0";
55 ignoreDataOutdated = true;
56
57 requirements = ''
58 humanize
59 tqdm
60 python-dateutil
61 xdg
62 python-unshare
63 pyprctl
64 halo
65 '';
66 postInstall = ''
67 wrapProgram $out/bin/copy_borg \
68 --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir}
69 '';
70
71 providers.python-unshare = "nixpkgs";
72 overridesPre = [
73 (self: super: { python-unshare = super.python-unshare.overrideAttrs (oldAttrs: { name = "python-unshare-0.2.1"; version = "0.2.1"; }); })
74 ];
75
76 # _.tomli.buildInputs.add = with pkgs."python3Packages"; [ flit-core ];
77 };
78in { 44in {
79 config = { 45 config = {
80 services.borgsnap = { 46 services.borgsnap = {
@@ -85,7 +51,15 @@ in {
85 keyfile = config.sops.secrets."yggdrasil.borgkey".path; 51 keyfile = config.sops.secrets."yggdrasil.borgkey".path;
86 }; 52 };
87 53
88 systemd.services = listToAttrs (map copyService [{ repo = "/srv/backup/borg/jotnar"; repoEscaped = "srv-backup-borg-jotnar"; }]); 54 services.copyborg.jotnar = {
55 from = "/srv/backup/borg/jotnar";
56 to = "yggdrasil.borgbase:repo";
57 inherit sshConfig;
58 keyfile = config.sops.secrets."yggdrasil.borgkey".path;
59 timerOptions.timerConfig = {
60 OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin";
61 };
62 };
89 63
90 services.borgbackup.repos.jotnar = { 64 services.borgbackup.repos.jotnar = {
91 path = "/srv/backup/borg/jotnar"; 65 path = "/srv/backup/borg/jotnar";
@@ -95,6 +69,27 @@ in {
95 in filter (v: v != null) (mapAttrsToList toAuthKey (builtins.readDir dir)); 69 in filter (v: v != null) (mapAttrsToList toAuthKey (builtins.readDir dir));
96 }; 70 };
97 71
72 systemd.services."check-borg@${utils.escapeSystemdPath "/srv/backup/borg/jotnar"}" = checkBorgUnit;
73 systemd.services."check-borg@${utils.escapeSystemdPath "yggdrasil.borgbase:repo"}" = recursiveUpdate checkBorgUnit {
74 serviceConfig = {
75 Environment = checkBorgUnit.serviceConfig.Environment ++ [
76 "BORG_KEY_FILE=${config.sops.secrets."yggdrasil.borgkey".path}"
77 ];
78 };
79 };
80 systemd.timers."check-borg@${utils.escapeSystemdPath "/srv/backup/borg/jotnar"}" = {
81 wantedBy = [ "timers.target" ];
82 timerConfig = {
83 OnCalendar = "Sun *-*-02..08 01:30:00 Europe/Berlin";
84 };
85 };
86 systemd.timers."check-borg@${utils.escapeSystemdPath "yggdrasil.borgbase:repo"}" = {
87 wantedBy = [ "timers.target" ];
88 timerConfig = {
89 OnCalendar = "Sun *-*-02..08 01:30:00 Europe/Berlin";
90 };
91 };
92
98 boot.postBootCommands = mkBefore '' 93 boot.postBootCommands = mkBefore ''
99 ${pkgs.findutils}/bin/find /srv/backup/borg -type d -empty -delete 94 ${pkgs.findutils}/bin/find /srv/backup/borg -type d -empty -delete
100 ''; 95 '';
@@ -123,13 +118,5 @@ in {
123 group = "borg"; 118 group = "borg";
124 mode = "0400"; 119 mode = "0400";
125 }; 120 };
126
127 systemd.timers."copy-borg@srv-backup-borg-jotnar" = {
128 wantedBy = ["multi-user.target"];
129
130 timerConfig = {
131 OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin";
132 };
133 };
134 }; 121 };
135} 122}
diff --git a/hosts/vidhar/borg/pyprctl-packages.nix b/hosts/vidhar/borg/pyprctl-packages.nix
deleted file mode 100644
index d3b4256a..00000000
--- a/hosts/vidhar/borg/pyprctl-packages.nix
+++ /dev/null
@@ -1,21 +0,0 @@
1# Generated by pip2nix 0.8.0.dev1
2# See https://github.com/nix-community/pip2nix
3
4{ pkgs, fetchurl, fetchgit, fetchhg }:
5
6self: super: {
7 "pyprctl" = super.buildPythonPackage rec {
8 pname = "pyprctl";
9 version = "0.1.3";
10 src = fetchurl {
11 url = "https://files.pythonhosted.org/packages/bf/5e/62765de39bbce8111fb1f4453a4a804913bf49179fa265fb713ed66c9d15/pyprctl-0.1.3-py3-none-any.whl";
12 sha256 = "1pgif990r92za5rx12mjnq5iiz72d455v0wrawzb73q79w8ya0k3";
13 };
14 format = "wheel";
15 doCheck = false;
16 buildInputs = [];
17 checkInputs = [];
18 nativeBuildInputs = [];
19 propagatedBuildInputs = [];
20 };
21}
diff --git a/hosts/vidhar/prometheus/ca/.gitignore b/hosts/vidhar/prometheus/ca/.gitignore
deleted file mode 100644
index 7c894574..00000000
--- a/hosts/vidhar/prometheus/ca/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
1ca.key
2ca.cnf
3*.old \ No newline at end of file
diff --git a/hosts/vidhar/prometheus/ca/ca.crt b/hosts/vidhar/prometheus/ca/ca.crt
index 922fed28..8cfea666 100644
--- a/hosts/vidhar/prometheus/ca/ca.crt
+++ b/hosts/vidhar/prometheus/ca/ca.crt
@@ -1,12 +1,12 @@
1-----BEGIN CERTIFICATE----- 1-----BEGIN CERTIFICATE-----
2MIIBsjCCAWSgAwIBAgIUOzZ8XcFb8XtI2yyWp4S/WMD6QxQwBQYDK2VwMB8xHTAb 2MIIBrjCCAS6gAwIBAgIUYV3YPBx91CbgMpOGb5HKMZ2hzRUwBQYDK2VxMB8xHTAb
3BgNVBAMMFHByb21ldGhldXMueWdnZHJhc2lsMCAXDTIyMDQwODE5NDgwMFoYDzIw 3BgNVBAMMFHByb21ldGhldXMueWdnZHJhc2lsMB4XDTIyMTEwNzE5MjgzNFoXDTMy
4OTAwNDI2MTk0ODAwWjAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAq 4MTEwNzE5MzMzNFowHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55Z2dkcmFzaWwwQzAF
5MAUGAytlcAMhAOoxPLBH6pnCRtE7V5gejM92gg1vLNLHw3rFIXXchOJmo4GvMIGs 5BgMrZXEDOgAVqcV3KGDhcbQt/UR3Yv6OuAGc+Kc8hrDHjAV8K9GTjahc/d49NK2v
6MB0GA1UdDgQWBBRnwBkgZFnueEa7aV8aEAoMRzW4CTBaBgNVHSMEUzBRgBRnwBkg 6FAz0uK8YidIaTVJZjzHhTgCjYzBhMB8GA1UdIwQYMBaAFObrhCUDCZk6/JeeDMNW
7ZFnueEa7aV8aEAoMRzW4CaEjpCEwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55Z2dk 7l8WeLr+MMB0GA1UdDgQWBBTm64QlAwmZOvyXngzDVpfFni6/jDAOBgNVHQ8BAf8E
8cmFzaWyCFDs2fF3BW/F7SNsslqeEv1jA+kMUMA8GA1UdEwEB/wQFMAMBAf8wCwYD 8BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwAFAqBlI7SpHaSE+0mMzx5x
9VR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQEAwICBDAFBgMrZXADQQD9AC2OHtzW8QSC 90M6T3iJtLxP36Qz5MHx3vvcbbx1eJhZWKewuyz+9LXaCkf8Jpd5AFoC+HhoikVSz
10HU/4rGdRWRqr3pfclKXimSWaAXMPly2M1qehPI402lhQrIAVF+D1pi/EAGJfbbzF 1046yVzmTBt6TISc4bh+eiWcXEKFbxEbXkwqZd2m/oHI4Em4qnDKp96FcOfq6RQ8pR
11aurykEMB 11AwA=
12-----END CERTIFICATE----- 12-----END CERTIFICATE-----
diff --git a/hosts/vidhar/prometheus/ca/ca.key b/hosts/vidhar/prometheus/ca/ca.key
new file mode 100644
index 00000000..32c4330a
--- /dev/null
+++ b/hosts/vidhar/prometheus/ca/ca.key
@@ -0,0 +1,21 @@
1{
2 "data": "ENC[AES256_GCM,data:yk8nI2Zz2F3XnBM9dqnA3UoWTTCGJLMZUYjpo+SW+ARmZVgYdcqHZunhoGRQP/r6qrIUvM/2Yl85Uosw43jllILCNESH17Gi6uI0gD9OE8I14oll8wCL+/GvP/IuU//1NEAeLF9cz8MBWPE0WW2wQk5DF4ikl+z3/McG+kaqeU+ka6aMmjIjUstjR2vCf+pfZN3KswylcLaeuvXP,iv:ByEIQCxQwjynCFxGZdYtg+nx9mFmwbqHL3iBhzLbKIQ=,tag:jIc+KcfbSmiZqM6Z2xIa0g==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-11-07T19:33:34Z",
10 "mac": "ENC[AES256_GCM,data:UE1+0M15ZBgsKOfEmz8DMeQsmzkRxcN5cjdpMswzc6vIgo6sRN4ArdtDKqAMcFtFhzokSZin6OIizsk6KLlsts5sgVHQHXKrqssc016OADRg4BoC9zM/MGLUXOHndrRSPGSQgRDCeVwmR9C5iE18VZ/NCcZtoztHt6DPt3xmGpo=,iv:JB6CTWUyyDpjciKfYugf78Xo+jDKCH3+tL8p9G7M5y0=,tag:n73uY6cX5EV6Rjq1/HM8kw==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-11-07T19:33:34Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAJxBqRR1DzDPs/sQgfZNaKZTWH+mbdQo9mpGRWcWkm1ww\nOjVRJjiBDyeItfbOS9hnEOJKwKUIk1tH7F5m+U5daFLSw/Ct/xzJ7iyphcfRzNFN\n0l4BHF6sMyoPFpSGpE+0d4IRqfDPF3t9d3NL1lAGV75MoEho38ptNCbAn32kWpZ9\n7/Vk3L+oR/3xhLAwm3/7JDed01zNnKRaxFh3zpYfwZWhMtEdoUoEIkojufEJ64s2\n=KZjL\n-----END PGP MESSAGE-----\n",
15 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
16 }
17 ],
18 "unencrypted_suffix": "_unencrypted",
19 "version": "3.7.3"
20 }
21} \ No newline at end of file
diff --git a/hosts/vidhar/prometheus/ca/ca.key.sops b/hosts/vidhar/prometheus/ca/ca.key.sops
deleted file mode 100644
index 5313056e..00000000
--- a/hosts/vidhar/prometheus/ca/ca.key.sops
+++ /dev/null
@@ -1,21 +0,0 @@
1{
2 "data": "ENC[AES256_GCM,data:XW6h0psHOSV0cR03vRg479A5XRM7KfiBfVgvm4QlxCZzhkk5U1ToDJIaCxqKpxlEu8wm79wmz+/CmSLDEBcs7x05a5vBDt81mlWJ49PolOrG9bL9Qkyq5u8sB8HWXRXxCP5kg2su+n9NqdHX9AIhYCXy7VJDuGo=,iv:v661AhF2Q/O+a7JtwHtnSkSI0mL8ltu5rPny8vWCL/Q=,tag:c7b0a6o6y/MI5vG85uFuUg==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": null,
9 "lastmodified": "2022-04-08T20:12:22Z",
10 "mac": "ENC[AES256_GCM,data:W/IF6WgTscbkcMUTR3aeqM/H/UwgFgILDbKBxYJQxcFtt4kq3UqzSd/e0hk5NQ9IkagAC4X0gZDuzco2mc7caUGyzMKRdA2ekgcdDwzruQ4i+UYyr80dFhqHpV+aksdZJVR+dJzkmIRmza3Ia5e/X01XNIbIrU13JKYm9jCskd0=,iv:2g+UFcSTxcTrf+toi4BDVvAaY5ydk7yRnhpQ/rrNvVo=,tag:3X01wEqL/Q8cIiF+DEMnpg==,type:str]",
11 "pgp": [
12 {
13 "created_at": "2022-04-08T20:12:22Z",
14 "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdADN+s7UQS8hEBc2mMRovD/zKuIoIAS3swLpP6ul9kRGMw\nDCUvOL41sxXmuodi4Pg69YB2YcL47Fod7nQWUYaK8L3CuyjWUq1cxomlYtTd03eH\n0l4BiyWTuZ+1OG4Xng8B4zdcM5jWfeTRWupDIXcnPFjwz47FetmrcCAaROKYL87e\nAjK76Y6gR/gSj0GTTAUIfKFpqsqAdBAf6oBekQcPgeqcrJcZ2ZZFWzmswGBvcGjs\n=gqhG\n-----END PGP MESSAGE-----\n",
15 "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51"
16 }
17 ],
18 "unencrypted_suffix": "_unencrypted",
19 "version": "3.7.2"
20 }
21} \ No newline at end of file
diff --git a/hosts/vidhar/prometheus/ca/certs/01.pem b/hosts/vidhar/prometheus/ca/certs/01.pem
deleted file mode 100644
index 81abe0b7..00000000
--- a/hosts/vidhar/prometheus/ca/certs/01.pem
+++ /dev/null
@@ -1,39 +0,0 @@
1Certificate:
2 Data:
3 Version: 3 (0x2)
4 Serial Number: 1 (0x1)
5 Signature Algorithm: ED25519
6 Issuer: CN=prometheus.yggdrasil
7 Validity
8 Not Before: Apr 8 20:03:55 2022 GMT
9 Not After : Apr 26 20:03:55 2090 GMT
10 Subject: CN=surtr.yggdrasil
11 Subject Public Key Info:
12 Public Key Algorithm: ED25519
13 ED25519 Public-Key:
14 pub:
15 02:5d:f0:8d:f6:5f:fc:fd:27:47:0e:d8:ec:fe:e0:
16 a0:28:20:9a:b4:8a:07:4c:75:b2:c1:03:ef:16:3b:
17 eb:e0
18 X509v3 extensions:
19 X509v3 Basic Constraints: critical
20 CA:FALSE
21 X509v3 Subject Alternative Name:
22 DNS:prometheus.surtr.yggdrasil, DNS:prometheus.surtr.yggdrasil.li
23 X509v3 Subject Key Identifier:
24 37:9D:AD:3D:CB:F9:14:5A:69:CD:E2:71:D8:08:97:93:A5:20:3C:38
25 Signature Algorithm: ED25519
26 3c:df:73:85:a7:81:07:60:b5:4e:ea:ec:74:04:47:d2:35:41:
27 cf:d8:34:75:18:4f:ee:c3:b9:64:6d:0a:fb:1a:76:e2:96:8b:
28 5e:24:c5:d6:b6:2e:6f:6e:29:ff:26:70:ef:5a:7b:33:40:40:
29 13:e8:49:a9:80:73:62:8e:58:05
30-----BEGIN CERTIFICATE-----
31MIIBXzCCARGgAwIBAgIBATAFBgMrZXAwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55
32Z2dkcmFzaWwwIBcNMjIwNDA4MjAwMzU1WhgPMjA5MDA0MjYyMDAzNTVaMBoxGDAW
33BgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd8I32X/z9J0cO2Oz+
344KAoIJq0igdMdbLBA+8WO+vgo3UwczAMBgNVHRMBAf8EAjAAMEQGA1UdEQQ9MDuC
35GnByb21ldGhldXMuc3VydHIueWdnZHJhc2lsgh1wcm9tZXRoZXVzLnN1cnRyLnln
36Z2RyYXNpbC5saTAdBgNVHQ4EFgQUN52tPcv5FFppzeJx2AiXk6UgPDgwBQYDK2Vw
37A0EAPN9zhaeBB2C1TursdARH0jVBz9g0dRhP7sO5ZG0K+xp24paLXiTF1rYub24p
38/yZw71p7M0BAE+hJqYBzYo5YBQ==
39-----END CERTIFICATE-----
diff --git a/hosts/vidhar/prometheus/ca/certs/02.pem b/hosts/vidhar/prometheus/ca/certs/02.pem
deleted file mode 100644
index d908ca7d..00000000
--- a/hosts/vidhar/prometheus/ca/certs/02.pem
+++ /dev/null
@@ -1,38 +0,0 @@
1Certificate:
2 Data:
3 Version: 3 (0x2)
4 Serial Number: 2 (0x2)
5 Signature Algorithm: ED25519
6 Issuer: CN=prometheus.yggdrasil
7 Validity
8 Not Before: Apr 8 20:07:13 2022 GMT
9 Not After : Apr 26 20:07:13 2090 GMT
10 Subject: CN=vidhar.yggdrasil
11 Subject Public Key Info:
12 Public Key Algorithm: ED25519
13 ED25519 Public-Key:
14 pub:
15 13:84:a6:01:07:7a:5e:8d:2b:8d:83:ee:73:1d:c6:
16 b8:9a:ad:b9:3d:40:51:ec:2c:f3:52:7d:81:90:e7:
17 ac:88
18 X509v3 extensions:
19 X509v3 Basic Constraints: critical
20 CA:FALSE
21 X509v3 Subject Alternative Name:
22 DNS:prometheus.vidhar.yggdrasil
23 X509v3 Subject Key Identifier:
24 44:AA:8E:CC:AB:C9:A7:D1:A1:D0:FA:7F:DB:87:1E:08:AA:6E:4D:59
25 Signature Algorithm: ED25519
26 47:65:87:17:50:96:77:56:20:ac:9e:f4:e4:6d:19:6d:b7:24:
27 11:af:0c:c3:f3:fd:75:19:d9:77:06:41:79:7f:a5:00:0c:18:
28 ee:82:3e:9e:09:61:34:cf:8f:f5:83:d1:5d:b2:e4:42:b6:3f:
29 9c:b6:5a:f3:40:92:e6:8f:24:0f
30-----BEGIN CERTIFICATE-----
31MIIBQTCB9KADAgECAgECMAUGAytlcDAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnln
32Z2RyYXNpbDAgFw0yMjA0MDgyMDA3MTNaGA8yMDkwMDQyNjIwMDcxM1owGzEZMBcG
33A1UEAwwQdmlkaGFyLnlnZ2RyYXNpbDAqMAUGAytlcAMhABOEpgEHel6NK42D7nMd
34xriarbk9QFHsLPNSfYGQ56yIo1cwVTAMBgNVHRMBAf8EAjAAMCYGA1UdEQQfMB2C
35G3Byb21ldGhldXMudmlkaGFyLnlnZ2RyYXNpbDAdBgNVHQ4EFgQURKqOzKvJp9Gh
360Pp/24ceCKpuTVkwBQYDK2VwA0EAR2WHF1CWd1YgrJ705G0ZbbckEa8Mw/P9dRnZ
37dwZBeX+lAAwY7oI+nglhNM+P9YPRXbLkQrY/nLZa80CS5o8kDw==
38-----END CERTIFICATE-----
diff --git a/hosts/vidhar/prometheus/ca/index.txt b/hosts/vidhar/prometheus/ca/index.txt
deleted file mode 100644
index 41ebb0f4..00000000
--- a/hosts/vidhar/prometheus/ca/index.txt
+++ /dev/null
@@ -1,2 +0,0 @@
1V 20900426200355Z 01 unknown /CN=surtr.yggdrasil
2V 20900426200713Z 02 unknown /CN=vidhar.yggdrasil
diff --git a/hosts/vidhar/prometheus/ca/index.txt.attr b/hosts/vidhar/prometheus/ca/index.txt.attr
deleted file mode 100644
index 8f7e63a3..00000000
--- a/hosts/vidhar/prometheus/ca/index.txt.attr
+++ /dev/null
@@ -1 +0,0 @@
1unique_subject = yes
diff --git a/hosts/vidhar/prometheus/ca/serial b/hosts/vidhar/prometheus/ca/serial
deleted file mode 100644
index 75016ea3..00000000
--- a/hosts/vidhar/prometheus/ca/serial
+++ /dev/null
@@ -1 +0,0 @@
103
diff --git a/hosts/vidhar/prometheus/tls.crt b/hosts/vidhar/prometheus/tls.crt
index 792ed542..6516f185 100644
--- a/hosts/vidhar/prometheus/tls.crt
+++ b/hosts/vidhar/prometheus/tls.crt
@@ -1,9 +1,12 @@
1-----BEGIN CERTIFICATE----- 1-----BEGIN CERTIFICATE-----
2MIIBQTCB9KADAgECAgECMAUGAytlcDAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnln 2MIIByDCCAUigAwIBAgIPQAAAAGNpXrc6y389EXtIMAUGAytlcTAfMR0wGwYDVQQD
3Z2RyYXNpbDAgFw0yMjA0MDgyMDA3MTNaGA8yMDkwMDQyNjIwMDcxM1owGzEZMBcG 3DBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAeFw0yMjExMDcxOTMyNTRaFw0zMjExMDcx
4A1UEAwwQdmlkaGFyLnlnZ2RyYXNpbDAqMAUGAytlcAMhABOEpgEHel6NK42D7nMd 4OTM3NTRaMBsxGTAXBgNVBAMMEHZpZGhhci55Z2dkcmFzaWwwKjAFBgMrZXADIQAT
5xriarbk9QFHsLPNSfYGQ56yIo1cwVTAMBgNVHRMBAf8EAjAAMCYGA1UdEQQfMB2C 5hKYBB3pejSuNg+5zHca4mq25PUBR7CzzUn2BkOesiKOBnjCBmzAfBgNVHSMEGDAW
6G3Byb21ldGhldXMudmlkaGFyLnlnZ2RyYXNpbDAdBgNVHQ4EFgQURKqOzKvJp9Gh 6gBTm64QlAwmZOvyXngzDVpfFni6/jDAdBgNVHQ4EFgQURKqOzKvJp9Gh0Pp/24ce
70Pp/24ceCKpuTVkwBQYDK2VwA0EAR2WHF1CWd1YgrJ705G0ZbbckEa8Mw/P9dRnZ 7CKpuTVkwDgYDVR0PAQH/BAQDAgXgMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYI
8dwZBeX+lAAwY7oI+nglhNM+P9YPRXbLkQrY/nLZa80CS5o8kDw== 8KwYBBQUHAwIwJgYDVR0RBB8wHYIbcHJvbWV0aGV1cy52aWRoYXIueWdnZHJhc2ls
9MAUGAytlcQNzAIPNcNWqVX4Ie971O/S2DL0HMFmPbR331U4snLBqPGWC1/j9NV4O
10cxJvLo8Hzb4I0BXn/nZbyk/ogCCJU69BVeK378qgLo68DIZ4TA3ka5ZPNRSt464Q
11NvbkDhtFVVxM04xUjI4dOeE9jczG9nN3jHESAA==
9-----END CERTIFICATE----- 12-----END CERTIFICATE-----
diff --git a/hosts/vidhar/borg/copy/copy_borg/__main__.py b/modules/borgcopy/copy/copy_borg/__main__.py
index 5b374d99..5b374d99 100755
--- a/hosts/vidhar/borg/copy/copy_borg/__main__.py
+++ b/modules/borgcopy/copy/copy_borg/__main__.py
diff --git a/hosts/vidhar/borg/copy/setup.py b/modules/borgcopy/copy/setup.py
index f77d9560..f77d9560 100644
--- a/hosts/vidhar/borg/copy/setup.py
+++ b/modules/borgcopy/copy/setup.py
diff --git a/modules/borgcopy/default.nix b/modules/borgcopy/default.nix
new file mode 100644
index 00000000..eae07dc8
--- /dev/null
+++ b/modules/borgcopy/default.nix
@@ -0,0 +1,120 @@
1{ config, pkgs, lib, utils, flakeInputs, ... }:
2
3with lib;
4
5let
6 copyBorg = flakeInputs.mach-nix.lib.${config.nixpkgs.system}.buildPythonPackage rec {
7 pname = "copy-borg";
8 src = ./copy;
9 version = "0.0.0";
10 ignoreDataOutdated = true;
11
12 requirements = ''
13 humanize
14 tqdm
15 python-dateutil
16 xdg
17 python-unshare
18 pyprctl
19 halo
20 '';
21 postInstall = ''
22 wrapProgram $out/bin/copy_borg \
23 --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir}
24 '';
25
26 providers.python-unshare = "nixpkgs";
27 overridesPre = [
28 (self: super: { python-unshare = super.python-unshare.overrideAttrs (oldAttrs: { name = "python-unshare-0.2.1"; version = "0.2.1"; }); })
29 ];
30
31 # _.tomli.buildInputs.add = with pkgs."python3Packages"; [ flit-core ];
32 };
33
34 copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" {
35 serviceConfig = {
36 Type = "oneshot";
37 ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}";
38 TimeoutStartSec = "8h";
39 # User = "borg";
40 # Group = "borg";
41 # StateDirectory = "borg";
42 RuntimeDirectory = "copy-borg";
43 Environment = [
44 "BORG_BASE_DIR=/var/lib/borg"
45 "BORG_CONFIG_DIR=/var/lib/borg/config"
46 "BORG_CACHE_DIR=/var/lib/borg/cache"
47 "BORG_SECURITY_DIR=/var/lib/borg/security"
48 "BORG_KEYS_DIR=/var/lib/borg/keys"
49 ]
50 ++ optional opts.unknownUnencryptedRepoAccessOk "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes"
51 ++ optional opts.hostnameIsUnique "BORG_HOSTNAME_IS_UNIQUE=yes"
52 ++ optional (!(isNull opts.sshConfig)) "BORG_RSH=\"${pkgs.openssh}/bin/ssh -F ${pkgs.writeText "config" opts.sshConfig}\""
53 ++ optional (!(isNull opts.keyfile)) "BORG_KEY_FILE=${opts.keyfile}";
54
55 LogRateLimitIntervalSec = 0;
56 };
57 };
58 copyTimer = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" (recursiveUpdate {
59 wantedBy = [ "timers.target" ];
60
61 timerConfig = {
62 Unit = "copy-borg@${utils.escapeSystemdPath name}.service";
63 };
64 } opts.timerOptions);
65
66 cfg = config.services.copyborg;
67in {
68 options = {
69 services.copyborg = mkOption {
70 type = types.attrsOf (types.submodule {
71 options = {
72 from = mkOption {
73 type = types.str;
74 };
75 to = mkOption {
76 type = types.str;
77 };
78
79 verbosity = mkOption {
80 type = types.int;
81 default = 3;
82 };
83
84 sshConfig = mkOption {
85 type = with types; nullOr str;
86 default = null;
87 };
88
89 keyfile = mkOption {
90 type = with types; nullOr str;
91 default = null;
92 };
93
94 unknownUnencryptedRepoAccessOk = mkOption {
95 type = types.bool;
96 default = false;
97 };
98 hostnameIsUnique = mkOption {
99 type = types.bool;
100 default = true;
101 };
102
103 timerOptions = mkOption {
104 # type = types.submodule utils.systemdUtils.unitOptions.stage2TimerOptions;
105 type = types.attrs;
106 default = {
107 wantedBy = ["timers.target"];
108 };
109 };
110 };
111 });
112 default = {};
113 };
114 };
115
116 config = {
117 systemd.services = mapAttrs' copyService cfg;
118 systemd.timers = mapAttrs' copyTimer cfg;
119 };
120}
diff --git a/modules/borgsnap/default.nix b/modules/borgsnap/default.nix
index f4c0eec4..0a674e64 100644
--- a/modules/borgsnap/default.nix
+++ b/modules/borgsnap/default.nix
@@ -74,6 +74,15 @@ in {
74 type = with types; listOf str; 74 type = with types; listOf str;
75 default = []; 75 default = [];
76 }; 76 };
77
78 unknownUnencryptedRepoAccessOk = mkOption {
79 type = types.bool;
80 default = false;
81 };
82 hostnameIsUnique = mkOption {
83 type = types.bool;
84 default = true;
85 };
77 }; 86 };
78 }; 87 };
79 88
@@ -95,9 +104,10 @@ in {
95 "BORG_CACHE_DIR=/var/lib/borg/cache" 104 "BORG_CACHE_DIR=/var/lib/borg/cache"
96 "BORG_SECURITY_DIR=/var/lib/borg/security" 105 "BORG_SECURITY_DIR=/var/lib/borg/security"
97 "BORG_KEYS_DIR=/var/lib/borg/keys" 106 "BORG_KEYS_DIR=/var/lib/borg/keys"
98 "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes" 107 ]
99 "BORG_HOSTNAME_IS_UNIQUE=yes" 108 ++ optional cfg.unknownUnencryptedRepoAccessOk "BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes"
100 ] ++ optional (!(isNull cfg.sshConfig)) "BORG_RSH=\"${pkgs.openssh}/bin/ssh -F ${pkgs.writeText "config" cfg.sshConfig}\"" 109 ++ optional cfg.hostnameIsUnique "BORG_HOSTNAME_IS_UNIQUE=yes"
110 ++ optional (!(isNull cfg.sshConfig)) "BORG_RSH=\"${pkgs.openssh}/bin/ssh -F ${pkgs.writeText "config" cfg.sshConfig}\""
101 ++ optional (!(isNull cfg.keyfile)) "BORG_KEY_FILE=${cfg.keyfile}"; 111 ++ optional (!(isNull cfg.keyfile)) "BORG_KEY_FILE=${cfg.keyfile}";
102 RuntimeDirectory = "zfssnap-prune"; 112 RuntimeDirectory = "zfssnap-prune";
103 }; 113 };
diff --git a/modules/zfssnap/zfssnap/zfssnap/__main__.py b/modules/zfssnap/zfssnap/zfssnap/__main__.py
index 274317e2..2ff8b309 100644
--- a/modules/zfssnap/zfssnap/zfssnap/__main__.py
+++ b/modules/zfssnap/zfssnap/zfssnap/__main__.py
@@ -1,5 +1,3 @@
1#!@python@/bin/python
2
3import csv 1import csv
4import subprocess 2import subprocess
5import io 3import io
diff --git a/overlays/matrix-synapse/1.70.1/default.nix b/overlays/matrix-synapse/1.70.1/default.nix
new file mode 100644
index 00000000..0c026914
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/default.nix
@@ -0,0 +1,111 @@
1{ lib, stdenv, fetchFromGitHub, python3, openssl, rustPlatform
2, enableSystemd ? stdenv.isLinux, nixosTests
3, enableRedis ? true
4, callPackage
5}:
6
7let
8 plugins = python3.pkgs.callPackage ./plugins { };
9 tools = callPackage ./tools { };
10in
11with python3.pkgs;
12buildPythonApplication rec {
13 pname = "matrix-synapse";
14 version = "1.70.1";
15 format = "pyproject";
16
17 src = fetchFromGitHub {
18 owner = "matrix-org";
19 repo = "synapse";
20 rev = "v${version}";
21 hash = "sha256-/clEY3sabaDEOAAowQ896vYOvzf5Teevoa7ZkzWw+fY=";
22 };
23
24 cargoDeps = rustPlatform.fetchCargoTarball {
25 inherit src;
26 name = "${pname}-${version}";
27 hash = "sha256-9wxWxrn+uPcz60710DROhDqNC6FvTtnqzWiWRk8kl6A=";
28 };
29
30 postPatch = ''
31 # Remove setuptools_rust from runtime dependencies
32 # https://github.com/matrix-org/synapse/blob/v1.69.0/pyproject.toml#L177-L185
33 sed -i '/^setuptools_rust =/d' pyproject.toml
34 '';
35
36 nativeBuildInputs = [
37 poetry-core
38 rustPlatform.cargoSetupHook
39 setuptools-rust
40 ] ++ (with rustPlatform.rust; [
41 cargo
42 rustc
43 ]);
44
45 buildInputs = [ openssl ];
46
47 propagatedBuildInputs = [
48 authlib
49 bcrypt
50 bleach
51 canonicaljson
52 daemonize
53 frozendict
54 ijson
55 jinja2
56 jsonschema
57 lxml
58 matrix-common
59 msgpack
60 netaddr
61 phonenumbers
62 pillow
63 prometheus-client
64 psutil
65 psycopg2
66 pyasn1
67 pydantic
68 pyjwt
69 pymacaroons
70 pynacl
71 pyopenssl
72 pysaml2
73 pyyaml
74 requests
75 setuptools
76 signedjson
77 sortedcontainers
78 treq
79 twisted
80 typing-extensions
81 unpaddedbase64
82 ] ++ lib.optional enableSystemd systemd
83 ++ lib.optionals enableRedis [ hiredis txredisapi ];
84
85 checkInputs = [ mock parameterized openssl ];
86
87 doCheck = !stdenv.isDarwin;
88
89 checkPhase = ''
90 runHook preCheck
91
92 # remove src module, so tests use the installed module instead
93 rm -rf ./synapse
94
95 PYTHONPATH=".:$PYTHONPATH" ${python3.interpreter} -m twisted.trial -j $NIX_BUILD_CORES tests
96
97 runHook postCheck
98 '';
99
100 passthru.tests = { inherit (nixosTests) matrix-synapse; };
101 passthru.plugins = plugins;
102 passthru.tools = tools;
103 passthru.python = python3;
104
105 meta = with lib; {
106 homepage = "https://matrix.org";
107 description = "Matrix reference homeserver";
108 license = licenses.asl20;
109 maintainers = teams.matrix.members;
110 };
111}
diff --git a/overlays/matrix-synapse/1.70.1/plugins/default.nix b/overlays/matrix-synapse/1.70.1/plugins/default.nix
new file mode 100644
index 00000000..e67d9075
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/plugins/default.nix
@@ -0,0 +1,8 @@
1{ callPackage }:
2
3{
4 matrix-synapse-ldap3 = callPackage ./ldap3.nix { };
5 matrix-synapse-mjolnir-antispam = callPackage ./mjolnir-antispam.nix { };
6 matrix-synapse-pam = callPackage ./pam.nix { };
7 matrix-synapse-shared-secret-auth = callPackage ./shared-secret-auth.nix { };
8}
diff --git a/overlays/matrix-synapse/1.70.1/plugins/ldap3.nix b/overlays/matrix-synapse/1.70.1/plugins/ldap3.nix
new file mode 100644
index 00000000..394c0f5e
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/plugins/ldap3.nix
@@ -0,0 +1,17 @@
1{ isPy3k, buildPythonPackage, fetchPypi, service-identity, ldap3, twisted, ldaptor, mock }:
2
3buildPythonPackage rec {
4 pname = "matrix-synapse-ldap3";
5 version = "0.1.5";
6
7 src = fetchPypi {
8 inherit pname version;
9 sha256 = "9fdf8df7c8ec756642aa0fea53b31c0b2f1924f70d7f049a2090b523125456fe";
10 };
11
12 propagatedBuildInputs = [ service-identity ldap3 twisted ];
13
14 # ldaptor is not ready for py3 yet
15 doCheck = !isPy3k;
16 checkInputs = [ ldaptor mock ];
17}
diff --git a/overlays/matrix-synapse/1.70.1/plugins/mjolnir-antispam.nix b/overlays/matrix-synapse/1.70.1/plugins/mjolnir-antispam.nix
new file mode 100644
index 00000000..7372c2f7
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/plugins/mjolnir-antispam.nix
@@ -0,0 +1,32 @@
1{ lib, buildPythonPackage, fetchFromGitHub, matrix-synapse }:
2
3buildPythonPackage rec {
4 pname = "matrix-synapse-mjolnir-antispam";
5 version = "1.5.0";
6
7 src = fetchFromGitHub {
8 owner = "matrix-org";
9 repo = "mjolnir";
10 rev = "refs/tags/v${version}";
11 sha256 = "sha256-YmP+r9W5e63Aw66lSQeTTbYwSF/vjPyHkoehJxtcRNw=";
12 };
13
14 sourceRoot = "./source/synapse_antispam";
15
16 propagatedBuildInputs = [ matrix-synapse ];
17
18 doCheck = false; # no tests
19 pythonImportsCheck = [ "mjolnir" ];
20
21 meta = with lib; {
22 description = "AntiSpam / Banlist plugin to be used with mjolnir";
23 longDescription = ''
24 Primarily meant to block invites from undesired homeservers/users,
25 Mjolnir's Synapse module is a way to interpret ban lists and apply
26 them to your entire homeserver.
27 '';
28 homepage = "https://github.com/matrix-org/mjolnir#synapse-module";
29 license = licenses.asl20;
30 maintainers = with maintainers; [ jojosch ];
31 };
32}
diff --git a/overlays/matrix-synapse/1.70.1/plugins/pam.nix b/overlays/matrix-synapse/1.70.1/plugins/pam.nix
new file mode 100644
index 00000000..a14fe6d6
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/plugins/pam.nix
@@ -0,0 +1,15 @@
1{ buildPythonPackage, fetchFromGitHub, twisted, python-pam }:
2
3buildPythonPackage rec {
4 pname = "matrix-synapse-pam";
5 version = "0.1.3";
6
7 src = fetchFromGitHub {
8 owner = "14mRh4X0r";
9 repo = "matrix-synapse-pam";
10 rev = "v${version}";
11 sha256 = "0jgz49cwiyih5cg3hr4byva04zjnq8aj7rima9874la9fc5sd2wf";
12 };
13
14 propagatedBuildInputs = [ twisted python-pam ];
15}
diff --git a/overlays/matrix-synapse/1.70.1/plugins/shared-secret-auth.nix b/overlays/matrix-synapse/1.70.1/plugins/shared-secret-auth.nix
new file mode 100644
index 00000000..a6e22db3
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/plugins/shared-secret-auth.nix
@@ -0,0 +1,26 @@
1{ lib, buildPythonPackage, fetchFromGitHub, matrix-synapse, twisted }:
2
3buildPythonPackage rec {
4 pname = "matrix-synapse-shared-secret-auth";
5 version = "2.0.2";
6
7 src = fetchFromGitHub {
8 owner = "devture";
9 repo = "matrix-synapse-shared-secret-auth";
10 rev = version;
11 sha256 = "sha256-qzXKwTEOMtdvsxoU3Xh3vQyhK+Q18LfkeSts7EyDIXE=";
12 };
13
14 doCheck = false;
15 pythonImportsCheck = [ "shared_secret_authenticator" ];
16
17 buildInputs = [ matrix-synapse ];
18 propagatedBuildInputs = [ twisted ];
19
20 meta = with lib; {
21 description = "Shared Secret Authenticator password provider module for Matrix Synapse";
22 homepage = "https://github.com/devture/matrix-synapse-shared-secret-auth";
23 license = licenses.agpl3Plus;
24 maintainers = with maintainers; [ sumnerevans ];
25 };
26}
diff --git a/overlays/matrix-synapse/1.70.1/tools/default.nix b/overlays/matrix-synapse/1.70.1/tools/default.nix
new file mode 100644
index 00000000..defc35bc
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/tools/default.nix
@@ -0,0 +1,6 @@
1{ callPackage }:
2{
3 rust-synapse-compress-state = callPackage ./rust-synapse-compress-state.nix { };
4
5 synadm = callPackage ./synadm.nix { };
6}
diff --git a/overlays/matrix-synapse/1.70.1/tools/rust-synapse-compress-state.nix b/overlays/matrix-synapse/1.70.1/tools/rust-synapse-compress-state.nix
new file mode 100644
index 00000000..fcf123d6
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/tools/rust-synapse-compress-state.nix
@@ -0,0 +1,30 @@
1{ lib, rustPlatform, python3, fetchFromGitHub, pkg-config, openssl }:
2
3rustPlatform.buildRustPackage rec {
4 pname = "rust-synapse-compress-state";
5 version = "0.1.3";
6
7 src = fetchFromGitHub {
8 owner = "matrix-org";
9 repo = pname;
10 rev = "v${version}";
11 sha256 = "sha256-SSfVtG8kwHarVbB1O7xC2SSbUpPGYMHTMyoxu8mpEk0=";
12 };
13
14 cargoSha256 = "sha256-PG+UeovhJMsIlm5dOYdtMxbUxZjwG3V59kAcB9aFP5c=";
15
16 cargoBuildFlags = [
17 "--all"
18 ];
19
20 nativeBuildInputs = [ python3 pkg-config ];
21
22 buildInputs = [ openssl ];
23
24 meta = with lib; {
25 description = "A tool to compress some state in a Synapse instance's database";
26 homepage = "https://github.com/matrix-org/rust-synapse-compress-state";
27 license = licenses.asl20;
28 maintainers = with maintainers; [ hexa maralorn ];
29 };
30}
diff --git a/overlays/matrix-synapse/1.70.1/tools/synadm.nix b/overlays/matrix-synapse/1.70.1/tools/synadm.nix
new file mode 100644
index 00000000..5075e42e
--- /dev/null
+++ b/overlays/matrix-synapse/1.70.1/tools/synadm.nix
@@ -0,0 +1,47 @@
1{ lib
2, python3Packages
3}:
4
5with python3Packages; buildPythonApplication rec {
6 pname = "synadm";
7 version = "0.36";
8 format = "setuptools";
9
10 src = fetchPypi {
11 inherit pname version;
12 sha256 = "sha256-OMXUbfAC927qJw0B5sq1lGJQRkFAUdohIOkCYUbZumI=";
13 };
14
15 postPatch = ''
16 substituteInPlace setup.py \
17 --replace "Click>=7.0,<8.0" "Click"
18 '';
19
20 propagatedBuildInputs = [
21 click
22 click-option-group
23 dnspython
24 tabulate
25 pyyaml
26 requests
27 ];
28
29 checkPhase = ''
30 runHook preCheck
31 export HOME=$TMPDIR
32 $out/bin/synadm -h > /dev/null
33 runHook postCheck
34 '';
35
36 meta = with lib; {
37 description = "Command line admin tool for Synapse";
38 longDescription = ''
39 A CLI tool to help admins of Matrix Synapse homeservers
40 conveniently issue commands available via its admin API's
41 (matrix-org/synapse@master/docs/admin_api)
42 '';
43 homepage = "https://github.com/JOJ0/synadm";
44 license = licenses.gpl3Plus;
45 maintainers = with maintainers; [ hexa ];
46 };
47}
diff --git a/overlays/matrix-synapse/default.nix b/overlays/matrix-synapse/default.nix
new file mode 100644
index 00000000..9db73e35
--- /dev/null
+++ b/overlays/matrix-synapse/default.nix
@@ -0,0 +1,3 @@
1{ final, prev, ... }: {
2 matrix-synapse = final.callPackage ./1.70.1/default.nix {};
3}
diff --git a/shell.nix b/shell.nix
index 6ada761e..14125d02 100644
--- a/shell.nix
+++ b/shell.nix
@@ -1,8 +1,29 @@
1{ pkgs ? import <nixpkgs> {}, deploy-rs, nvfetcher }: 1{ system, self, deploy-rs, nvfetcher, mach-nix, leapseconds, ... }:
2let 2let
3 tai64dec = pkgs.writeShellScriptBin "tai64dec" '' 3 pkgs = self.legacyPackages.${system};
4 echo $((16#$(${pkgs.daemontools}/bin/tai64n <<<"" | ${pkgs.coreutils}/bin/tail -c +2 | ${pkgs.coreutils}/bin/head -c 16))) 4
5 ''; 5 ca = mach-nix.lib.${system}.buildPythonPackage {
6 pname = "ca";
7 src = ./tools/ca;
8 version = "0.0.0";
9 ignoreDataOutdated = true;
10
11 requirements = ''
12 cryptography >=38.0.0
13 fqdn
14 atomicwrites
15 leapseconddata
16 xkcdpass
17 '';
18
19 _.cryptography.buildInputs = with pkgs; [ openssl ];
20
21 postInstall = ''
22 wrapProgram $out/bin/ca \
23 --set-default LEAPSECONDS_FILE ${leapseconds} \
24 --prefix PATH : ${pkgs.lib.makeBinPath (with pkgs; [sops])}
25 '';
26 };
6in pkgs.mkShell { 27in pkgs.mkShell {
7 name = "nixos"; 28 name = "nixos";
8 nativeBuildInputs = with pkgs; [ 29 nativeBuildInputs = with pkgs; [
@@ -10,10 +31,10 @@ in pkgs.mkShell {
10 wireguard-tools 31 wireguard-tools
11 gup 32 gup
12 nftables 33 nftables
13 deploy-rs 34 deploy-rs.packages.${system}.deploy-rs
14 tai64dec
15 knot-dns 35 knot-dns
16 yq 36 yq
17 nvfetcher 37 nvfetcher.defaultPackage.${system}
38 ca
18 ]; 39 ];
19} 40}
diff --git a/tools/ca/ca/__main__.py b/tools/ca/ca/__main__.py
new file mode 100644
index 00000000..e3e4bbe6
--- /dev/null
+++ b/tools/ca/ca/__main__.py
@@ -0,0 +1,568 @@
1import sys, os
2
3import logging
4import argparse
5
6from inspect import signature
7
8from enum import Enum, auto
9from contextlib import contextmanager
10
11from cryptography import __version__ as cryptography_version
12from cryptography.hazmat.backends import openssl
13from cryptography import x509
14from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, ExtensionOID
15from cryptography.hazmat.primitives import serialization, hashes
16from cryptography.hazmat.primitives.serialization import PrivateFormat, pkcs12
17from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
18from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
19from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
20from cryptography.hazmat.primitives.asymmetric import rsa
21from pathlib import Path
22from atomicwrites import atomic_write
23from fqdn import FQDN
24from datetime import datetime, timedelta, timezone
25from math import ceil, ldexp
26import re
27from getpass import getpass
28from itertools import count
29from tempfile import TemporaryFile
30import subprocess
31import json
32from leapseconddata import LeapSecondData
33
34
35class KeyType(Enum):
36 ED448 = 'ed448'
37 ED25519 = 'ed25519'
38 RSA4096 = 'rsa4096'
39 RSA2048 = 'rsa2048'
40
41 def generate(self):
42 match self:
43 case KeyType.ED448:
44 return Ed448PrivateKey.generate()
45 case KeyType.ED25519:
46 return Ed25519PrivateKey.generate()
47 case KeyType.RSA4096:
48 return rsa.generate_private_key(
49 public_exponent = 65537,
50 key_size = 4096,
51 )
52 case KeyType.RSA2048:
53 return rsa.generate_private_key(
54 public_exponent = 65537,
55 key_size = 2048,
56 )
57
58 def aligned(self, key):
59 match self:
60 case KeyType.ED448:
61 return isinstance(key, Ed448PrivateKey)
62 case KeyType.ED25519:
63 return isinstance(key, Ed25519PrivateKey)
64 case KeyType.RSA4096:
65 return isinstance(key, RSAPrivateKey) and key.key_size == 4096
66 case KeyType.RSA2048:
67 return isinstance(key, RSAPrivateKey) and key.key_size == 2048
68
69 def __str__(self):
70 return self.value
71
72 @classmethod
73 def from_string(cls, s):
74 try:
75 return cls(s)
76 except KeyError:
77 raise ValueError()
78
79class ValidFQDN(FQDN):
80 def __init__(self, *args, **kwds):
81 super().__init__(*args, **kwds)
82
83 if not self.is_valid:
84 raise ValueError(f'‘{self}’ is not valid')
85
86def duration(inp_str):
87 delta = timedelta()
88
89 item_re = re.compile(r'\W*(?P<value>\d+)\W*(?P<unit>(?i:d|h|m(?!s)|s|ms|µs))')
90
91 match = item_re.match(inp_str)
92 while match:
93 val = int(match.group('value'))
94 unit = match.group('unit').lower()
95
96 if unit == 'd':
97 delta += timedelta(days=val)
98 elif unit == 'h':
99 delta += timedelta(hours=val)
100 elif unit == 'm':
101 delta += timedelta(minutes=val)
102 elif unit == 's':
103 delta += timedelta(seconds=val)
104 elif unit == 'ms':
105 delta += timedelta(milliseconds=val)
106 elif unit == 'µs' or unit == 'us':
107 delta += timedelta(microseconds=val)
108 else:
109 raise ValueError(f'Unknown time unit ‘{unit:s}’')
110
111 inp_str = inp_str[match.end():]
112 match = item_re.match(inp_str)
113 else:
114 if re.match('\w', inp_str):
115 raise ValueError(f'Parsing of duration resulted in leftovers: ‘{inp_str:s}’')
116
117 return delta
118
119@contextmanager
120def umask(desired_umask):
121 """ A little helper to safely set and restore umask(2). """
122 try:
123 prev_umask = os.umask(0)
124 os.umask(prev_umask | desired_umask)
125 yield
126 finally:
127 os.umask(prev_umask)
128
129class BooleanAction(argparse.Action):
130 def __init__(self, option_strings, dest, nargs=None, **kwargs):
131 super(BooleanAction, self).__init__(option_strings, dest, nargs=0, **kwargs)
132
133 def __call__(self, parser, namespace, values, option_string=None):
134 setattr(namespace, self.dest, False if option_string.startswith('--no') else True)
135
136
137def load_key(keyfile, prompt='CA private key password: '):
138 key = None
139 with open(keyfile, 'rb') as f:
140 is_sops = False
141 try:
142 sops_json = json.load(f)
143 is_sops = 'sops' in sops_json
144 except json.JSONDecodeError:
145 pass
146
147 f.seek(0)
148
149 if not is_sops:
150 try:
151 key = serialization.load_pem_private_key(f.read(), password=None)
152 except TypeError:
153 pw = getpass(prompt=prompt)
154 key = serialization.load_pem_private_key(f.read(), password=bytes(pw, sys.stdin.encoding))
155 else:
156 cmd = ['sops', '-d', f'/dev/fd/{f.fileno()}']
157 with subprocess.Popen(cmd, stdout=subprocess.PIPE, pass_fds=(f.fileno(),)) as proc:
158 key = serialization.load_pem_private_key(proc.stdout.read(), password=None)
159 ret = proc.wait()
160 if ret != 0:
161 raise subprocess.CalledProcessErrror(ret, cmd)
162
163 return key
164
165def mv_bak(path):
166 global logger
167
168 bak_path = path.parent / f'{path.name}.bak'
169 for n in count(2):
170 if not bak_path.exists():
171 break
172 bak_path = path.parent / f'{path.name}.bak{n}'
173
174 logger.warn('Renaming ‘%s’ to ‘%s’...', path, bak_path)
175 path.rename(bak_path)
176
177def tai64nint(dt):
178 global leapsecond_data
179
180 have_data = False
181 try:
182 have_data = bool(leapsecond_data)
183 except NameError:
184 pass
185
186 if not have_data:
187 leapsecond_data = LeapSecondData.from_file(Path(os.getenv('LEAPSECONDS_FILE')))
188
189 tai_dt = leapsecond_data.to_tai(dt)
190 seconds = int(tai_dt.timestamp())
191 nanoseconds = int((tai_dt.timestamp() - seconds) / 1e-9)
192 seconds += int(ldexp(1, 62))
193 return seconds << 32 | nanoseconds
194
195def write_genkey(key_type, sops, keyfile):
196 if keyfile.exists():
197 raise ValueError(f'Keyfile exists: {keyfile}')
198
199 key = None
200
201 def genkey(fh):
202 nonlocal key, key_type
203
204 logger.debug('Generating new privkey...')
205 key = key_type.generate()
206 priv_bytes = key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption())
207 fh.write(priv_bytes)
208
209 if not sops:
210 with umask(0o0177), atomic_write(keyfile, overwrite=False, mode='wb') as fh:
211 logger.info('Writing new privkey to ‘%s’...', keyfile)
212 genkey(fh)
213 logger.debug('Adjusting permissions for ‘%s’...', keyfile)
214 os.chmod(keyfile, 0o0400)
215 else:
216 with TemporaryFile(mode='wb') as tf:
217 genkey(tf)
218 tf.seek(0)
219
220 with umask(0o0177), atomic_write(keyfile, overwrite=False, mode='wb') as fh:
221 logger.info('Encrypting new privkey to ‘%s’...', keyfile)
222 subprocess.run(['sops', '-e', f'/dev/fd/{tf.fileno()}'], stdout=fh, pass_fds=(tf.fileno(),), check=True)
223 logger.debug('Adjusting permissions for ‘%s’...', keyfile)
224 os.chmod(keyfile, 0o0400)
225
226 return key
227
228def initca(ca_cert, ca_key, key_type, subject, clock_skew, validity, sops):
229 global logger
230
231 key = None
232 try:
233 key = load_key(ca_key)
234 logger.info('Successfully loaded privkey from ‘%s’', ca_key)
235
236 if not key_type.aligned(key):
237 logger.warn('Private key ‘%s’ does not align with requested type %s', ca_key, key_type)
238
239 try:
240 mv_bak(ca_key)
241 except FileNotFoundError:
242 pass
243 try:
244 mv_bak(ca_cert)
245 except FileNotFoundError:
246 pass
247
248 raise FileNotFoundError(f'Key does not align with requested type: {ca_key}')
249 except FileNotFoundError:
250 key = write_genkey(key_type, sops, ca_key)
251
252 cert = None
253 try:
254 with open(ca_cert, 'rb') as fh:
255 cert = x509.load_pem_x509_certificate(fh.read())
256 logger.info('Successfully loaded certificate from ‘%s’', ca_cert)
257 except FileNotFoundError:
258 logger.debug('Generating new certificate...')
259
260 now = datetime.utcnow()
261 name = x509.Name([
262 x509.NameAttribute(NameOID.COMMON_NAME, subject.relative)
263 ])
264
265 cert = x509.CertificateBuilder().subject_name(
266 name
267 ).public_key(
268 key.public_key()
269 ).serial_number(
270 x509.random_serial_number()
271 ).not_valid_before(
272 now - clock_skew
273 ).not_valid_after(
274 now + validity
275 ).issuer_name(
276 name
277 ).add_extension(
278 x509.AuthorityKeyIdentifier.from_issuer_public_key(key.public_key()),
279 False
280 ).add_extension(
281 x509.SubjectKeyIdentifier.from_public_key(key.public_key()),
282 False
283 ).add_extension(
284 x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False),
285 True
286 ).add_extension(
287 x509.BasicConstraints(ca=True, path_length=None),
288 True
289 ).sign(key, None if isinstance(key, Ed25519PrivateKey) or isinstance(key, Ed448PrivateKey) else hashes.SHA512())
290
291 with umask(0o0133), atomic_write(ca_cert, overwrite=False, mode='wb') as cf:
292 logger.info('Writing new certificate to ‘%s’...', ca_cert)
293 cf.write(cert.public_bytes(serialization.Encoding.PEM))
294 logger.debug('Adjusting permissions for ‘%s’...', ca_cert)
295 os.chmod(ca_cert, 0o0444)
296
297def signcsr(ca_cert, ca_key, clock_skew, validity, subject, alternative_name, ignore_alternative_names, csr, output):
298 csr_bytes = None
299 try:
300 csr_bytes = csr.read()
301 except AttributeError:
302 csr_bytes = csr
303
304 csr = x509.load_pem_x509_csr(csr_bytes)
305 if not subject:
306 common_name_attrs = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
307 if len(common_name_attrs) != 1:
308 raise InvalidParamsError('Invalid name structure in CSR')
309 subject = common_name_attrs[0].value.lower()
310 logger.warn('Using subject common name from csr: %s', subject)
311 name = x509.Name([
312 x509.NameAttribute(NameOID.COMMON_NAME, subject)
313 ])
314
315 if not ignore_alternative_names:
316 ext = csr.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
317 csr_alt_names = ext.value.get_values_for_type(x509.DNSName)
318 logger.warn('Using alternative names from csr: %s', csr_alt_names)
319 alternative_name = list(set(alternative_name) | set(csr_alt_names))
320
321 ca_key = load_key(ca_key)
322 with open(ca_cert, 'rb') as fh:
323 ca_cert = x509.load_pem_x509_certificate(fh.read())
324
325 now = datetime.now(tz=timezone.utc)
326 cert = x509.CertificateBuilder().subject_name(
327 name
328 ).public_key(
329 csr.public_key()
330 ).serial_number(
331 (tai64nint(now) << 24) | (x509.random_serial_number() & int(ldexp(1, 24) - 1))
332 ).not_valid_before(
333 now - clock_skew
334 ).not_valid_after(
335 now + validity
336 ).issuer_name(
337 ca_cert.subject
338 ).add_extension(
339 x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_cert.public_key()),
340 False
341 ).add_extension(
342 x509.SubjectKeyIdentifier.from_public_key(csr.public_key()),
343 False
344 ).add_extension(
345 x509.KeyUsage(digital_signature=True, content_commitment=True, key_encipherment=True, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False),
346 True
347 ).add_extension(
348 x509.BasicConstraints(ca=False, path_length=None),
349 True
350 ).add_extension(
351 x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]),
352 False
353 )
354
355 if alternative_name:
356 cert = cert.add_extension(
357 x509.SubjectAlternativeName(
358 list(map(x509.DNSName, alternative_name))
359 ),
360 False
361 )
362
363 cert = cert.sign(ca_key, None if isinstance(ca_key, Ed25519PrivateKey) or isinstance(ca_key, Ed448PrivateKey) else hashes.SHA256())
364
365 output = output.with_suffix('.crt')
366
367 try:
368 mv_bak(output)
369 except FileNotFoundError:
370 pass
371 with umask(0o0133), atomic_write(output, overwrite=False, mode='wb') as cf:
372 logger.info('Writing new certificate to ‘%s’...', output)
373 cf.write(cert.public_bytes(serialization.Encoding.PEM))
374 logger.debug('Adjusting permissions for ‘%s’...', output)
375 os.chmod(output, 0o0444)
376
377def new_client(ca_cert, ca_key, key_type, clock_skew, validity, subject, alternative_name, sops, output):
378 key_file = output.with_suffix('.key')
379 cert_file = output.with_suffix('.crt')
380
381 key = None
382 try:
383 key = load_key(key_file)
384 logger.info('Successfully loaded privkey from ‘%s’', key_file)
385
386 if not key_type.aligned(key):
387 logger.warn('Private key ‘%s’ does not align with requested type %s', key_file, key_type)
388
389 try:
390 mv_bak(key_file)
391 except FileNotFoundError:
392 pass
393 try:
394 mv_bak(cert_file)
395 except FileNotFoundError:
396 pass
397
398 raise FileNotFoundError(f'Key does not align with requested type: {key_file}')
399 except FileNotFoundError:
400 key = write_genkey(key_type, sops, key_file)
401
402 csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
403 x509.NameAttribute(NameOID.COMMON_NAME, subject)
404 ]))
405 if alternative_name:
406 csr = csr.add_extension(
407 x509.SubjectAlternativeName(
408 list(map(x509.DNSName, alternative_name))
409 ),
410 False
411 )
412
413 return signcsr(
414 ca_cert=ca_cert,
415 ca_key=ca_key,
416 clock_skew=clock_skew,
417 validity=validity,
418 subject=None,
419 alternative_name=[],
420 ignore_alternative_names=False,
421 output=cert_file,
422 csr=csr.sign(
423 key,
424 None if isinstance(key, Ed25519PrivateKey) or isinstance(key, Ed448PrivateKey) else hashes.SHA256(),
425 ).public_bytes(serialization.Encoding.PEM)
426 )
427
428def to_pkcs12(random_password, filename, output):
429 key_file = filename.with_suffix('.key')
430 cert_file = filename.with_suffix('.crt')
431
432 if not output:
433 output = filename.with_suffix('.p12')
434
435 key = load_key(key_file)
436 logger.info('Successfully loaded privkey from ‘%s’', key_file)
437 cert = None
438 with open(cert_file, mode='rb') as fh:
439 cert = x509.load_pem_x509_certificate(fh.read())
440 logger.info('Successfully loaded certificate from ‘%s’', cert_file)
441
442 with umask(0o0177), atomic_write(output, overwrite=False, mode='wb') as fh:
443 logger.info('Writing to ‘%s’...', output)
444 common_name_attrs = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
445 if len(common_name_attrs) != 1:
446 raise InvalidParamsError('Invalid name structure in cert')
447 subject = common_name_attrs[0].value.lower()
448
449 pw = None
450 if not random_password:
451 pw2 = None
452 while not pw2 or pw2 != pw:
453 pw = getpass(prompt='Password: ')
454 if not pw:
455 pw = None
456 break
457 else:
458 pw2 = getpass(prompt='Repeat password: ')
459 else:
460 from xkcdpass import xkcd_password as xp
461 ws = xp.generate_wordlist(wordfile=xp.locate_wordfile())
462 pw = xp.generate_xkcdpassword(ws, numwords=12)
463 print(f'Password: {pw}', file=sys.stderr)
464
465 encryption = None
466 if pw:
467 encryption = PrivateFormat.PKCS12.encryption_builder().kdf_rounds(
468 500000
469 ).key_cert_algorithm(
470 pkcs12.PBES.PBESv2SHA256AndAES256CBC
471 ).hmac_hash(
472 hashes.SHA256()
473 ).build(bytes(pw, 'utf-8'))
474 fh.write(pkcs12.serialize_key_and_certificates(
475 bytes(subject, 'utf-8'),
476 key,
477 cert,
478 None,
479 encryption,
480 ))
481 logger.debug('Adjusting permissions for ‘%s’...', output)
482 os.chmod(output, 0o0400)
483
484
485def main():
486 global logger
487 logger = logging.getLogger(__name__)
488 console_handler = logging.StreamHandler()
489 console_handler.setFormatter( logging.Formatter('[%(levelname)s](%(name)s): %(message)s') )
490 if sys.stderr.isatty():
491 console_handler.setFormatter( logging.Formatter('%(asctime)s [%(levelname)s](%(name)s): %(message)s') )
492 logger.addHandler(console_handler)
493
494 # log uncaught exceptions
495 def log_exceptions(type, value, tb):
496 global logger
497
498 logger.error(value)
499 sys.__excepthook__(type, value, tb) # calls default excepthook
500
501 sys.excepthook = log_exceptions
502
503
504 parser = argparse.ArgumentParser(prog='ca', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
505 parser.add_argument('--verbosity', dest='log_level', action='append', type=int)
506 parser.add_argument('--verbose', '-v', dest='log_level', action='append_const', const=1)
507 parser.add_argument('--quiet', '-q', dest='log_level', action='append_const', const=-1)
508 subparsers = parser.add_subparsers(help='Subcommands', required=True)
509
510 subparser = subparsers.add_parser('init', aliases=['initca', 'init-ca', 'ca'], formatter_class=argparse.ArgumentDefaultsHelpFormatter)
511 subparser.add_argument('--ca-cert', type=Path, default=Path('ca.crt'))
512 subparser.add_argument('--ca-key', type=Path, default=Path('ca.key'))
513 subparser.add_argument('--key-type', type=KeyType.from_string, choices=list(KeyType), default=KeyType.ED448.value)
514 subparser.add_argument('--clock-skew', metavar='DURATION', type=duration, default=timedelta(minutes=5))
515 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10)))
516 subparser.add_argument('--sops', '--no-sops', action=BooleanAction, default=True)
517 subparser.add_argument('--subject', metavar='FQDN', type=ValidFQDN, required=True)
518 subparser.set_defaults(cmd=initca)
519
520 subparser = subparsers.add_parser('sign', aliases=['signcsr', 'sign-csr'], formatter_class=argparse.ArgumentDefaultsHelpFormatter)
521 subparser.add_argument('--ca-cert', type=Path, default=Path('ca.crt'))
522 subparser.add_argument('--ca-key', type=Path, default=Path('ca.key'))
523 subparser.add_argument('--clock-skew', metavar='DURATION', type=duration, default=timedelta(minutes=5))
524 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10)))
525 subparser.add_argument('--subject', metavar='CN', type=str, required=False)
526 subparser.add_argument('--ignore-alternative-names', '--no-ignore-alternative-names', action=BooleanAction, default=True)
527 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append')
528 subparser.add_argument('--output', type=Path, required=True)
529 subparser.add_argument('csr', metavar='FILE', type=argparse.FileType(mode='rb'))
530 subparser.set_defaults(cmd=signcsr)
531
532 subparser = subparsers.add_parser('new-client', aliases=['new', 'new-client', 'client'], formatter_class=argparse.ArgumentDefaultsHelpFormatter)
533 subparser.add_argument('--ca-cert', type=Path, default=Path('ca.crt'))
534 subparser.add_argument('--ca-key', type=Path, default=Path('ca.key'))
535 subparser.add_argument('--key-type', type=KeyType.from_string, choices=list(KeyType), default=KeyType.ED25519.value)
536 subparser.add_argument('--clock-skew', metavar='DURATION', type=duration, default=timedelta(minutes=5))
537 subparser.add_argument('--validity', metavar='DURATION', type=duration, default=timedelta(days=ceil(365.2425*10)))
538 subparser.add_argument('--sops', '--no-sops', action=BooleanAction, default=True)
539 subparser.add_argument('--subject', metavar='CN', type=str, required=True)
540 subparser.add_argument('--alternative-name', metavar='CN', type=str, action='append')
541 subparser.add_argument('--output', type=Path, required=True)
542 subparser.set_defaults(cmd=new_client)
543
544 subparser = subparsers.add_parser('pkcs12', aliases=['p12', 'pfx'], formatter_class=argparse.ArgumentDefaultsHelpFormatter)
545 subparser.add_argument('--random-password', '--no-random-password', action=BooleanAction, default=True)
546 subparser.add_argument('--output', type=Path)
547 subparser.add_argument('filename', metavar='BASENAME', type=Path)
548 subparser.set_defaults(cmd=to_pkcs12)
549
550 args = parser.parse_args()
551
552
553 LOG_LEVELS = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL]
554 DEFAULT_LOG_LEVEL = logging.INFO
555 log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)
556
557 for adjustment in args.log_level or ():
558 log_level = min(len(LOG_LEVELS) - 1, max(log_level - adjustment, 0))
559 logger.setLevel(LOG_LEVELS[log_level])
560
561
562 logger.debug('Using cryptography %s (%s)', cryptography_version, openssl.backend.openssl_version_text())
563
564
565 args.cmd(**{ k: v for k, v in vars(args).items() if k in signature(args.cmd).parameters.keys() })
566
567if __name__ == '__main__':
568 sys.exit(main())
diff --git a/tools/ca/setup.py b/tools/ca/setup.py
new file mode 100644
index 00000000..3342a7a6
--- /dev/null
+++ b/tools/ca/setup.py
@@ -0,0 +1,10 @@
1from setuptools import setup
2
3setup(name='ca',
4 packages=['ca'],
5 entry_points={
6 'console_scripts': [
7 'ca=ca.__main__:main'
8 ],
9 },
10)