diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2022-11-07 20:51:39 +0100 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2022-11-07 20:51:39 +0100 |
commit | 0e9f1e85cd8c6f9d546ef88e971043b909017170 (patch) | |
tree | 5cb4d14df7594ef123f20d82cb2ec423b6bca744 | |
parent | f563ddece04adfd8d80d4e984405f5c70a6c94f3 (diff) | |
download | nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.gz nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.bz2 nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.tar.xz nixos-0e9f1e85cd8c6f9d546ef88e971043b909017170.zip |
...
33 files changed, 1144 insertions, 220 deletions
@@ -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", |
@@ -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----- |
2 | MIIBXzCCARGgAwIBAgIBATAFBgMrZXAwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55 | 2 | MIIB5TCCAWWgAwIBAgIPQAAAAGNpYE436fsCRvVfMAUGAytlcTAfMR0wGwYDVQQD |
3 | Z2dkcmFzaWwwIBcNMjIwNDA4MjAwMzU1WhgPMjA5MDA0MjYyMDAzNTVaMBoxGDAW | 3 | DBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAeFw0yMjExMDcxOTM5NDFaFw0zMjExMDcx |
4 | BgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd8I32X/z9J0cO2Oz+ | 4 | OTQ0NDFaMBoxGDAWBgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd |
5 | 4KAoIJq0igdMdbLBA+8WO+vgo3UwczAMBgNVHRMBAf8EAjAAMEQGA1UdEQQ9MDuC | 5 | 8I32X/z9J0cO2Oz+4KAoIJq0igdMdbLBA+8WO+vgo4G8MIG5MB8GA1UdIwQYMBaA |
6 | GnByb21ldGhldXMuc3VydHIueWdnZHJhc2lsgh1wcm9tZXRoZXVzLnN1cnRyLnln | 6 | FObrhCUDCZk6/JeeDMNWl8WeLr+MMB0GA1UdDgQWBBQ3na09y/kUWmnN4nHYCJeT |
7 | Z2RyYXNpbC5saTAdBgNVHQ4EFgQUN52tPcv5FFppzeJx2AiXk6UgPDgwBQYDK2Vw | 7 | pSA8ODAOBgNVHQ8BAf8EBAMCBeAwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggr |
8 | A0EAPN9zhaeBB2C1TursdARH0jVBz9g0dRhP7sO5ZG0K+xp24paLXiTF1rYub24p | 8 | BgEFBQcDAjBEBgNVHREEPTA7ghpwcm9tZXRoZXVzLnN1cnRyLnlnZ2RyYXNpbIId |
9 | /yZw71p7M0BAE+hJqYBzYo5YBQ== | 9 | cHJvbWV0aGV1cy5zdXJ0ci55Z2dkcmFzaWwubGkwBQYDK2VxA3MAYHd3I/Mg/t34 |
10 | zdcxrIKOAKJ9ZVVoP0msk/viKrZ4b+Q9rKSNEnkyk0y56Z7FlLDxGLScaemqQ3uA | ||
11 | 5hjhdTci/xd4xYX/edLw1AWGRs2kBe3vs2WOmrdcKa849vdMH27G/P/+bgbdofCN | ||
12 | fukxYHzpESYA | ||
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 | ||
3 | with lib; | 3 | with 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 | }; | ||
78 | in { | 44 | in { |
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 | |||
6 | self: 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 @@ | |||
1 | ca.key | ||
2 | ca.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----- |
2 | MIIBsjCCAWSgAwIBAgIUOzZ8XcFb8XtI2yyWp4S/WMD6QxQwBQYDK2VwMB8xHTAb | 2 | MIIBrjCCAS6gAwIBAgIUYV3YPBx91CbgMpOGb5HKMZ2hzRUwBQYDK2VxMB8xHTAb |
3 | BgNVBAMMFHByb21ldGhldXMueWdnZHJhc2lsMCAXDTIyMDQwODE5NDgwMFoYDzIw | 3 | BgNVBAMMFHByb21ldGhldXMueWdnZHJhc2lsMB4XDTIyMTEwNzE5MjgzNFoXDTMy |
4 | OTAwNDI2MTk0ODAwWjAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAq | 4 | MTEwNzE5MzMzNFowHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55Z2dkcmFzaWwwQzAF |
5 | MAUGAytlcAMhAOoxPLBH6pnCRtE7V5gejM92gg1vLNLHw3rFIXXchOJmo4GvMIGs | 5 | BgMrZXEDOgAVqcV3KGDhcbQt/UR3Yv6OuAGc+Kc8hrDHjAV8K9GTjahc/d49NK2v |
6 | MB0GA1UdDgQWBBRnwBkgZFnueEa7aV8aEAoMRzW4CTBaBgNVHSMEUzBRgBRnwBkg | 6 | FAz0uK8YidIaTVJZjzHhTgCjYzBhMB8GA1UdIwQYMBaAFObrhCUDCZk6/JeeDMNW |
7 | ZFnueEa7aV8aEAoMRzW4CaEjpCEwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55Z2dk | 7 | l8WeLr+MMB0GA1UdDgQWBBTm64QlAwmZOvyXngzDVpfFni6/jDAOBgNVHQ8BAf8E |
8 | cmFzaWyCFDs2fF3BW/F7SNsslqeEv1jA+kMUMA8GA1UdEwEB/wQFMAMBAf8wCwYD | 8 | BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwAFAqBlI7SpHaSE+0mMzx5x |
9 | VR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQEAwICBDAFBgMrZXADQQD9AC2OHtzW8QSC | 9 | 0M6T3iJtLxP36Qz5MHx3vvcbbx1eJhZWKewuyz+9LXaCkf8Jpd5AFoC+HhoikVSz |
10 | HU/4rGdRWRqr3pfclKXimSWaAXMPly2M1qehPI402lhQrIAVF+D1pi/EAGJfbbzF | 10 | 46yVzmTBt6TISc4bh+eiWcXEKFbxEbXkwqZd2m/oHI4Em4qnDKp96FcOfq6RQ8pR |
11 | aurykEMB | 11 | AwA= |
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 @@ | |||
1 | Certificate: | ||
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----- | ||
31 | MIIBXzCCARGgAwIBAgIBATAFBgMrZXAwHzEdMBsGA1UEAwwUcHJvbWV0aGV1cy55 | ||
32 | Z2dkcmFzaWwwIBcNMjIwNDA4MjAwMzU1WhgPMjA5MDA0MjYyMDAzNTVaMBoxGDAW | ||
33 | BgNVBAMMD3N1cnRyLnlnZ2RyYXNpbDAqMAUGAytlcAMhAAJd8I32X/z9J0cO2Oz+ | ||
34 | 4KAoIJq0igdMdbLBA+8WO+vgo3UwczAMBgNVHRMBAf8EAjAAMEQGA1UdEQQ9MDuC | ||
35 | GnByb21ldGhldXMuc3VydHIueWdnZHJhc2lsgh1wcm9tZXRoZXVzLnN1cnRyLnln | ||
36 | Z2RyYXNpbC5saTAdBgNVHQ4EFgQUN52tPcv5FFppzeJx2AiXk6UgPDgwBQYDK2Vw | ||
37 | A0EAPN9zhaeBB2C1TursdARH0jVBz9g0dRhP7sO5ZG0K+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 @@ | |||
1 | Certificate: | ||
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----- | ||
31 | MIIBQTCB9KADAgECAgECMAUGAytlcDAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnln | ||
32 | Z2RyYXNpbDAgFw0yMjA0MDgyMDA3MTNaGA8yMDkwMDQyNjIwMDcxM1owGzEZMBcG | ||
33 | A1UEAwwQdmlkaGFyLnlnZ2RyYXNpbDAqMAUGAytlcAMhABOEpgEHel6NK42D7nMd | ||
34 | xriarbk9QFHsLPNSfYGQ56yIo1cwVTAMBgNVHRMBAf8EAjAAMCYGA1UdEQQfMB2C | ||
35 | G3Byb21ldGhldXMudmlkaGFyLnlnZ2RyYXNpbDAdBgNVHQ4EFgQURKqOzKvJp9Gh | ||
36 | 0Pp/24ceCKpuTVkwBQYDK2VwA0EAR2WHF1CWd1YgrJ705G0ZbbckEa8Mw/P9dRnZ | ||
37 | dwZBeX+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 @@ | |||
1 | V 20900426200355Z 01 unknown /CN=surtr.yggdrasil | ||
2 | V 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 @@ | |||
1 | unique_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 @@ | |||
1 | 03 | ||
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----- |
2 | MIIBQTCB9KADAgECAgECMAUGAytlcDAfMR0wGwYDVQQDDBRwcm9tZXRoZXVzLnln | 2 | MIIByDCCAUigAwIBAgIPQAAAAGNpXrc6y389EXtIMAUGAytlcTAfMR0wGwYDVQQD |
3 | Z2RyYXNpbDAgFw0yMjA0MDgyMDA3MTNaGA8yMDkwMDQyNjIwMDcxM1owGzEZMBcG | 3 | DBRwcm9tZXRoZXVzLnlnZ2RyYXNpbDAeFw0yMjExMDcxOTMyNTRaFw0zMjExMDcx |
4 | A1UEAwwQdmlkaGFyLnlnZ2RyYXNpbDAqMAUGAytlcAMhABOEpgEHel6NK42D7nMd | 4 | OTM3NTRaMBsxGTAXBgNVBAMMEHZpZGhhci55Z2dkcmFzaWwwKjAFBgMrZXADIQAT |
5 | xriarbk9QFHsLPNSfYGQ56yIo1cwVTAMBgNVHRMBAf8EAjAAMCYGA1UdEQQfMB2C | 5 | hKYBB3pejSuNg+5zHca4mq25PUBR7CzzUn2BkOesiKOBnjCBmzAfBgNVHSMEGDAW |
6 | G3Byb21ldGhldXMudmlkaGFyLnlnZ2RyYXNpbDAdBgNVHQ4EFgQURKqOzKvJp9Gh | 6 | gBTm64QlAwmZOvyXngzDVpfFni6/jDAdBgNVHQ4EFgQURKqOzKvJp9Gh0Pp/24ce |
7 | 0Pp/24ceCKpuTVkwBQYDK2VwA0EAR2WHF1CWd1YgrJ705G0ZbbckEa8Mw/P9dRnZ | 7 | CKpuTVkwDgYDVR0PAQH/BAQDAgXgMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYI |
8 | dwZBeX+lAAwY7oI+nglhNM+P9YPRXbLkQrY/nLZa80CS5o8kDw== | 8 | KwYBBQUHAwIwJgYDVR0RBB8wHYIbcHJvbWV0aGV1cy52aWRoYXIueWdnZHJhc2ls |
9 | MAUGAytlcQNzAIPNcNWqVX4Ie971O/S2DL0HMFmPbR331U4snLBqPGWC1/j9NV4O | ||
10 | cxJvLo8Hzb4I0BXn/nZbyk/ogCCJU69BVeK378qgLo68DIZ4TA3ka5ZPNRSt464Q | ||
11 | NvbkDhtFVVxM04xUjI4dOeE9jczG9nN3jHESAA== | ||
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 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
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; | ||
67 | in { | ||
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 | |||
3 | import csv | 1 | import csv |
4 | import subprocess | 2 | import subprocess |
5 | import io | 3 | import 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 | |||
7 | let | ||
8 | plugins = python3.pkgs.callPackage ./plugins { }; | ||
9 | tools = callPackage ./tools { }; | ||
10 | in | ||
11 | with python3.pkgs; | ||
12 | buildPythonApplication 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 | |||
3 | buildPythonPackage 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 | |||
3 | buildPythonPackage 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 | |||
3 | buildPythonPackage 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 | |||
3 | buildPythonPackage 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 | |||
3 | rustPlatform.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 | |||
5 | with 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 | } | ||
@@ -1,8 +1,29 @@ | |||
1 | { pkgs ? import <nixpkgs> {}, deploy-rs, nvfetcher }: | 1 | { system, self, deploy-rs, nvfetcher, mach-nix, leapseconds, ... }: |
2 | let | 2 | let |
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 | }; | ||
6 | in pkgs.mkShell { | 27 | in 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 @@ | |||
1 | import sys, os | ||
2 | |||
3 | import logging | ||
4 | import argparse | ||
5 | |||
6 | from inspect import signature | ||
7 | |||
8 | from enum import Enum, auto | ||
9 | from contextlib import contextmanager | ||
10 | |||
11 | from cryptography import __version__ as cryptography_version | ||
12 | from cryptography.hazmat.backends import openssl | ||
13 | from cryptography import x509 | ||
14 | from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID, ExtensionOID | ||
15 | from cryptography.hazmat.primitives import serialization, hashes | ||
16 | from cryptography.hazmat.primitives.serialization import PrivateFormat, pkcs12 | ||
17 | from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey | ||
18 | from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey | ||
19 | from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey | ||
20 | from cryptography.hazmat.primitives.asymmetric import rsa | ||
21 | from pathlib import Path | ||
22 | from atomicwrites import atomic_write | ||
23 | from fqdn import FQDN | ||
24 | from datetime import datetime, timedelta, timezone | ||
25 | from math import ceil, ldexp | ||
26 | import re | ||
27 | from getpass import getpass | ||
28 | from itertools import count | ||
29 | from tempfile import TemporaryFile | ||
30 | import subprocess | ||
31 | import json | ||
32 | from leapseconddata import LeapSecondData | ||
33 | |||
34 | |||
35 | class 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 | |||
79 | class 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 | |||
86 | def 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 | ||
120 | def 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 | |||
129 | class 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 | |||
137 | def 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 | |||
165 | def 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 | |||
177 | def 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 | |||
195 | def 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 | |||
228 | def 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 | |||
297 | def 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 | |||
377 | def 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 | |||
428 | def 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 | |||
485 | def 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 | |||
567 | if __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 @@ | |||
1 | from setuptools import setup | ||
2 | |||
3 | setup(name='ca', | ||
4 | packages=['ca'], | ||
5 | entry_points={ | ||
6 | 'console_scripts': [ | ||
7 | 'ca=ca.__main__:main' | ||
8 | ], | ||
9 | }, | ||
10 | ) | ||