python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / web-servers / ttyd.nix
blobaffd5bbeea3cc2a953c19071961def36b4d0548c
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
7   cfg = config.services.ttyd;
9   # Command line arguments for the ttyd daemon
10   args = [ "--port" (toString cfg.port) ]
11          ++ optionals (cfg.socket != null) [ "--interface" cfg.socket ]
12          ++ optionals (cfg.interface != null) [ "--interface" cfg.interface ]
13          ++ [ "--signal" (toString cfg.signal) ]
14          ++ (concatLists (mapAttrsToList (_k: _v: [ "--client-option" "${_k}=${_v}" ]) cfg.clientOptions))
15          ++ [ "--terminal-type" cfg.terminalType ]
16          ++ optionals cfg.checkOrigin [ "--check-origin" ]
17          ++ [ "--max-clients" (toString cfg.maxClients) ]
18          ++ optionals (cfg.indexFile != null) [ "--index" cfg.indexFile ]
19          ++ optionals cfg.enableIPv6 [ "--ipv6" ]
20          ++ optionals cfg.enableSSL [ "--ssl-cert" cfg.certFile
21                                       "--ssl-key" cfg.keyFile
22                                       "--ssl-ca" cfg.caFile ]
23          ++ [ "--debug" (toString cfg.logLevel) ];
29   ###### interface
31   options = {
32     services.ttyd = {
33       enable = mkEnableOption (lib.mdDoc "ttyd daemon");
35       port = mkOption {
36         type = types.port;
37         default = 7681;
38         description = lib.mdDoc "Port to listen on (use 0 for random port)";
39       };
41       socket = mkOption {
42         type = types.nullOr types.path;
43         default = null;
44         example = "/var/run/ttyd.sock";
45         description = lib.mdDoc "UNIX domain socket path to bind.";
46       };
48       interface = mkOption {
49         type = types.nullOr types.str;
50         default = null;
51         example = "eth0";
52         description = lib.mdDoc "Network interface to bind.";
53       };
55       username = mkOption {
56         type = types.nullOr types.str;
57         default = null;
58         description = lib.mdDoc "Username for basic authentication.";
59       };
61       passwordFile = mkOption {
62         type = types.nullOr types.path;
63         default = null;
64         apply = value: if value == null then null else toString value;
65         description = lib.mdDoc ''
66           File containing the password to use for basic authentication.
67           For insecurely putting the password in the globally readable store use
68           `pkgs.writeText "ttydpw" "MyPassword"`.
69         '';
70       };
72       signal = mkOption {
73         type = types.ints.u8;
74         default = 1;
75         description = lib.mdDoc "Signal to send to the command on session close.";
76       };
78       clientOptions = mkOption {
79         type = types.attrsOf types.str;
80         default = {};
81         example = literalExpression ''{
82           fontSize = "16";
83           fontFamily = "Fira Code";
85         }'';
86         description = lib.mdDoc ''
87           Attribute set of client options for xtermjs.
88           <https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/>
89         '';
90       };
92       terminalType = mkOption {
93         type = types.str;
94         default = "xterm-256color";
95         description = lib.mdDoc "Terminal type to report.";
96       };
98       checkOrigin = mkOption {
99         type = types.bool;
100         default = false;
101         description = lib.mdDoc "Whether to allow a websocket connection from a different origin.";
102       };
104       maxClients = mkOption {
105         type = types.int;
106         default = 0;
107         description = lib.mdDoc "Maximum clients to support (0, no limit)";
108       };
110       indexFile = mkOption {
111         type = types.nullOr types.path;
112         default = null;
113         description = lib.mdDoc "Custom index.html path";
114       };
116       enableIPv6 = mkOption {
117         type = types.bool;
118         default = false;
119         description = lib.mdDoc "Whether or not to enable IPv6 support.";
120       };
122       enableSSL = mkOption {
123         type = types.bool;
124         default = false;
125         description = lib.mdDoc "Whether or not to enable SSL (https) support.";
126       };
128       certFile = mkOption {
129         type = types.nullOr types.path;
130         default = null;
131         description = lib.mdDoc "SSL certificate file path.";
132       };
134       keyFile = mkOption {
135         type = types.nullOr types.path;
136         default = null;
137         apply = value: if value == null then null else toString value;
138         description = lib.mdDoc ''
139           SSL key file path.
140           For insecurely putting the keyFile in the globally readable store use
141           `pkgs.writeText "ttydKeyFile" "SSLKEY"`.
142         '';
143       };
145       caFile = mkOption {
146         type = types.nullOr types.path;
147         default = null;
148         description = lib.mdDoc "SSL CA file path for client certificate verification.";
149       };
151       logLevel = mkOption {
152         type = types.int;
153         default = 7;
154         description = lib.mdDoc "Set log level.";
155       };
156     };
157   };
159   ###### implementation
161   config = mkIf cfg.enable {
163     assertions =
164       [ { assertion = cfg.enableSSL
165             -> cfg.certFile != null && cfg.keyFile != null && cfg.caFile != null;
166           message = "SSL is enabled for ttyd, but no certFile, keyFile or caFile has been specefied."; }
167         { assertion = ! (cfg.interface != null && cfg.socket != null);
168           message = "Cannot set both interface and socket for ttyd."; }
169         { assertion = (cfg.username != null) == (cfg.passwordFile != null);
170           message = "Need to set both username and passwordFile for ttyd"; }
171       ];
173     systemd.services.ttyd = {
174       description = "ttyd Web Server Daemon";
176       wantedBy = [ "multi-user.target" ];
178       serviceConfig = {
179         # Runs login which needs to be run as root
180         # login: Cannot possibly work without effective root
181         User = "root";
182       };
184       script = if cfg.passwordFile != null then ''
185         PASSWORD=$(cat ${escapeShellArg cfg.passwordFile})
186         ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
187           --credential ${escapeShellArg cfg.username}:"$PASSWORD" \
188           ${pkgs.shadow}/bin/login
189       ''
190       else ''
191         ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
192           ${pkgs.shadow}/bin/login
193       '';
194     };
195   };