1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.shellinabox;
8
9 # If a certificate file is specified, shellinaboxd requires
10 # a file descriptor to retrieve it
11 fd = "3";
12 createFd = optionalString (cfg.certFile != null) "${fd}<${cfg.certFile}";
13
14 # Command line arguments for the shellinabox daemon
15 args = [ "--background" ]
16 ++ optional (! cfg.enableSSL) "--disable-ssl"
17 ++ optional (cfg.certFile != null) "--cert-fd=${fd}"
18 ++ optional (cfg.certDirectory != null) "--cert=${cfg.certDirectory}"
19 ++ cfg.extraOptions;
20
21 # Command to start shellinaboxd
22 cmd = "${pkgs.shellinabox}/bin/shellinaboxd ${concatStringsSep " " args}";
23
24 # Command to start shellinaboxd if certFile is specified
25 wrappedCmd = "${pkgs.bash}/bin/bash -c 'exec ${createFd} && ${cmd}'";
26
27in
28
29{
30
31 ###### interface
32
33 options = {
34 services.shellinabox = {
35 enable = mkEnableOption "shellinabox daemon";
36
37 user = mkOption {
38 type = types.str;
39 default = "root";
40 description = ''
41 User to run shellinaboxd as. If started as root, the server drops
42 privileges by changing to nobody, unless overridden by the
43 <literal>--user</literal> option.
44 '';
45 };
46
47 enableSSL = mkOption {
48 type = types.bool;
49 default = false;
50 description = ''
51 Whether or not to enable SSL (https) support.
52 '';
53 };
54
55 certDirectory = mkOption {
56 type = types.nullOr types.path;
57 default = null;
58 example = "/var/certs";
59 description = ''
60 The daemon will look in this directory far any certificates.
61 If the browser negotiated a Server Name Identification the daemon
62 will look for a matching certificate-SERVERNAME.pem file. If no SNI
63 handshake takes place, it will fall back on using the certificate in the
64 certificate.pem file.
65
66 If no suitable certificate is installed, shellinaboxd will attempt to
67 create a new self-signed certificate. This will only succeed if, after
68 dropping privileges, shellinaboxd has write permissions for this
69 directory.
70 '';
71 };
72
73 certFile = mkOption {
74 type = types.nullOr types.path;
75 default = null;
76 example = "/var/certificate.pem";
77 description = "Path to server SSL certificate.";
78 };
79
80 extraOptions = mkOption {
81 type = types.listOf types.str;
82 default = [ ];
83 example = [ "--port=443" "--service /:LOGIN" ];
84 description = ''
85 A list of strings to be appended to the command line arguments
86 for shellinaboxd. Please see the manual page
87 <link xlink:href="https://code.google.com/p/shellinabox/wiki/shellinaboxd_man"/>
88 for a full list of available arguments.
89 '';
90 };
91
92 };
93 };
94
95 ###### implementation
96
97 config = mkIf cfg.enable {
98
99 assertions =
100 [ { assertion = cfg.enableSSL == true
101 -> cfg.certDirectory != null || cfg.certFile != null;
102 message = "SSL is enabled for shellinabox, but no certDirectory or certFile has been specefied."; }
103 { assertion = ! (cfg.certDirectory != null && cfg.certFile != null);
104 message = "Cannot set both certDirectory and certFile for shellinabox."; }
105 ];
106
107 systemd.services.shellinaboxd = {
108 description = "Shellinabox Web Server Daemon";
109
110 wantedBy = [ "multi-user.target" ];
111 requires = [ "sshd.service" ];
112 after = [ "sshd.service" ];
113
114 serviceConfig = {
115 Type = "forking";
116 User = "${cfg.user}";
117 ExecStart = "${if cfg.certFile == null then "${cmd}" else "${wrappedCmd}"}";
118 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
119 };
120 };
121 };
122}