mirror of
https://github.com/SebastianWendel/nixpkgs.git
synced 2024-09-20 04:19:00 +02:00
Merge pull request #264087 from leonm1/matter-server-module
This commit is contained in:
commit
b84bc4ea3a
|
@ -10842,6 +10842,15 @@
|
||||||
githubId = 77865363;
|
githubId = 77865363;
|
||||||
name = "Leonid Belyaev";
|
name = "Leonid Belyaev";
|
||||||
};
|
};
|
||||||
|
leonm1 = {
|
||||||
|
github = "leonm1";
|
||||||
|
githubId = 32306579;
|
||||||
|
keys = [{
|
||||||
|
fingerprint = "C12D F14B DC9D 64E1 44C3 4D8A 755C DA4E 5923 416A";
|
||||||
|
}];
|
||||||
|
matrix = "@mattleon:matrix.org";
|
||||||
|
name = "Matt Leon";
|
||||||
|
};
|
||||||
leshainc = {
|
leshainc = {
|
||||||
email = "leshainc@fomalhaut.me";
|
email = "leshainc@fomalhaut.me";
|
||||||
github = "LeshaInc";
|
github = "LeshaInc";
|
||||||
|
|
|
@ -78,6 +78,10 @@ In addition to numerous new and upgraded packages, this release has the followin
|
||||||
|
|
||||||
- [hebbot](https://github.com/haecker-felix/hebbot), a Matrix bot to generate "This Week in X" like blog posts. Available as [services.hebbot](#opt-services.hebbot.enable).
|
- [hebbot](https://github.com/haecker-felix/hebbot), a Matrix bot to generate "This Week in X" like blog posts. Available as [services.hebbot](#opt-services.hebbot.enable).
|
||||||
|
|
||||||
|
- [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
|
||||||
|
Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
|
||||||
|
Available as [services.matter-server](#opt-services.matter-server.enable)
|
||||||
|
|
||||||
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
||||||
The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
|
The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
|
||||||
|
|
||||||
|
|
|
@ -585,6 +585,7 @@
|
||||||
./services/home-automation/govee2mqtt.nix
|
./services/home-automation/govee2mqtt.nix
|
||||||
./services/home-automation/home-assistant.nix
|
./services/home-automation/home-assistant.nix
|
||||||
./services/home-automation/homeassistant-satellite.nix
|
./services/home-automation/homeassistant-satellite.nix
|
||||||
|
./services/home-automation/matter-server.nix
|
||||||
./services/home-automation/zigbee2mqtt.nix
|
./services/home-automation/zigbee2mqtt.nix
|
||||||
./services/home-automation/zwave-js.nix
|
./services/home-automation/zwave-js.nix
|
||||||
./services/logging/SystemdJournal2Gelf.nix
|
./services/logging/SystemdJournal2Gelf.nix
|
||||||
|
|
125
nixos/modules/services/home-automation/matter-server.nix
Normal file
125
nixos/modules/services/home-automation/matter-server.nix
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
{ lib
|
||||||
|
, pkgs
|
||||||
|
, config
|
||||||
|
, ...
|
||||||
|
}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.matter-server;
|
||||||
|
storageDir = "matter-server";
|
||||||
|
storagePath = "/var/lib/${storageDir}";
|
||||||
|
vendorId = "4939"; # home-assistant vendor ID
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
meta.maintainers = with lib.maintainers; [ leonm1 ];
|
||||||
|
|
||||||
|
options.services.matter-server = with types; {
|
||||||
|
enable = mkEnableOption (lib.mdDoc "Matter-server");
|
||||||
|
|
||||||
|
package = mkPackageOptionMD pkgs "python-matter-server" { };
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 5580;
|
||||||
|
description = "Port to expose the matter-server service on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
logLevel = mkOption {
|
||||||
|
type = types.enum [ "critical" "error" "warning" "info" "debug" ];
|
||||||
|
default = "info";
|
||||||
|
description = "Verbosity of logs from the matter-server";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraArgs = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Extra arguments to pass to the matter-server executable.
|
||||||
|
See https://github.com/home-assistant-libs/python-matter-server?tab=readme-ov-file#running-the-development-server for options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
systemd.services.matter-server = {
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
before = [ "home-assistant.service" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
description = "Matter Server";
|
||||||
|
environment.HOME = storagePath;
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = (concatStringsSep " " [
|
||||||
|
"${cfg.package}/bin/matter-server"
|
||||||
|
"--port" (toString cfg.port)
|
||||||
|
"--vendorid" vendorId
|
||||||
|
"--storage-path" storagePath
|
||||||
|
"--log-level" "${cfg.logLevel}"
|
||||||
|
"${escapeShellArgs cfg.extraArgs}"
|
||||||
|
]);
|
||||||
|
# Start with a clean root filesystem, and allowlist what the container
|
||||||
|
# is permitted to access.
|
||||||
|
TemporaryFileSystem = "/";
|
||||||
|
# Allowlist /nix/store (to allow the binary to find its dependencies)
|
||||||
|
# and dbus.
|
||||||
|
ReadOnlyPaths = "/nix/store /run/dbus";
|
||||||
|
# Let systemd manage `/var/lib/matter-server` for us inside the
|
||||||
|
# ephemeral TemporaryFileSystem.
|
||||||
|
StateDirectory = storageDir;
|
||||||
|
# `python-matter-server` writes to /data even when a storage-path is
|
||||||
|
# specified. This bind-mount points /data at the systemd-managed
|
||||||
|
# /var/lib/matter-server, so all files get dropped into the state
|
||||||
|
# directory.
|
||||||
|
BindPaths = "${storagePath}:/data";
|
||||||
|
|
||||||
|
# Hardening bits
|
||||||
|
AmbientCapabilities = "";
|
||||||
|
CapabilityBoundingSet = "";
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
DynamicUser = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
"AF_NETLINK"
|
||||||
|
];
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallFilter = concatStringsSep " " [
|
||||||
|
"~" # Blocklist
|
||||||
|
"@clock"
|
||||||
|
"@cpu-emulation"
|
||||||
|
"@debug"
|
||||||
|
"@module"
|
||||||
|
"@mount"
|
||||||
|
"@obsolete"
|
||||||
|
"@privileged"
|
||||||
|
"@raw-io"
|
||||||
|
"@reboot"
|
||||||
|
"@resources"
|
||||||
|
"@swap"
|
||||||
|
];
|
||||||
|
UMask = "0077";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -512,6 +512,7 @@ in {
|
||||||
mastodon = discoverTests (import ./web-apps/mastodon { inherit handleTestOn; });
|
mastodon = discoverTests (import ./web-apps/mastodon { inherit handleTestOn; });
|
||||||
pixelfed = discoverTests (import ./web-apps/pixelfed { inherit handleTestOn; });
|
pixelfed = discoverTests (import ./web-apps/pixelfed { inherit handleTestOn; });
|
||||||
mate = handleTest ./mate.nix {};
|
mate = handleTest ./mate.nix {};
|
||||||
|
matter-server = handleTest ./matter-server.nix {};
|
||||||
matomo = handleTest ./matomo.nix {};
|
matomo = handleTest ./matomo.nix {};
|
||||||
matrix-appservice-irc = handleTest ./matrix/appservice-irc.nix {};
|
matrix-appservice-irc = handleTest ./matrix/appservice-irc.nix {};
|
||||||
matrix-conduit = handleTest ./matrix/conduit.nix {};
|
matrix-conduit = handleTest ./matrix/conduit.nix {};
|
||||||
|
|
45
nixos/tests/matter-server.nix
Normal file
45
nixos/tests/matter-server.nix
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import ./make-test-python.nix ({ pkgs, lib, ...} :
|
||||||
|
|
||||||
|
let
|
||||||
|
chipVersion = pkgs.python311Packages.home-assistant-chip-core.version;
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "matter-server";
|
||||||
|
meta.maintainers = with lib.maintainers; [ leonm1 ];
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
machine = { config, ... }: {
|
||||||
|
services.matter-server = {
|
||||||
|
enable = true;
|
||||||
|
port = 1234;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = /* python */ ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
machine.wait_for_unit("matter-server.service")
|
||||||
|
machine.wait_for_open_port(1234)
|
||||||
|
|
||||||
|
with subtest("Check websocket server initialized"):
|
||||||
|
output = machine.succeed("echo \"\" | ${pkgs.websocat}/bin/websocat ws://localhost:1234/ws")
|
||||||
|
machine.log(output)
|
||||||
|
|
||||||
|
assert '"sdk_version": "${chipVersion}"' in output, (
|
||||||
|
'CHIP version \"${chipVersion}\" not present in websocket message'
|
||||||
|
)
|
||||||
|
|
||||||
|
assert '"fabric_id": 1' in output, (
|
||||||
|
"fabric_id not propagated to server"
|
||||||
|
)
|
||||||
|
|
||||||
|
with subtest("Check storage directory is created"):
|
||||||
|
machine.succeed("ls /var/lib/matter-server/chip.json")
|
||||||
|
|
||||||
|
with subtest("Check systemd hardening"):
|
||||||
|
_, output = machine.execute("systemd-analyze security matter-server.service | grep -v '✓'")
|
||||||
|
machine.log(output)
|
||||||
|
'';
|
||||||
|
})
|
|
@ -2,6 +2,8 @@
|
||||||
, buildPythonPackage
|
, buildPythonPackage
|
||||||
, fetchFromGitHub
|
, fetchFromGitHub
|
||||||
, pythonOlder
|
, pythonOlder
|
||||||
|
, stdenvNoCC
|
||||||
|
, substituteAll
|
||||||
|
|
||||||
# build
|
# build
|
||||||
, setuptools
|
, setuptools
|
||||||
|
@ -28,6 +30,29 @@
|
||||||
, pytestCheckHook
|
, pytestCheckHook
|
||||||
}:
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
paaCerts = stdenvNoCC.mkDerivation rec {
|
||||||
|
pname = "matter-server-paa-certificates";
|
||||||
|
version = "1.2.0.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "project-chip";
|
||||||
|
repo = "connectedhomeip";
|
||||||
|
rev = "refs/tags/v${version}";
|
||||||
|
hash = "sha256-p3P0n5oKRasYz386K2bhN3QVfN6oFndFIUWLEUWB0ss=";
|
||||||
|
};
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
|
||||||
|
mkdir -p $out
|
||||||
|
cp $src/credentials/development/paa-root-certs/* $out/
|
||||||
|
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
|
||||||
buildPythonPackage rec {
|
buildPythonPackage rec {
|
||||||
pname = "python-matter-server";
|
pname = "python-matter-server";
|
||||||
version = "5.7.0b2";
|
version = "5.7.0b2";
|
||||||
|
@ -42,6 +67,13 @@ buildPythonPackage rec {
|
||||||
hash = "sha256-fMtvVizHeAzLdou0U1tqbmQATIBLK4w9I7EwMlzB8QA=";
|
hash = "sha256-fMtvVizHeAzLdou0U1tqbmQATIBLK4w9I7EwMlzB8QA=";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
patches = [
|
||||||
|
(substituteAll {
|
||||||
|
src = ./link-paa-root-certs.patch;
|
||||||
|
paacerts = paaCerts;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
substituteInPlace pyproject.toml \
|
substituteInPlace pyproject.toml \
|
||||||
--replace 'version = "0.0.0"' 'version = "${version}"' \
|
--replace 'version = "0.0.0"' 'version = "${version}"' \
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
diff --git a/matter_server/server/const.py b/matter_server/server/const.py
|
||||||
|
index b6cd839..f9f798f 100644
|
||||||
|
--- a/matter_server/server/const.py
|
||||||
|
+++ b/matter_server/server/const.py
|
||||||
|
@@ -5,14 +5,4 @@ from typing import Final
|
||||||
|
# The minimum schema version (of a client) the server can support
|
||||||
|
MIN_SCHEMA_VERSION = 5
|
||||||
|
|
||||||
|
-# the paa-root-certs path is hardcoded in the sdk at this time
|
||||||
|
-# and always uses the development subfolder
|
||||||
|
-# regardless of anything you pass into instantiating the controller
|
||||||
|
-# revisit this once matter 1.1 is released
|
||||||
|
-PAA_ROOT_CERTS_DIR: Final[pathlib.Path] = (
|
||||||
|
- pathlib.Path(__file__)
|
||||||
|
- .parent.resolve()
|
||||||
|
- .parent.resolve()
|
||||||
|
- .parent.resolve()
|
||||||
|
- .joinpath("credentials/development/paa-root-certs")
|
||||||
|
-)
|
||||||
|
+PAA_ROOT_CERTS_DIR: Final[pathlib.Path] = pathlib.Path("@paacerts@")
|
||||||
|
diff --git a/matter_server/server/helpers/paa_certificates.py b/matter_server/server/helpers/paa_certificates.py
|
||||||
|
index 9ac5a10..25230c1 100644
|
||||||
|
--- a/matter_server/server/helpers/paa_certificates.py
|
||||||
|
+++ b/matter_server/server/helpers/paa_certificates.py
|
||||||
|
@@ -58,84 +58,14 @@ async def fetch_dcl_certificates(
|
||||||
|
fetch_production_certificates: bool = True,
|
||||||
|
) -> int:
|
||||||
|
"""Fetch DCL PAA Certificates."""
|
||||||
|
- LOGGER.info("Fetching the latest PAA root certificates from DCL.")
|
||||||
|
- if not PAA_ROOT_CERTS_DIR.is_dir():
|
||||||
|
- loop = asyncio.get_running_loop()
|
||||||
|
- await loop.run_in_executor(None, makedirs, PAA_ROOT_CERTS_DIR)
|
||||||
|
- fetch_count: int = 0
|
||||||
|
- base_urls = set()
|
||||||
|
- # determine which url's need to be queried.
|
||||||
|
- # if we're going to fetch both prod and test, do test first
|
||||||
|
- # so any duplicates will be overwritten/preferred by the production version
|
||||||
|
- # NOTE: While Matter is in BETA we fetch the test certificates by default
|
||||||
|
- if fetch_test_certificates:
|
||||||
|
- base_urls.add(TEST_URL)
|
||||||
|
- if fetch_production_certificates:
|
||||||
|
- base_urls.add(PRODUCTION_URL)
|
||||||
|
|
||||||
|
- try:
|
||||||
|
- async with ClientSession(raise_for_status=True) as http_session:
|
||||||
|
- for url_base in base_urls:
|
||||||
|
- # fetch the paa certificates list
|
||||||
|
- async with http_session.get(
|
||||||
|
- f"{url_base}/dcl/pki/root-certificates"
|
||||||
|
- ) as response:
|
||||||
|
- result = await response.json()
|
||||||
|
- paa_list = result["approvedRootCertificates"]["certs"]
|
||||||
|
- # grab each certificate
|
||||||
|
- for paa in paa_list:
|
||||||
|
- # do not fetch a certificate if we already fetched it
|
||||||
|
- if paa["subjectKeyId"] in LAST_CERT_IDS:
|
||||||
|
- continue
|
||||||
|
- async with http_session.get(
|
||||||
|
- f"{url_base}/dcl/pki/certificates/{paa['subject']}/{paa['subjectKeyId']}"
|
||||||
|
- ) as response:
|
||||||
|
- result = await response.json()
|
||||||
|
-
|
||||||
|
- certificate_data: dict = result["approvedCertificates"]["certs"][0]
|
||||||
|
- certificate: str = certificate_data["pemCert"]
|
||||||
|
- subject = certificate_data["subjectAsText"]
|
||||||
|
- certificate = certificate.rstrip("\n")
|
||||||
|
-
|
||||||
|
- await write_paa_root_cert(
|
||||||
|
- certificate,
|
||||||
|
- subject,
|
||||||
|
- )
|
||||||
|
- LAST_CERT_IDS.add(paa["subjectKeyId"])
|
||||||
|
- fetch_count += 1
|
||||||
|
- except ClientError as err:
|
||||||
|
- LOGGER.warning(
|
||||||
|
- "Fetching latest certificates failed: error %s", err, exc_info=err
|
||||||
|
- )
|
||||||
|
- else:
|
||||||
|
- LOGGER.info("Fetched %s PAA root certificates from DCL.", fetch_count)
|
||||||
|
-
|
||||||
|
- return fetch_count
|
||||||
|
+ return 0
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_git_certificates() -> int:
|
||||||
|
"""Fetch Git PAA Certificates."""
|
||||||
|
- fetch_count = 0
|
||||||
|
- LOGGER.info("Fetching the latest PAA root certificates from Git.")
|
||||||
|
- try:
|
||||||
|
- async with ClientSession(raise_for_status=True) as http_session:
|
||||||
|
- for cert in GIT_CERTS:
|
||||||
|
- if cert in LAST_CERT_IDS:
|
||||||
|
- continue
|
||||||
|
|
||||||
|
- async with http_session.get(f"{GIT_URL}/{cert}.pem") as response:
|
||||||
|
- certificate = await response.text()
|
||||||
|
- await write_paa_root_cert(certificate, cert)
|
||||||
|
- LAST_CERT_IDS.add(cert)
|
||||||
|
- fetch_count += 1
|
||||||
|
- except ClientError as err:
|
||||||
|
- LOGGER.warning(
|
||||||
|
- "Fetching latest certificates failed: error %s", err, exc_info=err
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- LOGGER.info("Fetched %s PAA root certificates from Git.", fetch_count)
|
||||||
|
-
|
||||||
|
- return fetch_count
|
||||||
|
+ return 0
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_certificates(
|
||||||
|
@@ -144,12 +74,4 @@ async def fetch_certificates(
|
||||||
|
) -> int:
|
||||||
|
"""Fetch PAA Certificates."""
|
||||||
|
|
||||||
|
- fetch_count = await fetch_dcl_certificates(
|
||||||
|
- fetch_test_certificates=fetch_test_certificates,
|
||||||
|
- fetch_production_certificates=fetch_production_certificates,
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
- if fetch_test_certificates:
|
||||||
|
- fetch_count += await fetch_git_certificates()
|
||||||
|
-
|
||||||
|
- return fetch_count
|
||||||
|
+ return 0
|
||||||
|
|
Loading…
Reference in a new issue