{ config, lib, pkgs, ... }: with lib; let cfg = config.services.shellinabox; # If a certificate file is specified, shellinaboxd requires # a file descriptor to retrieve it fd = "3"; createFd = optionalString (cfg.certFile != null) "${fd}<${cfg.certFile}"; # Command line arguments for the shellinabox daemon args = [ "--background" ] ++ optional (! cfg.enableSSL) "--disable-ssl" ++ optional (cfg.certFile != null) "--cert-fd=${fd}" ++ optional (cfg.certDirectory != null) "--cert=${cfg.certDirectory}" ++ cfg.extraOptions; # Command to start shellinaboxd cmd = "${pkgs.shellinabox}/bin/shellinaboxd ${concatStringsSep " " args}"; # Command to start shellinaboxd if certFile is specified wrappedCmd = "${pkgs.bash}/bin/bash -c 'exec ${createFd} && ${cmd}'"; in { ###### interface options = { services.shellinabox = { enable = mkEnableOption "shellinabox daemon"; user = mkOption { type = types.str; default = "root"; description = '' User to run shellinaboxd as. If started as root, the server drops privileges by changing to nobody, unless overridden by the --user option. ''; }; enableSSL = mkOption { type = types.bool; default = false; description = '' Whether or not to enable SSL (https) support. ''; }; certDirectory = mkOption { type = types.nullOr types.path; default = null; example = "/var/certs"; description = '' The daemon will look in this directory far any certificates. If the browser negotiated a Server Name Identification the daemon will look for a matching certificate-SERVERNAME.pem file. If no SNI handshake takes place, it will fall back on using the certificate in the certificate.pem file. If no suitable certificate is installed, shellinaboxd will attempt to create a new self-signed certificate. This will only succeed if, after dropping privileges, shellinaboxd has write permissions for this directory. ''; }; certFile = mkOption { type = types.nullOr types.path; default = null; example = "/var/certificate.pem"; description = "Path to server SSL certificate."; }; extraOptions = mkOption { type = types.listOf types.str; default = [ ]; example = [ "--port=443" "--service /:LOGIN" ]; description = '' A list of strings to be appended to the command line arguments for shellinaboxd. Please see the manual page for a full list of available arguments. ''; }; }; }; ###### implementation config = mkIf cfg.enable { assertions = [ { assertion = cfg.enableSSL == true -> cfg.certDirectory != null || cfg.certFile != null; message = "SSL is enabled for shellinabox, but no certDirectory or certFile has been specefied."; } { assertion = ! (cfg.certDirectory != null && cfg.certFile != null); message = "Cannot set both certDirectory and certFile for shellinabox."; } ]; systemd.services.shellinaboxd = { description = "Shellinabox Web Server Daemon"; wantedBy = [ "multi-user.target" ]; requires = [ "sshd.service" ]; after = [ "sshd.service" ]; serviceConfig = { Type = "forking"; User = "${cfg.user}"; ExecStart = "${if cfg.certFile == null then "${cmd}" else "${wrappedCmd}"}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; }; }; }; }