summaryrefslogtreecommitdiff
path: root/Omni/Cloud
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Cloud')
-rw-r--r--Omni/Cloud/Chat.nix94
-rw-r--r--Omni/Cloud/Comms.nix5
-rw-r--r--Omni/Cloud/Comms/Coturn.nix10
-rw-r--r--Omni/Cloud/Comms/Jitsi.nix17
-rw-r--r--Omni/Cloud/Comms/Mumble.nix25
-rw-r--r--Omni/Cloud/Comms/Xmpp.nix210
-rw-r--r--Omni/Cloud/Git.nix119
-rw-r--r--Omni/Cloud/Gmnisrv.nix40
-rw-r--r--Omni/Cloud/Grocy.nix17
-rw-r--r--Omni/Cloud/Hardware.nix9
-rw-r--r--Omni/Cloud/Hub.nix57
-rw-r--r--Omni/Cloud/Mail.nix54
-rw-r--r--Omni/Cloud/Networking.nix48
-rw-r--r--Omni/Cloud/NostrRelay.nix39
-rw-r--r--Omni/Cloud/Ports.nix46
-rw-r--r--Omni/Cloud/Web.nix303
-rw-r--r--Omni/Cloud/Znc.nix76
-rwxr-xr-xOmni/Cloud/post-receive.sh39
18 files changed, 1208 insertions, 0 deletions
diff --git a/Omni/Cloud/Chat.nix b/Omni/Cloud/Chat.nix
new file mode 100644
index 0000000..7f86621
--- /dev/null
+++ b/Omni/Cloud/Chat.nix
@@ -0,0 +1,94 @@
+{ config, pkgs, ... }:
+#
+# a homeserver for matrix.org.
+#
+# this uses the config.networking.domain as the ACME host. be sure to add the
+# fqdn and element subdomains to security.acme.certs.<name>.extraDomainNames
+#
+# - nixos manual: https://nixos.org/nixos/manual/index.html#module-services-matrix
+#
+# to create new users:
+#
+# nix run nixpkgs.matrix-synapse
+# register_new_matrix_user -k <registration_shared_secret> http://localhost:<matrix_port>
+#
+let
+ fqdn = "matrix.${config.networking.domain}";
+ element = "chat.${config.networking.domain}";
+ matrix_port = 8448;
+in {
+ # matrix-synapse server. for what the settings mean, see:
+ # https://nixos.org/nixos/manual/index.html#module-services-matrix
+ #
+ services.matrix-synapse = {
+ enable = false;
+ settings.server_name = config.networking.domain;
+ #registration_shared_secret = "AkGRWSQLga3RoKRFnHhKoeCEIeZzu31y4TRzMRkMyRbBnETkVTSxilf24qySLzQn";
+ settings.listeners = [{
+ port = matrix_port;
+ bind_address = "::1";
+ type = "http";
+ tls = false;
+ x_forwarded = true;
+ resources = [{
+ names = [ "client" "federation" ];
+ compress = false;
+ }];
+ }];
+ };
+ # matrix needs a database
+ #
+ services.postgresql.enable = true;
+ # web proxy for the matrix server
+ #
+ services.nginx = {
+ enable = true;
+ recommendedTlsSettings = true;
+ recommendedOptimisation = true;
+ recommendedGzipSettings = true;
+ recommendedProxySettings = true;
+ virtualHosts = {
+ # route to matrix-synapse
+ "${config.networking.domain}" = {
+ locations."= /.well-known/matrix/server".extraConfig =
+ let server = { "m.server" = "${fqdn}:443"; };
+ in ''
+ add_header Content-Type application/json;
+ return 200 '${builtins.toJSON server}';
+ '';
+ locations."= /.well-known/matrix/client".extraConfig = let
+ client = {
+ "m.homeserver" = { "base_url" = "https://${fqdn}"; };
+ "m.identity_server" = { "base_url" = "https://vector.im"; };
+ };
+ in ''
+ add_header Content-Type application/json;
+ add_header Access-Control-Allow-Origin *;
+ return 200 '${builtins.toJSON client}';
+ '';
+ };
+ # reverse proxy for matrix client-server and server-server communication
+ "${fqdn}" = {
+ forceSSL = true;
+ useACMEHost = config.networking.domain;
+ locations."/".extraConfig = ''
+ return 404;
+ '';
+ locations."/_matrix" = {
+ proxyPass = "http://[::1]:${toString matrix_port}";
+ };
+ };
+ };
+ };
+ # matrix client, available at chat.simatime.com
+ #
+ # note that element and matrix-synapse must be on separate fqdn's to
+ # protect from XSS attacks:
+ # https://github.com/vector-im/element-web#important-security-note
+ #
+ services.nginx.virtualHosts."${element}" = {
+ useACMEHost = config.networking.domain;
+ forceSSL = true;
+ root = pkgs.element-web;
+ };
+}
diff --git a/Omni/Cloud/Comms.nix b/Omni/Cloud/Comms.nix
new file mode 100644
index 0000000..bf7a763
--- /dev/null
+++ b/Omni/Cloud/Comms.nix
@@ -0,0 +1,5 @@
+{ ... }:
+
+{
+ imports = [ ./Comms/Xmpp.nix ./Comms/Mumble.nix ];
+}
diff --git a/Omni/Cloud/Comms/Coturn.nix b/Omni/Cloud/Comms/Coturn.nix
new file mode 100644
index 0000000..93093f0
--- /dev/null
+++ b/Omni/Cloud/Comms/Coturn.nix
@@ -0,0 +1,10 @@
+{ config, ... }:
+
+{
+ services.coturn = {
+ enable = true;
+ cert = "/var/lib/acme/${config.networking.domain}/fullchain.pem";
+ pkey = "/var/lib/acme/${config.networking.domain}/key.pem";
+ cli-ip = "127.0.0.1";
+ };
+}
diff --git a/Omni/Cloud/Comms/Jitsi.nix b/Omni/Cloud/Comms/Jitsi.nix
new file mode 100644
index 0000000..17aeced
--- /dev/null
+++ b/Omni/Cloud/Comms/Jitsi.nix
@@ -0,0 +1,17 @@
+{ config, ... }:
+
+{
+ services.jitsi-meet = {
+ enable = true;
+ config = {
+ enableWelcomePage = false;
+ defaulLang = "en";
+ };
+
+ prosody.enable = true;
+ nginx.enable = true;
+ jibri.enable = false;
+ jicofo.enable = false;
+ videobridge.enable = false;
+ };
+}
diff --git a/Omni/Cloud/Comms/Mumble.nix b/Omni/Cloud/Comms/Mumble.nix
new file mode 100644
index 0000000..66d21a5
--- /dev/null
+++ b/Omni/Cloud/Comms/Mumble.nix
@@ -0,0 +1,25 @@
+{ config, ... }:
+
+# mumble and related services
+let ports = import ../Ports.nix;
+in {
+ services.murmur = {
+ enable = true;
+ openFirewall = true;
+ environmentFile = "/var/lib/murmur/murmurd.env";
+ registerHostname = config.networking.domain;
+ registerName = config.networking.domain;
+ };
+
+ services.botamusique = {
+ enable = true;
+ settings = {
+ webinterface = {
+ enabled = true;
+ listening_addr = "127.0.0.1";
+ listening_port = ports.botamusique;
+ };
+ radio = { lofi = "https://live.hunter.fm/lofi_high"; };
+ };
+ };
+}
diff --git a/Omni/Cloud/Comms/Xmpp.nix b/Omni/Cloud/Comms/Xmpp.nix
new file mode 100644
index 0000000..ad8649b
--- /dev/null
+++ b/Omni/Cloud/Comms/Xmpp.nix
@@ -0,0 +1,210 @@
+{ config, pkgs, ... }:
+#
+# xmpp chat service
+#
+let
+ rootDomain = config.networking.domain; # simatime.com
+ ssl = {
+ cert = "/var/lib/acme/${rootDomain}/fullchain.pem";
+ key = "/var/lib/acme/${rootDomain}/key.pem";
+ };
+in {
+ networking.firewall.allowedTCPPorts = [
+ # https://prosody.im/doc/ports
+ 5000 # file transfer
+ 5222 # client connections
+ 5269 # server-to-server
+ 5280 # http
+ 5281 # https
+ 5347 # external components
+ 5582 # telnet console
+ ];
+
+ services.prosody = {
+ enable = true;
+ package =
+ pkgs.prosody.override { withCommunityModules = [ "conversejs" ]; };
+
+ # when i learn how to use security.acme better, and use separate certs, then i
+ # can fix this group
+ group = "nginx";
+ admins = [ "bsima@${rootDomain}" ];
+ allowRegistration = true;
+ inherit ssl;
+ uploadHttp = {
+ domain = "upload.${rootDomain}";
+ uploadExpireAfter = toString (60 * 60 * 24 * 30); # 30 days, as seconds
+ };
+
+ modules = {
+ announce = true;
+ blocklist = true;
+ bookmarks = true;
+ bosh = true;
+ carbons = true;
+ cloud_notify = true;
+ csi = true;
+ dialback = true;
+ disco = true;
+ groups = true;
+ http_files = false; # hm, look into this
+ motd = true;
+ pep = true;
+ ping = true;
+ private = true;
+ proxy65 = true;
+ register = true;
+ roster = true;
+ server_contact_info = true;
+ smacks = true;
+ vcard = true;
+ watchregistrations = true;
+ websocket = true;
+ welcome = true;
+ };
+
+ extraConfig = ''
+ conversejs_options = {
+ allow_registration = true;
+ bosh_service_url = "https://${rootDomain}/http-bind";
+ debug = true;
+ loglevel = "debug";
+ -- default_domain = "${rootDomain}";
+ -- domain_placeholder = "${rootDomain}";
+ -- jid = "${rootDomain}";
+ -- keepalive = true;
+ -- registration_domain = "${rootDomain}";
+ websocket_url = "wss://${rootDomain}/xmpp-websocket";
+ }
+
+ cross_domain_websocket = { "https://${rootDomain}", "https://anon.${rootDomain}" }
+ cross_domain_bosh = false; -- handle this with nginx
+ consider_bosh_secure = true;
+
+ -- this is a virtualhost that allows anonymous authentication. use this
+ -- for a public lobby. the nix module doesn't support 'authentication'
+ -- so i have to do this here.
+ VirtualHost "anon.${rootDomain}"
+ authentication = "anonymous"
+ ssl = {
+ cafile = "/etc/ssl/certs/ca-bundle.crt";
+ key = "${ssl.key}";
+ certificate = "${ssl.cert}";
+ };
+ '';
+
+ muc = [
+ {
+ domain = "conference.${rootDomain}";
+ maxHistoryMessages = 10000;
+ name = "Chat Rooms";
+ restrictRoomCreation = "admin";
+ roomDefaultHistoryLength = 20;
+ roomDefaultMembersOnly = true;
+ roomDefaultModerated = true;
+ roomDefaultPublic = false;
+ }
+ {
+ domain = "chat.${rootDomain}";
+ maxHistoryMessages = 10000;
+ name = "Chat Rooms";
+ restrictRoomCreation = false;
+ roomDefaultHistoryLength = 200;
+ roomDefaultMembersOnly = false;
+ roomDefaultModerated = false;
+ roomDefaultPublic = true;
+ roomDefaultPublicJids = true;
+ }
+ ];
+
+ virtualHosts = {
+ "${rootDomain}" = {
+ domain = "${rootDomain}";
+ enabled = true;
+ inherit ssl;
+ };
+ };
+ };
+
+ services.prosody-filer = { enable = true; };
+
+ services.nginx.virtualHosts."${rootDomain}".locations = {
+ "/http-bind" = {
+ proxyPass = "https://${rootDomain}:5281/http-bind";
+ extraConfig = ''
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_buffering off;
+ add_header Access-Control-Allow-Origin "*";
+ '';
+ };
+
+ "/xmpp-websocket" = {
+ proxyPass = "https://${rootDomain}:5281/xmpp-websocket";
+ extraConfig = ''
+ proxy_http_version 1.1;
+ proxy_buffering off;
+ proxy_set_header Host $host;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 86400;
+ add_header Access-Control-Allow-Origin "*";
+ '';
+ };
+
+ "/chat" = {
+ proxyPass = "https://${rootDomain}:5281/conversejs";
+ extraConfig = ''
+ add_header Access-Control-Allow-Origin "*";
+ '';
+ };
+ };
+
+ services.nginx.virtualHosts."anon.${rootDomain}" = {
+ useACMEHost = "${rootDomain}";
+ forceSSL = true;
+ locations = {
+ "/http-bind" = {
+ proxyPass = "https://anon.${rootDomain}:5281/http-bind";
+ extraConfig = ''
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_buffering off;
+ if ($request_method ~* "(GET|POST)") {
+ add_header Access-Control-Allow-Origin "*";
+ }
+ if ($request_method = OPTIONS) {
+ add_header Access-Control-Allow-Origin "*";
+ add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, HEAD";
+ add_header Access-Control-Allow-Headers "Authorization, Origin, X-Requested-With, Content-Type, Accept";
+ return 200;
+ }
+ '';
+ };
+ };
+ };
+
+ users.users.nginx.extraGroups = [ "prosody" ];
+
+ security.acme.certs.${rootDomain}.extraDomainNames = [
+ # these stopped working idk why
+ #"upload.${rootDomain}"
+ #"conference.${rootDomain}"
+ "anon.${rootDomain}"
+ "chat.${rootDomain}"
+ ];
+
+ #security.acme.certs.prosody = {
+ # domain = "${domain}";
+ # group = "prosody";
+ # dnsProvider = "rfc2136";
+ # #credentialsFile = config.secrets.files.dns_creds.path;
+ # postRun = "systemctl restart prosody";
+ # extraDomainNames = [
+ # domain
+ # "upload.${domain}"
+ # ];
+ #};
+}
diff --git a/Omni/Cloud/Git.nix b/Omni/Cloud/Git.nix
new file mode 100644
index 0000000..bc97d23
--- /dev/null
+++ b/Omni/Cloud/Git.nix
@@ -0,0 +1,119 @@
+{ lib, config, pkgs, ... }:
+
+let
+ inherit (config.networking) domain;
+ root = "/var/git";
+ ports = import ./Ports.nix;
+in {
+ services = {
+ cgit.cloud = {
+ enable = true;
+ nginx.location = "/git";
+ nginx.virtualHost = "/git";
+ scanPath = "/var/git/repositories";
+ settings = {
+ strict-export = "git-daemon-export-ok";
+ css = "/git/cgit.css";
+ logo = "/git/cgit.png";
+ root-title = "ben's git repos";
+ root-desc = "building";
+ enable-git-config = 1;
+ clone-url = lib.strings.concatStringsSep " " [
+ "https://$HTTP_HOST/git/$CGIT_REPO_URL"
+ "git://$HTTP_HOST/$CGIT_REPO_URL"
+ "git@$HTTP_HOST:$CGIT_REPO_URL"
+ ];
+ };
+ };
+ gitolite = {
+ enable = true;
+ enableGitAnnex = true;
+ dataDir = root;
+ user = "git";
+ group = "git";
+ # the umask is necessary to give the git group read permissions, otherwise
+ # git-daemon et al can't access the repos
+ extraGitoliteRc = ''
+ $RC{SITE_INFO} = 'a computer is a bicycle for the mind.';
+ $RC{UMASK} = 0027;
+ $RC{GIT_CONFIG_KEYS} = '.*';
+ '';
+ adminPubkey = lib.trivial.pipe ../Keys/Ben.pub [
+ builtins.readFile
+ (lib.strings.splitString "\n")
+ lib.lists.head
+ ];
+ # commonHooks = [ ./git-hooks ];
+ };
+ gitDaemon = {
+ enable = true;
+ basePath = "${root}/repositories";
+ listenAddress = domain;
+ user = "gitDaemon";
+ group = "gitDaemon";
+ };
+ gerrit = {
+ enable = false;
+ builtinPlugins = [
+ "commit-message-length-validator"
+ "delete-project"
+ "plugin-manager"
+ "singleusergroup"
+ "reviewnotes"
+ ];
+ jvmOpts = [
+ # https://stackoverflow.com/a/71817404
+ "--add-opens"
+ "java.base/java.lang=ALL-UNNAMED"
+ "--add-opens"
+ "java.base/java.util=ALL-UNNAMED"
+ ];
+ plugins = [
+ (pkgs.fetchurl {
+ url =
+ "https://github.com/davido/gerrit-oauth-provider/releases/download/v3.5.1/gerrit-oauth-provider.jar";
+ sha256 = "sha256-MS3ElMRUrBX4miiflepMETRK3SaASqpqO3nUn9kq3Gk=";
+ })
+ ];
+ listenAddress = "[::]:${toString ports.gerrit}";
+ serverId = "cc6cca15-2a7e-4946-89b9-67f5d6d996ae";
+ settings = {
+ auth.type = "OAUTH";
+ auth.gitBasicAuthPolicy = "HTTP";
+ download.command = [ "checkout" "cherry_pick" "pull" "format_patch" ];
+ gerrit.canonicalWebUrl = "https://gerrit.${domain}";
+ httpd.listenUrl =
+ "proxy-https://${config.services.gerrit.listenAddress}";
+ plugin.gerrit-oauth-provider-github-oauth = {
+ root-url = "https://github.com";
+ client-id = "e48084aa0eebe31a2b18";
+ };
+ sshd.advertisedAddress =
+ "gerrit.${domain}:${toString ports.gerrit-ssh}";
+ sshd.listenAddress = "[::]:${toString ports.gerrit-ssh}";
+ };
+ };
+ nginx.virtualHosts."gerrit.${domain}" = {
+ forceSSL = true;
+ useACMEHost = domain;
+ locations."/" = {
+ proxyPass = "http://localhost:${toString ports.gerrit}";
+ extraConfig = ''
+ proxy_set_header X-Forwarded-For $remote_addr;
+ '';
+ };
+ };
+ };
+ # need to specify that these users can access git files by being part of the
+ # git group
+ users.users = {
+ gitDaemon = {
+ group = "gitDaemon";
+ isSystemUser = true;
+ description = "Git daemon user";
+ extraGroups = [ "git" ];
+ };
+ "nginx".extraGroups = [ "git" ];
+ };
+ users.groups = { gitDaemon = { }; };
+}
diff --git a/Omni/Cloud/Gmnisrv.nix b/Omni/Cloud/Gmnisrv.nix
new file mode 100644
index 0000000..e2a66f6
--- /dev/null
+++ b/Omni/Cloud/Gmnisrv.nix
@@ -0,0 +1,40 @@
+{ lib, config, pkgs, ... }:
+
+let cfg = config.services.gmnisrv;
+in {
+ meta.maintainers = [ lib.maintainers.bsima ];
+ options.services.gmnisrv = {
+ enable = lib.mkEnableOption "Enable the gmnisrv service";
+ listen = lib.mkOption {
+ description = "Addresses and ports on which to listen.";
+ default = lib.mkDefault "0.0.0.0:1965 [::]:1965";
+ };
+ settings = lib.mkOption {
+ # type = cfgFormat.type;
+ description = ''
+ Configuration for gmnisrv. See gmnisrv.ini(5) for supported settings.
+ '';
+ default = {
+ ":tls" = { "store" = lib.mkDefault "${cfg.dataDir}/certs"; };
+ };
+ };
+ dataDir = lib.mkOption {
+ type = lib.types.str;
+ default = "/var/lib/gemini";
+ description = "Where gmnisrv should store certs and other data.";
+ };
+ };
+ config = lib.mkIf cfg.enable {
+ systemd.services.gmnisrv = {
+ description = "gmnisrv service";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ script = let
+ ini = lib.generators.toINIWithGlobalSection { } {
+ globalSection = { "listen" = cfg.listen; };
+ sections = cfg.settings;
+ };
+ in "${pkgs.gmnisrv}/bin/gmnisrv -C ${ini}";
+ };
+ };
+}
diff --git a/Omni/Cloud/Grocy.nix b/Omni/Cloud/Grocy.nix
new file mode 100644
index 0000000..697c2f1
--- /dev/null
+++ b/Omni/Cloud/Grocy.nix
@@ -0,0 +1,17 @@
+{ ... }:
+
+{
+ services.grocy = {
+ enable = true;
+ hostName = "grocy.simatime.com";
+ nginx.enableSSL = false; # set in Web.nix
+ settings = {
+ calendar = {
+ firstDayOfWeek = 1;
+ showWeekNumber = true;
+ };
+ currency = "USD";
+ culture = "en";
+ };
+ };
+}
diff --git a/Omni/Cloud/Hardware.nix b/Omni/Cloud/Hardware.nix
new file mode 100644
index 0000000..8fdbd4e
--- /dev/null
+++ b/Omni/Cloud/Hardware.nix
@@ -0,0 +1,9 @@
+{ modulesPath, ... }: {
+ imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
+ boot.loader.grub.device = "/dev/vda";
+ boot.initrd.kernelModules = [ "nvme" ];
+ fileSystems."/" = {
+ device = "/dev/vda1";
+ fsType = "ext4";
+ };
+}
diff --git a/Omni/Cloud/Hub.nix b/Omni/Cloud/Hub.nix
new file mode 100644
index 0000000..39bbdd0
--- /dev/null
+++ b/Omni/Cloud/Hub.nix
@@ -0,0 +1,57 @@
+{ lib, pkgs, config, ... }:
+
+let
+ ssl = {
+ cert = "/var/lib/acme/${config.networking.domain}/fullchain.pem";
+ key = "/var/lib/acme/${config.networking.domain}/key.pem";
+ };
+ ports = import ./Ports.nix;
+ mkPlugin = path: settings: {
+ plugin = "${pkgs.uhub}/plugins/${path}";
+ inherit settings;
+ };
+ motdFile = pkgs.writeText "motd" ''
+ Meshheads write code.
+ '';
+ rulesFile = pkgs.writeText "rules" ''
+ 1. x
+ 2. (λx.M)
+ 3. (M N)
+ 4. Profit.
+ '';
+in {
+ networking.firewall.allowedUDPPorts = [ ports.meshhub ];
+ networking.firewall.allowedTCPPorts = [ ports.meshhub ];
+
+ services.uhub = {
+ "meshhub" = {
+ enable = true;
+ enableTLS = false;
+ settings = {
+ hub_name = "meshhub";
+ hub_description = "vaporware is dead. long live vaporware";
+ server_port = toString ports.meshhub;
+ server_bind_addr = "any";
+ max_users = 14; # we only have 14 users in meshheads chat
+ tls_private_key = ssl.key;
+ tls_certificate = ssl.cert;
+ tls_enable = "no";
+ tls_require = "no";
+ };
+ plugins = lib.attrsets.mapAttrsToList mkPlugin {
+ #"mod_logging.so" = {
+ # file = "/var/log/uhub/meshhub.log";
+ #};
+ "mod_welcome.so" = {
+ motd = "${motdFile}";
+ rules = "${rulesFile}";
+ };
+ "mod_chat_history.so" = {
+ history_max = "200";
+ history_default = "10";
+ history_connect = "5";
+ };
+ };
+ };
+ };
+}
diff --git a/Omni/Cloud/Mail.nix b/Omni/Cloud/Mail.nix
new file mode 100644
index 0000000..fa99cf3
--- /dev/null
+++ b/Omni/Cloud/Mail.nix
@@ -0,0 +1,54 @@
+{ ... }:
+
+/* Known issues:
+
+ - when the acme cert gets refreshed, you need to manually restart dovecot
+ - when restarting dovecot, it might hang, in that case do:
+ systemctl --job-mode=ignore-dependencies restart dovecot2 postfix
+*/
+
+{
+ mailserver = {
+ enable = true;
+ monitoring = {
+ enable = false;
+ alertAddress = "bsima@me.com";
+ };
+ fqdn = "simatime.com";
+ domains = [ "simatime.com" "bsima.me" ];
+ certificateScheme = 3; # let's encrypt
+ enableImap = true;
+ enablePop3 = true;
+ enableImapSsl = true;
+ enablePop3Ssl = true;
+ enableManageSieve = true;
+ virusScanning = false; # ur on ur own
+ localDnsResolver = true;
+
+ loginAccounts = {
+ "ben@simatime.com" = {
+ hashedPasswordFile = "/home/ben/hashed-mail-password";
+ aliases = [
+ # my default email
+ "ben@bsima.me"
+ # admin stuff
+ "postmaster@simatime.com"
+ "abuse@simatime.com"
+ ];
+ catchAll = [ "simatime.com" "bsima.me" ];
+ quota = "10G";
+ };
+ "dev@simatime.com" = {
+ hashedPasswordFile = "/home/ben/hashed-mail-password";
+ aliases = [ "dev@bsima.me" ];
+ quota = "10G";
+ };
+ "nick@simatime.com" = {
+ hashedPassword =
+ "$6$31P/Mg8k8Pezy1e$Fn1tDyssf.1EgxmLYFsQpSq6RP4wbEvP/UlBlXQhyKA9FnmFtJteXsbJM1naa8Kyylo8vZM9zmeoSthHS1slA1";
+ aliases = [ "nicolai@simatime.com" ];
+ quota = "1G";
+ };
+ };
+ };
+}
diff --git a/Omni/Cloud/Networking.nix b/Omni/Cloud/Networking.nix
new file mode 100644
index 0000000..1c1f832
--- /dev/null
+++ b/Omni/Cloud/Networking.nix
@@ -0,0 +1,48 @@
+{ lib, ... }: {
+ # This file was populated at runtime with the networking
+ # details gathered from the active system.
+ networking = {
+ nameservers = [ "8.8.8.8" ];
+ defaultGateway = "143.198.112.1";
+ defaultGateway6 = "2604:a880:400:d0::1";
+ dhcpcd.enable = false;
+ usePredictableInterfaceNames = lib.mkForce false;
+ interfaces = {
+ eth0 = {
+ ipv4.addresses = [
+ {
+ address = "143.198.118.179";
+ prefixLength = 20;
+ }
+ {
+ address = "10.10.0.7";
+ prefixLength = 16;
+ }
+ ];
+ ipv6.addresses = [
+ {
+ address = "2604:a880:400:d0::19f1:7001";
+ prefixLength = 64;
+ }
+ {
+ address = "fe80::a06e:26ff:fee1:941";
+ prefixLength = 64;
+ }
+ ];
+ ipv4.routes = [{
+ address = "143.198.112.1";
+ prefixLength = 32;
+ }];
+ ipv6.routes = [{
+ address = "2604:a880:400:d0::1";
+ prefixLength = 128;
+ }];
+ };
+
+ };
+ };
+ services.udev.extraRules = ''
+ ATTR{address}=="a2:6e:26:e1:09:41", NAME="eth0"
+ ATTR{address}=="f2:4e:52:1a:72:ef", NAME="eth1"
+ '';
+}
diff --git a/Omni/Cloud/NostrRelay.nix b/Omni/Cloud/NostrRelay.nix
new file mode 100644
index 0000000..0be8a6f
--- /dev/null
+++ b/Omni/Cloud/NostrRelay.nix
@@ -0,0 +1,39 @@
+{ config, pkgs, ... }:
+
+let
+ ports = import ./Ports.nix;
+ dataDir = "/var/lib/nostr-rs-relay";
+ # https://git.sr.ht/~gheartsfield/nostr-rs-relay/tree/master/config.toml
+ cfg = pkgs.writeText "config.toml" ''
+ [info]
+ name = "simatime"
+ relay_url = "wss://nostr.simatime.com"
+ description = "yet another nostr relay"
+
+ [database]
+ data_directory = "/var/lib/nostr-rs-relay"
+
+ [network]
+ address = "0.0.0.0"
+ port = ${toString ports.nostr-relay}
+ '';
+in {
+ config.systemd.services.nostr-relay = {
+ path = [ pkgs.nostr-rs-relay ];
+ wantedBy = [ "multi-user.target" ];
+ preStart = ''
+ mkdir -p ${dataDir}
+ cat "${cfg}" > ${dataDir}/config.toml
+ '';
+ script = "nostr-rs-relay --db ${dataDir}";
+ serviceConfig = {
+ Environment =
+ [ "RUST_LOG=info,nostr_rs_relay=info" "APP_DATA=${dataDir}" ];
+ WorkingDirectory = dataDir;
+ KillSignal = "INT";
+ Type = "simple";
+ Restart = "always";
+ RestartSec = "1";
+ };
+ };
+}
diff --git a/Omni/Cloud/Ports.nix b/Omni/Cloud/Ports.nix
new file mode 100644
index 0000000..5b8446c
--- /dev/null
+++ b/Omni/Cloud/Ports.nix
@@ -0,0 +1,46 @@
+{
+ bensIp = "24.140.205.252"; # not a port, but it's convenient
+ barrier = 24800;
+ bitcoind = 8333;
+ bitcoind-rpc = 8332;
+ botamusique = 8181;
+ dandel-rovbur = 8080;
+ delugeWeb = 8112;
+ dns = 53;
+ dragons = 8095;
+ et = 2022;
+ gemini = 1965;
+ gerrit = 8081;
+ gerrit-ssh = 2222;
+ git = 9418;
+ headscale = 8844;
+ hoogle = 8008;
+ http = 80;
+ httpdev = {
+ from = 8000;
+ to = 8099;
+ };
+ https = 443;
+ invidious = 8086;
+ jellyfin = 8096;
+ jupyter = 4000;
+ k3s = 6443;
+ libreddit = 8085;
+ meshhub = 1511;
+ mpd = 6600;
+ mpd-stream = 8097;
+ murmur = 64738;
+ nostr-relay = 8084;
+ radicale = 5232;
+ sabten = 8081;
+ ssh = 22;
+ stableDiffusion = 8501;
+ syncthing-gui = 8384;
+ tor = 144;
+ torrents = {
+ from = 3000;
+ to = 3099;
+ };
+ wireguard = 51820;
+ znc = 5000;
+}
diff --git a/Omni/Cloud/Web.nix b/Omni/Cloud/Web.nix
new file mode 100644
index 0000000..0e080f5
--- /dev/null
+++ b/Omni/Cloud/Web.nix
@@ -0,0 +1,303 @@
+{ config, ... }:
+
+let
+ rootDomain = config.networking.domain;
+ ports = import ./Ports.nix;
+in {
+ imports = [ ./Gmnisrv.nix ];
+ networking.firewall = {
+ allowedTCPPorts = [
+ ports.ssh
+ ports.git
+ ports.http
+ ports.https
+ ports.sabten
+ ports.gemini
+ ports.radicale
+ ports.znc
+ ports.gerrit-ssh
+ ];
+ };
+
+ services = {
+
+ libreddit = {
+ enable = true;
+ address = "127.0.0.1";
+ openFirewall = true;
+ port = ports.libreddit;
+ };
+
+ invidious = {
+ enable = true;
+ database.createLocally = true;
+ domain = "youtube.${rootDomain}";
+ nginx.enable = false; # do this myself, below
+ port = ports.invidious;
+ };
+
+ radicale = {
+ enable = true;
+ rights = {
+ # Allow reading root collection for authenticated users
+ root = {
+ user = ".+";
+ collection = "";
+ permissions = "R";
+ };
+ # Allow reading and writing principal collection (same as username)
+ principal = {
+ user = ".+";
+ collection = "{user}";
+ permissions = "RW";
+ };
+ # Allow reading and writing calendars and address books that are direct
+ # children of the principal collection
+ calendars = {
+ user = ".+";
+ collection = "{user}/[^/]+";
+ permissions = "rw";
+ };
+ # Allow any authenticated user to modify the public collection
+ public = {
+ user = ".*";
+ collection = "public/.*";
+ permissions = "rw";
+ };
+ };
+ settings = {
+ server = {
+ hosts = [
+ "0.0.0.0:${toString ports.radicale}"
+ "[::]:${toString ports.radicale}"
+ ];
+ };
+ auth = {
+ type = "htpasswd";
+ htpasswd_filename = "/etc/radicale/users";
+ htpasswd_encryption = "plain";
+ };
+ };
+ };
+
+ gmnisrv = {
+ enable = false;
+ listen = "0.0.0.0:${toString ports.gemini} [::]:${toString ports.gemini}";
+ settings = {
+ ":tls" = { store = "/var/lib/gmnisrv"; };
+ "bsima.me" = { "root" = "/var/web/ben"; };
+ "${rootDomain}" = {
+ "root" = "/var/web/simatime.com";
+ "cgi" = "on";
+ };
+ };
+ };
+
+ nginx = {
+ enable = true;
+ recommendedGzipSettings = true;
+ recommendedOptimisation = true;
+ recommendedProxySettings = true;
+ recommendedTlsSettings = true;
+ statusPage = true;
+
+ user = "nginx";
+ group = "nginx";
+
+ virtualHosts = {
+ ${rootDomain} = {
+ forceSSL = true;
+ enableACME = true;
+ locations = {
+ # the nginx/cgit module puts a '/' at the end of 'location', so we need to
+ # redirect '/git' to '/git/'
+ "/git".return = "301 https://$host/git/";
+ # nostr nip-5 verification
+ "/.well-known/nostr.json".return = "200 '${
+ builtins.toJSON {
+ names.bensima =
+ "2fa4b9ba71b6dab17c4723745bb7850dfdafcb6ae1a8642f76f9c64fa5f43436";
+ }
+ }'";
+ # disabled for nixpert test
+ "/" = {
+ root = "/var/web/simatime.com";
+ extraConfig = ''
+ autoindex on;
+ '';
+ };
+ # serve /~$USER paths
+ "~ ^/~(.+?)(/.*)?$" = {
+ alias = "/var/web/$1$2";
+ index = "index.html index.htm";
+ extraConfig = ''
+ autoindex on;
+ '';
+ };
+ };
+ };
+
+ "bsima.me" = {
+ locations."/" = {
+ root = "/var/web/ben";
+ index = "index.html index.htm";
+ extraConfig = ''
+ autoindex on;
+ '';
+ };
+ serverAliases = [ "www.bsima.me" ];
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+
+ "hoogle.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://${ports.bensIp}:${toString ports.hoogle}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+
+ "tv.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://${ports.bensIp}:${toString ports.jellyfin}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+
+ "cal.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://localhost:${toString ports.radicale}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ extraConfig = ''
+ proxy_set_header X-Script-Name /radicale;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+ proxy_pass_header Authorization;
+ '';
+ };
+
+ "reddit.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://localhost:${toString ports.libreddit}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+ "www.reddit.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ globalRedirect = "reddit.${rootDomain}";
+ };
+ "old.reddit.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ globalRedirect = "reddit.${rootDomain}";
+ };
+
+ "youtube.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://localhost:${toString ports.invidious}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+ "www.youtube.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ globalRedirect = "youtube.${rootDomain}";
+ };
+ "m.youtube.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ globalRedirect = "youtube.${rootDomain}";
+ };
+
+ "dandel-rovbur.${rootDomain}" = {
+ locations."/".proxyPass =
+ "http://${ports.bensIp}:${toString ports.dandel-rovbur}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+
+ "sabten.${rootDomain}" = {
+ locations."/".proxyPass = "http://localhost:${toString ports.sabten}";
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ };
+
+ "sd.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ locations."/" = {
+ proxyPass =
+ "http://${ports.bensIp}:${toString ports.stableDiffusion}";
+ proxyWebsockets = true;
+ };
+ };
+
+ "music.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ locations."/".proxyPass =
+ "http://localhost:${toString ports.botamusique}";
+ };
+
+ "nostr.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ locations."/" = {
+ proxyPass = "http://localhost:${toString ports.nostr-relay}";
+ extraConfig = ''
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_set_header Host $host;
+ '';
+ };
+ };
+
+ "notebook.${rootDomain}" = {
+ forceSSL = true;
+ useACMEHost = rootDomain;
+ locations = {
+ "/" = {
+ proxyPass = "http://${ports.bensIp}:${toString ports.jupyter}";
+ proxyWebsockets = true;
+ extraConfig = ''
+ proxy_buffering off;
+ proxy_read_timeout 86400;
+ '';
+ };
+ "/(api/kernels/[^/]+/channels|terminals/websocket)/" = {
+ proxyPass = "http://${ports.bensIp}:${toString ports.jupyter}";
+ proxyWebsockets = true;
+ };
+ };
+ };
+
+ };
+ };
+ };
+
+ # This must contain all of the other domains we host
+ security.acme.certs.${rootDomain}.extraDomainNames =
+ [ "bsima.me" "www.bsima.me" ] ++ map (sub: "${sub}.${rootDomain}") [
+ "music"
+ "tv"
+ "matrix"
+ "chat"
+ "hoogle"
+ "dandel-rovbur"
+ "sabten"
+ "cal"
+ "notebook"
+ "nostr"
+ "reddit"
+ "old.reddit"
+ "www.reddit"
+ "youtube"
+ "www.youtube"
+ "m.youtube"
+ "sd"
+ "gerrit"
+ ];
+}
diff --git a/Omni/Cloud/Znc.nix b/Omni/Cloud/Znc.nix
new file mode 100644
index 0000000..e68ebc7
--- /dev/null
+++ b/Omni/Cloud/Znc.nix
@@ -0,0 +1,76 @@
+/* N.B.: generate znc passwords with 'nix-shell -p znc --command "znc --makepass"'
+
+ - https://wiki.znc.in/Configuration
+*/
+
+{ pkgs, ... }:
+
+{
+ services = {
+ znc = {
+ enable = true;
+ openFirewall = true;
+ modulePackages = with pkgs.zncModules;
+ [
+ #backlog clientaway clientbuffer
+ #ignore
+ ];
+ useLegacyConfig = false;
+ config = {
+ LoadModule = [ "adminlog" ];
+ Motd = "welcome to znc.simatime.com";
+ User.bsima = {
+ Admin = true;
+ Nick = "bsima";
+ AltNick = "bsima1";
+ LoadModule = [ "chansaver" "controlpanel" "log" ];
+ Network = {
+ efnet = {
+ Server = "irc.efnet.info +6697";
+ LoadModule = [ "simple_away" ];
+ };
+ libera = {
+ Server = "irc.libera.chat +6697";
+ LoadModule = [ "simple_away" "nickserv" "sasl" ];
+ Nick = "bsima";
+ Chan = {
+ "#emacs" = { Detached = true; };
+ "#guile" = { };
+ "#guix" = { Detached = true; };
+ "#haskell" = { };
+ "#hledger" = { };
+ "#nixos" = { };
+ "#notmuch" = { Detached = true; };
+ "#org-mode" = { Detached = true; };
+ "#scheme" = { Detached = true; };
+ "#sr.ht" = { Detached = true; };
+ "#xmonad" = { Detached = true; };
+ };
+ };
+ oftc = {
+ Server = "irc.oftc.net +6697";
+ LoadModule = [ "simple_away" "nickserv" ];
+ Nick = "bsima";
+ Chan = { "#home-manager" = { }; };
+ };
+ zeronode = {
+ Server = "irc.zeronode.net +6697";
+ LoadModule = [ "simple_away" "nickserv" ];
+ Nick = "bsima";
+ Chan = { "#NoAgenda" = { }; };
+ };
+ #sorcery = {
+ # Server = "irc.sorcery.net +6697";
+ #};
+ };
+ Pass.password = {
+ Method = "sha256";
+ Hash =
+ "bead16d806e7bf5cbbc31d572b20f01e2b253eb60e2497ce465df56306becd02";
+ Salt = "/GhmBMc+E6b7qd8muFEe";
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/Omni/Cloud/post-receive.sh b/Omni/Cloud/post-receive.sh
new file mode 100755
index 0000000..179fbd0
--- /dev/null
+++ b/Omni/Cloud/post-receive.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+#
+# creates an archive of a git repo on push
+#
+# unfortunately the nixos gitolite module does not copy the 'commonHooks'
+# properly, so we have to manually deploy this like so:
+#
+# scp Omni/Cloud/post-receive \
+# root@simatime.com:/srv/git/.gitolite/hooks/common/post-receive
+#
+# One time only:
+#
+# ssh root@simatime.com "sudo -u git gitolite setup -ho"
+#
+# Also on first-time setup, might need to manually check the permissions are
+# correct on $webroot/archive or wherever else.
+#
+set -euo pipefail
+while read -r _ newrev refname
+do
+ if [[ -e ./git-daemon-export-ok ]]
+ then
+ repo=$(basename "$PWD" | sed 's/.git//g')
+ branch=$(git rev-parse --symbolic --abbrev-ref "$refname")
+ webroot="/srv/www/simatime.com/"
+ outdir="$webroot/archive/$repo/$branch"
+ mkdir -p "$outdir"
+ echo " making: https://simatime.com/archive/$repo/$branch/$newrev.tar.gz"
+ git archive "$branch" --prefix "$repo-$branch/" --format tar \
+ | gzip > "$outdir/$newrev.tar.gz"
+ echo " making: https://simatime.com/archive/$repo/$branch/$newrev.sha256"
+ hash=$(nix-prefetch-url --unpack file://"$outdir"/"$newrev".tar.gz 2>/dev/null)
+ echo "$hash" > "$outdir/$newrev.sha256"
+ echo " commit: $newrev"
+ echo " sha256: $hash"
+ echo "in omni: deps update $repo --branch $branch --rev $newrev --attribute sha256=$hash"
+ chmod -R 755 "$webroot/archive"
+ fi
+done