Move all flakes into modules/ and sep't, create deploy script for a1d2
Change-Id: Ie4d50fb8f16da193195beb139922a366b72b0b0a
diff --git a/modules/common/nginx.nix b/modules/common/nginx.nix
new file mode 100644
index 0000000..6bd95e3
--- /dev/null
+++ b/modules/common/nginx.nix
@@ -0,0 +1,199 @@
+{ config, lib, pkgs, helpers, base, ... }:
+lib.recursiveUpdate {
+ options.clicks = {
+ nginx = {
+ services = lib.mkOption {
+ type = with lib.types;
+ listOf (submodule {
+ options = {
+ host = lib.mkOption { type = str; };
+ extraHosts = lib.mkOption { type = listOf str; };
+ secure = lib.mkOption { type = bool; };
+ service = lib.mkOption {
+ type = let
+ validServiceTypes = {
+ "redirect" = {
+ to = [ "string" str ];
+ permanent = [ "bool" bool ];
+ };
+ "reverseproxy" = { to = [ "string" str ]; };
+ "php" = {
+ root = [ "string" str ];
+ socket = [ "string" str ];
+ };
+ "directory" = {
+ private = [ "bool" bool ];
+ root = [ "string" str ];
+ };
+ "file" = { path = [ "string" str ]; };
+ "path" = {
+ path = [ "string" str ];
+ service = [ "set" serviceType ];
+ };
+ "compose" = { services = [ "list" (listOf serviceType) ]; };
+ "status" = { statusCode = [ "int" int ]; };
+ };
+
+ serviceType = mkOptionType {
+ name = "Service";
+
+ description = "clicks Nginx service";
+ descriptionClass = "noun";
+
+ check = (x:
+ if (builtins.typeOf x) != "set" then
+ lib.warn
+ "clicks nginx services must be sets but ${x} is not a set"
+ false
+ else if !(builtins.hasAttr "type" x) then
+ lib.warn
+ "clicks nginx services must have a type attribute but ${x} does not"
+ false
+ else if !(builtins.hasAttr x.type validServiceTypes) then
+ lib.warn
+ "clicks nginx services must have a valid type, but ${x.type} is not one"
+ false
+ else
+ (let
+ optionTypes =
+ (builtins.mapAttrs (n: o: builtins.elemAt o 0)
+ validServiceTypes.${x.type}) // {
+ type = "string";
+ };
+ in (lib.pipe x [
+ (builtins.mapAttrs (n: o:
+ (builtins.hasAttr n optionTypes) && optionTypes.${n}
+ == (builtins.typeOf o)))
+ lib.attrValues
+ (builtins.all (x: x))
+ ]) && (lib.pipe optionTypes [
+ (builtins.mapAttrs (n: _: builtins.hasAttr n x))
+ lib.attrValues
+ (builtins.all (x: x))
+ ])));
+ };
+ in serviceType;
+ };
+ type = lib.mkOption { type = strMatching "hosts"; };
+ };
+ });
+ example = lib.literalExpression ''
+ with helpers.nginx; [
+ (Host "example.clicks.codes" (ReverseProxy "generic:1001"))
+ ]'';
+ description = lib.mdDoc ''
+ Connects hostnames to services for your nginx server. We recommend using the Clicks helper to generate these
+ '';
+ default = [ ];
+ };
+ serviceAliases = lib.mkOption {
+ type = with lib.types;
+ listOf (submodule {
+ options = {
+ host = lib.mkOption {
+ type = str;
+ example = "example.clicks.codes";
+ description = ''
+ The ServerName of the server. If you override this in the nginx server block, you still need to put in the name of the attribute
+ '';
+ };
+ aliases = lib.mkOption {
+ type = listOf str;
+ example = [ "example2.clicks.codes" "example.coded.codes" ];
+ description = ''
+ A list of servers to add as aliases
+ '';
+ };
+ type = lib.mkOption { type = strMatching "aliases"; };
+ };
+ });
+ example = lib.literalExpression ''
+ with helpers.nginx; [
+ (Host "example.clicks.codes" (ReverseProxy "generic:1001"))
+ ]'';
+ description = lib.mdDoc ''
+ Adds additional host names to your nginx server. If you're using `clicks.nginx.services`
+ you should generally use a Hosts block instead
+ '';
+ default = [ ];
+ };
+ streams = lib.mkOption {
+ type = with lib.types;
+ listOf (submodule {
+ options = {
+ internal = lib.mkOption { type = str; };
+ external = lib.mkOption { type = port; };
+ protocol = lib.mkOption { type = strMatching "^(tcp|udp)$"; };
+ haproxy = lib.mkOption { type = bool; };
+ };
+ });
+ example = lib.literalExpression ''
+ with helpers.nginx; [
+ (Stream 1001 "generic:1002" "tcp")
+ ]'';
+ description = lib.mdDoc ''
+ A list of servers to be placed in the nginx streams block. We recommend using the Clicks helper to generate these
+ '';
+ default = [ ];
+ };
+ };
+ };
+ config = {
+ services.nginx = {
+ enable = true;
+ enableReload = true;
+
+ virtualHosts = lib.recursiveUpdate (helpers.nginx.Merge
+ config.clicks.nginx.services) # clicks.nginx.services
+ (lib.pipe config.clicks.nginx.serviceAliases [
+ (map (alias: {
+ name = alias.host;
+ value.serverAliases = alias.aliases;
+ }))
+ builtins.listToAttrs
+ ]); # clicks.nginx.serviceAliases
+
+ streamConfig = builtins.concatStringsSep "\n" (map (stream: ''
+ server {
+ listen ${builtins.toString stream.external}${
+ lib.optionalString (stream.protocol == "udp") " udp"
+ };
+ proxy_pass ${stream.internal};
+ ${if stream.haproxy then "proxy_protocol on;" else ""}
+ }
+ '') config.clicks.nginx.streams);
+ };
+
+ networking.firewall.allowedTCPPorts = lib.pipe config.clicks.nginx.streams [
+ (builtins.filter (stream: stream.protocol == "tcp"))
+ (map (stream: stream.external))
+ ];
+ networking.firewall.allowedUDPPorts = lib.pipe config.clicks.nginx.streams [
+ (builtins.filter (stream: stream.protocol == "udp"))
+ (map (stream: stream.external))
+ ];
+
+ security.acme.defaults = {
+ email = "admin@clicks.codes";
+ credentialsFile = config.sops.secrets.cloudflare_cert__api_token.path;
+ };
+ security.acme.acceptTerms = true;
+
+ sops.secrets.cloudflare_cert__api_token = {
+ mode = "0660";
+ owner = config.users.users.nginx.name;
+ group = config.users.users.acme.group;
+ sopsFile = ../../secrets/cloudflare-cert.env.bin;
+ format = "binary";
+ };
+
+ users.users.nginx.extraGroups = [ config.users.users.acme.group ];
+ };
+} (if base != null then {
+ config.security.acme.certs = lib.mkForce (builtins.mapAttrs (_: v:
+ (lib.filterAttrs (n: _: n != "directory") v) // {
+ webroot = null;
+ dnsProvider = "cloudflare";
+ }) base.config.security.acme.certs);
+} else
+ { })