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