2017-05-09 05:30:37 +02:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
|
|
|
|
cfg = config.programs.ssh;
|
|
|
|
|
|
|
|
yn = flag: if flag then "yes" else "no";
|
|
|
|
|
2017-09-10 13:59:59 +02:00
|
|
|
matchBlockModule = types.submodule ({ name, ... }: {
|
2017-05-09 05:30:37 +02:00
|
|
|
options = {
|
|
|
|
host = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
example = "*.example.org";
|
|
|
|
description = ''
|
|
|
|
The host pattern used by this conditional block.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
type = types.nullOr types.int;
|
|
|
|
default = null;
|
|
|
|
description = "Specifies port number to connect on remote host.";
|
|
|
|
};
|
|
|
|
|
|
|
|
forwardX11 = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Specifies whether X11 connections will be automatically redirected
|
|
|
|
over the secure channel and <envar>DISPLAY</envar> set.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
forwardX11Trusted = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Specifies whether remote X11 clients will have full access to the
|
|
|
|
original X11 display.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
identitiesOnly = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Specifies that ssh should only use the authentication
|
|
|
|
identity explicitly configured in the
|
|
|
|
<filename>~/.ssh/config</filename> files or passed on the
|
|
|
|
ssh command-line, even if <command>ssh-agent</command>
|
|
|
|
offers more identities.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
identityFile = mkOption {
|
2017-05-13 12:23:59 +02:00
|
|
|
type = types.nullOr types.str;
|
2017-05-09 05:30:37 +02:00
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
Specifies a file from which the user identity is read.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
user = mkOption {
|
2017-05-13 12:23:59 +02:00
|
|
|
type = types.nullOr types.str;
|
2017-05-09 05:30:37 +02:00
|
|
|
default = null;
|
|
|
|
description = "Specifies the user to log in as.";
|
|
|
|
};
|
|
|
|
|
|
|
|
hostname = mkOption {
|
2017-05-13 12:23:59 +02:00
|
|
|
type = types.nullOr types.str;
|
2017-05-09 05:30:37 +02:00
|
|
|
default = null;
|
|
|
|
description = "Specifies the real host name to log into.";
|
|
|
|
};
|
|
|
|
|
|
|
|
serverAliveInterval = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 0;
|
|
|
|
description =
|
|
|
|
"Set timeout in seconds after which response will be requested.";
|
|
|
|
};
|
|
|
|
|
2018-01-06 01:41:44 +01:00
|
|
|
compression = mkOption {
|
2018-01-14 22:06:32 +01:00
|
|
|
type = types.nullOr types.bool;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
Specifies whether to use compression. Omitted from the host
|
|
|
|
block when <literal>null</literal>.
|
|
|
|
'';
|
2018-01-06 01:41:44 +01:00
|
|
|
};
|
|
|
|
|
2017-05-09 05:30:37 +02:00
|
|
|
checkHostIP = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = true;
|
|
|
|
description = ''
|
|
|
|
Check the host IP address in the
|
|
|
|
<filename>known_hosts</filename> file.
|
|
|
|
'';
|
|
|
|
};
|
2017-05-13 12:08:09 +02:00
|
|
|
|
|
|
|
proxyCommand = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
description = "The command to use to connect to the server.";
|
|
|
|
};
|
2018-01-10 16:40:07 +01:00
|
|
|
|
|
|
|
extraOptions = mkOption {
|
|
|
|
type = types.attrsOf types.str;
|
|
|
|
default = {};
|
|
|
|
description = "Extra configuration options for the host.";
|
|
|
|
};
|
2017-05-09 05:30:37 +02:00
|
|
|
};
|
2017-09-10 13:59:59 +02:00
|
|
|
|
|
|
|
config.host = mkDefault name;
|
|
|
|
});
|
2017-05-09 05:30:37 +02:00
|
|
|
|
|
|
|
matchBlockStr = cf: concatStringsSep "\n" (
|
|
|
|
["Host ${cf.host}"]
|
|
|
|
++ optional (cf.port != null) " Port ${toString cf.port}"
|
|
|
|
++ optional cf.forwardX11 " ForwardX11 yes"
|
|
|
|
++ optional cf.forwardX11Trusted " ForwardX11Trusted yes"
|
|
|
|
++ optional cf.identitiesOnly " IdentitiesOnly yes"
|
|
|
|
++ optional (cf.user != null) " User ${cf.user}"
|
|
|
|
++ optional (cf.identityFile != null) " IdentityFile ${cf.identityFile}"
|
|
|
|
++ optional (cf.hostname != null) " HostName ${cf.hostname}"
|
|
|
|
++ optional (cf.serverAliveInterval != 0)
|
|
|
|
" ServerAliveInterval ${toString cf.serverAliveInterval}"
|
2018-01-14 22:06:32 +01:00
|
|
|
++ optional (cf.compression != null) " Compression ${yn cf.compression}"
|
2017-05-09 05:30:37 +02:00
|
|
|
++ optional (!cf.checkHostIP) " CheckHostIP no"
|
2017-05-13 12:08:09 +02:00
|
|
|
++ optional (cf.proxyCommand != null) " ProxyCommand ${cf.proxyCommand}"
|
2018-01-10 16:40:07 +01:00
|
|
|
++ mapAttrsToList (n: v: " ${n} ${v}") cf.extraOptions
|
2017-05-09 05:30:37 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{
|
2017-09-26 23:40:31 +02:00
|
|
|
meta.maintainers = [ maintainers.rycee ];
|
|
|
|
|
2017-05-09 05:30:37 +02:00
|
|
|
options.programs.ssh = {
|
|
|
|
enable = mkEnableOption "SSH client configuration";
|
|
|
|
|
|
|
|
forwardAgent = mkOption {
|
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
description = ''
|
|
|
|
Whether connection to authentication agent (if any) will be forwarded
|
|
|
|
to remote machine.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2018-01-06 01:41:44 +01:00
|
|
|
compression = mkOption {
|
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
description = "Specifies whether to use compression.";
|
|
|
|
};
|
|
|
|
|
|
|
|
serverAliveInterval = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 0;
|
|
|
|
description = ''
|
|
|
|
Set default timeout in seconds after which response will be requested.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
hashKnownHosts = mkOption {
|
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
description = ''
|
|
|
|
Indicates that
|
|
|
|
<citerefentry>
|
|
|
|
<refentrytitle>ssh</refentrytitle>
|
|
|
|
<manvolnum>1</manvolnum>
|
|
|
|
</citerefentry>
|
|
|
|
should hash host names and addresses when they are added to
|
|
|
|
the known hosts file.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
userKnownHostsFile = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "~/.ssh/known_hosts";
|
|
|
|
description = ''
|
|
|
|
Specifies one or more files to use for the user host key
|
|
|
|
database, separated by whitespace. The default is
|
|
|
|
<filename>~/.ssh/known_hosts</filename>.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2017-05-09 05:30:37 +02:00
|
|
|
controlMaster = mkOption {
|
|
|
|
default = "no";
|
|
|
|
type = types.enum ["yes" "no" "ask" "auto" "autoask"];
|
|
|
|
description = ''
|
|
|
|
Configure sharing of multiple sessions over a single network connection.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
controlPath = mkOption {
|
2017-05-13 12:23:59 +02:00
|
|
|
type = types.str;
|
2017-05-09 05:30:37 +02:00
|
|
|
default = "~/.ssh/master-%r@%h:%p";
|
|
|
|
description = ''
|
|
|
|
Specify path to the control socket used for connection sharing.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2017-07-29 17:47:18 +02:00
|
|
|
controlPersist = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "no";
|
|
|
|
example = "10m";
|
|
|
|
description = ''
|
|
|
|
Whether control socket should remain open in the background.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2018-01-10 16:40:07 +01:00
|
|
|
extraConfig = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
|
|
|
description = ''
|
|
|
|
Extra configuration.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2018-03-23 19:32:02 +01:00
|
|
|
extraOptionOverrides = mkOption {
|
|
|
|
type = types.attrsOf types.str;
|
|
|
|
default = {};
|
|
|
|
description = ''
|
|
|
|
Extra SSH configuration options that take precedence over any
|
|
|
|
host specific configuration.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2017-05-09 05:30:37 +02:00
|
|
|
matchBlocks = mkOption {
|
2017-09-10 13:59:59 +02:00
|
|
|
type = types.loaOf matchBlockModule;
|
2018-03-23 19:32:02 +01:00
|
|
|
default = {};
|
2017-09-10 13:59:59 +02:00
|
|
|
example = literalExample ''
|
|
|
|
{
|
|
|
|
"john.example.com" = {
|
|
|
|
hostname = "example.com";
|
|
|
|
user = "john";
|
|
|
|
};
|
|
|
|
foo = {
|
|
|
|
hostname = "example.com";
|
|
|
|
identityFile = "/home/john/.ssh/foo_rsa";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
'';
|
2017-05-09 05:30:37 +02:00
|
|
|
description = ''
|
2017-09-10 13:59:59 +02:00
|
|
|
Specify per-host settings. Note, if the order of rules matter
|
|
|
|
then this must be a list. See
|
|
|
|
<citerefentry>
|
|
|
|
<refentrytitle>ssh_config</refentrytitle>
|
|
|
|
<manvolnum>5</manvolnum>
|
|
|
|
</citerefentry>.
|
2017-05-09 05:30:37 +02:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
home.file.".ssh/config".text = ''
|
2018-03-23 19:32:02 +01:00
|
|
|
${concatStringsSep "\n" (
|
|
|
|
mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides)}
|
2018-01-10 16:40:07 +01:00
|
|
|
|
2017-09-10 13:59:59 +02:00
|
|
|
${concatStringsSep "\n\n" (
|
|
|
|
map matchBlockStr (
|
|
|
|
builtins.attrValues cfg.matchBlocks))}
|
2018-03-23 19:32:02 +01:00
|
|
|
|
|
|
|
Host *
|
|
|
|
ForwardAgent ${yn cfg.forwardAgent}
|
|
|
|
Compression ${yn cfg.compression}
|
|
|
|
ServerAliveInterval ${toString cfg.serverAliveInterval}
|
|
|
|
HashKnownHosts ${yn cfg.hashKnownHosts}
|
|
|
|
UserKnownHostsFile ${cfg.userKnownHostsFile}
|
|
|
|
ControlMaster ${cfg.controlMaster}
|
|
|
|
ControlPath ${cfg.controlPath}
|
|
|
|
ControlPersist ${cfg.controlPersist}
|
|
|
|
|
|
|
|
${replaceStrings ["\n"] ["\n "] cfg.extraConfig}
|
2017-05-09 05:30:37 +02:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
}
|