I would like to run a separate instance of the sshd
service on my machine. Why I want to do that is at the bottom of the post.
My first attempt was to naively make a copy of the config.systemd.services.sshd
and try to combine/union it with the values I wanted overriden. This didn’t work and ended up with errors around infinite recursion during evaluation. I think I tracked that down to the systemd-lib.nix
and how it’s evaluating that value, and I think I see how it ends up with an infinite recursion there.
Code I used in this attempt is below.
systemd.services.alt-sshd = let
oldSSHCOnfig = config.systemd.services.sshd;
in
oldSSHCOnfig
// {
after = ["sshd.service"];
# after = oldSSHCOnfig ++ ["sshd.service"];
preStart = "";
serviceConfig.ExecStart =
"${pkgs.openssh}/bin/sshd "
+ "-D "
+ # don't detach into a daemon process
"-f /etc/ssh/sshd_config"
+ "-p 4044";
};
Is there a way to do this than copying the sshd nix module into my package and disabling the built in one? I found this reddit post that seems to be trying to do the same thing, but that seems to pretty complex. I’m unsure what if any issues are involved with what appears to be a second evaluation of the nixosSystem described in that post.
I need to copy the sshd nix module into my package and disable the built in one?
Why I want to do this
The impetus behind this is to have a the main sshd
service bind to port 22, while my alt-sshd
service binds to another port.
I’m aware that it’s possible to have sshd
listen on multiple ports, but openssh doesn’t log the port a connection came in on, which prevents me from comparing statistics for connections across the different ports, or use different sshguard
policies based on the port that’s being connected to.
oldSSHConfig // { /* foo bar */ }
probably isn’t the right way to go about it. Probably mkMerge
would be better. i.e.
systemd.services.alt-sshd = lib.mkMerge [
config.systemd.services.sshd
{
serviceConfig.ExecStart = lib.mkForce ....;
}
];
Regardless, I haven’t scoured the documentation myself, but I’d be very surprised if there is in fact no way to tell which port someone logged in through with a single sshd
instance using multiple ports.
Using lib.mkMerge
doesn’t seem to work, since i end up with running into the infinite recursion problem.
# alt-ssh.nix
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkOption types;
in {
options = {
};
config = {
systemd.services.alt-sshd = let
oldSSHCOnfig = config.systemd.services.sshd;
in
lib.mkMerge [
oldSSHCOnfig
{
serviceConfig.ExecStart =
lib.mkForce "${pkgs.openssh}/bin/sshd "
+ "-D "
"-f /etc/ssh/sshd_config"
+ "-p 4044";
}
];
};
}
As far as the SSH service, I looked at the openssh portable source, and the log message only shows the source port, not the destination port. This is the code that convinced me that I couldn’t get the port that was connected to from the logs.
I was also very surprised, but now I’m trying to find way to run a variant of a service to get around this.
I attempted to do this by simply copying the individual attributes, and that mostly worked. However one strange thing happened that I’m not able to understand.
Setting the path of the systemd service attrset to the value in config.systemd.services.sshd.path
resulted in a different value being materialized in the final configuration. The nix file below might help explain what confused me…
{
config,
lib,
pkgs,
...
}: let
cfg = config.services.openssh;
cfgc = config.programs.ssh;
baseSSHConfig = config.systemd.services.sshd;
in {
options = {
};
config = {
systemd = let
service = {
description = "Additional SSH Daemon";
after = ["ssh.service"];
stopIfChanged = false;
# This works
path = [cfgc.package pkgs.gawk];
# This does not
# path = baseSSHConfig.path;
environment.LD_LIBRARY_PATH = baseSSHConfig.environment.LD_LIBRARY_PATH;
restartTriggers = baseSSHConfig.restartTriggers;
serviceConfig =
{
ExecStart = baseSSHConfig.serviceConfig.ExecStart + " -p 4044";
KillMode = baseSSHConfig.serviceConfig.KillMode;
}
// (
if cfg.startWhenNeeded
then {
StandardInput = baseSSHConfig.serviceConfig.StandardInput;
StandardError = baseSSHConfig.serviceConfig.StandardError;
}
else {
Restart = baseSSHConfig.serviceConfig.Restart;
Type = baseSSHConfig.serviceConfig.Type;
}
);
};
in {
services.alt-sshd = service;
};
};
}
When path = baseSSHConfig.path
was un-commented instead of the path variable used there, the generated environment file for alt-sshd.service
had the path value
Environment="PATH=/nix/store/1s5f2qcr8dp0mxmh8k26cbi1xgcqp8xg-openssh-9.3p1/bin:/nix/store/an56bphjscw6pcj4yyjzi1z0l3lddkfx-gawk-5.2.2/bin:/nix/store/02dr9ymdqpkb75vf0v1z2l91z2q3izy9-coreutils-9.3/bin:/nix/store/92iyjlsh7c66x4mv2bqkj5mwn4059dmz-findutils-4.9.0/bin:/nix/store/7ws5zmh4llmml550x02pqpcyzv5j4m4c-gnugrep-3.11/bin:/nix/store/zdra3vjgn5sbbwhiwp5b2f0c2akkyl6h-gnused-4.9/bin:/nix/store/9adpz6y8s4mgkxfhc9rzy25r7f3wdyll-systemd-253.5/bin:/nix/store/02dr9ymdqpkb75vf0v1z2l91z2q3izy9-coreutils-9.3/bin:/
# Final two elements of PATH are
/nix/store/02dr9ymdqpkb75vf0v1z2l91z2q3izy9-coreutils-9.3/bin:/
Wheras the sshd.service
file had the value
Environment="PATH=/nix/store/1s5f2qcr8dp0mxmh8k26cbi1xgcqp8xg-openssh-9.3p1/bin:/nix/store/an56bphjscw6pcj4yyjzi1z0l3lddkfx-gawk-5.2.2/bin:/nix/store/02dr9ymdqpkb75vf0v1z2l91z2q3izy9-coreutils-9.3/bin:/nix/store/92iyjlsh7c66x4mv2bqkj5mwn4059dmz-findutils-4.9.0/bin:/nix/store/7ws5zmh4llmml550x02pqpcyzv5j4m4c-gnugrep-3.11/bin:/nix/store/zdra3vjgn5sbbwhiwp5b2f0c2akkyl6h-gnused-4.9/bin:/nix/store/9adpz6y8s4mgkxfhc9rzy25r7f3wdyll-systemd-253.5/bin:/nix/store/1s5f2qcr8dp0mxmh8k26cbi1xgcqp8xg-openssh-9.3p1/sbin:
# Final element of PATH is
/nix/store/1s5f2qcr8dp0mxmh8k26cbi1xgcqp8xg-openssh-9.3p1/sbin
If anyone knows how that’s possible I’d love to learn why this happens.