at 25.11-pre 4.4 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 literalExpression 11 maintainers 12 mkEnableOption 13 mkIf 14 mkOption 15 types 16 ; 17 18 cfg = config.services.esphome; 19 20 stateDir = "/var/lib/esphome"; 21 22 esphomeParams = 23 if cfg.enableUnixSocket then 24 "--socket /run/esphome/esphome.sock" 25 else 26 "--address ${cfg.address} --port ${toString cfg.port}"; 27in 28{ 29 meta.maintainers = with maintainers; [ oddlama ]; 30 31 options.services.esphome = { 32 enable = mkEnableOption "esphome, for making custom firmwares for ESP32/ESP8266"; 33 34 package = lib.mkPackageOption pkgs "esphome" { }; 35 36 enableUnixSocket = mkOption { 37 type = types.bool; 38 default = false; 39 description = "Listen on a unix socket `/run/esphome/esphome.sock` instead of the TCP port."; 40 }; 41 42 address = mkOption { 43 type = types.str; 44 default = "localhost"; 45 description = "esphome address"; 46 }; 47 48 port = mkOption { 49 type = types.port; 50 default = 6052; 51 description = "esphome port"; 52 }; 53 54 openFirewall = mkOption { 55 default = false; 56 type = types.bool; 57 description = "Whether to open the firewall for the specified port."; 58 }; 59 60 allowedDevices = mkOption { 61 default = [ 62 "char-ttyS" 63 "char-ttyUSB" 64 ]; 65 example = [ 66 "/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0" 67 ]; 68 description = '' 69 A list of device nodes to which {command}`esphome` has access to. 70 Refer to DeviceAllow in {manpage}`systemd.resource-control(5)` for more information. 71 Beware that if a device is referred to by an absolute path instead of a device category, 72 it will only allow devices that already are plugged in when the service is started. 73 ''; 74 type = types.listOf types.str; 75 }; 76 77 usePing = mkOption { 78 default = false; 79 type = types.bool; 80 description = "Use ping to check online status of devices instead of mDNS"; 81 }; 82 }; 83 84 config = mkIf cfg.enable { 85 networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall && !cfg.enableUnixSocket) [ cfg.port ]; 86 87 systemd.services.esphome = { 88 description = "ESPHome dashboard"; 89 after = [ "network.target" ]; 90 wantedBy = [ "multi-user.target" ]; 91 path = [ cfg.package ]; 92 93 environment = { 94 # platformio fails to determine the home directory when using DynamicUser 95 PLATFORMIO_CORE_DIR = "${stateDir}/.platformio"; 96 } // lib.optionalAttrs cfg.usePing { ESPHOME_DASHBOARD_USE_PING = "true"; }; 97 98 serviceConfig = { 99 ExecStart = "${cfg.package}/bin/esphome dashboard ${esphomeParams} ${stateDir}"; 100 DynamicUser = true; 101 User = "esphome"; 102 Group = "esphome"; 103 WorkingDirectory = stateDir; 104 StateDirectory = "esphome"; 105 StateDirectoryMode = "0750"; 106 Restart = "on-failure"; 107 RuntimeDirectory = mkIf cfg.enableUnixSocket "esphome"; 108 RuntimeDirectoryMode = "0750"; 109 110 # Hardening 111 CapabilityBoundingSet = ""; 112 LockPersonality = true; 113 MemoryDenyWriteExecute = true; 114 DevicePolicy = "closed"; 115 DeviceAllow = map (d: "${d} rw") cfg.allowedDevices; 116 SupplementaryGroups = [ "dialout" ]; 117 #NoNewPrivileges = true; # Implied by DynamicUser 118 PrivateUsers = true; 119 #PrivateTmp = true; # Implied by DynamicUser 120 ProtectClock = true; 121 ProtectControlGroups = true; 122 ProtectHome = true; 123 ProtectHostname = false; # breaks bwrap 124 ProtectKernelLogs = false; # breaks bwrap 125 ProtectKernelModules = true; 126 ProtectKernelTunables = false; # breaks bwrap 127 ProtectProc = "invisible"; 128 ProcSubset = "all"; # Using "pid" breaks bwrap 129 ProtectSystem = "strict"; 130 #RemoveIPC = true; # Implied by DynamicUser 131 RestrictAddressFamilies = [ 132 "AF_INET" 133 "AF_INET6" 134 "AF_NETLINK" 135 "AF_UNIX" 136 ]; 137 RestrictNamespaces = false; # Required by platformio for chroot 138 RestrictRealtime = true; 139 #RestrictSUIDSGID = true; # Implied by DynamicUser 140 SystemCallArchitectures = "native"; 141 SystemCallFilter = [ 142 "@system-service" 143 "@mount" # Required by platformio for chroot 144 ]; 145 UMask = "0077"; 146 }; 147 }; 148 }; 149}