1{
2 config,
3 options,
4 pkgs,
5 lib,
6 ...
7}:
8let
9 inherit (lib)
10 concatStringsSep
11 literalExpression
12 makeLibraryPath
13 mkEnableOption
14 mkForce
15 mkIf
16 mkOption
17 mkPackageOption
18 mkRemovedOptionModule
19 optional
20 types
21 ;
22
23 cfg = config.services.aesmd;
24 opt = options.services.aesmd;
25
26 sgx-psw = cfg.package;
27
28 configFile =
29 with cfg.settings;
30 pkgs.writeText "aesmd.conf" (
31 concatStringsSep "\n" (
32 optional (whitelistUrl != null) "whitelist url = ${whitelistUrl}"
33 ++ optional (proxy != null) "aesm proxy = ${proxy}"
34 ++ optional (proxyType != null) "proxy type = ${proxyType}"
35 ++ optional (defaultQuotingType != null) "default quoting type = ${defaultQuotingType}"
36 ++
37 # Newline at end of file
38 [ "" ]
39 )
40 );
41in
42{
43 imports = [
44 (mkRemovedOptionModule [ "services" "aesmd" "debug" ] ''
45 Enable debug mode by overriding the aesmd package directly:
46
47 services.aesmd.package = pkgs.sgx-psw.override { debug = true; };
48 '')
49 ];
50
51 options.services.aesmd = {
52 enable = mkEnableOption "Intel's Architectural Enclave Service Manager (AESM) for Intel SGX";
53 package = mkPackageOption pkgs "sgx-psw" { };
54 environment = mkOption {
55 type = with types; attrsOf str;
56 default = { };
57 description = "Additional environment variables to pass to the AESM service.";
58 # Example environment variable for `sgx-azure-dcap-client` provider library
59 example = {
60 AZDCAP_COLLATERAL_VERSION = "v2";
61 AZDCAP_DEBUG_LOG_LEVEL = "INFO";
62 };
63 };
64 quoteProviderLibrary = mkOption {
65 type = with types; nullOr path;
66 default = null;
67 example = literalExpression "pkgs.sgx-azure-dcap-client";
68 description = "Custom quote provider library to use.";
69 };
70 settings = mkOption {
71 description = "AESM configuration";
72 default = { };
73 type = types.submodule {
74 options.whitelistUrl = mkOption {
75 type = with types; nullOr str;
76 default = null;
77 example = "http://whitelist.trustedservices.intel.com/SGX/LCWL/Linux/sgx_white_list_cert.bin";
78 description = "URL to retrieve authorized Intel SGX enclave signers.";
79 };
80 options.proxy = mkOption {
81 type = with types; nullOr str;
82 default = null;
83 example = "http://proxy_url:1234";
84 description = "HTTP network proxy.";
85 };
86 options.proxyType = mkOption {
87 type =
88 with types;
89 nullOr (enum [
90 "default"
91 "direct"
92 "manual"
93 ]);
94 default = if (cfg.settings.proxy != null) then "manual" else null;
95 defaultText = literalExpression ''
96 if (config.${opt.settings}.proxy != null) then "manual" else null
97 '';
98 example = "default";
99 description = ''
100 Type of proxy to use. The `default` uses the system's default proxy.
101 If `direct` is given, uses no proxy.
102 A value of `manual` uses the proxy from
103 {option}`services.aesmd.settings.proxy`.
104 '';
105 };
106 options.defaultQuotingType = mkOption {
107 type =
108 with types;
109 nullOr (enum [
110 "ecdsa_256"
111 "epid_linkable"
112 "epid_unlinkable"
113 ]);
114 default = null;
115 example = "ecdsa_256";
116 description = "Attestation quote type.";
117 };
118 };
119 };
120 };
121
122 config = mkIf cfg.enable {
123 assertions = [
124 {
125 assertion = !(config.boot.specialFileSystems."/dev".options ? "noexec");
126 message = "SGX requires exec permission for /dev";
127 }
128 ];
129
130 hardware.cpu.intel.sgx.provision.enable = true;
131
132 # Make sure the AESM service can find the SGX devices until
133 # https://github.com/intel/linux-sgx/issues/772 is resolved
134 # and updated in nixpkgs.
135 hardware.cpu.intel.sgx.enableDcapCompat = mkForce true;
136
137 systemd.services.aesmd =
138 let
139 storeAesmFolder = "${sgx-psw}/aesm";
140 # Hardcoded path AESM_DATA_FOLDER in psw/ae/aesm_service/source/oal/linux/aesm_util.cpp
141 aesmDataFolder = "/var/opt/aesmd/data";
142 in
143 {
144 description = "Intel Architectural Enclave Service Manager";
145 wantedBy = [ "multi-user.target" ];
146
147 after = [
148 "auditd.service"
149 "network.target"
150 ];
151
152 environment = {
153 NAME = "aesm_service";
154 AESM_PATH = storeAesmFolder;
155 LD_LIBRARY_PATH = makeLibraryPath [ cfg.quoteProviderLibrary ];
156 }
157 // cfg.environment;
158
159 # Make sure any of the SGX application enclave devices is available
160 unitConfig.AssertPathExists = [
161 # legacy out-of-tree driver
162 "|/dev/isgx"
163 # DCAP driver
164 "|/dev/sgx/enclave"
165 # in-tree driver
166 "|/dev/sgx_enclave"
167 ];
168
169 serviceConfig = {
170 ExecStartPre = pkgs.writeShellScript "copy-aesmd-data-files.sh" ''
171 set -euo pipefail
172 whiteListFile="${aesmDataFolder}/white_list_cert_to_be_verify.bin"
173 if [[ ! -f "$whiteListFile" ]]; then
174 ${pkgs.coreutils}/bin/install -m 644 -D \
175 "${storeAesmFolder}/data/white_list_cert_to_be_verify.bin" \
176 "$whiteListFile"
177 fi
178 '';
179 ExecStart = "${sgx-psw}/bin/aesm_service --no-daemon";
180 ExecReload = ''${pkgs.coreutils}/bin/kill -SIGHUP "$MAINPID"'';
181
182 Restart = "on-failure";
183 RestartSec = "15s";
184
185 DynamicUser = true;
186 Group = "sgx";
187 SupplementaryGroups = [
188 config.hardware.cpu.intel.sgx.provision.group
189 ];
190
191 Type = "simple";
192
193 WorkingDirectory = storeAesmFolder;
194 StateDirectory = "aesmd";
195 StateDirectoryMode = "0700";
196 RuntimeDirectory = "aesmd";
197 RuntimeDirectoryMode = "0750";
198
199 # Hardening
200
201 # chroot into the runtime directory
202 RootDirectory = "%t/aesmd";
203 BindReadOnlyPaths = [
204 builtins.storeDir
205 # Hardcoded path AESM_CONFIG_FILE in psw/ae/aesm_service/source/utils/aesm_config.cpp
206 "${configFile}:/etc/aesmd.conf"
207 ];
208 BindPaths = [
209 # Hardcoded path CONFIG_SOCKET_PATH in psw/ae/aesm_service/source/core/ipc/SocketConfig.h
210 "%t/aesmd:/var/run/aesmd"
211 "%S/aesmd:/var/opt/aesmd"
212 ];
213
214 # PrivateDevices=true will mount /dev noexec which breaks AESM
215 PrivateDevices = false;
216 DevicePolicy = "closed";
217 DeviceAllow = [
218 # legacy out-of-tree driver
219 "/dev/isgx rw"
220 # DCAP driver
221 "/dev/sgx rw"
222 # in-tree driver
223 "/dev/sgx_enclave rw"
224 "/dev/sgx_provision rw"
225 ];
226
227 # Requires Internet access for attestation
228 PrivateNetwork = false;
229
230 RestrictAddressFamilies = [
231 # Allocates the socket /var/run/aesmd/aesm.socket
232 "AF_UNIX"
233 # Uses the HTTP protocol to initialize some services
234 "AF_INET"
235 "AF_INET6"
236 ];
237
238 # True breaks stuff
239 MemoryDenyWriteExecute = false;
240
241 # needs the ipc syscall in order to run
242 SystemCallFilter = [
243 "@system-service"
244 "~@aio"
245 "~@chown"
246 "~@clock"
247 "~@cpu-emulation"
248 "~@debug"
249 "~@keyring"
250 "~@memlock"
251 "~@module"
252 "~@mount"
253 "~@privileged"
254 "~@raw-io"
255 "~@reboot"
256 "~@resources"
257 "~@setuid"
258 "~@swap"
259 "~@sync"
260 "~@timer"
261 ];
262 SystemCallArchitectures = "native";
263 SystemCallErrorNumber = "EPERM";
264
265 CapabilityBoundingSet = "";
266 KeyringMode = "private";
267 LockPersonality = true;
268 NoNewPrivileges = true;
269 NotifyAccess = "none";
270 PrivateMounts = true;
271 PrivateTmp = true;
272 PrivateUsers = true;
273 ProcSubset = "pid";
274 ProtectClock = true;
275 ProtectControlGroups = true;
276 ProtectHome = true;
277 ProtectHostname = true;
278 ProtectKernelLogs = true;
279 ProtectKernelModules = true;
280 ProtectKernelTunables = true;
281 ProtectProc = "invisible";
282 ProtectSystem = "strict";
283 RemoveIPC = true;
284 RestrictNamespaces = true;
285 RestrictRealtime = true;
286 RestrictSUIDSGID = true;
287 UMask = "0066";
288 };
289 };
290 };
291}