pi/intermediate/remote.nix
2026-04-04 19:45:40 +02:00

88 lines
No EOL
3.2 KiB
Nix

let
lib = import <nixpkgs/lib>;
net = import ../config/network.nix;
end = import ../config/endpoints.nix;
endpointValidation = import ../validation/endpoints.nix;
autoSshValidation = import ../validation/auto_ssh.nix;
endpoints = endpointValidation.validateEndpointsShape end;
devices = autoSshValidation.getDevices net;
autoSshDevices = autoSshValidation.getAutoSshDevices devices;
matchesDomain = endpointDomain: remoteDomain:
if builtins.isString endpointDomain && builtins.isString remoteDomain then
endpointDomain == remoteDomain || lib.hasSuffix ".${remoteDomain}" endpointDomain
else
throw "Endpoint and remote domains must be strings.";
getRemotePortMap = device:
autoSshValidation.getRemotePortMap device;
getPortOffset = device:
autoSshValidation.getPortOffset device;
ensurePortRange = context: port:
if builtins.isInt port && port >= 1 && port <= 65535 then
port
else
throw "${context} must be in range 1..65535.";
resolveRemotePort = remotePortMap: portOffset: endpoint:
let
localPort =
if endpoint ? port then
ensurePortRange "Endpoint port '${toString endpoint.port}'" endpoint.port
else
throw "Endpoint is missing required field: port.";
overrides = lib.filter (entry: entry.localPort == localPort) remotePortMap;
in
if overrides != [] then
ensurePortRange "remotePortMap remotePort '${toString (builtins.head overrides).remotePort}'" (builtins.head overrides).remotePort
else
ensurePortRange "Computed remote port (local ${toString localPort} + offset ${toString portOffset})" (localPort + portOffset);
mapEndpointToForward = remotePortMap: portOffset: endpoint: {
remote = resolveRemotePort remotePortMap portOffset endpoint;
localAddress = "localhost";
localPort = endpoint.port;
};
endpointForHttpRedirect = endpoint:
endpoint // { port = 80; };
portsByRemote = lib.mapAttrs (_: device:
let
remoteDomain =
if device ? domain then
device.domain
else
throw "Auto SSH device is missing required field: domain.";
remotePortMap = getRemotePortMap device;
portOffset = getPortOffset device;
matchedEndpoints =
lib.filter (endpoint:
if endpoint.force_ssl && endpoint.port == 80 then
throw "Endpoint '${endpoint.domain}${endpoint.endpoint}' cannot use force_ssl=true on port 80."
else if endpoint.type == "proxy" && endpoint.content.type != "service" then
throw "Proxy endpoint '${endpoint.domain}${endpoint.endpoint}' must use content.type = 'service'."
else
matchesDomain endpoint.domain remoteDomain
) endpoints;
forwards = lib.concatMap (endpoint:
let
baseForward = mapEndpointToForward remotePortMap portOffset endpoint;
httpRedirectForward = mapEndpointToForward remotePortMap portOffset (endpointForHttpRedirect endpoint);
in
if endpoint.force_ssl then
[ baseForward httpRedirectForward ]
else
[ baseForward ]
) matchedEndpoints;
in
lib.unique forwards
) autoSshDevices;
in
rec {
inherit portsByRemote;
}