{ config, pkgs, ... }: let lib = pkgs.lib; #webserver = "nginx" # httpd caddy lighttpd domain = "${config.networking.hostName}.${config.networking.domain}"; mkDomain = subname: "${subname}.${domain}"; subdomains = lib.sort (x: y: x #/home/pbsds/repos/nixpkgs-trees/polaris/nixos/modules/services/misc/polaris.nix /home/pbsds/repos/nixpkgs/polaris-14/nixos/modules/services/misc/polaris.nix ]; disabledModules = [ "services/misc/polaris.nix" ]; nixpkgs.overlays = [ (final: prev: { polaris = prev.callPackage /home/pbsds/repos/nixpkgs/polaris-14/pkgs/servers/polaris { }; polaris-web = prev.callPackage /home/pbsds/repos/nixpkgs/polaris-14/pkgs/servers/polaris/web.nix { }; }) ]; # ACME # regenerate certs with: # systemctl clean --what=state acme-noximilien.pbsds.net.service security.acme.acceptTerms = true; security.acme.defaults.email = "pbsds+acme@hotmail.com"; #security.acme.defaults.renewInterval = "daily"; #security.acme.defaults.reloadServices # https://www.xf.is/2020/06/30/list-of-free-acme-ssl-providers/ #security.acme.defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; # STAGING #security.acme.defaults.server = "https://api.buypass.com/acme/directory"; # no wildcards, rate limit: 20 domains/week, 5 duplicate certs / week #security.acme.defaults.server = "https://api.test4.buypass.no/acme/directory"; # STAGING. no wildcards, rate limit: 20 domains/week, 5 duplicate certs / week # DNS-based ACME: # - https://go-acme.github.io/lego/dns/domeneshop/ # - https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns-with-vhosts #security.acme.defaults.dnsProvider = "domeneshop"; #security.acme.defaults.credentialsFile = "/var/lib/secrets/domeneshop.key"; # TODO: this file must be made by hand, containing env variables. services.nginx.enable = true; networking.firewall.allowedTCPPorts = [ 80 443 ]; # Website tunnel # TODO: move to web profile? services.nginx.virtualHosts.${domain} = { forceSSL = true; # addSSL = true; enableACME = true; #acmeRoot = null; # use DNS default = true; serverAliases = map mkDomain [ "www" #"*" # requires DNS ACME ]; # The alternative to ^ is: config.security.acme.certs."${acmeDomain}".extraDomainNames = [ (mkDomain "foo") ]; # TODO: 'nox' alias for everything locations."/" = { proxyPass = "http://pbuntu.pbsds.net"; proxyWebsockets = true; }; }; #services.nginx.virtualHosts.${mkDomain "www"} = { # addSSL = true; # useACMEHost = acmeDomain; #enableACME = true; # locations."/" = { # proxyPass = "http://pbuntu.pbsds.net"; # proxyWebsockets = true; # }; #}; # service index # TODO: move to web profile? services.nginx.virtualHosts.${mkDomain "index"} = { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; root = let getName = domain: head (lib.splitString "." domain); getDomain = domain: concatStringsSep "." (tail (lib.splitString "." domain)); custom = rec { index = "This page"; links = "Linktree"; element = pkgs.element-web.meta.description; refleksjon = "My dad is a cheapskate"; roroslyd = "My dad is a cheapskate"; www = "wwwwwwwwwwwwwww"; noximilien = www; shlink = "Url shortener"; }; getDesc = domain: let name = getName domain; in if lib.hasAttr name custom then custom.${name} else if lib.hasAttr name pkgs.python3Packages then pkgs.python3Packages.${name}.meta.description else if lib.hasAttr name pkgs then pkgs.${name}.meta.description else if lib.hasAttrByPath [name "package"] config.services then config.services.${name}.package.meta.description else ""; mkRow = domain: ''${getName domain}.${getDomain domain}${getDesc domain}''; in pkgs.writeTextDir "index.html" ''
urldescription ${lib.concatStringsSep "\n" (map mkRow subdomains)}
''; }; # webdav # Simple WebDAV server services.webdav = { enable = true; # the webdav user uid:gid is fixed settings = { address = "127.0.0.1"; port = 9568; prefix = "/"; scope = "/mnt/reidun/pub"; modify = false; auth = true; users = [ { username = "zotero"; password = "{bcrypt}$2y$10$9zzZuwd2AvNZXb8WCG/bM..ibOroNnX0sN94UTAV.Jco9LnZ8Whs2"; #prefix = "/zotero/"; scope = "/mnt/reidun/Various/Zotero"; modify = true; } /** / { username = "guest"; password = "hunter2"; } /**/ ]; }; }; services.nginx.virtualHosts.${mkDomain "webdav"} = lib.mkIf config.services.webdav.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.webdav.settings.port}"; #proxyWebsockets = true; extraConfig = '' proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; client_max_body_size 2G; ''; }; }; # Home assistant services.home-assistant = { #enable = true; config = { # https://www.home-assistant.io/integrations/default_config/ default_config = {}; # https://www.home-assistant.io/integrations/esphome/ #esphome = {}; # https://www.home-assistant.io/integrations/met/ #met = {}; }; }; # Flexget # Multipurpose automation tool for all of your media services.flexget = { enable = true; user = "flexget"; # The user under which to run flexget. homeDir = "/var/lib/flexget"; interval = "30m"; config = '' tasks: shanaproject: rss: 'https://www.shanaproject.com/feeds/secure/user/35853/J98B7OXAHO/' accept_all: yes no_entries_ok: yes transmission: host: 192.168.1.3 port: 9091 path: '/Reidun/shared/Downloads/shana project/' username: pbsds password: spismeg ''; }; users.groups."${config.services.flexget.user}" = lib.mkIf config.services.flexget.enable { }; users.users."${config.services.flexget.user}" = lib.mkIf config.services.flexget.enable { isSystemUser = true; createHome = true; home = config.services.flexget.homeDir; group = "${config.services.flexget.user}"; }; # Graphana # Gorgeous metric viz, dashboards & editors for Graphite, InfluxDB & OpenTSDB services.grafana = rec { #enable = true; #addr = "127.0.0.1"; settings.server.http_addr = "0.0.0.0"; settings.server.http_port = 3000; settings.server.domain = mkDomain "grafana"; #rootUrl = "https://${domain}/grafana/"; # Not needed if it is `https://your.domain/` }; services.nginx.virtualHosts."${config.services.grafana.settings.server.domain}" = lib.mkIf config.services.grafana.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; #locations."/grafana/" = { locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.grafana.port}"; proxyWebsockets = true; }; }; # OwnCast # self-hosted video live streaming solution services.owncast = { # the default /admin account is admin:abc123, don't enable if you don't intend to change it! enable = true; port = 3456; # default is 8080 rtmp-port = 1935; # remember to punch a TCP hole in the NAT #listen = "0.0.0.0"; # default is "127.0.0.1" openFirewall = true; # the rtmp port, and the http port if listen != "127.0.0.1" }; services.nginx.virtualHosts.${mkDomain "owncast"} = lib.mkIf config.services.owncast.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.owncast.port}"; proxyWebsockets = true; }; }; # Cryptpad # A collaborative office suite that is end-to-end encrypted and open-source. # TODO: https://github.com/NixOS/nixpkgs/pull/180066 /* services.cryptpad = { #enable = true; # current node version used is marked insecure # reference: https://github.com/xwiki-labs/cryptpad/blob/main/config/config.example.js configFile = toFile "cryptpad-config.js" '' module.exports = { httpUnsafeOrigin: 'http://localhost:3457', httpSafeOrigin: 'https://${mkDomain "cryptpad"}', httpAddress: '127.0.0.1', httpPort: 3457, //adminKeys: [ // can be found on the settings page for registered users // "[cryptpad-user1@my.awesome.website/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]", //], // storage //inactiveTime: 90, // days //archiveRetentionTime: 15, // days //accountRetentionTime: 365, // days, default is never //maxUploadSize: 20 * 1024 * 1024, // bytes //premiumUploadSize: 100 * 1024 * 1024, // bytes, (users with a plan in their customLimit) filePath: './datastore/', archivePath: './data/archive', // recovery in the event of accidental deletion pinPath: './data/pins', // content stored indefinetly taskPath: './data/tasks', // scheduled tasks blockPath: './block', // users' authenticated blocks blobPath: './blob', // uploaded encrypted blobs blobStagingPath: './data/blobstage', // incomplete blobs decreePath: './data/decrees', // undocumented logPath: false, // logging of events, may be set to false logToStdout: true, logLevel: 'info', // silly, verbose, debug, feedback, info, warn, error logFeedback: false, // data collection verbose: false, // logging installMethod: 'nixpkgs', // telemetry for devs }; ''; }; services.nginx.virtualHosts.${mkDomain "cryptpad"} = lib.mkIf config.services.cryptpad.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:3457"; proxyWebsockets = true; }; }; */ # Jellyfin services.jellyfin = { enable = true; # don't enable unless you intend to first-time-setup the admin user # from https://jellyfin.org/docs/general/networking/index.html: # - 8096/tcp is used by default for HTTP traffic. You can change this in the dashboard. # - 8920/tcp is used by default for HTTPS traffic. You can change this in the dashboard. # - 1900/udp is used for service auto-discovery. This is not configurable. # - 7359/udp is also used for auto-discovery. This is not configurable. openFirewall = false; # I do it manually below: # TODO: configure initial collections and extensions }; # firewall - not needed? /* networking.firewall = lib.mkIf config.services.jellyfin.enable { # TODO: does this overwrite rules set by other stuff? should i use ++ ? #allowedTCPPorts = [ 8096 8920 ]; allowedUDPPorts = [ 1900 7359 ]; # TODO: Only if behind a NAT? }; */ services.nginx.virtualHosts.${mkDomain "jellyfin"} = lib.mkIf config.services.jellyfin.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:8096"; proxyWebsockets = true; }; }; # Hardware acceleration # https://nixos.wiki/wiki/Jellyfin nixpkgs.config.packageOverrides = pkgs: { vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; }; hardware.opengl = { enable = true; extraPackages = with pkgs; [ intel-media-driver vaapiIntel vaapiVdpau libvdpau-va-gl intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) ]; }; # Allow Jellyfin access to VAAPI users.users.${config.services.jellyfin.user}.extraGroups = [ "video" "render" ]; systemd.services.jellyfin.serviceConfig.PrivateDevices = lib.mkForce false; systemd.services.jellyfin.serviceConfig.DeviceAllow = lib.mkForce [ "/dev/dri/renderD128" ]; # Navidrome # Music Server and Streamer compatible with Subsonic/Airsonic services.navidrome = { enable = true; settings = { # default hostname:port = "127.0.0.1:4533" MusicFolder = "/mnt/reidun/Music/Albums"; #MusicFolder = pkgs.linkFarm "navidrome-music-library" [ # { name = "Albums"; path = "/mnt/reidun/Music/Albums"; } # { name = "OST"; path = "/mnt/reidun/Music/OST"; } # { name = "dojin.co"; path = "/mnt/reidun/Music/dojin.co"; } # { name = "Touhou"; path = "/mnt/reidun/Music/Touhou"; } # { name = "Kancolle"; path = "/mnt/reidun/Music/Kancolle"; } # { name = "Vocaloid"; path = "/mnt/reidun/Music/Vocaloid"; } #]; UIWelcomeMessage = "Spis meg"; DefaultTheme = "Spotify-ish"; }; }; services.nginx.virtualHosts.${mkDomain "navidrome"} = lib.mkIf config.services.navidrome.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:4533"; proxyWebsockets = true; }; }; # Polaris # Self-host your music collection, and access it from any computer and mobile device services.polaris = { enable = true; #user = "pbsds"; #group = "users"; port = 7890; #package = pkgs.unstable.polaris; # instead of my overlay, TODO: move that overlay here settings = { settings.reindex_every_n_seconds = 7*24*60*60; # weekly, default is 1800, i.e. hourly settings.album_art_pattern = "([Cc]over|COVER|[Ff]older|FOLDER|[Ff]ront|FRONT)\.(jpeg|JPEG|jpg|JPG|png|PNG|bmp|BMP|gif|GIF)"; #"(?i)(cover|folder|front)\.(jpeg|jpg|png|bmp|gif)"; mount_dirs = [ { name = "Albums"; source = "/mnt/reidun/Music/Albums"; } { name = "dojin.co"; source = "/mnt/reidun/Music/dojin.co"; } { name = "Vocaloid"; source = "/mnt/reidun/Music/Vocaloid"; } { name = "Touhou"; source = "/mnt/reidun/Music/Touhou"; } { name = "OST"; source = "/mnt/reidun/Music/OST"; } { name = "Kancolle"; source = "/mnt/reidun/Music/Kancolle"; } { name = "Downloads"; source = "/mnt/reidun/Downloads/music"; } ]; }; }; services.nginx.virtualHosts.${mkDomain "polaris"} = lib.mkIf config.services.polaris.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.polaris.port}"; proxyWebsockets = true; }; }; # Hydra # Nix-based continuous build system # https://github.com/NixOS/hydra # https://nixos.wiki/wiki/Hydra # sudo -u hydra hydra-create-user 'admin' --full-name '' --email-address '' --password-prompt --role admin # https://blog.matejc.com/blogs/myblog/nixos-hydra-nginx services.hydra = { enable = true; hydraURL = "https://${mkDomain "hydra"}"; #smtpHost = ; listenHost = "localhost"; port = 4758; notificationSender = "hydra@${domain}"; # Sender email address used for email notifications. #buildMachinesFiles = []; #useSubstitutes = true; #debugServer = true; #logo = /some/path.png; #minimumDiskFree = 0; # Minimum disk space (GiB) determining if queue runner runs or not. #minimumDiskFreeEvaluator = 0; # Minimum disk space (GiB) determining if evaluator runs or not. }; services.nginx.virtualHosts.${mkDomain "hydra"} = lib.mkIf config.services.hydra.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.hydra.port}"; proxyWebsockets = true; extraConfig = '' proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; add_header Front-End-Https on; ''; }; }; # Sourcegraph # Understand, fix, and automate across your codebase with this code intelligence platform /** / # First user regitration becomes admin # data can be destryoed with `nixos-container destroy sourcegraph` virtualisation.oci-containers.containers."sourcegraph" = { autoStart = true; #image = "sourcegraph/server:3.41.0"; #image = "sourcegraph/server:latest"; image = "sourcegraph/server:insiders"; environment = {}; ports = [ "127.0.0.1:7080:7080/tcp" # webui? "127.0.0.1:3370:3370/tcp" # admin? (graphana and stuff) ]; volumes = [ "/var/lib/sourcegraph/config:/etc/sourcegraph" "/var/lib/sourcegraph/data:/var/opt/sourcegraph" ]; }; systemd.services."create-sourcegraph-volume-dirs" = { wantedBy = [ "${config.virtualisation.oci-containers.backend}-sourcegraph.service" ]; serviceConfig.Type = "oneshot"; script = '' mkdir -p /var/lib/sourcegraph/config mkdir -p /var/lib/sourcegraph/data ''; }; services.nginx.virtualHosts.${mkDomain "sourcegraph"} = lib.mkIf config.virtualisation.oci-containers.containers."sourcegraph".autoStart { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:7080"; proxyWebsockets = true; }; #locations."/graphana/" = { # proxyPass = "http://127.0.0.1:3370"; # proxyWebsockets = true; #}; }; /**/ # Shlink # URL shortener with REST API and command line interface # manage with https://app.shlink.io/ # TODO: self-host shlink web client? https://shlink.io/documentation/shlink-web-client/ /** / # data can be destryoed with `nixos-container destroy shlink` virtualisation.oci-containers.containers."shlink" = { autoStart = true; image = "shlinkio/shlink:stable"; # https://shlink.io/documentation/install-docker-image/ environment = { "DEFAULT_DOMAIN" = mkDomain "shlink"; "IS_HTTPS_ENABLED" = "true"; "TIMEZONE" = "Europe/Oslo"; #"GEOLITE_LICENSE_KEY" = ; # https://shlink.io/documentation/geolite-license-key/ # TODO: use postgres? default is sqlite3? }; ports = [ "127.0.0.1:5757:8080/tcp" # webui ]; volumes = [ "/var/lib/shlink/database.sqlite:/etc/shlink/data/database.sqlite" # TODO: where is the sqlite file? ]; }; systemd.services."create-shlink-volume-dirs" = { wantedBy = [ "${config.virtualisation.oci-containers.backend}-shlink.service" ]; serviceConfig.Type = "oneshot"; script = '' mkdir -p /var/lib/shlink touch /var/lib/shlink/database.sqlite ''; }; services.nginx.virtualHosts.${mkDomain "shlink"} = lib.mkIf config.virtualisation.oci-containers.containers."shlink".autoStart { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:5757"; proxyWebsockets = true; }; }; programs.bash.shellAliases = { shlink = "docker exec -it shlink shlink"; }; /**/ # Resilio Sync # Automatically sync files via secure, distributed technology services.resilio = { #enable = true; #downloadLimit = 0; #uploadLimit = 0; #directoryRoot = "/media" # Default directory to add folders in the web UI. #storagePath = "/var/lib/resilio-sync/"; # Where BitTorrent Sync will store it's database files httpLogin = ""; httpPass = ""; deviceName = "${config.networking.hostName}"; #apiKey = ; # API key, which enables the developer API. #httpListenPort = 9000; #httpListenAddr = "[::1]"; enableWebUI = false; # default is false }; services.nginx.virtualHosts.${mkDomain "resilio"} = let cfg = config.services.resilio; in lib.mkIf (cfg.enable && cfg.enableWebUI) { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${cfg.httpListenPort}"; proxyWebsockets = true; }; }; # Gitea # Git with a cup of tea services.gitea = rec { enable = true; settings.service.DISABLE_REGISTRATION = true; # disable after initial deploy #https://docs.gitea.io/en-us/config-cheat-sheet/ #settings = { # "cron.sync_external_users" = { # RUN_AT_START = true; # SCHEDULE = "@every 24h"; # UPDATE_EXISTING = true; # }; # mailer = { # ENABLED = true; # MAILER_TYPE = "sendmail"; # FROM = "do-not-reply@example.org"; # SENDMAIL_PATH = "${pkgs.system-sendmail}/bin/sendmail"; # }; # other = { # SHOW_FOOTER_VERSION = false; # }; #}; #appName = "gitea: spis meg"; appName = "gitea: private instance"; domain = mkDomain "gitea"; #ssh.enable # default is true rootUrl = "https://${domain}/"; #ssh.clonePort # default is 22 #log.level = "Debug"; # default is "Info" #lfs.enable = true; # default is false httpPort = 9675; # default is 3000 httpAddress = "127.0.0.1"; # default is "0.0.0.0" #extraConfig #database.type # default is "sqlite3" settings.session.COOKIE_SECURE = true; # default is false, only send cookies over https #stateDir # default is "/var/lib/gitea" #mailerPasswordFile # Path to a file containing the SMTP password #repositoryRoot # default is "${config.services.gitea.stateDir}/repositories" #log.rootPath # TODO: move? #lfs.contentDir #dump.enable # default is false staticRootPath = pkgs.symlinkJoin { name = "gitea-static-root-data"; paths = let giteaModern = pkgs.fetchFromGitea { # https://codeberg.org/Freeplay/Gitea-Modern domain = "codeberg.org"; owner = "Freeplay"; repo = "Gitea-Modern"; rev = "0c0a05e6f0496521c166402dd56441a714487fd8"; sha256 = "q14E5ni2BvpGsmGOHWQgbCqD4lBh4bFtBFtIyNfAf0Q="; }; giteaEarlGray = pkgs.fetchFromGitHub { # https://github.com/acoolstraw/earl-grey owner = "acoolstraw"; repo = "earl-grey"; rev = "a6ca3dd3b9e6b48f6e45032b2aa691c2f16dc9bc"; sha256 = "55Piafc7kQ5hybwHQczx36AP+kX1AtWugxERYNdmqWk="; }; in [ config.services.gitea.package.data (pkgs.linkFarm "gitea-custom-dir" [ { name = "public/css/theme-gitea-modern.css"; path = "${giteaModern}/Gitea/theme-gitea-modern.css"; } { name = "public/css/theme-earl-grey.css"; path = "${giteaEarlGray}/theme-earl-grey.css"; } ]) ]; }; settings = { # https://docs.gitea.io/en-us/config-cheat-sheet/ ui.THEMES = "gitea,arc-green,earl-grey,gitea-modern"; ui.DEFAULT_THEME = "earl-grey"; }; }; services.nginx.virtualHosts.${mkDomain "gitea"} = lib.mkIf config.services.gitea.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.gitea.httpPort}"; proxyWebsockets = true; }; }; # TODO: mailcatcher # TODO: configure stuff to send its shit here # ntopng # High-speed web-based traffic analysis and flow collection tool # WARNING: default username and password is admin:admin services.ntopng = { enable = true; # also enables redis for persistent data storage httpPort = 3987; # HTTP port of embedded web server #interfaces = [ "any" ]; #extraConfig = "; #redis.address = ""; #redis.createInstance = "ntopng"; }; services.nginx.virtualHosts.${mkDomain "ntopng"} = lib.mkIf config.services.ntopng.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.ntopng.httpPort}"; proxyWebsockets = true; }; }; # TODO: kukkee or rallly # https://noted.lol/2-self-hosted-alternatives-to-doodle-meeting-scheduling/ #https://rallly.co/ # Kukkee # Self-hosted Doodle alternative: a meeting poll tool /** / services.kukkee = { #enable = true; port = 5666; baseUrl = "https://${mkDomain "kukkee"}"; #mongodb.enable = false; }; services.nginx.virtualHosts.${mkDomain "kukkee"} = lib.mkIf config.services.kukkee.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.kukkee.port}"; proxyWebsockets = true; }; }; /**/ # Nitter # Alternative Twitter front-end services.nitter = { enable = true; package = pkgs.unstable.nitter; #openFirewall #config.base64Media = false; # Use base64 encoding for proxied media URLs. server.title = "Pjitter"; server.address = "127.0.0.1"; server.hostname = mkDomain "nitter"; server.https = true; # Secure cookies server.port = 4965; #preferences.autoplayGifs = ; # default is true #preferences.bidiSupport = ; # Support bidirectional text (makes clicking on tweets harder). default is false #preferences.hideBanner = ; # Hide profile banner. default is false #preferences.hidePins = ; # Hide pinned tweets. default is false #preferences.hideReplies = ; # Hide tweet replies. default is false #preferences.hideTweetStats = ; # Hide tweet stats (replies, retweets, likes). default is false preferences.hlsPlayback = true; # Enable HLS video streaming (requires JavaScript). default is false preferences.infiniteScroll = true; # Infinite scrolling (requires JavaScript, experimental!). default is false #preferences.mp4Playback = ; # Enable MP4 video playback. default is true #preferences.muteVideos = ; # Mute videos by default. default is false #preferences.proxyVideos = ; # Proxy video streaming through the server (might be slow). default is true preferences.replaceInstagram = "bibliogram.art"; # Replace Instagram links with links to this instance (blank to disable). default is "" preferences.replaceTwitter = mkDomain "nitter"; # Replace Twitter links with links to this instance (blank to disable). default is "" preferences.replaceYouTube = lib.mkIf config.services.invidious.enable (mkDomain "invidious"); # Replace YouTube links with links to this instance (blank to disable). default is "" settings = lib.mkIf config.services.libreddit.enable { Preferences.replaceReddit = (mkDomain "libreddit"); # Replace Reddit links with links to this instance (blank to disable). default is "" }; #preferences.stickyProfile = ; # Make profile sidebar stick to top. default is true preferences.theme = "Twitter Dark"; # Instance theme. default is "Nitter" }; services.nginx.virtualHosts.${mkDomain "nitter"} = lib.mkIf config.services.nitter.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.nitter.server.port}"; proxyWebsockets = true; }; }; # Invidious # An open source alternative front-end to YouTube services.invidious = { enable = true; domain = mkDomain "invidious"; port = 4765; settings = { host_binding = "127.0.0.1"; external_port = 443; https_only = true; statistics_enabled = false; # api endpoint required for public instances registration_enabled = false; login_enabled = false; #admins = ["pbsds"]; banner = "spis meg"; default_user_preferences = { #feed_menu = ["Popular", "Trending", "Subscriptions", "Playlists"] feed_menu = ["Trending" "Subscriptions" "Playlists"]; default_home = "Trending"; }; }; }; services.nginx.virtualHosts.${mkDomain "invidious"} = lib.mkIf config.services.invidious.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.invidious.port}"; proxyWebsockets = true; }; }; # Libreddit # Private front-end for Reddit services.libreddit = { enable = true; address = "127.0.0.1"; port = 4876; }; systemd.services.libreddit.environment = lib.mkIf config.services.libreddit.enable { # https://github.com/spikecodes/libreddit#change-default-settings= # TODO: merge my module addition LIBREDDIT_DEFAULT_THEME = "gold"; }; services.nginx.virtualHosts.${mkDomain "libreddit"} = lib.mkIf config.services.libreddit.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.libreddit.port}"; proxyWebsockets = true; }; }; # paperless-ngx # A supercharged version of paperless: scan, index, and archive all of your physical documents services.paperless = { enable = true; #package = pkgs.paperless-ngx; #port = 28981; #address = "localhost"; #passwordfile = null; # file contining the superuser 'admin' password, optionally set with `${datadir}/paperless-manage createsuperuser` #datadir = "/var/lib/paperless"; #mediadir = "${datadir}/media"; #consumptiondir = "${datadir}/consume"; # Directory from which new documents are imported. (TODO: zotero) #extraconfig = {}; #consumptiondirispublic = false; # Whether all users can write to the consumption dir }; services.nginx.virtualHosts.${mkDomain "paperless"} = lib.mkIf config.services.paperless.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.paperless.port}"; proxyWebsockets = true; }; }; # Netdata # Real-time performance monitoring tool services.netdata = { enable = true; #python.enable = false; # default is true #python.extraPackages = ps: []; #config = { # https://github.com/netdata/netdata/blob/master/daemon/config/README.md # hostname = ""; # port = 19999; #}; #configDir = {}; #extraPluginPaths = []; }; services.nginx.virtualHosts.${mkDomain "netdata"} = lib.mkIf config.services.netdata.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:19999"; proxyWebsockets = true; }; }; # upterm # Secure terminal-session sharing services.uptermd = { enable = false; openFirewall = true; #listenAddress # default is "[::]"; #port = 2222; # default is 2222, uses ssh #extraFlags #hostKey = null; }; # thelunge # The self-hosted Web IRC client services.thelounge = { # configure user accounts by using the 'thelounge' command, or by adding entries to /var/lib/thelounge/users enable = true; public = false; port = 5876; # theLoungePlugins.themes is view of nodePackages_latest.thelounge-theme-* # theLoungePlugins.plugins is view of nodePackages_latest.thelounge-plugin-* plugins = with pkgs.theLoungePlugins; (with lib; attrValues (filterAttrs (name: _: name != "recurseForDerivations") themes)) ++ [ #plugins.giphy #plugins.shortcuts plugins.closepms ]; extraConfig.theme = "One Dark"; extraConfig.fileUpload.enable = true; extraConfig.fileUpload.baseUrl = "${mkDomain "thelounge"}"; }; services.nginx.virtualHosts.${mkDomain "thelounge"} = lib.mkIf config.services.thelounge.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.thelounge.port}"; proxyWebsockets = true; }; }; # Convos # The simplest way to use IRC in your browser services.convos = { enable = false; # user registration is borken. new major version (7) in unstable. reverseProxy = true; listenAddress = "127.0.0.1"; listenPort = 44649; }; services.nginx.virtualHosts.${mkDomain "convos"} = lib.mkIf config.services.convos.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.convos.listenPort}"; proxyWebsockets = true; extraConfig = '' #proxy_redirect off; client_max_body_size 0; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Request-Base "$scheme://$host/"; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header REMOTE-HOST $remote_addr; ''; }; }; # Roundcube # Open Source Webmail Software services.roundcube = { enable = true; hostName = mkDomain "roundcube"; plugins = [ "archive" "zipdownload" "managesieve" ]; extraConfig = '' $config['product_name'] = 'Spis meg'; $config['skin_logo'] = [ #'elastic:login' => 'https://links.pbsds.net/img/piuy_render.png', #'elastic:*[small]' => 'https://links.pbsds.net/img/piuy_render.png', 'elastic:*' => 'https://links.pbsds.net/img/piuy_render.png', #'elastic:*' => 'https://links.pbsds.net/img/nox.png', ]; #$config['blankpage_url'] = '/something.html' # TODO <- $config['default_host'] = [ 'tls://imap.fyrkat.no' => 'Fyrkat', 'tls://imap.pvv.ntnu.no' => 'PVV', 'tls://imap.nvg.ntnu.no' => 'NVG', ]; $config['smtp_server'] = [ 'imap.fyrkat.no' => 'tls://smtp.fyrkat.no', 'imap.pvv.ntnu.no' => 'tls://smtp.pvv.ntnu.no', 'imap.nvg.ntnu.no' => 'tls://smtp.nvg.ntnu.no', ]; # plugins/managesieve/config.inc.php.dist $config['managesieve_host'] = 'tls://%h'; ''; }; services.nginx.virtualHosts.${mkDomain "roundcube"} = lib.mkIf config.services.roundcube.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/skins/elastic/images/logo.svg" = { #alias = "/path/to/file"; #return = "302 https://links.pbsds.net/img/piuy_render.png"; return = "302 https://links.pbsds.net/img/nox.png"; }; }; # Galene # Videoconferencing server that is easy to deploy, written in Go services.galene = { #enable = true; insecure = true; # reverse proxy instead, but can i feed it the acme cert? httpAddress = "127.0.0.1"; httpPort = 3975; }; services.nginx.virtualHosts.${mkDomain "galene"} = lib.mkIf config.services.galene.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.galene.httpPort}"; proxyWebsockets = true; }; }; #networking.firewall = lib.mkIf config.service.jellyfin.enable { # allowedTCPPorts = [ 1194 ]; # allowedUDPPorts = [ 1194 ]; # TODO: Only if behind a NAT? #}; # Jitsi meet services.jitsi-meet = { #enable = true; hostName = mkDomain "jitsi-meet"; config = { # https://github.com/jitsi/jitsi-meet/blob/master/config.js #enableWelcomePage = false; defaultLang = "nb"; }; interfaceConfig = { # https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js" APP_NAME = "Spis meg"; # SHOW_JITSI_WATERMARK = false; # SHOW_WATERMARK_FOR_GUESTS = false; }; jibri.enable = false; # record in a headless chrome instance nginx.enable = true; # force ssl, acme, lots of routing rules }; # Rocketchat # A self-hosted discord/slack alternative # TODO, docker exists, but no nixos module # Mattermost # Open-source, self-hosted Slack-alternative services.mattermost = { enable = true; # will create and use a psql db listenAddress = "[::1]:8065"; siteName = "Spis meg"; siteUrl = "https://${mkDomain "mattermost"}"; #mutableConfig = true; # default is false, if true, see also "preferNixConfig" extraConfig = { # https://docs.mattermost.com/configure/configuration-settings.html#reporting # TODO: smtp }; matterircd = { #enable = true; # default is false parameters = [ "-mmserver chat.example.com" "-bind [::]:6667" ]; }; }; services.nginx.virtualHosts.${mkDomain "mattermost"} = lib.mkIf config.services.mattermost.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://${config.services.mattermost.listenAddress}"; proxyWebsockets = true; }; }; # hedgedoc # Realtime collaborative markdown notes on all platforms services.hedgedoc = { #enable = true; # FIXME: make it load settings.host = "127.0.0.1"; settings.port = 44776; settings.db.dialect = "sqlite"; settings.db.storage = "${config.services.hedgedoc.workDir}/db.hedgedoc.sqlite"; settings.domain = mkDomain "hedgedoc"; settings.allowAnonymous = true; settings.allowEmailRegister = false; # default is true settings.allowAnonymousEdits = false; # default is false settings.protocolUseSSL = true; # https prefix settings.useSSL = false; # nginx terminates ssl #settings.csp = {TODO}; # content security policy #settings.useCDN = true; #settings.debug = true; # there are also a metric fuckton of integration services, like github, twitter, minio, mattermost, dropbox etc. # there are also auth options, like ldap, saml and oauth2 }; services.nginx.virtualHosts.${mkDomain "hedgedoc"} = lib.mkIf config.services.hedgedoc.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.hedgedoc.settings.port}"; proxyWebsockets = true; # TODO: proxy headers: # https://docs.hedgedoc.org/guides/reverse-proxy/ }; }; # Cinny # Yet another Matrix client for the web services.nginx.virtualHosts.${mkDomain "cinny"} = { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; root = pkgs.unstable.cinny.override { conf = { defaultHomeserver = 0; homeserverList = [ "pvv.ntnu.no" "matrix.org" "dodsorf.as" ]; }; }; }; # Element-web # A glossy Matrix collaboration client for the web services.nginx.virtualHosts.${mkDomain "element"} = { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; root = pkgs.element-web.override { conf = { # https://github.com/vector-im/element-web/blob/develop/docs/config.md # https://github.com/vector-im/element-web/blob/develop/config.sample.json # https://github.com/vector-im/element-web/blob/develop/docs/labs.md brand = "spis meg"; default_country_code = "NO"; default_server_config."m.homeserver" = { server_name = "pvv.ntnu.no"; base_url = "https://matrix.pvv.ntnu.no"; }; roomDirectory.servers = [ "pvv.ntnu.no" "matrix.org" "nixos.org" "agdersam.no" "trygve.me" "utwente.io" ]; disable_guests = true; showLabsSettings = true; features.feature_pinning = "labs"; features.feature_custom_status = "labs"; features.feature_custom_tags = "labs"; features.feature_state_counters = "labs"; features.feature_latex_maths = "labs"; setting_defaults.breadcrumbs = true; UIFeature.urlPreviews = true; UIFeature.shareQrCode = true; UIFeature.registration = false; }; }; }; # vaultwarden # Unofficial Bitwarden compatible server written in Rust services.vaultwarden = { enable = true; config = { # https://github.com/dani-garcia/vaultwarden/blob/1.24.0/.env.template # camelCase is converted to UPPER_SNAKE_CASE domain = "https://${mkDomain "vaultwarden"}"; # port is supported signupsAllowed = false; # rocket is the http library rocketAddress = "127.0.0.1"; rocketPort = 8222; #rocketWorkers = 10; rocketLog = "critical"; }; #dbBackend = "sqlite"; # backupDir = ""; # TODO }; services.nginx.virtualHosts.${mkDomain "vaultwarden"} = lib.mkIf config.services.vaultwarden.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/" = { proxyPass = "http://127.0.0.1:${toString config.services.vaultwarden.config.rocketPort}"; proxyWebsockets = true; }; }; # matrix-synapse /** / services.matrix-synapse = { enable = true; settings = { server_name = "pbsds.net" public_baseurl = mkDomain "matrix"; url_preview_enabled = false; max_upload_size = "100M"; trusted_key_servers = [ {server_name = "matrix.org";} {server_name = "dodsorf.as";} {server_name = "pvv.ntnu.no";} ]; listeners = [ { bind_addresses = [ "127.0.0.1" ]; port = 8008; resources = [ { compress = true; names = [ "client" ]; } { compress = false; names = [ "federation" ]; } ]; tls = false; type = "http"; x_forwarded = true; } ]; }; }; services.nginx.virtualHosts.${mkDomain "matrix"} = lib.mkIf config.services.matrix-synapse.enable { forceSSL = true; # addSSL = true; enableACME = true; #useACMEHost = acmeDomain; locations."/_matrix" = { proxyPass = "http://127.0.0.1:${toString (builtins.elemAt 0 config.services.matrix-synaps.listeners).port}"; #proxyWebsockets = true; extraConfig = '' client_max_body_size ${config.services.matrix-synaps.max_upload_size}; ''; }; locations."/_synapse/client" = { proxyPass = "http://127.0.0.1:${toString (builtins.elemAt 0 config.services.matrix-synaps.listeners).port}/_synapse/client"; #proxyWebsockets = true; }; }; /**/ }