1# Copyright (c) 2023 soopyc
2# Permission is given to copy and use under the terms of Apache 2.0.
3#
4# you may copy-paste this entire file to anywhere else. just keep the comments.
5# see /docs/src/utils/ for a usage guide
6{
7 inputs,
8 system,
9 ...
10}:
11let
12 pkgs = inputs.nixpkgs.legacyPackages.${system};
13 lib = pkgs.lib;
14in
15rec {
16 mkVhost =
17 opts:
18 lib.mkMerge [
19 {
20 forceSSL = lib.mkDefault true;
21 useACMEHost = lib.mkDefault "global.c.soopy.moe";
22 kTLS = lib.mkDefault true;
23 quic = lib.mkDefault true;
24
25 locations."/_cgi/error/" = {
26 alias = "${inputs.mystia.packages.${system}.staticly}/nginx_error_pages/";
27 };
28
29 # To override, mkForce {}
30 locations."= /robots.txt" = mkNginxFile {
31 filename = "robots.txt";
32 content = ''
33 # Please stop hammering and/or scraping our services.
34 User-Agent: *
35 Disallow: /
36 '';
37 };
38
39 extraConfig = ''
40 error_page 503 /_cgi/error/503.html;
41 error_page 502 /_cgi/error/502.html;
42 error_page 404 /_cgi/error/404.html;
43 add_header strict-transport-security "max-age=63072000; includeSubDomains; preload" always;
44 add_header alt-svc 'h3=":443";ma=86400' always;
45 '';
46 }
47 opts
48 ];
49
50 mkSimpleProxy =
51 {
52 protocol ? "http",
53 host ? "localhost",
54 port ? null,
55 socketPath ? null,
56 location ? "/",
57 websockets ? false,
58 extraConfig ? { },
59 }:
60 assert lib.assertMsg (
61 port != null || socketPath != null
62 ) "one of port or socketPath must be specified";
63 # i dislike logic gates
64 assert lib.assertMsg (
65 !(port != null && socketPath != null)
66 ) "only one of port or socketPath may be specified at the same time";
67 assert lib.assertMsg (
68 socketPath != null -> host == "localhost"
69 ) "setting host has no effect when socketPath is set";
70 assert lib.assertMsg (port != null -> builtins.isInt port) "port must be an integer if specified";
71 mkVhost (
72 lib.mkMerge [
73 extraConfig
74 {
75 locations."${location}" = {
76 proxyPass =
77 "${protocol}://"
78 + (if (socketPath == null) then "${host}:${builtins.toString port}" else "unix:${socketPath}");
79 proxyWebsockets = websockets;
80 };
81 }
82 ]
83 );
84
85 setupSecrets =
86 _config:
87 {
88 namespace ? (
89 lib.warn "secret namespace left as default, which is empty. it is encouraged to set a namespace for easier secret management. to override, explicitly set this to an empty value." ""
90 ),
91 secrets,
92 config ? { },
93 }:
94 let
95 _r_ns = namespace + lib.optionalString (lib.stringLength namespace != 0) "/";
96 check =
97 path:
98 assert lib.assertMsg (lib.elem path secrets)
99 "secret path `${path}` is not defined in namespace `${namespace}`. (resolved to: ${_r_ns}${path})";
100 path;
101 getRealPath = path: _r_ns + check path;
102 in
103 builtins.addErrorContext "while setting up secrets with namespace ${namespace}" {
104 generate = {
105 sops.secrets = genSecrets namespace secrets config;
106 }; # i love trolling
107 get = path: _config.sops.secrets.${getRealPath path}.path;
108
109 placeholder = path: _config.sops.placeholder.${getRealPath path};
110 getTemplate = file: _config.sops.templates.${file}.path;
111 mkTemplate =
112 file: content:
113 builtins.addErrorContext "while generating sops template ${file}" {
114 sops.templates.${file} = {
115 inherit content;
116 }
117 // (builtins.removeAttrs config [ "content" ]);
118 # // (lib.optionalAttrs (builtins.hasAttr "owner" config) {inherit (config) owner;})
119 # // (lib.optionalAttrs (builtins.hasAttr "group" config) {inherit (config) group;});
120 };
121 };
122
123 genSecrets =
124 namespace: files: value:
125 lib.genAttrs (map (
126 x: namespace + lib.optionalString (lib.stringLength namespace != 0) "/" + x
127 ) files) (_: value);
128
129 mkNginxFile =
130 {
131 filename ? "index.html",
132 content,
133 }:
134 builtins.addErrorContext "while creating a static nginx file ${filename}" (
135 let
136 contentDir =
137 assert lib.assertMsg (
138 builtins.typeOf content == "string"
139 ) "content must be a string, got `${builtins.typeOf content}`";
140 builtins.toString (pkgs.writeTextDir filename content) + "/";
141 in
142 {
143 alias = contentDir;
144 tryFiles = "${filename} =500"; # if it can't find the file something has gone wrong.
145 }
146 );
147
148 mkNginxJSON =
149 filename: attrset:
150 builtins.addErrorContext "while creating a static nginx JSON file ${filename}" (
151 assert lib.assertMsg (
152 builtins.typeOf attrset == "set"
153 ) "expected argument type `set`, got `${builtins.typeOf attrset}` instead.";
154 mkNginxFile {
155 inherit filename;
156 content = builtins.toJSON attrset;
157 }
158 );
159}