Unverified Commit 77bbd729 authored by Robert Hensing's avatar Robert Hensing Committed by GitHub
Browse files

snid: add modular service and test (#507875)

parents 11ba5520 94fa9d4b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1477,6 +1477,7 @@ in
  smokeping = runTest ./smokeping.nix;
  snapcast = runTest ./snapcast.nix;
  snapper = runTest ./snapper.nix;
  snid = runTest ./snid.nix;
  snipe-it = runTest ./web-apps/snipe-it.nix;
  snips-sh = runTest ./snips-sh.nix;
  snmpd = runTest ./snmpd.nix;

nixos/tests/snid.nix

0 → 100644
+31 −0
Original line number Diff line number Diff line
{ lib, ... }:
{
  _class = "nixosTest";
  name = "snid";

  nodes = {
    machine =
      { pkgs, ... }:
      {
        system.services.snid = {
          imports = [ pkgs.snid.services.default ];
          snid = {
            listen = [ "tcp:8443" ];
            mode = "tcp";
            backendCidrs = [ "127.0.0.0/8" ];
          };
        };

        networking.firewall.allowedTCPPorts = [ 8443 ];
      };
  };

  testScript = ''
    start_all()
    machine.wait_for_unit("multi-user.target")
    machine.wait_for_unit("snid.service")
    machine.wait_for_open_port(8443)
  '';

  meta.maintainers = with lib.maintainers; [ tomfitzhenry ];
}
+9 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
  lib,
  buildGoModule,
  fetchFromGitHub,
  nixosTests,
}:

buildGoModule (finalAttrs: {
@@ -17,6 +18,14 @@ buildGoModule (finalAttrs: {

  vendorHash = "sha256-cVarG6Tx4yWpZE5BLZsMtLV9LF1lsiFfIXxhYiNjQlY=";

  passthru = {
    tests.nixos = nixosTests.snid;
    services.default = {
      imports = [ (lib.modules.importApply ./service.nix { }) ];
      snid.package = finalAttrs.finalPackage;
    };
  };

  meta = {
    description = "Zero config TLS proxy server that uses SNI";
    homepage = "https://github.com/AGWA/snid";
+173 −0
Original line number Diff line number Diff line
# Non-module dependencies (`importApply`)
{ }:

# Service module
{
  lib,
  config,
  options,
  ...
}:
let
  inherit (lib)
    concatMap
    getExe
    mkOption
    optional
    types
    ;
  cfg = config.snid;
in
{
  # https://nixos.org/manual/nixos/unstable/#modular-services
  _class = "service";

  options = {
    snid = {
      package = mkOption {
        description = "Package to use for snid.";
        defaultText = "The snid package that provided this module.";
        type = types.package;
      };

      listen = mkOption {
        description = ''
          Addresses to listen on, in go-listener syntax.

          Examples: `"tcp:443"`, `"tcp:0.0.0.0:443"`, `"tcp:192.0.2.4:443"`.
        '';
        type = types.listOf types.str;
        example = [ "tcp:0.0.0.0:443" ];
      };

      mode = mkOption {
        description = ''
          Proxy mode. One of `nat46`, `tcp`, or `unix`.
        '';
        type = types.enum [
          "nat46"
          "tcp"
          "unix"
        ];
      };

      defaultHostname = mkOption {
        description = ''
          Hostname to use if a client does not include the SNI extension.
          If null, SNI-less connections will be terminated with a TLS alert.
        '';
        type = types.nullOr types.str;
        default = null;
      };

      nat46Prefix = mkOption {
        description = ''
          IPv6 prefix for the source address when connecting to the backend
          in NAT46 mode. The client's IPv4 address is placed in the lower 4
          bytes.

          Note: this prefix must be routed to the local host, e.g.
          ```
          ip route add local 64:ff9b:1::/96 dev lo
          ```
        '';
        type = types.nullOr types.str;
        default = null;
        example = "64:ff9b:1::";
      };

      backendCidrs = mkOption {
        description = ''
          Subnets to which connections may be forwarded. Connections to
          addresses outside these subnets are rejected. Used in `nat46` and
          `tcp` modes.
        '';
        type = types.listOf types.str;
        default = [ ];
        example = [
          "2001:db8::/64"
          "192.0.2.0/24"
        ];
      };

      backendPort = mkOption {
        description = ''
          Port number to connect to on the backend in TCP mode. If null,
          snid uses the same port as the inbound connection.
        '';
        type = types.nullOr types.port;
        default = null;
      };

      unixDirectory = mkOption {
        description = ''
          Path to the directory containing UNIX domain sockets, used in
          `unix` mode.
        '';
        type = types.nullOr types.path;
        default = null;
      };

      proxyProto = mkOption {
        description = ''
          Use PROXY protocol v2 to convey the client IP address to the
          backend. Applicable in `tcp` and `unix` modes.
        '';
        type = types.bool;
        default = false;
      };
    };
  };

  config = {
    assertions = [
      {
        assertion = cfg.mode == "nat46" -> cfg.nat46Prefix != null;
        message = "snid: `nat46Prefix` must be set when `mode` is `nat46`.";
      }
      {
        assertion = cfg.mode == "nat46" -> cfg.backendCidrs != [ ];
        message = "snid: `backendCidrs` must be set when `mode` is `nat46`.";
      }
      {
        assertion = cfg.mode == "tcp" -> cfg.backendCidrs != [ ];
        message = "snid: `backendCidrs` must be set when `mode` is `tcp`.";
      }
      {
        assertion = cfg.mode == "unix" -> cfg.unixDirectory != null;
        message = "snid: `unixDirectory` must be set when `mode` is `unix`.";
      }
    ];

    process.argv = [
      (getExe cfg.package)
      "-mode"
      cfg.mode
    ]
    ++ concatMap (l: [
      "-listen"
      l
    ]) cfg.listen
    ++ concatMap (c: [
      "-backend-cidr"
      c
    ]) cfg.backendCidrs
    ++ optional (cfg.defaultHostname != null) "-default-hostname=${cfg.defaultHostname}"
    ++ optional (cfg.nat46Prefix != null) "-nat46-prefix=${cfg.nat46Prefix}"
    ++ optional (cfg.backendPort != null) "-backend-port=${toString cfg.backendPort}"
    ++ optional (cfg.unixDirectory != null) "-unix-directory=${cfg.unixDirectory}"
    ++ optional cfg.proxyProto "-proxy-proto";
  }
  // lib.optionalAttrs (options ? systemd) {
    systemd.service = {
      after = [ "network.target" ];
      wants = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        Restart = "on-failure";
        DynamicUser = true;
        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
      };
    };
  };
}