add hackyplayer/hacky-vouchproxy/emfminiserv bits
This commit is contained in:
parent
bcb2f287e1
commit
93d5a104da
8 changed files with 624 additions and 1 deletions
|
@ -15,6 +15,7 @@ args: {
|
||||||
access = import ./access args;
|
access = import ./access args;
|
||||||
vault = import ./vault args;
|
vault = import ./vault args;
|
||||||
tumblrandom = import ./tumblrandom args;
|
tumblrandom = import ./tumblrandom args;
|
||||||
|
emfminiserv = import ./emfminiserv args;
|
||||||
|
|
||||||
buildgo2 = import ./buildgo2 args;
|
buildgo2 = import ./buildgo2 args;
|
||||||
}
|
}
|
||||||
|
|
12
go/emfminiserv/default.nix
Normal file
12
go/emfminiserv/default.nix
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.program {
|
||||||
|
name = "emfminiserv";
|
||||||
|
srcs = [ ./emfminiserv.go ];
|
||||||
|
deps = with depot.third_party; [
|
||||||
|
# gopkgs."github.com".golang.glog
|
||||||
|
];
|
||||||
|
}
|
216
go/emfminiserv/emfminiserv.go
Normal file
216
go/emfminiserv/emfminiserv.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/base64"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseDir = flag.String("base_dir", "/store/emf/2024/video/output/", "base directory for output")
|
||||||
|
httpListen = flag.String("http_listen", "", "TCP address to listen&serve")
|
||||||
|
httpListenUNIX = flag.String("http_listen_unix", "", "UNIX socket path to listen&serve")
|
||||||
|
|
||||||
|
computeForBase = flag.String("compute_base", "https://prerelease.voc.emf.camp/", "base URL to prepend when computing a secret")
|
||||||
|
computeFor = flag.String("compute", "", "something to compute the secret for")
|
||||||
|
|
||||||
|
list = flag.Bool("list", false, "list the available content")
|
||||||
|
|
||||||
|
devMode = flag.Bool("dev_mode", false, "enable insecure dev mode")
|
||||||
|
)
|
||||||
|
|
||||||
|
func computeSignature(content, secret string) string {
|
||||||
|
mac := hmac.New(sha256.New, []byte(secret))
|
||||||
|
mac.Write([]byte(content))
|
||||||
|
return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(mac.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSignature(content, secret, signature string) bool {
|
||||||
|
computed := computeSignature(content, secret)
|
||||||
|
if *devMode {
|
||||||
|
log.Printf("got signature %q; computed signature %q", signature, computed)
|
||||||
|
}
|
||||||
|
return subtle.ConstantTimeCompare([]byte(computed), []byte(signature)) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
talkRegexFragment = `[0-9]+(_[\p{L}\p{N}\-]+)?`
|
||||||
|
fileEndRegexFragment = `[0-9]{8}-[0-9]{6}[.]mp4`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
validContentRegex = regexp.MustCompile("^" + talkRegexFragment + "$")
|
||||||
|
validFileRegex = regexp.MustCompile("^" + fileEndRegexFragment + "$")
|
||||||
|
filenameRegex = regexp.MustCompile(`^(` + talkRegexFragment + ")-(" + fileEndRegexFragment + ")$")
|
||||||
|
)
|
||||||
|
|
||||||
|
func logResultFactory(r *http.Request, what string) func(string, ...any) {
|
||||||
|
return func(f string, bits ...any) {
|
||||||
|
log.Printf("%s{%s}: %s %s - %s", what, r.RemoteAddr, r.Method, r.RequestURI, fmt.Sprintf(f, bits...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
contentFS := os.DirFS(*baseDir)
|
||||||
|
|
||||||
|
secret := os.Getenv("EMFMINISERV_SECRET")
|
||||||
|
if secret == "" {
|
||||||
|
if !*devMode {
|
||||||
|
log.Fatal("set EMFMINISERV_SECRET or -dev_mode")
|
||||||
|
}
|
||||||
|
secret = "testing-do-not-use-in-production"
|
||||||
|
log.Printf("secret is set to %q! development mode only.", secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *httpListen == "" && *httpListenUNIX == "" && *computeFor == "" && !*list {
|
||||||
|
log.Printf("need -http_listen, -http_listen_unix, -compute or -list")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *list {
|
||||||
|
matches, err := fs.Glob(contentFS, "*.mp4")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("globbing for *.mp4: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
available := map[string]bool{}
|
||||||
|
|
||||||
|
for _, m := range matches {
|
||||||
|
bits := filenameRegex.FindStringSubmatch(m)
|
||||||
|
if bits == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
available[bits[1]] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
availableKeys := make([]string, 0, len(available))
|
||||||
|
for k := range available {
|
||||||
|
availableKeys = append(availableKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(availableKeys)
|
||||||
|
|
||||||
|
for _, k := range availableKeys {
|
||||||
|
fmt.Printf("%s\n", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *computeFor != "" {
|
||||||
|
sig := computeSignature(*computeFor, secret)
|
||||||
|
fmt.Printf("%s%s/%s/\n", *computeForBase, *computeFor, sig)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/{content}/{signature}/{file}", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
logResult := logResultFactory(r, "contentget")
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
logResult("method not allowed")
|
||||||
|
http.Error(rw, "GET only", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("contentget{%s}: %s %s", r.RemoteAddr, r.Method, r.RequestURI)
|
||||||
|
|
||||||
|
content := r.PathValue("content")
|
||||||
|
file := r.PathValue("file")
|
||||||
|
if !validContentRegex.MatchString(content) || !validFileRegex.MatchString(file) {
|
||||||
|
logResult("404 invalid content or file segment")
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !validateSignature(content, secret, r.PathValue("signature")) {
|
||||||
|
logResult("404 invalid signature")
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.Header().Set("x-accel-redir", fmt.Sprintf("/%s-%s", content, file))
|
||||||
|
logResult("200 OK")
|
||||||
|
})
|
||||||
|
http.HandleFunc("/{content}/{signature}/{$}", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
logResult := logResultFactory(r, "contentdir")
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
logResult("method not allowed")
|
||||||
|
http.Error(rw, "GET only", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := r.PathValue("content")
|
||||||
|
if !validContentRegex.MatchString(content) {
|
||||||
|
logResult("404 invalid content segment")
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !validateSignature(content, secret, r.PathValue("signature")) {
|
||||||
|
logResult("404 invalid signature")
|
||||||
|
http.NotFound(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := fs.Glob(contentFS, fmt.Sprintf("%s-*", content))
|
||||||
|
if err != nil {
|
||||||
|
logResult("500 globbing with %s-*: %v", content, err)
|
||||||
|
http.Error(rw, "internal server error finding content", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Strings(matches)
|
||||||
|
|
||||||
|
unprefixed := make([]string, len(matches))
|
||||||
|
for n, m := range matches {
|
||||||
|
unprefixed[n] = strings.TrimPrefix(m, fmt.Sprintf("%s-", content))
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.Header().Set("Content-type", "text/html; encoding=utf-8")
|
||||||
|
|
||||||
|
fmt.Fprintf(rw, "<ul>")
|
||||||
|
for _, m := range unprefixed {
|
||||||
|
fmt.Fprintf(rw, `<li><a href="%s">%s</a></li>`, m, m)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(rw, "</ul>")
|
||||||
|
logResult("200 OK")
|
||||||
|
})
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
if *httpListen != "" {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
log.Fatal(http.ListenAndServe(*httpListen, nil))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if *httpListenUNIX != "" {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
l, err := net.Listen("unix", *httpListenUNIX)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("listening on unix:%v: %v", *httpListenUNIX, err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
|
||||||
|
if err := os.Chmod(*httpListenUNIX, 0770); err != nil {
|
||||||
|
log.Fatalf("chmodding unix:%v: %v", *httpListenUNIX, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(http.Serve(l, nil))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
86
ops/nixos/lib/emfminiserv.nix
Normal file
86
ops/nixos/lib/emfminiserv.nix
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, pkgs, lib, config, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
emfminiserv = depot.go.emfminiserv;
|
||||||
|
|
||||||
|
cfg = config.my.emfminiserv;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.emfminiserv = {
|
||||||
|
enable = lib.mkEnableOption "emfminiserv";
|
||||||
|
hostname = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "prerelease.voc.emf.camp";
|
||||||
|
};
|
||||||
|
listenAddresses = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
serveDir = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/store/emf/2024/video/output/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf cfg.enable {
|
||||||
|
users.groups.hackyplayer = {};
|
||||||
|
|
||||||
|
systemd.services.emfminiserv = {
|
||||||
|
serviceConfig = {
|
||||||
|
User = "emfminiserv";
|
||||||
|
Group = "hackyplayer";
|
||||||
|
RuntimeDirectory = "emfminiserv";
|
||||||
|
DynamicUser = true;
|
||||||
|
ExecStart = "${emfminiserv}/bin/emfminiserv -http_listen_unix /run/emfminiserv/listen.sock -base_dir '${cfg.serveDir}'";
|
||||||
|
EnvironmentFile = config.my.vault.secrets.emfminiserv-environment.path;
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
my.vault.secrets.emfminiserv-environment = {
|
||||||
|
reloadOrRestartUnits = ["emfminiserv.service"];
|
||||||
|
group = "hackyplayer";
|
||||||
|
template = ''
|
||||||
|
{{ with secret "kv/apps/emfminiserv" }}
|
||||||
|
{{ .Data.data.environment }}
|
||||||
|
{{ end }}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "emfminiserv";
|
||||||
|
text = ''
|
||||||
|
read -ra vars < <(xargs <"${config.my.vault.secrets.emfminiserv-environment.path}")
|
||||||
|
export "''${vars[@]}"
|
||||||
|
exec "${emfminiserv}/bin/emfminiserv" -base_dir '${cfg.serveDir}' "$@"
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."${cfg.hostname}" = {
|
||||||
|
listenAddresses = lib.mkIf (cfg.listenAddresses != null) cfg.listenAddresses;
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy unix//run/emfminiserv/listen.sock {
|
||||||
|
@accel header X-Accel-Redir *
|
||||||
|
handle_response @accel {
|
||||||
|
root * ${cfg.serveDir}
|
||||||
|
rewrite * {rp.header.X-Accel-Redir}
|
||||||
|
method * GET
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.caddy.serviceConfig.SupplementaryGroups = lib.mkAfter [ "hackyplayer" ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
271
ops/nixos/lib/hackyplayer.nix
Normal file
271
ops/nixos/lib/hackyplayer.nix
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, pkgs, lib, config, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
hackyplayerSrc = /home/lukegb/Projects/hackyplayer;
|
||||||
|
hackyplayer = import hackyplayerSrc { inherit pkgs; };
|
||||||
|
|
||||||
|
yamlFormat = pkgs.formats.yaml {};
|
||||||
|
|
||||||
|
cfg = config.my.hackyplayer;
|
||||||
|
processedConfig = lib.mapAttrs' (name: value: lib.nameValuePair "FLASK_${name}" (builtins.toJSON value)) cfg.config;
|
||||||
|
readWritePaths = [
|
||||||
|
cfg.config.VIDEO_OUTPUT
|
||||||
|
cfg.config.VIDEO_TEMP
|
||||||
|
cfg.config.LOG_DIR
|
||||||
|
]
|
||||||
|
++ (map (value: value.OUTPUT_DIR) cfg.config.WATCHFOLDERS)
|
||||||
|
++ (map (value: value.FULLPATH) cfg.config.WATCHFOLDERS);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.my.hackyplayer = {
|
||||||
|
enable = lib.mkEnableOption "hackyplayer";
|
||||||
|
hostname = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
listenAddresses = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.listOf lib.types.str);
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
config = lib.mkOption {
|
||||||
|
type = lib.types.anything;
|
||||||
|
default = let
|
||||||
|
paths = {
|
||||||
|
input = "/store/emf/2024/video/input";
|
||||||
|
source = "/store/emf/2024/video/source";
|
||||||
|
output = "/store/emf/2024/video/output";
|
||||||
|
temp = "/store/emf/2024/video/tmp";
|
||||||
|
logs = "/store/emf/2024/video/logs";
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
VIDEO_SOURCES = [{
|
||||||
|
DISKDIR = paths.source;
|
||||||
|
WEBDIR = "source";
|
||||||
|
EXT = [".mp4"];
|
||||||
|
NAME = "Source";
|
||||||
|
} {
|
||||||
|
DISKDIR = paths.output;
|
||||||
|
WEBDIR = "output";
|
||||||
|
EXT = [".mp4"];
|
||||||
|
NAME = "Output";
|
||||||
|
}];
|
||||||
|
VIDEO_OUTPUT = paths.output;
|
||||||
|
VIDEO_TEMP = paths.temp;
|
||||||
|
WATCHFOLDERS = [{
|
||||||
|
NAME = "input";
|
||||||
|
FULLPATH = paths.input;
|
||||||
|
OUTPUT_DIR = paths.source;
|
||||||
|
}];
|
||||||
|
LOG_DIR = paths.logs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
caddyPrefixExtraHandles = lib.mkOption {
|
||||||
|
type = lib.types.lines;
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
caddyPrefixExtraConfig = lib.mkOption {
|
||||||
|
type = lib.types.lines;
|
||||||
|
default = "";
|
||||||
|
};
|
||||||
|
gunicornConcurrency = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 4;
|
||||||
|
};
|
||||||
|
workerConcurrency = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 4;
|
||||||
|
};
|
||||||
|
vouch = {
|
||||||
|
enable = lib.mkEnableOption "hackyplayer-vouch";
|
||||||
|
config = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
freeformType = yamlFormat.type;
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf cfg.enable {
|
||||||
|
users.users.hackyplayer = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "hackyplayer";
|
||||||
|
};
|
||||||
|
users.groups.hackyplayer = {};
|
||||||
|
|
||||||
|
systemd.services.hackyplayer = {
|
||||||
|
serviceConfig = {
|
||||||
|
User = "hackyplayer";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
PrivateTmp = true;
|
||||||
|
RemoveIPC = true;
|
||||||
|
RuntimeDirectory = "hackyplayer";
|
||||||
|
ExecStart = "${hackyplayer}/bin/gunicorn -w ${toString cfg.gunicornConcurrency} --bind unix:/run/hackyplayer/hackyplayer.sock hackyplayer.app:app";
|
||||||
|
EnvironmentFile = config.my.vault.secrets.hackyplayer-environment.path;
|
||||||
|
};
|
||||||
|
wantedBy = [ "hackyplayer.target" ];
|
||||||
|
environment = processedConfig;
|
||||||
|
};
|
||||||
|
systemd.services.hackyplayer-celery = {
|
||||||
|
serviceConfig = {
|
||||||
|
User = "hackyplayer";
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = "read-only";
|
||||||
|
PrivateTmp = true;
|
||||||
|
RemoveIPC = true;
|
||||||
|
RuntimeDirectory = "hackyplayer-celery";
|
||||||
|
ExecStart = "${hackyplayer}/bin/celery -A hackyplayer.tasks worker --concurrency ${toString cfg.workerConcurrency} --loglevel INFO --statedb /run/hackyplayer-celery/state.db";
|
||||||
|
ReadWritePaths = readWritePaths;
|
||||||
|
EnvironmentFile = config.my.vault.secrets.hackyplayer-environment.path;
|
||||||
|
};
|
||||||
|
wantedBy = [ "hackyplayer.target" ];
|
||||||
|
environment = processedConfig;
|
||||||
|
};
|
||||||
|
systemd.targets.hackyplayer = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
my.vault.secrets.hackyplayer-environment = {
|
||||||
|
reloadOrRestartUnits = ["hackyplayer.service"];
|
||||||
|
group = "root";
|
||||||
|
template = ''
|
||||||
|
{{ with secret "kv/apps/hackyplayer" }}
|
||||||
|
{{ .Data.data.environment }}
|
||||||
|
{{ end }}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = let
|
||||||
|
envPrelude = lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (var: val: "export ${var}=${lib.escapeShellArg val}") processedConfig);
|
||||||
|
in [
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "hackyplayer-celery";
|
||||||
|
text = ''
|
||||||
|
${envPrelude}
|
||||||
|
exec ${hackyplayer}/bin/celery -A hackyplayer.tasks "$@"
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "hackyplayer-flask";
|
||||||
|
text = ''
|
||||||
|
${envPrelude}
|
||||||
|
exec ${hackyplayer}/bin/flask -A hackyplayer.app:app "$@"
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."${cfg.hostname}" = {
|
||||||
|
listenAddresses = lib.mkIf (cfg.listenAddresses != null) cfg.listenAddresses;
|
||||||
|
extraConfig = ''
|
||||||
|
${cfg.caddyPrefixExtraHandles}
|
||||||
|
handle {
|
||||||
|
${cfg.caddyPrefixExtraConfig}
|
||||||
|
${lib.concatMapStrings (src: ''
|
||||||
|
route /static/video/${src.WEBDIR}/* {
|
||||||
|
uri strip_prefix /static/video/${src.WEBDIR}
|
||||||
|
root * ${src.DISKDIR}
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
'') cfg.config.VIDEO_SOURCES}
|
||||||
|
route /static/* {
|
||||||
|
uri strip_prefix /static
|
||||||
|
root * ${hackyplayer}/${hackyplayer.python.sitePackages}/hackyplayer/static
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
reverse_proxy unix//run/hackyplayer/hackyplayer.sock
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
systemd.services.caddy.serviceConfig.SupplementaryGroups = lib.mkAfter [ "hackyplayer" ];
|
||||||
|
|
||||||
|
services.redis.servers."".enable = true;
|
||||||
|
|
||||||
|
my.hackyplayer.vouch.enable = lib.mkDefault true;
|
||||||
|
my.hackyplayer.vouch.config = lib.mkDefault {
|
||||||
|
vouch = {
|
||||||
|
allowAllUsers = true;
|
||||||
|
cookie.domain = "voc.emf.camp";
|
||||||
|
listen = "unix:/run/hacky-vouchproxy/sock";
|
||||||
|
socket_mode = "0770";
|
||||||
|
socket_group = "hackyplayer";
|
||||||
|
document_root = "/_vouch";
|
||||||
|
};
|
||||||
|
|
||||||
|
oauth = let
|
||||||
|
base = "https://identity.emfcamp.org";
|
||||||
|
in {
|
||||||
|
provider = "oidc";
|
||||||
|
auth_url = "${base}/oauth2/authorize";
|
||||||
|
token_url = "${base}/oauth2/token";
|
||||||
|
user_info_url = "${base}/oauth2/userinfo";
|
||||||
|
scopes = [ "profile" ];
|
||||||
|
callback_url = "https://hackyplayer.voc.emf.camp/auth";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}) (lib.mkIf cfg.vouch.enable {
|
||||||
|
environment.etc."hacky-vouchproxy/config.yaml".source = yamlFormat.generate "hackyplayer-vouch-config.yaml" cfg.vouch.config;
|
||||||
|
systemd.services.hacky-vouchproxy = {
|
||||||
|
serviceConfig = {
|
||||||
|
User = "hacky-vouchproxy";
|
||||||
|
SupplementaryGroups = [ "hackyplayer" ];
|
||||||
|
DynamicUser = true;
|
||||||
|
RuntimeDirectory = "hacky-vouchproxy";
|
||||||
|
ExecStart = "${pkgs.vouch-proxy}/bin/vouch-proxy -config /etc/hacky-vouchproxy/config.yaml";
|
||||||
|
EnvironmentFile = config.my.vault.secrets.hacky-vouchproxy-environment.path;
|
||||||
|
};
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
my.vault.secrets.hacky-vouchproxy-environment = {
|
||||||
|
reloadOrRestartUnits = ["hacky-vouchproxy.service"];
|
||||||
|
group = "root";
|
||||||
|
template = ''
|
||||||
|
{{ with secret "kv/apps/hacky-vouchproxy" }}
|
||||||
|
{{ .Data.data.environment }}
|
||||||
|
{{ end }}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
my.hackyplayer.caddyPrefixExtraHandles = lib.mkBefore ''
|
||||||
|
handle /auth {
|
||||||
|
reverse_proxy unix//run/hacky-vouchproxy/sock {
|
||||||
|
rewrite /_vouch/auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle /_vouch/* {
|
||||||
|
reverse_proxy unix//run/hacky-vouchproxy/sock
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
my.hackyplayer.caddyPrefixExtraConfig = lib.mkBefore ''
|
||||||
|
reverse_proxy unix//run/hacky-vouchproxy/sock {
|
||||||
|
method GET
|
||||||
|
rewrite /_vouch/validate
|
||||||
|
|
||||||
|
header_up Host {host}
|
||||||
|
|
||||||
|
@good status 2xx
|
||||||
|
handle_response @good {
|
||||||
|
request_header X-Vouch-User {rp.header.X-Vouch-User}
|
||||||
|
}
|
||||||
|
|
||||||
|
@bad status 4xx
|
||||||
|
handle_response @bad {
|
||||||
|
header Location /_vouch/login?url=https://{host}{uri}&vouch-failcount={rp.header.X-Vouch-Failcount}&X-Vouch-Token={rp.header.X-Vouch-JWT}&error={rp.header.X-Vouch-Err}
|
||||||
|
respond 303 {
|
||||||
|
close
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ in {
|
||||||
../lib/rexxar-distributed.nix
|
../lib/rexxar-distributed.nix
|
||||||
../lib/quadv-ca/default.nix
|
../lib/quadv-ca/default.nix
|
||||||
((import ../../../third_party/lanzaboote.nix { }).nixosModules.lanzaboote)
|
((import ../../../third_party/lanzaboote.nix { }).nixosModules.lanzaboote)
|
||||||
|
../lib/hackyplayer.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
boot = lib.mkMerge [{
|
boot = lib.mkMerge [{
|
||||||
|
@ -299,6 +300,12 @@ in {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my.hackyplayer = {
|
||||||
|
enable = true;
|
||||||
|
listenAddresses = [ "127.0.0.1" "::1" ];
|
||||||
|
hostname = "hackyplayer.localhost";
|
||||||
|
};
|
||||||
|
|
||||||
# This value determines the NixOS release with which your system is to be
|
# This value determines the NixOS release with which your system is to be
|
||||||
# compatible, in order to avoid breaking some software such as database
|
# compatible, in order to avoid breaking some software such as database
|
||||||
# servers. You should change this only after NixOS release notes say you
|
# servers. You should change this only after NixOS release notes say you
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
../lib/bgp.nix
|
../lib/bgp.nix
|
||||||
../lib/gitlab-runner-cacher.nix
|
../lib/gitlab-runner-cacher.nix
|
||||||
#../lib/nixbuild-distributed.nix # error: build of '/nix/store/3r7456yr8r9g4fl7w6xbgqlbsdjwfvr4-stdlib-pkgs.json.drv' on 'ssh://eu.nixbuild.net' failed: unexpected: Built outputs are invalid
|
#../lib/nixbuild-distributed.nix # error: build of '/nix/store/3r7456yr8r9g4fl7w6xbgqlbsdjwfvr4-stdlib-pkgs.json.drv' on 'ssh://eu.nixbuild.net' failed: unexpected: Built outputs are invalid
|
||||||
|
../lib/hackyplayer.nix
|
||||||
|
../lib/emfminiserv.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# Otherwise _this_ machine won't enumerate things properly.
|
# Otherwise _this_ machine won't enumerate things properly.
|
||||||
|
@ -56,6 +58,13 @@
|
||||||
"/store" = zfs "zu2/safe/store";
|
"/store" = zfs "zu2/safe/store";
|
||||||
"/home" = zfs "zu2/safe/home";
|
"/home" = zfs "zu2/safe/home";
|
||||||
|
|
||||||
|
"/store/emf" = zfs "zu2/safe/store/emf";
|
||||||
|
"/store/emf/2024" = zfs "zu2/safe/store/emf/2024";
|
||||||
|
"/store/emf/2024/video" = zfs "zu2/safe/store/emf/2024/video";
|
||||||
|
"/store/emf/2024/video/source" = zfs "zu2/safe/store/emf/2024/video/source";
|
||||||
|
"/store/emf/2024/video/input" = zfs "zu2/safe/store/emf/2024/video/input";
|
||||||
|
"/store/emf/2024/video/output" = zfs "zu2/safe/store/emf/2024/video/output";
|
||||||
|
|
||||||
"/boot" = {
|
"/boot" = {
|
||||||
device = "/dev/disk/by-label/ESP";
|
device = "/dev/disk/by-label/ESP";
|
||||||
fsType = "vfat";
|
fsType = "vfat";
|
||||||
|
@ -81,6 +90,10 @@
|
||||||
allowedUDPPorts = [
|
allowedUDPPorts = [
|
||||||
51821 51822 51823
|
51821 51822 51823
|
||||||
34197 # factorio
|
34197 # factorio
|
||||||
|
443
|
||||||
|
];
|
||||||
|
allowedTCPPorts = [
|
||||||
|
80 443
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -348,8 +361,10 @@
|
||||||
wg-cofractal-ams01-private = wireguardSecret "privateKeyToCofractalAms01";
|
wg-cofractal-ams01-private = wireguardSecret "privateKeyToCofractalAms01";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
users.users.lukegb.extraGroups = lib.mkAfter [ "hackyplayer" ];
|
||||||
users.users.samw = {
|
users.users.samw = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
|
extraGroups = lib.mkAfter [ "hackyplayer "];
|
||||||
openssh.authorizedKeys.keys = [
|
openssh.authorizedKeys.keys = [
|
||||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDQdGzwYiallvWXIHgSAf2GOwMJKA8bxPmwyuO+vsd1HwB65hMRPCpKS+FNLIpkrADNnuhGS3xGCGSSuQ+zAu/g="
|
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDQdGzwYiallvWXIHgSAf2GOwMJKA8bxPmwyuO+vsd1HwB65hMRPCpKS+FNLIpkrADNnuhGS3xGCGSSuQ+zAu/g="
|
||||||
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLrJyUXmiFWb9vhlTWZLYr64IsKut+c9TGqq3/uwPDeF4X0Qb2jzxqXfQcDSztjR09JHbC8BOqfpYYT9LHahIo="
|
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLrJyUXmiFWb9vhlTWZLYr64IsKut+c9TGqq3/uwPDeF4X0Qb2jzxqXfQcDSztjR09JHbC8BOqfpYYT9LHahIo="
|
||||||
|
@ -377,5 +392,17 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my.hackyplayer = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "hackyplayer.voc.emf.camp";
|
||||||
|
workerConcurrency = 13;
|
||||||
|
};
|
||||||
|
my.emfminiserv = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "prerelease.voc.emf.camp";
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.rasdaemon.enable = true;
|
||||||
|
|
||||||
system.stateVersion = "24.05";
|
system.stateVersion = "24.05";
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,9 @@
|
||||||
my.apps.nixbuild = {};
|
my.apps.nixbuild = {};
|
||||||
my.apps.tumblrandom = {};
|
my.apps.tumblrandom = {};
|
||||||
my.apps.netbox = {};
|
my.apps.netbox = {};
|
||||||
|
my.apps.hacky-vouchproxy = {};
|
||||||
|
my.apps.hackyplayer = {};
|
||||||
|
my.apps.emfminiserv = {};
|
||||||
|
|
||||||
my.servers.etheroute-lon01.apps = [ "pomerium" ];
|
my.servers.etheroute-lon01.apps = [ "pomerium" ];
|
||||||
my.servers.howl.apps = [ "nixbuild" ];
|
my.servers.howl.apps = [ "nixbuild" ];
|
||||||
|
@ -88,5 +91,5 @@
|
||||||
my.servers.bvm-nixosmgmt.apps = [ "plex-pass" ];
|
my.servers.bvm-nixosmgmt.apps = [ "plex-pass" ];
|
||||||
my.servers.blade-tuvok.apps = [ "fup" ];
|
my.servers.blade-tuvok.apps = [ "fup" ];
|
||||||
my.servers.bvm-netbox.apps = [ "netbox" ];
|
my.servers.bvm-netbox.apps = [ "netbox" ];
|
||||||
my.servers.rexxar.apps = [ "deluge" "gitlab-runner" "nixbuild" ];
|
my.servers.rexxar.apps = [ "deluge" "gitlab-runner" "nixbuild" "hacky-vouchproxy" "hackyplayer" "emfminiserv" ];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue