···11+{ config, lib, pkgs, ... }:
22+33+with lib;
44+55+let
66+77+ cfg = config.services.shellinabox;
88+99+ # If a certificate file is specified, shellinaboxd requires
1010+ # a file descriptor to retrieve it
1111+ fd = "3";
1212+ createFd = optionalString (cfg.certFile != null) "${fd}<${cfg.certFile}";
1313+1414+ # Command line arguments for the shellinabox daemon
1515+ args = [ "--background" ]
1616+ ++ optional (! cfg.enableSSL) "--disable-ssl"
1717+ ++ optional (cfg.certFile != null) "--cert-fd=${fd}"
1818+ ++ optional (cfg.certDirectory != null) "--cert=${cfg.certDirectory}"
1919+ ++ cfg.extraOptions;
2020+2121+ # Command to start shellinaboxd
2222+ cmd = "${pkgs.shellinabox}/bin/shellinaboxd ${concatStringsSep " " args}";
2323+2424+ # Command to start shellinaboxd if certFile is specified
2525+ wrappedCmd = "${pkgs.bash}/bin/bash -c 'exec ${createFd} && ${cmd}'";
2626+2727+in
2828+2929+{
3030+3131+ ###### interface
3232+3333+ options = {
3434+ services.shellinabox = {
3535+ enable = mkEnableOption "shellinabox daemon";
3636+3737+ user = mkOption {
3838+ type = types.str;
3939+ default = "root";
4040+ description = ''
4141+ User to run shellinaboxd as. If started as root, the server drops
4242+ privileges by changing to nobody, unless overridden by the
4343+ <literal>--user</literal> option.
4444+ '';
4545+ };
4646+4747+ enableSSL = mkOption {
4848+ type = types.bool;
4949+ default = false;
5050+ description = ''
5151+ Whether or not to enable SSL (https) support.
5252+ '';
5353+ };
5454+5555+ certDirectory = mkOption {
5656+ type = types.nullOr types.path;
5757+ default = null;
5858+ example = "/var/certs";
5959+ description = ''
6060+ The daemon will look in this directory far any certificates.
6161+ If the browser negotiated a Server Name Identification the daemon
6262+ will look for a matching certificate-SERVERNAME.pem file. If no SNI
6363+ handshake takes place, it will fall back on using the certificate in the
6464+ certificate.pem file.
6565+6666+ If no suitable certificate is installed, shellinaboxd will attempt to
6767+ create a new self-signed certificate. This will only succeed if, after
6868+ dropping privileges, shellinaboxd has write permissions for this
6969+ directory.
7070+ '';
7171+ };
7272+7373+ certFile = mkOption {
7474+ type = types.nullOr types.path;
7575+ default = null;
7676+ example = "/var/certificate.pem";
7777+ description = "Path to server SSL certificate.";
7878+ };
7979+8080+ extraOptions = mkOption {
8181+ type = types.listOf types.str;
8282+ default = [ ];
8383+ example = [ "--port=443" "--service /:LOGIN" ];
8484+ description = ''
8585+ A list of strings to be appended to the command line arguments
8686+ for shellinaboxd. Please see the manual page
8787+ <link xlink:href="https://code.google.com/p/shellinabox/wiki/shellinaboxd_man"/>
8888+ for a full list of available arguments.
8989+ '';
9090+ };
9191+9292+ };
9393+ };
9494+9595+ ###### implementation
9696+9797+ config = mkIf cfg.enable {
9898+9999+ assertions =
100100+ [ { assertion = cfg.enableSSL == true
101101+ -> cfg.certDirectory != null || cfg.certFile != null;
102102+ message = "SSL is enabled for shellinabox, but no certDirectory or certFile has been specefied."; }
103103+ { assertion = ! (cfg.certDirectory != null && cfg.certFile != null);
104104+ message = "Cannot set both certDirectory and certFile for shellinabox."; }
105105+ ];
106106+107107+ systemd.services.shellinaboxd = {
108108+ description = "Shellinabox Web Server Daemon";
109109+110110+ wantedBy = [ "multi-user.target" ];
111111+ requires = [ "sshd.service" ];
112112+ after = [ "sshd.service" ];
113113+114114+ serviceConfig = {
115115+ Type = "forking";
116116+ User = "${cfg.user}";
117117+ ExecStart = "${if cfg.certFile == null then "${cmd}" else "${wrappedCmd}"}";
118118+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
119119+ };
120120+ };
121121+ };
122122+}