197 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
 | 
						|
with lib;
 | 
						|
 | 
						|
let
 | 
						|
 | 
						|
  cfg = config.services.ttyd;
 | 
						|
 | 
						|
  # Command line arguments for the ttyd daemon
 | 
						|
  args = [ "--port" (toString cfg.port) ]
 | 
						|
         ++ optionals (cfg.socket != null) [ "--interface" cfg.socket ]
 | 
						|
         ++ optionals (cfg.interface != null) [ "--interface" cfg.interface ]
 | 
						|
         ++ [ "--signal" (toString cfg.signal) ]
 | 
						|
         ++ (concatLists (mapAttrsToList (_k: _v: [ "--client-option" "${_k}=${_v}" ]) cfg.clientOptions))
 | 
						|
         ++ [ "--terminal-type" cfg.terminalType ]
 | 
						|
         ++ optionals cfg.checkOrigin [ "--check-origin" ]
 | 
						|
         ++ [ "--max-clients" (toString cfg.maxClients) ]
 | 
						|
         ++ optionals (cfg.indexFile != null) [ "--index" cfg.indexFile ]
 | 
						|
         ++ optionals cfg.enableIPv6 [ "--ipv6" ]
 | 
						|
         ++ optionals cfg.enableSSL [ "--ssl-cert" cfg.certFile
 | 
						|
                                      "--ssl-key" cfg.keyFile
 | 
						|
                                      "--ssl-ca" cfg.caFile ]
 | 
						|
         ++ [ "--debug" (toString cfg.logLevel) ];
 | 
						|
 | 
						|
