git.iko.soy/typeable/nixpkgs
on master help sign in
Merge pull request #120526 from mweinelt/home-assistant
commitfb5b00d2eb1ffe95aeaadbeb50595e2d6714ff79 authorMartin Weinelt <mweinelt@users.noreply.github.com> dateMon 3 May 2021, 00:35 (6 years ago) parent8b0515ef41349d
2 files changed+81−11
nixos/modules/services/misc/home-assistant.nix+69−7 nixos/tests/home-assistant.nix+12−4
nixos/modules/services/misc/home-assistant.nix+69−7
@@ -245,22 +245,85 @@ in {
245245 rm -f "${cfg.configDir}/ui-lovelace.yaml"
246246 ln -s ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
247247 '');
248− serviceConfig = {
249− ExecStart = "${package}/bin/hass --config '${cfg.configDir}'";
248+ serviceConfig = let
249+ # List of capabilities to equip home-assistant with, depending on configured components
250+ capabilities = [
251+ # Empty string first, so we will never accidentally have an empty capability bounding set
252+ # https://github.com/NixOS/nixpkgs/issues/120617#issuecomment-830685115
253+ ""
254+ ] ++ (unique (optionals (useComponent "bluetooth_tracker" || useComponent "bluetooth_le_tracker") [
255+ # Required for interaction with hci devices and bluetooth sockets
256+ # https://www.home-assistant.io/integrations/bluetooth_le_tracker/#rootless-setup-on-core-installs
257+ "CAP_NET_ADMIN"
258+ "CAP_NET_RAW"
259+ ] ++ lib.optionals (useComponent "emulated_hue") [
260+ # Alexa looks for the service on port 80
261+ # https://www.home-assistant.io/integrations/emulated_hue
262+ "CAP_NET_BIND_SERVICE"
263+ ] ++ lib.optionals (useComponent "nmap_tracker") [
264+ # https://www.home-assistant.io/integrations/nmap_tracker#linux-capabilities
265+ "CAP_NET_ADMIN"
266+ "CAP_NET_BIND_SERVICE"
267+ "CAP_NET_RAW"
268+ ]));
269+ in {
270+ ExecStart = "${package}/bin/hass --runner --config '${cfg.configDir}'";
250271 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
251272 User = "hass";
252273 Group = "hass";
253274 Restart = "on-failure";
275+ RestartForceExitStatus = "100";
276+ SuccessExitStatus = "100";
277+ KillSignal = "SIGINT";
278+
279+ # Hardening
280+ AmbientCapabilities = capabilities;
281+ CapabilityBoundingSet = capabilities;
282+ DeviceAllow = [
283+ "char-ttyACM rw"
284+ "char-ttyAMA rw"
285+ "char-ttyUSB rw"
286+ ];
287+ DevicePolicy = "closed";
288+ LockPersonality = true;
289+ MemoryDenyWriteExecute = true;
290+ NoNewPrivileges = true;
291+ PrivateTmp = true;
292+ PrivateUsers = false; # prevents gaining capabilities in the host namespace
293+ ProtectClock = true;
294+ ProtectControlGroups = true;
295+ ProtectHome = true;
296+ ProtectHostname = true;
297+ ProtectKernelLogs = true;
298+ ProtectKernelModules = true;
299+ ProtectKernelTunables = true;
300+ ProtectProc = "invisible";
301+ ProcSubset = "pid";
254302 ProtectSystem = "strict";
303+ RemoveIPC = true;
255304 ReadWritePaths = let
305+ # Allow rw access to explicitly configured paths
256306 cfgPath = [ "config" "homeassistant" "allowlist_external_dirs" ];
257307 value = attrByPath cfgPath [] cfg;
258308 allowPaths = if isList value then value else singleton value;
259309 in [ "${cfg.configDir}" ] ++ allowPaths;
260− KillSignal = "SIGINT";
261− PrivateTmp = true;
262− RemoveIPC = true;
263− AmbientCapabilities = "cap_net_raw,cap_net_admin+eip";
310+ RestrictAddressFamilies = [
311+ "AF_UNIX"
312+ "AF_INET"
313+ "AF_INET6"
314+ ] ++ optionals (useComponent "bluetooth_tracker" || useComponent "bluetooth_le_tracker") [
315+ "AF_BLUETOOTH"
316+ ];
317+ RestrictNamespaces = true;
318+ RestrictRealtime = true;
319+ RestrictSUIDSGID = true;
320+ SupplementaryGroups = [ "dialout" ];
321+ SystemCallArchitectures = "native";
322+ SystemCallFilter = [
323+ "@system-service"
324+ "~@privileged"
325+ ];
326+ UMask = "0077";
264327 };
265328 path = [
266329 "/run/wrappers" # needed for ping
@@ -278,7 +341,6 @@ in {
278341 home = cfg.configDir;
279342 createHome = true;
280343 group = "hass";
281− extraGroups = [ "dialout" ];
282344 uid = config.ids.uids.hass;
283345 };
284346
nixos/tests/home-assistant.nix+12−4
@@ -1,4 +1,4 @@
1−import ./make-test-python.nix ({ pkgs, ... }:
1+import ./make-test-python.nix ({ pkgs, lib, ... }:
22
33let
44 configDir = "/var/lib/foobar";
@@ -6,9 +6,7 @@ let
66 mqttPassword = "secret";
77in {
88 name = "home-assistant";
9− meta = with pkgs.lib; {
10− maintainers = with maintainers; [ dotlambda ];
11− };
9+ meta.maintainers = lib.teams.home-assistant.members;
1210
1311 nodes.hass = { pkgs, ... }: {
1412 environment.systemPackages = with pkgs; [ mosquitto ];
@@ -47,6 +45,10 @@ in {
4745 payload_on = "let_there_be_light";
4846 payload_off = "off";
4947 }];
48+ emulated_hue = {
49+ host_ip = "127.0.0.1";
50+ listen_port = 80;
51+ };
5052 logger = {
5153 default = "info";
5254 logs."homeassistant.components.mqtt" = "debug";
@@ -82,6 +84,9 @@ in {
8284 hass.succeed(
8385 "mosquitto_pub -V mqttv5 -t home-assistant/test -u ${mqttUsername} -P '${mqttPassword}' -m let_there_be_light"
8486 )
87+ with subtest("Check that capabilities are passed for emulated_hue to bind to port 80"):
88+ hass.wait_for_open_port(80)
89+ hass.succeed("curl --fail http://localhost:80/description.xml")
8590 with subtest("Print log to ease debugging"):
8691 output_log = hass.succeed("cat ${configDir}/home-assistant.log")
8792 print("\n### home-assistant.log ###\n")
@@ -93,5 +98,8 @@ in {
9398 # example line: 2020-06-20 10:01:32 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on home-assistant/test: b'let_there_be_light'
9499 with subtest("Check we received the mosquitto message"):
95100 assert "let_there_be_light" in output_log
101+
102+ with subtest("Check systemd unit hardening"):
103+ hass.log(hass.succeed("systemd-analyze security home-assistant.service"))
96104 '';
97105})