nixos/tmate-ssh-server: init module (#192270)

* nixos/tmate-ssh-server: init module

Co-authored-by: Aaron Andersen <aaron@fosslib.net>

Changed files
+232 -9
nixos
doc
manual
from_md
release-notes
release-notes
modules
tests
pkgs
servers
tmate-ssh-server
+9
nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
···
</listitem>
<listitem>
<para>
+
<link xlink:href="https://github.com/tmate-io/tmate-ssh-server">tmate-ssh-server</link>,
+
server side part of
+
<link xlink:href="https://tmate.io/">tmate</link>. Available
+
as
+
<link linkend="opt-services.tmate-ssh-server.enable">services.tmate-ssh-server</link>.
+
</para>
+
</listitem>
+
<listitem>
+
<para>
<link xlink:href="https://www.grafana.com/oss/tempo/">Grafana
Tempo</link>, a distributed tracing store. Available as
<link linkend="opt-services.tempo.enable">services.tempo</link>.
+2
nixos/doc/manual/release-notes/rl-2211.section.md
···
- [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).
+1
nixos/modules/module-list.nix
···
./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
+122
nixos/modules/services/networking/tmate-ssh-server.nix
···
+
{ 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 ];
+
};
+
+
}
+1
nixos/tests/all-tests.nix
···
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
+73
nixos/tests/tmate-ssh-server.nix
···
+
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")
+
'';
+
})
+24 -9
pkgs/servers/tmate-ssh-server/default.nix
···
-
{ 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=";
};
···
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 ];
};
}
-