diff --git a/flake.lock b/flake.lock index d43de6ca..49197cad 100644 --- a/flake.lock +++ b/flake.lock @@ -1126,6 +1126,7 @@ "nixpkgsMaster": "nixpkgsMaster", "nixpkgsNix": "nixpkgsNix", "nixpkgsUnstable": "nixpkgsUnstable", + "sbd": "sbd", "sops-nix": "sops-nix", "srvos": "srvos", "threefold-rfs": "threefold-rfs", @@ -1179,6 +1180,23 @@ "type": "github" } }, + "sbd": { + "flake": false, + "locked": { + "lastModified": 1716568215, + "narHash": "sha256-ROvhoxRovsm3KCKTVLap1NigCXbjo0HikToO7raHnAU=", + "owner": "holochain", + "repo": "sbd", + "rev": "466fcebc0227526597295e054455db7c82748b96", + "type": "github" + }, + "original": { + "owner": "holochain", + "ref": "sbd-server-v0.0.4-alpha", + "repo": "sbd", + "type": "github" + } + }, "scaffolding": { "flake": false, "locked": { diff --git a/flake.nix b/flake.nix index eefdeff5..ff1cd810 100644 --- a/flake.nix +++ b/flake.nix @@ -86,6 +86,10 @@ tx5.url = "github:holochain/tx5/tx5-signal-srv-v0.0.8-alpha"; tx5.flake = false; + sbd.url = + "github:holochain/sbd/sbd-server-v0.0.4-alpha" + ; + sbd.flake = false; holochain-versions.url = "github:holochain/holochain?dir=versions/weekly"; holochain = { diff --git a/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/configuration.nix b/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/configuration.nix new file mode 100644 index 00000000..64726c38 --- /dev/null +++ b/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/configuration.nix @@ -0,0 +1,58 @@ +{ + config, + inputs, + self, + pkgs, + ... +}: let + # https://console.hetzner.cloud/projects/1982619/servers/47746862/overview + hostName = "sbd-0"; + domain = "main.infra.holo.host"; + ipv4 = "65.108.241.120"; + fqdn = "${config.networking.hostName}.${config.networking.domain}"; +in { + imports = [ + inputs.disko.nixosModules.disko + inputs.srvos.nixosModules.server + inputs.srvos.nixosModules.mixins-terminfo + inputs.srvos.nixosModules.hardware-hetzner-cloud + self.nixosModules.hardware-hetzner-cloud-ccx + + inputs.sops-nix.nixosModules.sops + + self.nixosModules.holo-users + ../../nixos/shared.nix + ../../nixos/shared-nix-settings.nix + self.nixosModules.ps1 + + self.nixosModules.sbd-server + ]; + + networking = {inherit hostName domain;}; + + hostName = ipv4; + + nix.settings.max-jobs = 8; + + nix.settings.substituters = [ + "https://holochain-ci.cachix.org" + ]; + + nix.settings.trusted-public-keys = [ + "holochain-ci.cachix.org-3:5IUSkZc0aoRS53rfkvH9Kid40NpyjwCMCzwRTXy+QN8=" + ]; + + system.stateVersion = "23.11"; + + services.sbd-server = { + enable = true; + url = fqdn; + address = ipv4; + tls-port = 443; + trusted-ip-header = "cf-connecting-ip"; + + # unlike the tx5-signal-server the sbd-server doesn't know about the STUN servers. + # going forward its' going to be part of the conductor client config + # "stun:${config.services.holochain-turn-server.url}:80" + }; +} diff --git a/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/default.nix b/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/default.nix new file mode 100644 index 00000000..f415640d --- /dev/null +++ b/modules/flake-parts/nixosConfigurations.sbd-0.main.infra.holo.host/default.nix @@ -0,0 +1,12 @@ +{ + self, + lib, + inputs, + ... +}: { + flake.nixosConfigurations.sbd-0_main_infra_holo_host = inputs.nixpkgs.lib.nixosSystem { + modules = [./configuration.nix]; + system = "x86_64-linux"; + specialArgs = self.specialArgs; + }; +} diff --git a/modules/flake-parts/packages.holochain-sbd.nix b/modules/flake-parts/packages.holochain-sbd.nix new file mode 100644 index 00000000..2d50625b --- /dev/null +++ b/modules/flake-parts/packages.holochain-sbd.nix @@ -0,0 +1,53 @@ +{ + # System independent arguments. + lib, + inputs, + ... +}: { + perSystem = { + # Arguments specific to the `perSystem` context. + self', + pkgs, + ... + }: { + # system specific outputs like, apps, checks, packages + + packages = let + system = pkgs.system; + craneLib = inputs.crane.lib.${system}; + cranePkgs = inputs.crane.inputs.nixpkgs.legacyPackages.${system}; + + sbdArgs = { + pname = "sbd"; + src = inputs.sbd; + version = inputs.sbd.rev; + cargoExtraArgs = "--examples --bins"; + nativeBuildInputs = [ + pkgs.pkg-config + ]; + buildInputs = [ + pkgs.openssl + ]; + + doCheck = false; + }; + sbdDeps = lib.makeOverridable craneLib.buildDepsOnly sbdArgs; + in { + sbd = lib.makeOverridable craneLib.buildPackage (sbdArgs + // { + cargoArtifacts = sbdDeps; + }); + + sbd-serverd = self'.packages.sbd.override { + name = "sbd-serverd"; + cargoExtraArgs = "--bin sbd-serverd"; + meta.mainProgram = "sbd-serverd"; + }; + }; + }; + flake = { + # system independent outputs like nixosModules, nixosConfigurations, etc. + + # nixosConfigurations.example-host = ... + }; +} diff --git a/modules/nixos/ps1.nix b/modules/nixos/ps1.nix new file mode 100644 index 00000000..bb7debc7 --- /dev/null +++ b/modules/nixos/ps1.nix @@ -0,0 +1,20 @@ +{config, ...}: let + fqdn = "${config.networking.hostName}.${config.networking.domain}"; +in { + programs.bash.promptInit = '' + # Provide a nice prompt if the terminal supports it. + if [ "$TERM" != "dumb" ] || [ -n "$INSIDE_EMACS" ]; then + PROMPT_COLOR="1;31m" + ((UID)) && PROMPT_COLOR="1;32m" + if [ -n "$INSIDE_EMACS" ]; then + # Emacs term mode doesn't support xterm title escape sequence (\e]0;) + PS1="\n\[\033[$PROMPT_COLOR\][\u@${fqdn}:\w]\n\\$\[\033[0m\] " + else + PS1="\n\[\033[$PROMPT_COLOR\][\[\e]0;\u@${fqdn}: \w\a\]\u@${fqdn}:\w]\n\\$\[\033[0m\] " + fi + if test "$TERM" = "xterm"; then + PS1="\[\033]2;${fqdn}:\u:\w\007\]$PS1" + fi + fi + ''; +} diff --git a/modules/nixos/sbd-server.nix b/modules/nixos/sbd-server.nix new file mode 100644 index 00000000..31706e27 --- /dev/null +++ b/modules/nixos/sbd-server.nix @@ -0,0 +1,128 @@ +{ + self, + config, + lib, + pkgs, + ... +}: let + cfg = config.services.sbd-server; + types = lib.types; +in { + options.services.sbd-server = { + enable = lib.mkEnableOption "sbd-server"; + + package = lib.mkOption { + default = self.packages.${pkgs.system}.sbd-serverd; + type = types.package; + }; + + address = lib.mkOption { + description = "address to bind"; + type = types.str; + }; + + tls-port = lib.mkOption { + description = "port to bind for incoming TLS connections"; + type = types.int; + }; + + url = lib.mkOption { + description = "url for incoming TLS connections to the signal server"; + type = types.str; + }; + + trusted-ip-header = lib.mkOption { + description = "request header key to extract the trusted IP from"; + type = types.nullOr types.str; + default = null; + }; + }; + + config = lib.mkIf (cfg.enable) { + # TODO: can be tested with check-services tool on the sbd integration branch + + systemd.services.sbd-server = { + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + + environment = { + TMPDIR = "%T"; + }; + + serviceConfig = { + DynamicUser = true; + PrivateTmp = true; + + # use this mechanism to let systemd take care of file permissions for the dynamic user it creates + LoadCredential = [ + "cert.pem:${config.security.acme.certs."${cfg.url}".directory}/cert.pem" + "key.pem:${config.security.acme.certs."${cfg.url}".directory}/key.pem" + ]; + Restart = "always"; + + AmbientCapabilities = + # needed for binding to ports <1024 + lib.lists.optionals (cfg.tls-port + < 1024) [ + "CAP_NET_BIND_SERVICE" + ]; + + ExecStart = builtins.concatStringsSep " " ( + [ + (lib.meta.getExe cfg.package) + + # bind to the public interface + "--bind=${cfg.address}:${builtins.toString cfg.tls-port}" + + # configure TLS certificates + ''--cert-pem-file="''${CREDENTIALS_DIRECTORY}/cert.pem"'' + ''--priv-key-pem-file="''${CREDENTIALS_DIRECTORY}/key.pem"'' + ] + ++ lib.lists.optionals (cfg.trusted-ip-header != null) [ + ''--trusted-ip-header=${cfg.trusted-ip-header}'' + ] + ); + }; + }; + + networking.firewall.allowedTCPPorts = [ + 80 + + cfg.tls-port + ]; + + services.nginx = { + enable = true; + virtualHosts."${cfg.url}" = { + serverName = cfg.url; + enableACME = true; + addSSL = true; + + locations."/".root = "/var/www/${cfg.url}"; + + listen = [ + { + addr = "${cfg.address}"; + port = 80; + ssl = false; + } + ]; + }; + }; + + security.acme = { + acceptTerms = true; + defaults = { + email = "acme@holo.host"; + }; + + # note: the directory watching tls reload story has not yet been implemented. when tls certs are updated, the service must be restarted + certs."${cfg.url}" = { + reloadServices = ["sbd-server"]; + + # staging server has higher retry limits. uncomment the following when debugging ACME challenges. + # server = "https://acme-staging-v02.api.letsencrypt.org/directory"; + }; + }; + }; +}