1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.endlessh-go;
7in
8{
9 options.services.endlessh-go = {
10 enable = mkEnableOption (mdDoc "endlessh-go service");
11
12 listenAddress = mkOption {
13 type = types.str;
14 default = "0.0.0.0";
15 example = "[::]";
16 description = mdDoc ''
17 Interface address to bind the endlessh-go daemon to SSH connections.
18 '';
19 };
20
21 port = mkOption {
22 type = types.port;
23 default = 2222;
24 example = 22;
25 description = mdDoc ''
26 Specifies on which port the endlessh-go daemon listens for SSH
27 connections.
28
29 Setting this to `22` may conflict with {option}`services.openssh`.
30 '';
31 };
32
33 prometheus = {
34 enable = mkEnableOption (mdDoc "Prometheus integration");
35
36 listenAddress = mkOption {
37 type = types.str;
38 default = "0.0.0.0";
39 example = "[::]";
40 description = mdDoc ''
41 Interface address to bind the endlessh-go daemon to answer Prometheus
42 queries.
43 '';
44 };
45
46 port = mkOption {
47 type = types.port;
48 default = 2112;
49 example = 9119;
50 description = mdDoc ''
51 Specifies on which port the endlessh-go daemon listens for Prometheus
52 queries.
53 '';
54 };
55 };
56
57 extraOptions = mkOption {
58 type = with types; listOf str;
59 default = [ ];
60 example = [ "-conn_type=tcp4" "-max_clients=8192" ];
61 description = mdDoc ''
62 Additional command line options to pass to the endlessh-go daemon.
63 '';
64 };
65
66 openFirewall = mkOption {
67 type = types.bool;
68 default = false;
69 description = lib.mdDoc ''
70 Whether to open a firewall port for the SSH listener.
71 '';
72 };
73 };
74
75 config = mkIf cfg.enable {
76 systemd.services.endlessh-go = {
77 description = "SSH tarpit";
78 requires = [ "network.target" ];
79 wantedBy = [ "multi-user.target" ];
80 serviceConfig =
81 let
82 needsPrivileges = cfg.port < 1024 || cfg.prometheus.port < 1024;
83 capabilities = [ "" ] ++ optionals needsPrivileges [ "CAP_NET_BIND_SERVICE" ];
84 rootDirectory = "/run/endlessh-go";
85 in
86 {
87 Restart = "always";
88 ExecStart = with cfg; concatStringsSep " " ([
89 "${pkgs.endlessh-go}/bin/endlessh-go"
90 "-logtostderr"
91 "-host=${listenAddress}"
92 "-port=${toString port}"
93 ] ++ optionals prometheus.enable [
94 "-enable_prometheus"
95 "-prometheus_host=${prometheus.listenAddress}"
96 "-prometheus_port=${toString prometheus.port}"
97 ] ++ extraOptions);
98 DynamicUser = true;
99 RootDirectory = rootDirectory;
100 BindReadOnlyPaths = [ builtins.storeDir ];
101 InaccessiblePaths = [ "-+${rootDirectory}" ];
102 RuntimeDirectory = baseNameOf rootDirectory;
103 RuntimeDirectoryMode = "700";
104 AmbientCapabilities = capabilities;
105 CapabilityBoundingSet = capabilities;
106 UMask = "0077";
107 LockPersonality = true;
108 MemoryDenyWriteExecute = true;
109 NoNewPrivileges = true;
110 PrivateDevices = true;
111 PrivateTmp = true;
112 PrivateUsers = !needsPrivileges;
113 ProtectClock = true;
114 ProtectControlGroups = true;
115 ProtectHome = true;
116 ProtectHostname = true;
117 ProtectKernelLogs = true;
118 ProtectKernelModules = true;
119 ProtectKernelTunables = true;
120 ProtectSystem = "strict";
121 ProtectProc = "noaccess";
122 ProcSubset = "pid";
123 RemoveIPC = true;
124 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
125 RestrictNamespaces = true;
126 RestrictRealtime = true;
127 RestrictSUIDSGID = true;
128 SystemCallArchitectures = "native";
129 SystemCallFilter = [ "@system-service" "~@privileged" ];
130 };
131 };
132
133 networking.firewall.allowedTCPPorts = with cfg;
134 optionals openFirewall [ port prometheus.port ];
135 };
136
137 meta.maintainers = with maintainers; [ azahi ];
138}