it evaluates
This commit is contained in:
@@ -4,20 +4,12 @@
|
||||
../common/nixos.nix
|
||||
./hardware-configuration.nix
|
||||
./awootrip.nix
|
||||
./frontproxy.nix
|
||||
# ./kanidm.nix
|
||||
# ./keycloak.nix
|
||||
./database.nix
|
||||
./vacustore.nix
|
||||
./nix-cache-nginx.nix
|
||||
./jl-stats.nix
|
||||
./static-stuff.nix
|
||||
#./vms.nix
|
||||
./networking.nix
|
||||
./emily.nix
|
||||
./jellyfin.nix
|
||||
./yt-archiver.nix
|
||||
./habitat-fwd.nix
|
||||
./proxied
|
||||
];
|
||||
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
|
@@ -1,114 +0,0 @@
|
||||
global
|
||||
close-spread-time 1s
|
||||
hard-stop-after 3s
|
||||
description "triple-dezert frontproxy"
|
||||
no insecure-fork-wanted
|
||||
log stdout format short daemon
|
||||
numa-cpu-mapping
|
||||
tune.listener.default-shards by-thread
|
||||
tune.ssl.lifetime 24h
|
||||
zero-warning
|
||||
log 127.0.0.1 syslog debug
|
||||
|
||||
|
||||
defaults
|
||||
# https://world.hey.com/goekesmi/haproxy-chrome-tcp-preconnect-and-error-408-a-post-preserved-from-the-past-2497d1f7
|
||||
timeout server 30s
|
||||
timeout client 10s
|
||||
timeout connect 10s
|
||||
option http-ignore-probes
|
||||
|
||||
timeout tunnel 1h
|
||||
log global
|
||||
mode http
|
||||
option httplog
|
||||
|
||||
frontend main
|
||||
bind :80
|
||||
bind :443 ssl crt /certs/shelvacu.com/full.pem crt /certs/vacu.store/full.pem crt /certs/jean-luc.org/full.pem crt /certs/pwrhs.win/full.pem crt /certs/jf.finaltask.xyz/full.pem
|
||||
|
||||
mode http
|
||||
|
||||
acl has_sni ssl_fc_sni -m found
|
||||
acl has_host_hdr req.fhdr(host) -m found
|
||||
|
||||
http-request set-var(req.host) req.fhdr(host),host_only
|
||||
# Check whether the client is attempting domain fronting.
|
||||
acl ssl_sni_http_host_match ssl_fc_sni,strcmp(req.host) eq 0
|
||||
|
||||
# acl host_auth var(req.host) -m str "auth.shelvacu.com"
|
||||
acl host_vacustore var(req.host) -m str "vacu.store"
|
||||
acl host_cache var(req.host) -m str "nixcache.shelvacu.com"
|
||||
acl host_stats_jl var(req.host) -m str "stats.jean-luc.org"
|
||||
acl host_tulpaudcast_jl var(req.host) -m str "tulpaudcast.jean-luc.org"
|
||||
acl host_habitat_pwrhs var(req.host) -m str "habitat.pwrhs.win"
|
||||
acl host_jellyfin var(req.host) -m str "jf.finaltask.xyz"
|
||||
|
||||
http-after-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" if { ssl_fc }
|
||||
|
||||
#http-request lua.sslkeylog /tmp/haproxy-sslkeylog
|
||||
http-request deny status 400 if !{ req.fhdr_cnt(host) eq 1 }
|
||||
http-request deny status 421 if has_sni has_host_hdr !ssl_sni_http_host_match
|
||||
http-request return lf-string "%ci\n" content-type text/plain if { path /ip }
|
||||
http-request redirect scheme https code 301 if !{ ssl_fc }
|
||||
|
||||
# garunteed ssl-only from here on
|
||||
http-request redirect location "https://shelvacu.com%[capture.req.uri]" code 301 if { var(req.host) -m str "www.shelvacu.com" }
|
||||
http-request redirect location "https://vacu.store%[capture.req.uri]" code 301 if { var(req.host) -m str "www.vacu.store" }
|
||||
http-request redirect location "https://jean-luc.org%[capture.req.uri]" code 301 if { var(req.host) -m str "www.jean-luc.org" }
|
||||
http-request redirect location "https://tulpaudcast.jean-luc.org%[capture.req.uri]" code 301 if { var(req.host) -m str "podcast.jean-luc.org" }
|
||||
http-request return string "Shelvacu is awesome" content-type text/plain if { path / } { var(req.host) -m str "shelvacu.com" }
|
||||
http-request return string "Jean-luc is awesome" content-type text/plain if { path / } { var(req.host) -m str "jean-luc.org" }
|
||||
http-request allow if host_vacustore
|
||||
# http-request allow if host_auth
|
||||
http-request allow if host_cache
|
||||
http-request allow if host_stats_jl
|
||||
http-request allow if host_tulpaudcast_jl
|
||||
http-request allow if host_habitat_pwrhs
|
||||
http-request allow if host_jellyfin
|
||||
http-request return status 404 string "not found" content-type text/plain
|
||||
|
||||
use_backend vacustore if host_vacustore
|
||||
# use_backend keycloak if host_auth
|
||||
use_backend nix-cache if host_cache
|
||||
use_backend jl_stats if host_stats_jl
|
||||
use_backend static_stuff if host_tulpaudcast_jl
|
||||
use_backend habitat if host_habitat_pwrhs
|
||||
use_backend jellyfin if host_jellyfin
|
||||
|
||||
backend vacustore
|
||||
mode http
|
||||
option forwardfor
|
||||
server main vacustore:80 check maxconn 500 proto h1
|
||||
|
||||
backend kani
|
||||
mode http
|
||||
option forwardfor
|
||||
server main kani:8443 check maxconn 500 ssl verify none ssl-reuse
|
||||
|
||||
backend jellyfin
|
||||
mode http
|
||||
option forwardfor
|
||||
server main jellyfin:8096 check maxconn 100 proto h1
|
||||
|
||||
# backend keycloak
|
||||
# mode http
|
||||
# option forwardfor
|
||||
# option forwarded proto host for
|
||||
# server main keycloak:80 check maxconn 500 proto h1
|
||||
|
||||
backend nix-cache
|
||||
mode http
|
||||
server main nix-cache:80 check maxconn 500 proto h1
|
||||
|
||||
backend jl_stats
|
||||
mode http
|
||||
server main jl_stats:80 check maxconn 500 proto h1
|
||||
|
||||
backend static_stuff
|
||||
mode http
|
||||
server main static_stuff:80 check maxconn 500 proto h1
|
||||
|
||||
backend habitat
|
||||
mode http
|
||||
server main 10.78.79.114:8123 check maxconn 500 proto h1
|
@@ -1,43 +0,0 @@
|
||||
{
|
||||
...
|
||||
}:
|
||||
{
|
||||
systemd.tmpfiles.settings.whatever."/trip/llm-models".d = {
|
||||
mode = "0744";
|
||||
};
|
||||
|
||||
containers.llm = {
|
||||
privateNetwork = true;
|
||||
hostAddress = "192.168.100.26";
|
||||
localAddress = "192.168.100.27";
|
||||
|
||||
autoStart = true;
|
||||
ephemeral = false;
|
||||
restartIfChanged = true;
|
||||
bindMounts."/models" = {
|
||||
hostPath = "/trip/llm-models";
|
||||
isReadOnly = false;
|
||||
};
|
||||
|
||||
config =
|
||||
{ config, ... }:
|
||||
{
|
||||
system.stateVersion = "24.05";
|
||||
networking.firewall.enable = false;
|
||||
|
||||
services.open-webui = {
|
||||
enable = true;
|
||||
port = 8080;
|
||||
environment.OLLAMA_API_BASE_URL = "http://${config.services.ollama.listenAddress}";
|
||||
};
|
||||
services.ollama = {
|
||||
enable = true;
|
||||
listenAddress = "127.0.0.1:11434";
|
||||
models = "/models";
|
||||
sandbox = true;
|
||||
writablePaths = "/models";
|
||||
acceleration = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
outerConfig = config;
|
||||
ip4Address = types.addCheck
|
||||
(types.strMatching ''\\d{1,3}.\d{1,3}.\d{1,3}.d{1,3}'')
|
||||
(s:
|
||||
lib.all (p: (lib.toInt p) < 255) (lib.stringSplit "." s)
|
||||
)
|
||||
;
|
||||
# Note: This accepts plenty of strings that aren't valid ipv6 addresses, this is just to catch when you accidentally put an ipv4 or something else in
|
||||
ip6Address = types.strMatching ''([a-fA-F0-9]{4}::?){1,7}[a-fA-F0-9]{4}'';
|
||||
ipAddress = types.either ip4Address ip6Address;
|
||||
in
|
||||
{
|
||||
options.vacu.proxiedServices = {
|
||||
services = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({name, config, ...}: {
|
||||
options = {
|
||||
enable = mkOption { type = types.bool; default = false; };
|
||||
|
||||
name = mkOption {
|
||||
default = name;
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
fromContainer = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
|
||||
port = mkOption { type = types.port; };
|
||||
|
||||
ipAddress = mkOption {
|
||||
type = ipAddress;
|
||||
};
|
||||
|
||||
domain = mkOption { type = types.str; };
|
||||
|
||||
forwardFor = mkOption { type = types.bool; default = false; };
|
||||
|
||||
maxConnections = mkOption { type = types.int; default = 500; };
|
||||
|
||||
useSSL = mkOption { type = types.bool; default = false; };
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf (config.fromContainer != null) {
|
||||
ipAddress = outerConfig.containers.${config.fromContainer}.localAddress;
|
||||
})
|
||||
];
|
||||
}));
|
||||
};
|
||||
};
|
||||
}
|
28
triple-dezert/proxied/default.nix
Normal file
28
triple-dezert/proxied/default.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
imports = [
|
||||
./frontproxy.nix
|
||||
./options.nix
|
||||
./services/habitat-fwd.nix
|
||||
./services/jellyfin.nix
|
||||
./services/jl-stats.nix
|
||||
./services/kanidm.nix
|
||||
./services/keycloak.nix
|
||||
./services/llm.nix
|
||||
./services/nix-cache-nginx.nix
|
||||
./services/static-stuff.nix
|
||||
./services/vacustore.nix
|
||||
];
|
||||
|
||||
vacu.proxiedServices = {
|
||||
habitat.enable = true;
|
||||
vacustore.enable = true;
|
||||
jl-stats.enable = true;
|
||||
static-stuff.enable = true;
|
||||
nix-cache.enable = true;
|
||||
llm.enable = true;
|
||||
|
||||
keycloak.enable = false;
|
||||
kanidm.enable = false;
|
||||
jellyfin.enable = false;
|
||||
};
|
||||
}
|
@@ -17,14 +17,13 @@ let
|
||||
"pwrhs.win"
|
||||
"jf.finaltask.xyz"
|
||||
];
|
||||
proxied = lib.pipe [
|
||||
config.vacu.proxiedServices
|
||||
lib.attrVals
|
||||
(lib.filter (c: c.enabled))
|
||||
proxied = lib.pipe config.vacu.proxiedServices [
|
||||
lib.attrValues
|
||||
(lib.filter (c: c.enable))
|
||||
];
|
||||
serviceValidDomainAssertions = map (proxiedConfig: {
|
||||
assertion = lib.any (availableDomain:
|
||||
(lib.endsWith proxiedConfig.domain ("." + availableDomain)) ||
|
||||
(lib.hasSuffix ("." + availableDomain) proxiedConfig.domain) ||
|
||||
(proxiedConfig.domain == availableDomain)
|
||||
) domains;
|
||||
message = "proxiedService ${proxiedConfig.name}'s `domain` does not match any of the known domains";
|
||||
@@ -45,14 +44,9 @@ in
|
||||
postRun = "${pkgs.nixos-container}/bin/nixos-container run frontproxy -- systemctl reload haproxy";
|
||||
};
|
||||
|
||||
security.acme.certs = builtins.listToAttrs (
|
||||
map (domain: {
|
||||
name = domain;
|
||||
value = {
|
||||
extraDomainNames = [ "*.${domain}" ];
|
||||
};
|
||||
}) domains
|
||||
);
|
||||
security.acme.certs = mapListToAttrs (domain:
|
||||
lib.nameValuePair domain { extraDomainNames = [ "*.${domain}" ]; }
|
||||
) domains;
|
||||
|
||||
users.groups.acme.gid = 993;
|
||||
|
||||
@@ -75,15 +69,10 @@ in
|
||||
autoStart = true;
|
||||
restartIfChanged = true;
|
||||
ephemeral = true;
|
||||
bindMounts = builtins.listToAttrs (
|
||||
map (d: {
|
||||
name = "/certs/${d}";
|
||||
value = {
|
||||
hostPath = outer_config.security.acme.certs.${d}.directory;
|
||||
isReadOnly = true;
|
||||
};
|
||||
}) domains
|
||||
);
|
||||
bindMounts = mapListToAttrs (d: lib.nameValuePair "/certs/${d}" {
|
||||
hostPath = outer_config.security.acme.certs.${d}.directory;
|
||||
isReadOnly = true;
|
||||
}) domains;
|
||||
config =
|
||||
{ config, ... }:
|
||||
{
|
||||
@@ -93,15 +82,6 @@ in
|
||||
services.haproxy.enable = true;
|
||||
services.haproxy.config = import ./haproxy-config.nix { inherit lib domains proxied; };
|
||||
networking.hosts = mapListToAttrs (c: lib.nameValuePair c.ipAddress [ c.name ]) proxied;
|
||||
# networking.hosts = {
|
||||
# "${outer_config.containers.vacustore.localAddress}" = [ "vacustore" ];
|
||||
# "127.4.20.165" = [ "kani" ];
|
||||
# # "${outer_config.containers.keycloak.localAddress}" = [ "keycloak" ];
|
||||
# "${outer_config.containers.nix-cache-nginx.localAddress}" = [ "nix-cache" ];
|
||||
# "${outer_config.containers.jl-stats.localAddress}" = [ "jl_stats" ];
|
||||
# "${outer_config.containers.static-stuff.localAddress}" = [ "static_stuff" ];
|
||||
# "${outer_config.containers.jellyfin.localAddress}" = [ "jellyfin" ];
|
||||
# };
|
||||
};
|
||||
};
|
||||
}
|
@@ -92,7 +92,7 @@ ${concatMap "\n\n" (c:
|
||||
backend ${backendName c}
|
||||
mode http
|
||||
${lib.optionalString c.forwardFor "option forwardfor"}
|
||||
server main ${c.name}:${c.port} check maxconn ${c.maxConnections} ${if c.useSSL then "ssl verify none ssl-reuse" else "proto h1"}
|
||||
server main ${c.name}:${builtins.toString c.port} check maxconn ${builtins.toString c.maxConnections} ${if c.useSSL then "ssl verify none ssl-reuse" else "proto h1"}
|
||||
''
|
||||
) proxied}
|
||||
''
|
56
triple-dezert/proxied/options.nix
Normal file
56
triple-dezert/proxied/options.nix
Normal file
@@ -0,0 +1,56 @@
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
outerConfig = config;
|
||||
ip4Segment = ''[0-9]{1,3}'';
|
||||
ip4Address = types.addCheck
|
||||
(types.strMatching (lib.concatStringsSep ''\.'' [ip4Segment ip4Segment ip4Segment ip4Segment]))
|
||||
(s:
|
||||
lib.all (p: (lib.toInt p) < 255) (lib.splitString "." s)
|
||||
)
|
||||
;
|
||||
# Note: This accepts plenty of strings that aren't valid ipv6 addresses, this is just to catch when you accidentally put an ipv4 or something else in
|
||||
ip6Address = types.strMatching ''([a-fA-F0-9]{4}::?){1,7}[a-fA-F0-9]{4}'';
|
||||
ipAddress = types.either ip4Address ip6Address;
|
||||
in
|
||||
{
|
||||
# vacu.proxiedServices.habitat
|
||||
options.vacu.proxiedServices = mkOption {
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({name, config, ...}: {
|
||||
options = {
|
||||
enable = mkOption { type = types.bool; default = false; };
|
||||
|
||||
name = mkOption {
|
||||
default = name;
|
||||
type = types.str;
|
||||
};
|
||||
|
||||
fromContainer = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.str;
|
||||
};
|
||||
|
||||
port = mkOption { type = types.port; };
|
||||
|
||||
ipAddress = mkOption {
|
||||
type = ipAddress;
|
||||
};
|
||||
|
||||
domain = mkOption { type = types.str; };
|
||||
|
||||
forwardFor = mkOption { type = types.bool; default = false; };
|
||||
|
||||
maxConnections = mkOption { type = types.int; default = 500; };
|
||||
|
||||
useSSL = mkOption { type = types.bool; default = false; };
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf (config.fromContainer != null) {
|
||||
ipAddress = outerConfig.containers.${config.fromContainer}.localAddress;
|
||||
})
|
||||
];
|
||||
}));
|
||||
};
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
{
|
||||
config,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
webListenPort = 8443;
|
||||
webListenIP = "127.4.20.165";
|
||||
in
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [ 636 ];
|
||||
@@ -18,6 +18,7 @@ in
|
||||
forwardFor = true;
|
||||
useSSL = true;
|
||||
port = webListenPort;
|
||||
ipAddress = webListenIP;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ config.services.kanidm.package ]; # adds the binary to the PATH
|
||||
@@ -38,14 +39,13 @@ in
|
||||
tls_dir = config.security.acme.certs."shelvacu.com".directory;
|
||||
in
|
||||
rec {
|
||||
package = inputs.nixpkgs-unstable.legacyPackages.x86_64-linux.kanidm;
|
||||
enableServer = true;
|
||||
serverSettings = {
|
||||
domain = "id.shelvacu.com";
|
||||
origin = "https://id.shelvacu.com";
|
||||
# db_path = "/trip/sqlites/kani/kani.sqlite";
|
||||
db_fs_type = "zfs";
|
||||
bindaddress = "127.4.20.165:${webListenPort}";
|
||||
bindaddress = "${webListenIP}:${builtins.toString webListenPort}";
|
||||
ldapbindaddress = "[::]:636";
|
||||
trust_x_forward_for = true;
|
||||
tls_chain = tls_dir + "/fullchain.pem";
|
67
triple-dezert/proxied/services/llm.nix
Normal file
67
triple-dezert/proxied/services/llm.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
config, ...
|
||||
}:
|
||||
let
|
||||
contain = config.containers.llm;
|
||||
in
|
||||
{
|
||||
vacu.proxiedServices.llm = {
|
||||
domain = "llm.shelvacu.com";
|
||||
fromContainer = "llm";
|
||||
port = contain.config.services.open-webui.port;
|
||||
};
|
||||
vacu.databases.open-webui = {
|
||||
authByIp = contain.localAddress;
|
||||
};
|
||||
systemd.tmpfiles.settings.whatever."/trip/llm-models".d = {
|
||||
mode = "0744";
|
||||
};
|
||||
|
||||
containers.llm = {
|
||||
privateNetwork = true;
|
||||
hostAddress = "192.168.100.26";
|
||||
localAddress = "192.168.100.27";
|
||||
|
||||
autoStart = true;
|
||||
ephemeral = false;
|
||||
restartIfChanged = true;
|
||||
bindMounts."/models" = {
|
||||
hostPath = "/trip/llm-models";
|
||||
isReadOnly = false;
|
||||
};
|
||||
|
||||
config =
|
||||
let
|
||||
outer_config = config;
|
||||
in
|
||||
{ config, ... }:
|
||||
{
|
||||
system.stateVersion = "24.05";
|
||||
networking.firewall.enable = false;
|
||||
|
||||
services.open-webui = {
|
||||
enable = true;
|
||||
port = 8080;
|
||||
environment = {
|
||||
OLLAMA_API_BASE_URL = "http://${config.services.ollama.listenAddress}";
|
||||
ENV = "prod";
|
||||
WEBUI_URL = "https://${outer_config.vacu.proxiedServices.llm.domain}/";
|
||||
ENABLE_COMMUNITY_SHARING = "False";
|
||||
DATABASE_URL = "postgresql://open-webui@${contain.hostAddress}/open-webui";
|
||||
SAFE_MODE = "True";
|
||||
WEBUI_SESSION_COOKIE_SAME_SITE = "strict";
|
||||
WEBUI_SESSION_COOKIE_SECURE = "True";
|
||||
ENABLE_OPENAI_API = "False";
|
||||
};
|
||||
};
|
||||
services.ollama = {
|
||||
enable = true;
|
||||
listenAddress = "127.0.0.1:11434";
|
||||
models = "/models";
|
||||
sandbox = true;
|
||||
writablePaths = [ "/models" ];
|
||||
acceleration = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
--[[
|
||||
This script can be used to decipher SSL traffic coming through haproxy. It
|
||||
must first be loaded in the global section of haproxy configuration with
|
||||
TLS keys logging activated :
|
||||
|
||||
tune.ssl.keylog on
|
||||
lua-load sslkeylogger.lua
|
||||
|
||||
Then a http-request rule can be inserted for the desired frontend :
|
||||
http-request lua.sslkeylog <path_to_keylog_file>
|
||||
|
||||
The generated keylog file can then be injected into wireshark to decipher a
|
||||
network capture.
|
||||
]]
|
||||
|
||||
local function sslkeylog(txn)
|
||||
local fields = {
|
||||
CLIENT_EARLY_TRAFFIC_SECRET = function() return txn.f:ssl_fc_client_early_traffic_secret() end,
|
||||
CLIENT_HANDSHAKE_TRAFFIC_SECRET = function() return txn.f:ssl_fc_client_handshake_traffic_secret() end,
|
||||
SERVER_HANDSHAKE_TRAFFIC_SECRET = function() return txn.f:ssl_fc_server_handshake_traffic_secret() end,
|
||||
CLIENT_TRAFFIC_SECRET_0 = function() return txn.f:ssl_fc_client_traffic_secret_0() end,
|
||||
SERVER_TRAFFIC_SECRET_0 = function() return txn.f:ssl_fc_server_traffic_secret_0() end,
|
||||
EXPORTER_SECRET = function() return txn.f:ssl_fc_exporter_secret() end,
|
||||
EARLY_EXPORTER_SECRET = function() return txn.f:ssl_fc_early_exporter_secret() end
|
||||
}
|
||||
|
||||
local client_random = txn.c:hex(txn.f:ssl_fc_client_random())
|
||||
|
||||
-- ensure that a key is written only once by using a session variable
|
||||
if not txn:get_var('sess.sslkeylogdone') then
|
||||
for fieldname, fetch in pairs(fields) do
|
||||
if fetch() then
|
||||
core.Warning(string.format('SSLKEYLOG:: %s %s %s\n', fieldname, client_random, fetch()))
|
||||
end
|
||||
end
|
||||
|
||||
txn:set_var('sess.sslkeylogdone', true)
|
||||
end
|
||||
end
|
||||
|
||||
core.register_action('sslkeylog', { 'http-req' }, sslkeylog)
|
Reference in New Issue
Block a user