diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
index 582b1715d1a4..0b59f19a934e 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
@@ -320,6 +320,15 @@
services.go-autoconfig.
+
+
+ tmate-ssh-server,
+ server side part of
+ tmate. Available
+ as
+ services.tmate-ssh-server.
+
+
Grafana
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index 3e38f85b8f10..7201b0e1cc09 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -110,6 +110,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [go-autoconfig](https://github.com/L11R/go-autoconfig), IMAP/SMTP autodiscover server. Available as [services.go-autoconfig](#opt-services.go-autoconfig.enable).
+- [tmate-ssh-server](https://github.com/tmate-io/tmate-ssh-server), server side part of [tmate](https://tmate.io/). Available as [services.tmate-ssh-server](#opt-services.tmate-ssh-server.enable).
+
- [Grafana Tempo](https://www.grafana.com/oss/tempo/), a distributed tracing store. Available as [services.tempo](#opt-services.tempo.enable).
- [AusweisApp2](https://www.ausweisapp.bund.de/), the authentication software for the German ID card. Available as [programs.ausweisapp](#opt-programs.ausweisapp.enable).
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c25450167075..db07d6312c42 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -960,6 +960,7 @@
./services/networking/tinc.nix
./services/networking/tinydns.nix
./services/networking/tftpd.nix
+ ./services/networking/tmate-ssh-server.nix
./services/networking/trickster.nix
./services/networking/tox-bootstrapd.nix
./services/networking/tox-node.nix
diff --git a/nixos/modules/services/networking/tmate-ssh-server.nix b/nixos/modules/services/networking/tmate-ssh-server.nix
new file mode 100644
index 000000000000..1b8f6662ef4c
--- /dev/null
+++ b/nixos/modules/services/networking/tmate-ssh-server.nix
@@ -0,0 +1,122 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+ cfg = config.services.tmate-ssh-server;
+
+ defaultKeysDir = "/etc/tmate-ssh-server-keys";
+ edKey = "${defaultKeysDir}/ssh_host_ed25519_key";
+ rsaKey = "${defaultKeysDir}/ssh_host_rsa_key";
+
+ keysDir =
+ if cfg.keysDir == null
+ then defaultKeysDir
+ else cfg.keysDir;
+
+ domain = config.networking.domain;
+in
+{
+ options.services.tmate-ssh-server = {
+ enable = mkEnableOption (mdDoc "tmate ssh server");
+
+ package = mkOption {
+ type = types.package;
+ description = mdDoc "The package containing tmate-ssh-server";
+ defaultText = literalExpression "pkgs.tmate-ssh-server";
+ default = pkgs.tmate-ssh-server;
+ };
+
+ host = mkOption {
+ type = types.str;
+ description = mdDoc "External host name";
+ defaultText = lib.literalExpression "config.networking.domain or config.networking.hostName ";
+ default =
+ if domain == null then
+ config.networking.hostName
+ else
+ domain;
+ };
+
+ port = mkOption {
+ type = types.port;
+ description = mdDoc "Listen port for the ssh server";
+ default = 2222;
+ };
+
+ openFirewall = mkOption {
+ type = types.bool;
+ default = true;
+ description = mdDoc "Whether to automatically open the specified ports in the firewall.";
+ };
+
+ advertisedPort = mkOption {
+ type = types.port;
+ description = mdDoc "External port advertised to clients";
+ };
+
+ keysDir = mkOption {
+ type = with types; nullOr str;
+ description = mdDoc "Directory containing ssh keys, defaulting to auto-generation";
+ default = null;
+ };
+ };
+
+ config = mkIf cfg.enable {
+
+ networking.firewall.allowedTCPPorts = optionals cfg.openFirewall [ cfg.port ];
+
+ services.tmate-ssh-server = {
+ advertisedPort = mkDefault cfg.port;
+ };
+
+ environment.systemPackages =
+ let
+ tmate-config = pkgs.writeText "tmate.conf"
+ ''
+ set -g tmate-server-host "${cfg.host}"
+ set -g tmate-server-port ${toString cfg.port}
+ set -g tmate-server-ed25519-fingerprint "@ed25519_fingerprint@"
+ set -g tmate-server-rsa-fingerprint "@rsa_fingerprint@"
+ '';
+ in
+ [
+ (pkgs.writeShellApplication {
+ name = "tmate-client-config";
+ runtimeInputs = with pkgs;[ openssh coreutils sd ];
+ text = ''
+ RSA_SIG="$(ssh-keygen -l -E SHA256 -f "${keysDir}/ssh_host_rsa_key.pub" | cut -d ' ' -f 2)"
+ ED25519_SIG="$(ssh-keygen -l -E SHA256 -f "${keysDir}/ssh_host_ed25519_key.pub" | cut -d ' ' -f 2)"
+ sd -sp '@ed25519_fingerprint@' "$ED25519_SIG" ${tmate-config} | \
+ sd -sp '@rsa_fingerprint@' "$RSA_SIG"
+ '';
+ })
+ ];
+
+ systemd.services.tmate-ssh-server = {
+ description = "tmate SSH Server";
+ after = [ "network.target" ];
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/tmate-ssh-server -h ${cfg.host} -p ${toString cfg.port} -q ${toString cfg.advertisedPort} -k ${keysDir}";
+ };
+ preStart = mkIf (cfg.keysDir == null) ''
+ if [[ ! -d ${defaultKeysDir} ]]
+ then
+ mkdir -p ${defaultKeysDir}
+ fi
+ if [[ ! -f ${edKey} ]]
+ then
+ ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f ${edKey} -N ""
+ fi
+ if [[ ! -f ${rsaKey} ]]
+ then
+ ${pkgs.openssh}/bin/ssh-keygen -t rsa -f ${rsaKey} -N ""
+ fi
+ '';
+ };
+ };
+
+ meta = {
+ maintainers = with maintainers; [ jlesquembre ];
+ };
+
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 21d8fa597532..131936a87c37 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -626,6 +626,7 @@ in {
tinc = handleTest ./tinc {};
tinydns = handleTest ./tinydns.nix {};
tinywl = handleTest ./tinywl.nix {};
+ tmate-ssh-server = handleTest ./tmate-ssh-server.nix { };
tomcat = handleTest ./tomcat.nix {};
tor = handleTest ./tor.nix {};
# traefik test relies on docker-containers
diff --git a/nixos/tests/tmate-ssh-server.nix b/nixos/tests/tmate-ssh-server.nix
new file mode 100644
index 000000000000..e7f94db9bfcf
--- /dev/null
+++ b/nixos/tests/tmate-ssh-server.nix
@@ -0,0 +1,73 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+let
+ inherit (import ./ssh-keys.nix pkgs)
+ snakeOilPrivateKey snakeOilPublicKey;
+
+ setUpPrivateKey = name: ''
+ ${name}.succeed(
+ "mkdir -p /root/.ssh",
+ "chown 700 /root/.ssh",
+ "cat '${snakeOilPrivateKey}' > /root/.ssh/id_snakeoil",
+ "chown 600 /root/.ssh/id_snakeoil",
+ )
+ ${name}.wait_for_file("/root/.ssh/id_snakeoil")
+ '';
+
+ sshOpts = "-oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oIdentityFile=/root/.ssh/id_snakeoil";
+
+in
+{
+ name = "tmate-ssh-server";
+ nodes =
+ {
+ server = { ... }: {
+ services.tmate-ssh-server = {
+ enable = true;
+ port = 2223;
+ };
+ };
+ client = { ... }: {
+ environment.systemPackages = [ pkgs.tmate ];
+ services.openssh.enable = true;
+ users.users.root.openssh.authorizedKeys.keys = [ snakeOilPublicKey ];
+ };
+ client2 = { ... }: {
+ environment.systemPackages = [ pkgs.openssh ];
+ };
+ };
+ testScript = ''
+ start_all()
+
+ server.wait_for_unit("tmate-ssh-server.service")
+ server.wait_for_open_port(2223)
+ server.wait_for_file("/etc/tmate-ssh-server-keys/ssh_host_ed25519_key.pub")
+ server.wait_for_file("/etc/tmate-ssh-server-keys/ssh_host_rsa_key.pub")
+ server.succeed("tmate-client-config > /tmp/tmate.conf")
+ server.wait_for_file("/tmp/tmate.conf")
+
+ ${setUpPrivateKey "server"}
+ client.wait_for_unit("sshd.service")
+ client.wait_for_open_port(22)
+ server.succeed("scp ${sshOpts} /tmp/tmate.conf client:/tmp/tmate.conf")
+
+ client.wait_for_file("/tmp/tmate.conf")
+ client.send_chars("root\n")
+ client.sleep(2)
+ client.send_chars("tmate -f /tmp/tmate.conf\n")
+ client.sleep(2)
+ client.send_chars("q")
+ client.sleep(2)
+ client.send_chars("tmate display -p '#{tmate_ssh}' > /tmp/ssh_command\n")
+ client.wait_for_file("/tmp/ssh_command")
+ ssh_cmd = client.succeed("cat /tmp/ssh_command")
+
+ client2.succeed("mkdir -p ~/.ssh; ssh-keyscan -p 2223 server > ~/.ssh/known_hosts")
+ client2.send_chars("root\n")
+ client2.sleep(2)
+ client2.send_chars(ssh_cmd.strip() + "\n")
+ client2.sleep(2)
+ client2.send_chars("touch /tmp/client_2\n")
+
+ client.wait_for_file("/tmp/client_2")
+ '';
+})
diff --git a/pkgs/servers/tmate-ssh-server/default.nix b/pkgs/servers/tmate-ssh-server/default.nix
index dd1c6ad05c48..539b456c9d7f 100644
--- a/pkgs/servers/tmate-ssh-server/default.nix
+++ b/pkgs/servers/tmate-ssh-server/default.nix
@@ -1,14 +1,28 @@
-{ lib, stdenv, fetchFromGitHub, autoreconfHook, cmake, libtool, pkg-config
-, zlib, openssl, libevent, ncurses, ruby, msgpack, libssh }:
+{ lib
+, stdenv
+, fetchFromGitHub
+, autoreconfHook
+, cmake
+, libtool
+, pkg-config
+, zlib
+, openssl
+, libevent
+, ncurses
+, ruby
+, msgpack
+, libssh
+, nixosTests
+}:
stdenv.mkDerivation rec {
pname = "tmate-ssh-server";
version = "unstable-2021-10-17";
src = fetchFromGitHub {
- owner = "tmate-io";
- repo = "tmate-ssh-server";
- rev = "1f314123df2bb29cb07427ed8663a81c8d9034fd";
+ owner = "tmate-io";
+ repo = "tmate-ssh-server";
+ rev = "1f314123df2bb29cb07427ed8663a81c8d9034fd";
sha256 = "sha256-9/xlMvtkNWUBRYYnJx20qEgtEcjagH2NtEKZcDOM1BY=";
};
@@ -17,12 +31,13 @@ stdenv.mkDerivation rec {
buildInputs = [ libtool zlib openssl libevent ncurses ruby msgpack libssh ];
nativeBuildInputs = [ autoreconfHook cmake pkg-config ];
+ passthru.tests.tmate-ssh-server = nixosTests.tmate-ssh-server;
+
meta = with lib; {
- homepage = "https://tmate.io/";
+ homepage = "https://tmate.io/";
description = "tmate SSH Server";
- license = licenses.mit;
- platforms = platforms.unix;
+ license = licenses.mit;
+ platforms = platforms.unix;
maintainers = with maintainers; [ ck3d ];
};
}
-