it evaluates

This commit is contained in:
Shelvacu
2024-10-19 19:21:59 -07:00
parent 565bb08be3
commit c3120e5b79
18 changed files with 167 additions and 298 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
};
};
};
}

View File

@@ -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;
})
];
}));
};
};
}

View 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;
};
}

View File

@@ -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" ];
# };
};
};
}

View File

@@ -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}
''

View 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;
})
];
}));
};
}

View File

@@ -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";

View 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;
};
};
};
}

View File

@@ -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)