in
 | 
						|
 | 
						|
{
 | 
						|
 | 
						|
  ###### interface
 | 
						|
 | 
						|
  options = {
 | 
						|
    services.ttyd = {
 | 
						|
      enable = mkEnableOption "ttyd daemon";
 | 
						|
 | 
						|
      port = mkOption {
 | 
						|
        type = types.int;
 | 
						|
        default = 7681;
 | 
						|
        description = "Port to listen on (use 0 for random port)";
 | 
						|
      };
 | 
						|
 | 
						|
      socket = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        example = "/var/run/ttyd.sock";
 | 
						|
        description = "UNIX domain socket path to bind.";
 | 
						|
      };
 | 
						|
 | 
						|
      interface = mkOption {
 | 
						|
        type = types.nullOr types.str;
 | 
						|
        default = null;
 | 
						|
        example = "eth0";
 | 
						|
        description = "Network interface to bind.";
 | 
						|
      };
 | 
						|
 | 
						|
      username = mkOption {
 | 
						|
        type = types.nullOr types.str;
 | 
						|
        default = null;
 | 
						|
        description = "Username for basic authentication.";
 | 
						|
      };
 | 
						|
 | 
						|
      passwordFile = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        apply = value: if value == null then null else toString value;
 | 
						|
        description = ''
 | 
						|
          File containing the password to use for basic authentication.
 | 
						|
          For insecurely putting the password in the globally readable store use
 | 
						|
          <literal>pkgs.writeText "ttydpw" "MyPassword"</literal>.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      signal = mkOption {
 | 
						|
        type = types.ints.u8;
 | 
						|
        default = 1;
 | 
						|
        description = "Signal to send to the command on session close.";
 | 
						|
      };
 | 
						|
 | 
						|
      clientOptions = mkOption {
 | 
						|
        type = types.attrsOf types.str;
 | 
						|
        default = {};
 | 
						|
        example = literalExample ''{
 | 
						|
          fontSize = "16";
 | 
						|
          fontFamily = "Fira Code";
 | 
						|
 | 
						|
        }'';
 | 
						|
        description = ''
 | 
						|
          Attribute set of client options for xtermjs.
 | 
						|
          <link xlink:href="https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/"/>
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      terminalType = mkOption {
 | 
						|
        type = types.str;
 | 
						|
        default = "xterm-256color";
 | 
						|
        description = "Terminal type to report.";
 | 
						|
      };
 | 
						|
 | 
						|
      checkOrigin = mkOption {
 | 
						|
        type = types.bool;
 | 
						|
        default = false;
 | 
						|
        description = "Whether to allow a websocket connection from a different origin.";
 | 
						|
      };
 | 
						|
 | 
						|
      maxClients = mkOption {
 | 
						|
        type = types.int;
 | 
						|
        default = 0;
 | 
						|
        description = "Maximum clients to support (0, no limit)";
 | 
						|
      };
 | 
						|
 | 
						|
      indexFile = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        description = "Custom index.html path";
 | 
						|
      };
 | 
						|
 | 
						|
      enableIPv6 = mkOption {
 | 
						|
        type = types.bool;
 | 
						|
        default = false;
 | 
						|
        description = "Whether or not to enable IPv6 support.";
 | 
						|
      };
 | 
						|
 | 
						|
      enableSSL = mkOption {
 | 
						|
        type = types.bool;
 | 
						|
        default = false;
 | 
						|
        description = "Whether or not to enable SSL (https) support.";
 | 
						|
      };
 | 
						|
 | 
						|
      certFile = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        description = "SSL certificate file path.";
 | 
						|
      };
 | 
						|
 | 
						|
      keyFile = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        apply = value: if value == null then null else toString value;
 | 
						|
        description = ''
 | 
						|
          SSL key file path.
 | 
						|
          For insecurely putting the keyFile in the globally readable store use
 | 
						|
          <literal>pkgs.writeText "ttydKeyFile" "SSLKEY"</literal>.
 | 
						|
        '';
 | 
						|
      };
 | 
						|
 | 
						|
      caFile = mkOption {
 | 
						|
        type = types.nullOr types.path;
 | 
						|
        default = null;
 | 
						|
        description = "SSL CA file path for client certificate verification.";
 | 
						|
      };
 | 
						|
 | 
						|
      logLevel = mkOption {
 | 
						|
        type = types.int;
 | 
						|
        default = 7;
 | 
						|
        description = "Set log level.";
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  ###### implementation
 | 
						|
 | 
						|
  config = mkIf cfg.enable {
 | 
						|
 | 
						|
    assertions =
 | 
						|
      [ { assertion = cfg.enableSSL
 | 
						|
            -> cfg.certFile != null && cfg.keyFile != null && cfg.caFile != null;
 | 
						|
          message = "SSL is enabled for ttyd, but no certFile, keyFile or caFile has been specefied."; }
 | 
						|
        { assertion = ! (cfg.interface != null && cfg.socket != null);
 | 
						|
          message = "Cannot set both interface and socket for ttyd."; }
 | 
						|
        { assertion = (cfg.username != null) == (cfg.passwordFile != null);
 | 
						|
          message = "Need to set both username and passwordFile for ttyd"; }
 | 
						|
      ];
 | 
						|
 | 
						|
    systemd.services.ttyd = {
 | 
						|
      description = "ttyd Web Server Daemon";
 | 
						|
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
 | 
						|
      serviceConfig = {
 | 
						|
        # Runs login which needs to be run as root
 | 
						|
        # login: Cannot possibly work without effective root
 | 
						|
        User = "root";
 | 
						|
      };
 | 
						|
 | 
						|
      script = if cfg.passwordFile != null then ''
 | 
						|
        PASSWORD=$(cat ${escapeShellArg cfg.passwordFile})
 | 
						|
        ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
 | 
						|
          --credential ${escapeShellArg cfg.username}:"$PASSWORD" \
 | 
						|
          ${pkgs.shadow}/bin/login
 | 
						|
      ''
 | 
						|
      else ''
 | 
						|
        ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
 | 
						|
          ${pkgs.shadow}/bin/login
 | 
						|
      '';
 | 
						|
    };
 | 
						|
  };
 | 
						|
}
 |