1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.envoy;
9 format = pkgs.formats.json { };
10 conf = format.generate "envoy.json" cfg.settings;
11 validateConfig =
12 required: file:
13 pkgs.runCommand "validate-envoy-conf" { } ''
14 ${cfg.package}/bin/envoy --log-level error --mode validate -c "${file}" ${
15 lib.optionalString (!required) "|| true"
16 }
17 cp "${file}" "$out"
18 '';
19in
20
21{
22 options.services.envoy = {
23 enable = lib.mkEnableOption "Envoy reverse proxy";
24
25 package = lib.mkPackageOption pkgs "envoy" { };
26
27 requireValidConfig = lib.mkOption {
28 type = lib.types.bool;
29 default = true;
30 description = ''
31 Whether a failure during config validation at build time is fatal.
32 When the config can't be checked during build time, for example when it includes
33 other files, disable this option.
34 '';
35 };
36
37 settings = lib.mkOption {
38 type = format.type;
39 default = { };
40 example = lib.literalExpression ''
41 {
42 admin = {
43 access_log_path = "/dev/null";
44 address = {
45 socket_address = {
46 protocol = "TCP";
47 address = "127.0.0.1";
48 port_value = 9901;
49 };
50 };
51 };
52 static_resources = {
53 listeners = [];
54 clusters = [];
55 };
56 }
57 '';
58 description = ''
59 Specify the configuration for Envoy in Nix.
60 '';
61 };
62 };
63
64 config = lib.mkIf cfg.enable {
65 environment.systemPackages = [ cfg.package ];
66 systemd.services.envoy = {
67 description = "Envoy reverse proxy";
68 after = [ "network-online.target" ];
69 requires = [ "network-online.target" ];
70 wantedBy = [ "multi-user.target" ];
71 serviceConfig = {
72 ExecStart = "${cfg.package}/bin/envoy -c ${validateConfig cfg.requireValidConfig conf}";
73 CacheDirectory = [ "envoy" ];
74 LogsDirectory = [ "envoy" ];
75 Restart = "no";
76 # Hardening
77 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
78 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
79 DeviceAllow = [ "" ];
80 DevicePolicy = "closed";
81 DynamicUser = true;
82 LockPersonality = true;
83 MemoryDenyWriteExecute = false; # at least wasmr needs WX permission
84 PrivateDevices = true;
85 PrivateUsers = false; # breaks CAP_NET_BIND_SERVICE
86 ProcSubset = "pid";
87 ProtectClock = true;
88 ProtectControlGroups = true;
89 ProtectHome = true;
90 ProtectHostname = true;
91 ProtectKernelLogs = true;
92 ProtectKernelModules = true;
93 ProtectKernelTunables = true;
94 ProtectProc = "ptraceable";
95 ProtectSystem = "strict";
96 RestrictAddressFamilies = [
97 "AF_UNIX"
98 "AF_INET"
99 "AF_INET6"
100 "AF_NETLINK"
101 "AF_XDP"
102 ];
103 RestrictNamespaces = true;
104 RestrictRealtime = true;
105 SystemCallArchitectures = "native";
106 SystemCallErrorNumber = "EPERM";
107 SystemCallFilter = [
108 "@system-service"
109 "~@privileged"
110 "~@resources"
111 ];
112 UMask = "0066";
113 };
114 };
115 };
116}