1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.echoip;
9in
10{
11 meta.maintainers = with lib.maintainers; [ defelo ];
12
13 options.services.echoip = {
14 enable = lib.mkEnableOption "echoip";
15
16 package = lib.mkPackageOption pkgs "echoip" { };
17
18 virtualHost = lib.mkOption {
19 type = lib.types.nullOr lib.types.str;
20 description = ''
21 Name of the nginx virtual host to use and setup. If null, do not setup anything.
22 '';
23 default = null;
24 };
25
26 extraArgs = lib.mkOption {
27 type = lib.types.listOf lib.types.str;
28 description = "Extra command line arguments to pass to echoip. See <https://github.com/mpolden/echoip> for details.";
29 default = [ ];
30 };
31
32 listenAddress = lib.mkOption {
33 type = lib.types.str;
34 description = "The address echoip should listen on";
35 default = ":8080";
36 example = "127.0.0.1:8000";
37 };
38
39 enablePortLookup = lib.mkEnableOption "port lookup";
40
41 enableReverseHostnameLookups = lib.mkEnableOption "reverse hostname lookups";
42
43 remoteIpHeader = lib.mkOption {
44 type = lib.types.nullOr lib.types.str;
45 description = "Header to trust for remote IP, if present";
46 default = null;
47 example = "X-Real-IP";
48 };
49 };
50
51 config = lib.mkIf cfg.enable {
52 systemd.services.echoip = {
53 wantedBy = [ "multi-user.target" ];
54
55 wants = [ "network-online.target" ];
56 after = [ "network-online.target" ];
57
58 serviceConfig = {
59 User = "echoip";
60 Group = "echoip";
61 DynamicUser = true;
62 ExecStart = lib.escapeShellArgs (
63 [
64 (lib.getExe cfg.package)
65 "-l"
66 cfg.listenAddress
67 ]
68 ++ lib.optional cfg.enablePortLookup "-p"
69 ++ lib.optional cfg.enableReverseHostnameLookups "-r"
70 ++ lib.optionals (cfg.remoteIpHeader != null) [
71 "-H"
72 cfg.remoteIpHeader
73 ]
74 ++ cfg.extraArgs
75 );
76
77 # Hardening
78 AmbientCapabilities = "";
79 CapabilityBoundingSet = [ "" ];
80 DevicePolicy = "closed";
81 LockPersonality = true;
82 MemoryDenyWriteExecute = true;
83 NoNewPrivileges = true;
84 PrivateDevices = true;
85 PrivateTmp = true;
86 PrivateUsers = true;
87 ProcSubset = "pid";
88 ProtectClock = true;
89 ProtectControlGroups = true;
90 ProtectHome = true;
91 ProtectHostname = true;
92 ProtectKernelLogs = true;
93 ProtectKernelModules = true;
94 ProtectKernelTunables = true;
95 ProtectProc = "invisible";
96 ProtectSystem = "strict";
97 RemoveIPC = true;
98 RestrictAddressFamilies = [ "AF_INET AF_INET6 AF_UNIX" ];
99 RestrictNamespaces = true;
100 RestrictRealtime = true;
101 RestrictSUIDSGID = true;
102 SystemCallArchitectures = "native";
103 SystemCallFilter = [
104 "@system-service"
105 "~@privileged"
106 "~@resources"
107 "setrlimit"
108 ];
109 UMask = "0077";
110 };
111 };
112
113 services.nginx = lib.mkIf (cfg.virtualHost != null) {
114 enable = true;
115 virtualHosts.${cfg.virtualHost} = {
116 locations."/" = {
117 proxyPass = "http://${cfg.listenAddress}";
118 recommendedProxySettings = true;
119 };
120 };
121 };
122
123 services.echoip = lib.mkIf (cfg.virtualHost != null) {
124 listenAddress = lib.mkDefault "127.0.0.1:8080";
125 remoteIpHeader = "X-Real-IP";
126 };
127 };
128}