Unverified Commit c132df63 authored by Lin Jian's avatar Lin Jian Committed by GitHub
Browse files

Merge pull request #250792 from ckiee/init-rkvm

{,nixos/}rkvm: init
parents f6eea3d5 2295751a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -723,6 +723,7 @@
  ./services/misc/ripple-data-api.nix
  ./services/misc/rippled.nix
  ./services/misc/rmfakecloud.nix
  ./services/misc/rkvm.nix
  ./services/misc/rshim.nix
  ./services/misc/safeeyes.nix
  ./services/misc/sdrplay.nix
+164 −0
Original line number Diff line number Diff line
{ options, config, pkgs, lib, ... }:

with lib;
let
  opt = options.services.rkvm;
  cfg = config.services.rkvm;
  toml = pkgs.formats.toml { };
in
{
  meta.maintainers = with maintainers; [ ckie ];

  options.services.rkvm = {
    enable = mkOption {
      default = cfg.server.enable || cfg.client.enable;
      defaultText = literalExpression "config.${opt.server.enable} || config.${opt.client.enable}";
      type = types.bool;
      description = mdDoc ''
        Whether to enable rkvm, a Virtual KVM switch for Linux machines.
      '';
    };

    package = mkPackageOption pkgs "rkvm" { };

    server = {
      enable = mkEnableOption "the rkvm server daemon (input transmitter)";

      settings = mkOption {
        type = types.submodule
          {
            freeformType = toml.type;
            options = {
              listen = mkOption {
                type = types.str;
                default = "0.0.0.0:5258";
                description = mdDoc ''
                  An internet socket address to listen on, either IPv4 or IPv6.
                '';
              };

              switch-keys = mkOption {
                type = types.listOf types.str;
                default = [ "left-alt" "left-ctrl" ];
                description = mdDoc ''
                  A key list specifying a host switch combination.

                  _A list of key names is available in <https://github.com/htrefil/rkvm/blob/master/switch-keys.md>._
                '';
              };

              certificate = mkOption {
                type = types.path;
                default = "/etc/rkvm/certificate.pem";
                description = mdDoc ''
                  TLS certificate path.

                  ::: {.note}
                  This should be generated with {command}`rkvm-certificate-gen`.
                  :::
                '';
              };

              key = mkOption {
                type = types.path;
                default = "/etc/rkvm/key.pem";
                description = mdDoc ''
                  TLS key path.

                  ::: {.note}
                  This should be generated with {command}`rkvm-certificate-gen`.
                  :::
                '';
              };

              password = mkOption {
                type = types.str;
                description = mdDoc ''
                  Shared secret token to authenticate the client.
                  Make sure this matches your client's config.
                '';
              };
            };
          };

        default = { };
        description = mdDoc "Structured server daemon configuration";
      };
    };

    client = {
      enable = mkEnableOption "the rkvm client daemon (input receiver)";

      settings = mkOption {
        type = types.submodule
          {
            freeformType = toml.type;
            options = {
              server = mkOption {
                type = types.str;
                example = "192.168.0.123:5258";
                description = mdDoc ''
                  An RKVM server's internet socket address, either IPv4 or IPv6.
                '';
              };

              certificate = mkOption {
                type = types.path;
                default = "/etc/rkvm/certificate.pem";
                description = mdDoc ''
                  TLS ceritficate path.

                  ::: {.note}
                  This should be generated with {command}`rkvm-certificate-gen`.
                  :::
                '';
              };

              password = mkOption {
                type = types.str;
                description = mdDoc ''
                  Shared secret token to authenticate the client.
                  Make sure this matches your server's config.
                '';
              };
            };
          };

        default = {};
        description = mdDoc "Structured client daemon configuration";
      };
    };

  };

  config = mkIf cfg.enable {
    environment.systemPackages = [ cfg.package ];

    systemd.services =
      let
        mkBase = component: {
          description = "RKVM ${component}";
          wantedBy = [ "multi-user.target" ];
          after = {
            server = [ "network.target" ];
            client = [ "network-online.target" ];
          }.${component};
          wants = {
            server = [ ];
            client = [ "network-online.target" ];
          }.${component};
          serviceConfig = {
            ExecStart = "${cfg.package}/bin/rkvm-${component} ${toml.generate "rkvm-${component}.toml" cfg.${component}.settings}";
            Restart = "always";
            RestartSec = 5;
            Type = "simple";
          };
        };
      in
      {
        rkvm-server = mkIf cfg.server.enable (mkBase "server");
        rkvm-client = mkIf cfg.client.enable (mkBase "client");
      };
  };

}
+1 −0
Original line number Diff line number Diff line
@@ -699,6 +699,7 @@ in {
  restartByActivationScript = handleTest ./restart-by-activation-script.nix {};
  restic = handleTest ./restic.nix {};
  retroarch = handleTest ./retroarch.nix {};
  rkvm = handleTest ./rkvm {};
  robustirc-bridge = handleTest ./robustirc-bridge.nix {};
  roundcube = handleTest ./roundcube.nix {};
  rshim = handleTest ./rshim.nix {};
+18 −0
Original line number Diff line number Diff line
-----BEGIN CERTIFICATE-----
MIIC3jCCAcagAwIBAgIUWW1hb9xdRtxAhA42jkS89goW9LUwDQYJKoZIhvcNAQEL
BQAwDzENMAsGA1UEAwwEcmt2bTAeFw0yMzA4MjIxOTI1NDlaFw0zMzA4MTkxOTI1
NDlaMA8xDTALBgNVBAMMBHJrdm0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCuBsh0+LDXN4b2o/PJjzuiZ9Yv9Pz1Oho9WRiXtNIuHTRdBCcht/iu3PGF
ICIX+H3dqQOziGSCTAQGJD2p+1ik8d+boJbpa0oxXuHuomsMAT3mib3GpipQoBLP
KaEbWEsvQbr3RMx8WOtG4dmRQFzSVVtmAXyM0pNyisd4eUCplyIl9gsRJIvsO/0M
OkgOZW9XLfKiAWlZoyXEkBmPAshg3EkwQtmwxPA/NgWbAOW3zJKSChxnnGYiuIIu
R/wJ8OQXHP6boQLQGUhCWBKa1uK1gEBmV3Pj6uK8RzTkQq6/47F5sPa6VfqQYdyl
TCs9bSqHXZjqMBoiSp22uH6+Lh9RAgMBAAGjMjAwMA8GA1UdEQQIMAaHBAoAAAEw
HQYDVR0OBBYEFEh9HEsnY3dfNKVyPWDbwfR0qHopMA0GCSqGSIb3DQEBCwUAA4IB
AQB/r+K20JqegUZ/kepPxIU95YY81aUUoxvLbu4EAgh8o46Fgm75qrTZPg4TaIZa
wtVejekrF+p3QVf0ErUblh/iCjTZPSzCmKHZt8cc9OwTH7bt3bx7heknzLDyIa5z
szAL+6241UggQ5n5NUGn5+xZHA7TMe47xAZPaRMlCQ/tp5pWFjH6WSSQSP5t4Ag9
ObhY+uudFjmWi3QIBTr3iIscbWx7tD8cjus7PzM7+kszSDRV04xb6Ox8JzW9MKIN
GwgwVgs3zCuyqBmTGnR1og3aMk6VtlyZUYE78uuc+fMBxqoBZ0mykeOp0Tbzgtf7
gPkYcQ6vonoQhuTXYj/NrY+b
-----END CERTIFICATE-----
+104 −0
Original line number Diff line number Diff line
import ../make-test-python.nix ({ pkgs, ... }:
let
  # Generated with
  #
  # nix shell .#rkvm --command "rkvm-certificate-gen --ip-addresses 10.0.0.1 cert.pem key.pem"
  #
  snakeoil-cert = ./cert.pem;
  snakeoil-key = ./key.pem;
in
{
  name = "rkvm";

  nodes = {
    server = { pkgs, ... }: {
      imports = [ ../common/user-account.nix ];

      virtualisation.vlans = [ 1 ];

      networking = {
        useNetworkd = true;
        useDHCP = false;
        firewall.enable = false;
      };

      systemd.network.networks."01-eth1" = {
        name = "eth1";
        networkConfig.Address = "10.0.0.1/24";
      };

      services.getty.autologinUser = "alice";

      services.rkvm.server = {
        enable = true;
        settings = {
          certificate = snakeoil-cert;
          key = snakeoil-key;
          password = "snakeoil";
          switch-keys = [ "left-alt" "right-alt" ];
        };
      };
    };

    client = { pkgs, ... }: {
      imports = [ ../common/user-account.nix ];

      virtualisation.vlans = [ 1 ];

      networking = {
        useNetworkd = true;
        useDHCP = false;
        firewall.enable = false;
      };

      systemd.network.networks."01-eth1" = {
        name = "eth1";
        networkConfig.Address = "10.0.0.2/24";
      };

      services.getty.autologinUser = "alice";

      services.rkvm.client = {
        enable = true;
        settings = {
          server = "10.0.0.1:5258";
          certificate = snakeoil-cert;
          key = snakeoil-key;
          password = "snakeoil";
        };
      };
    };
  };

  testScript = ''
    server.wait_for_unit("getty@tty1.service")
    server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
    server.wait_for_unit("rkvm-server")
    server.wait_for_open_port(5258)

    client.wait_for_unit("getty@tty1.service")
    client.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
    client.wait_for_unit("rkvm-client")

    server.sleep(1)

    # Switch to client
    server.send_key("alt-alt_r", delay=0.2)
    server.send_chars("echo 'hello client' > /tmp/test.txt\n")

    # Switch to server
    server.send_key("alt-alt_r", delay=0.2)
    server.send_chars("echo 'hello server' > /tmp/test.txt\n")

    server.sleep(1)

    client.systemctl("stop rkvm-client.service")
    server.systemctl("stop rkvm-server.service")

    server_file = server.succeed("cat /tmp/test.txt")
    assert server_file.strip() == "hello server"

    client_file = client.succeed("cat /tmp/test.txt")
    assert client_file.strip() == "hello client"
  '';
})
Loading