Add an nginx helper
- This will allow us to easily make nginx servers without having to worry about
the normal nginx boilerplate/syntax
Change-Id: I87225864ea8983ef9742bec9c624471794be54d2
diff --git a/README.md b/README.md
index bbc48b3..285106e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
# Clicks Nix Helpers
+---
+
We write nix to manage our infrastructure, but sometimes it's too boilerplatey for us to want to write. This repository contains all the things that have made our jobs easier, we hope they help you too!
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..404acb2
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,3 @@
+inputs: {
+ nginx = import ./nginx.nix inputs;
+}
diff --git a/nginx.nix b/nginx.nix
new file mode 100644
index 0000000..1fce41b
--- /dev/null
+++ b/nginx.nix
@@ -0,0 +1,179 @@
+{ pkgs, ... }: let
+ lib = pkgs.lib;
+in {
+ Host = host: service: {
+ inherit host service;
+ extraHosts = [];
+ secure = true;
+ type = "hosts";
+ };
+ Hosts = hosts: service: {
+ inherit service;
+ host = elemAt hosts 0;
+ extraHosts = tail hosts;
+ secure = true;
+ type = "hosts";
+ };
+ InsecureHost = host: service: {
+ inherit host service;
+ extraHosts = [];
+ secure = false;
+ type = "hosts";
+ };
+ InsecureHosts = hosts: service: {
+ inherit service;
+ host = elemAt hosts 0;
+ extraHosts = tail hosts;
+ secure = false;
+ type = "hosts";
+ };
+ ReverseProxy = to: {
+ inherit to;
+ type = "reverseproxy";
+ };
+ PHP = root: socket: {
+ inherit root socket;
+ type = "php";
+ };
+ Redirect = to: permanent: {
+ inherit to permanent;
+ type = "redirect";
+ };
+ Directory = root: {
+ inherit root;
+ private = false;
+ type = "directory";
+ };
+ PrivateDirectory = root: {
+ inherit root;
+ private = true;
+ type = "directory";
+ };
+ File = path: {
+ inherit path;
+ type = "file";
+ };
+ Compose = services: {
+ inherit services;
+ type = "compose";
+ };
+ Path = path: service: {
+ inherit path service;
+ type = "path";
+ };
+
+ Merge = let
+ # builtins.length and count up
+ _iterateCompose = services: currentConfig: currentPath: secure: priority: i:
+ if i > length services
+ then currentConfig
+ else _iterateCompose services (_merge (elemAt services i) currentConfig currentPath secure priority+i) currentPath secure priority (i+1);
+
+ _iterateMerge = i: current: services:
+ if i > length services
+ then current
+ else _iterateMerge (i+1) (current++[_merge (elemAt services i) {} "/" true 1000]) services;
+
+ _merge = service: currentConfig: currentPath: secure: priority:
+ if service.type == "hosts"
+ then _merge service.service (lib.mkMerge currentConfig {
+ name = service.host;
+ value = {
+ serverAliases = service.extraHosts;
+
+ enableACME = service.secure;
+ forceSSL = service.secure;
+ listen = [
+ {
+ addr = "0.0.0.0";
+ port = if service.secure then 443 else 80;
+ ssl = service.secure;
+ }
+ ];
+ };
+ }) currentPath service.secure priority
+ else if service.type == "reverseproxy"
+ then (lib.mkMerge currentConfig {
+ value.locations.${currentPath} = {
+ proxyPass = service.to;
+ proxyWebsockets = true;
+ recommendedProxySettings = true;
+ };
+ })
+ else if service.type == "php"
+ then (lib.mkMerge currentConfig {
+ value.locations.${currentPath} = {
+ root = service.root;
+ index = "index.php index.html index.htm";
+ tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
+ };
+ value.locations."~ ^${currentPath}.*\.php$" = {
+ tryFiles = "$uri $uri/ ${currentPath}index.php?$query_string =404";
+ extraConfig = ''
+ include ${pkgs.nginx}/conf/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param REDIRECT_STATUS 200;
+ fastcgi_pass unix:${service.socket};
+ fastcgi_intercept_errors on;
+ ${lib.optionalString secure "fastcgi_param HTTPS on;"}
+ '';
+ };
+ })
+ else if service.type == "redirect"
+ then (lib.mkMerge currentConfig {
+ value.locations.${currentPath}.return =
+ if service.permanent
+ then "308 ${service.to}"
+ else "307 ${service.to}";
+ })
+ else if service.type == "directory"
+ then (lib.mkMerge currentConfig {
+ value.locations.${currentPath} = {
+ root = service.root;
+ index = "index.html index.htm";
+ tryFiles = "$uri $uri/ =404";
+ extraConfig = lib.optionalString !service.private "autoindex on;";
+ };
+ })
+ else if service.type == "file"
+ then (lib.mkMerge currentConfig {
+ value.locations.${currentPath} = {
+ tryFiles = "${service.path} =404";
+ };
+ })
+ else if service.type == "path"
+ then _merge service.service currentConfig service.path service.secure
+ else if service.type == "compose"
+ then (_iterateCompose service.services currentConfig currentPath secure priority 0)
+ else throw "Unknown service type: ${service.type}";
+ in (services: lib.pipe services [
+ (_iterateMerge 0 [])
+ builtins.listToAttrs
+ ]);
+
+ # https://www.nginx.com/resources/wiki/start/topics/examples/full/
+
+ /**
+ Internal needs to be a string that is both a host and a port, e.g. generic:1000
+ External should only be a port
+ Protocol should be TCP or UDP
+ */
+ Stream = external: internal: protocol: ''
+ server {
+ listen ${builtins.toString external} ${protocol};
+ proxy_pass ${internal};
+ }
+ '';
+
+ Alias = host: alias: {
+ inherit host;
+ aliases = [ alias ];
+ type = "aliases";
+ };
+
+ Aliases = host: aliases: {
+ inherit host aliases;
+ type = "aliases";
+ };
+}
+