feat(reverse-proxy): derive backends from endpoints
This commit was merged in pull request #52.
This commit is contained in:
95
flake.nix
95
flake.nix
@@ -240,10 +240,13 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
endpoints = self.lib.networkEndpoints.collectFleetEndpoints {
|
||||||
|
pikachu = self.nixosConfigurations.pikachu;
|
||||||
|
};
|
||||||
fleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes {
|
fleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes {
|
||||||
inherit routeHost;
|
inherit routeHost;
|
||||||
pikachu = self.nixosConfigurations.pikachu;
|
pikachu = self.nixosConfigurations.pikachu;
|
||||||
};
|
} endpoints;
|
||||||
testConfig =
|
testConfig =
|
||||||
(self.nixosConfigurations.pikachu.extendModules {
|
(self.nixosConfigurations.pikachu.extendModules {
|
||||||
specialArgs.reverseProxyFleetRoutes = fleetRoutes;
|
specialArgs.reverseProxyFleetRoutes = fleetRoutes;
|
||||||
@@ -264,6 +267,96 @@
|
|||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
reverse-proxy-endpoint-backends =
|
||||||
|
let
|
||||||
|
endpointHost =
|
||||||
|
lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs.my = self.lib;
|
||||||
|
modules = [
|
||||||
|
./modules/network.nix
|
||||||
|
{
|
||||||
|
modules.network.endpoint.address = "10.23.50.146";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
routeHost =
|
||||||
|
lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs.my = self.lib;
|
||||||
|
modules = [
|
||||||
|
./modules/reverse-proxy.nix
|
||||||
|
{
|
||||||
|
modules.reverse-proxy.routes.app = {
|
||||||
|
host = "worker.alexion.dev";
|
||||||
|
endpoint = "worker";
|
||||||
|
port = 8080;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
guestRouteHost =
|
||||||
|
lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs.my = self.lib;
|
||||||
|
modules = [
|
||||||
|
inputs.sops-nix.nixosModules.sops
|
||||||
|
./modules/network.nix
|
||||||
|
./modules/reverse-proxy.nix
|
||||||
|
(self.lib.guest { name = "files"; })
|
||||||
|
{
|
||||||
|
networking.hostName = "pikachu";
|
||||||
|
modules.network.vlans = [ 20 ];
|
||||||
|
guests.files = {
|
||||||
|
enable = true;
|
||||||
|
vlan = 20;
|
||||||
|
endpoint.address = "10.23.20.126";
|
||||||
|
reverseProxy = {
|
||||||
|
enable = true;
|
||||||
|
host = "files.alexion.dev";
|
||||||
|
port = 3923;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
endpoints = self.lib.networkEndpoints.collectFleetEndpoints {
|
||||||
|
worker = endpointHost;
|
||||||
|
pikachu = guestRouteHost;
|
||||||
|
};
|
||||||
|
fleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes {
|
||||||
|
inherit routeHost;
|
||||||
|
pikachu = guestRouteHost;
|
||||||
|
} endpoints;
|
||||||
|
testConfig =
|
||||||
|
(lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs.my = self.lib;
|
||||||
|
modules = [
|
||||||
|
./modules/reverse-proxy.nix
|
||||||
|
{
|
||||||
|
modules.reverse-proxy = {
|
||||||
|
enable = true;
|
||||||
|
routes = fleetRoutes;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}).config;
|
||||||
|
vhosts = testConfig.services.nginx.virtualHosts;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "reverse-proxy-endpoint-backends-check" { } ''
|
||||||
|
${
|
||||||
|
lib.optionalString (
|
||||||
|
!(
|
||||||
|
vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.50.146:8080"
|
||||||
|
&& fleetRoutes."pikachu-files".endpoint == "pikachu-files"
|
||||||
|
&& vhosts."files.alexion.dev".locations."/".proxyPass == "http://10.23.20.126:3923"
|
||||||
|
)
|
||||||
|
) "exit 1"
|
||||||
|
}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
|
||||||
reverse-proxy-secondary-path-requires-root =
|
reverse-proxy-secondary-path-requires-root =
|
||||||
let
|
let
|
||||||
testConfig =
|
testConfig =
|
||||||
|
|||||||
@@ -34,12 +34,15 @@ let
|
|||||||
) (builtins.readDir dir)
|
) (builtins.readDir dir)
|
||||||
);
|
);
|
||||||
|
|
||||||
specialArgs = {
|
specialArgs =
|
||||||
inherit inputs self;
|
let
|
||||||
my = self.lib;
|
networkFleetEndpoints = self.lib.networkEndpoints.collectFleetEndpoints self.nixosConfigurations;
|
||||||
networkFleetEndpoints = self.lib.networkEndpoints.collectFleetEndpoints self.nixosConfigurations;
|
in
|
||||||
reverseProxyFleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes self.nixosConfigurations;
|
{
|
||||||
};
|
inherit inputs networkFleetEndpoints self;
|
||||||
|
my = self.lib;
|
||||||
|
reverseProxyFleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes self.nixosConfigurations networkFleetEndpoints;
|
||||||
|
};
|
||||||
|
|
||||||
bridgeName = id: "br-vlan${toString id}";
|
bridgeName = id: "br-vlan${toString id}";
|
||||||
|
|
||||||
|
|||||||
@@ -295,7 +295,12 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
modules.reverse-proxy.routes.${machineName} = lib.mkIf cfg.reverseProxy.enable (
|
modules.reverse-proxy.routes.${machineName} = lib.mkIf cfg.reverseProxy.enable (
|
||||||
reverseProxyRoutes.routeFields cfg.reverseProxy
|
reverseProxyRoutes.routeFields (
|
||||||
|
cfg.reverseProxy
|
||||||
|
// lib.optionalAttrs (cfg.reverseProxy.backend == null && cfg.reverseProxy.endpoint == null) {
|
||||||
|
endpoint = "${config.networking.hostName}-${machineName}";
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assertions = [
|
assertions = [
|
||||||
|
|||||||
@@ -37,10 +37,30 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
backend = lib.mkOption {
|
backend = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
description = "Full HTTP backend URL that receives proxied traffic.";
|
description = "Full HTTP backend URL that receives proxied traffic.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
endpoint = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Fleet network endpoint key used to derive the backend URL.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.ints.between 1 65535);
|
||||||
|
default = null;
|
||||||
|
example = 3923;
|
||||||
|
description = "Backend TCP port used with the fleet network endpoint.";
|
||||||
|
};
|
||||||
|
|
||||||
|
scheme = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "http";
|
||||||
|
description = "Backend URL scheme used with the fleet network endpoint.";
|
||||||
|
};
|
||||||
|
|
||||||
tls = lib.mkOption {
|
tls = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
@@ -60,22 +80,45 @@ let
|
|||||||
aliases
|
aliases
|
||||||
path
|
path
|
||||||
backend
|
backend
|
||||||
|
endpoint
|
||||||
|
port
|
||||||
|
scheme
|
||||||
tls
|
tls
|
||||||
websockets
|
websockets
|
||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
endpointBackend = endpoints: route:
|
||||||
|
let
|
||||||
|
endpoint = endpoints.${route.endpoint} or (
|
||||||
|
throw "Unknown reverse-proxy endpoint `${route.endpoint}`. Known endpoints: ${lib.concatStringsSep ", " (lib.attrNames endpoints)}"
|
||||||
|
);
|
||||||
|
in
|
||||||
|
"${route.scheme}://${endpoint.address}:${toString route.port}";
|
||||||
|
|
||||||
|
resolvedRouteFields = endpoints: route:
|
||||||
|
let
|
||||||
|
routeWithBackend =
|
||||||
|
if route.backend != null then
|
||||||
|
route
|
||||||
|
else if route.endpoint != null && route.port != null then
|
||||||
|
route // { backend = endpointBackend endpoints route; }
|
||||||
|
else
|
||||||
|
throw "Reverse-proxy route `${route.host}${route.path}` needs either backend or endpoint plus port.";
|
||||||
|
in
|
||||||
|
routeFields routeWithBackend;
|
||||||
|
|
||||||
collectFleetRoutes =
|
collectFleetRoutes =
|
||||||
hosts:
|
hosts: endpoints:
|
||||||
lib.concatMapAttrs (
|
lib.concatMapAttrs (
|
||||||
hostName: host:
|
hostName: host:
|
||||||
lib.mapAttrs' (
|
lib.mapAttrs' (
|
||||||
routeName: route: lib.nameValuePair "${hostName}-${routeName}" route
|
routeName: route: lib.nameValuePair "${hostName}-${routeName}" (resolvedRouteFields endpoints route)
|
||||||
) host.config.modules.reverse-proxy.routes
|
) host.config.modules.reverse-proxy.routes
|
||||||
) hosts;
|
) hosts;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
inherit collectFleetRoutes mkRouteOptions routeFields;
|
inherit collectFleetRoutes mkRouteOptions resolvedRouteFields routeFields;
|
||||||
|
|
||||||
routeType = lib.types.submodule (
|
routeType = lib.types.submodule (
|
||||||
{ name, ... }:
|
{ name, ... }:
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ let
|
|||||||
in
|
in
|
||||||
lib.length paths != lib.length (lib.unique paths);
|
lib.length paths != lib.length (lib.unique paths);
|
||||||
|
|
||||||
|
hasUnresolvedBackend = route: route.backend == null;
|
||||||
|
|
||||||
mkLocation = route: {
|
mkLocation = route: {
|
||||||
proxyPass = route.backend;
|
proxyPass = route.backend;
|
||||||
proxyWebsockets = route.websockets;
|
proxyWebsockets = route.websockets;
|
||||||
@@ -34,7 +36,9 @@ let
|
|||||||
enableACME = rootRoute.tls;
|
enableACME = rootRoute.tls;
|
||||||
serverAliases = rootRoute.aliases;
|
serverAliases = rootRoute.aliases;
|
||||||
locations = lib.listToAttrs (
|
locations = lib.listToAttrs (
|
||||||
map (route: lib.nameValuePair route.path (mkLocation route)) routes
|
map (route: lib.nameValuePair route.path (mkLocation route)) (
|
||||||
|
builtins.filter (route: route.backend != null) routes
|
||||||
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
@@ -58,7 +62,11 @@ in
|
|||||||
++ lib.mapAttrsToList (host: routes: {
|
++ lib.mapAttrsToList (host: routes: {
|
||||||
assertion = !hasDuplicatePaths routes;
|
assertion = !hasDuplicatePaths routes;
|
||||||
message = "modules.reverse-proxy.routes for ${host} must not declare the same path more than once.";
|
message = "modules.reverse-proxy.routes for ${host} must not declare the same path more than once.";
|
||||||
}) routesByHost;
|
}) routesByHost
|
||||||
|
++ map (route: {
|
||||||
|
assertion = !hasUnresolvedBackend route;
|
||||||
|
message = "modules.reverse-proxy.routes for ${route.host}${route.path} must resolve to a backend URL before actualization.";
|
||||||
|
}) (builtins.filter hasUnresolvedBackend (lib.attrValues enabledRoutes));
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
80
|
80
|
||||||
|
|||||||
Reference in New Issue
Block a user