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