Add interactive NetBox host installer
This commit is contained in:
@@ -32,7 +32,7 @@ The agent owns only these two configured files:
|
||||
In the operator-owned NetBox `configuration.py`, add once, after the normal `PLUGINS` declaration:
|
||||
|
||||
```python
|
||||
from store_plugins import STORE_PLUGINS
|
||||
from netbox.store_plugins import STORE_PLUGINS
|
||||
|
||||
PLUGINS += STORE_PLUGINS
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "mrblake-netbox-store-agent"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
description = "Fail-closed host agent for curated NetBox plugin lifecycle operations"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
@@ -260,7 +260,11 @@ class OperationProcessor:
|
||||
temp_root = self.config.paths.temp_dir
|
||||
if temp_root.is_symlink():
|
||||
raise PolicyError("temporary directory may not be a symlink")
|
||||
temp_root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
||||
temp_root.mkdir(parents=True, exist_ok=True, mode=0o711)
|
||||
# The dropped-privilege source builder needs traversal to its own
|
||||
# 0700 operation directory. It cannot list this root, the journal,
|
||||
# or the separately protected backup directory.
|
||||
os.chmod(temp_root, 0o711)
|
||||
with tempfile.TemporaryDirectory(prefix="operation-", dir=temp_root) as temporary:
|
||||
self._event(operation_id, "artifact", "Downloading and verifying approved artifact")
|
||||
artifact = self.store.download_release(plan, Path(temporary))
|
||||
|
||||
@@ -10,9 +10,9 @@ User=root
|
||||
Group=root
|
||||
ExecStart=/usr/local/bin/netbox-store-agent --config /etc/netbox-store-agent/agent.toml daemon
|
||||
StateDirectory=netbox-store-agent
|
||||
StateDirectoryMode=0700
|
||||
StateDirectoryMode=0711
|
||||
RuntimeDirectory=netbox-store-agent
|
||||
RuntimeDirectoryMode=0750
|
||||
RuntimeDirectoryMode=0755
|
||||
UMask=0077
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
@@ -7,7 +7,7 @@ SocketMode=0660
|
||||
SocketUser=root
|
||||
# Replace with the group of the NetBox service.
|
||||
SocketGroup=netbox
|
||||
DirectoryMode=0750
|
||||
DirectoryMode=0755
|
||||
RemoveOnStop=true
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
@@ -56,6 +58,8 @@ class ExecutorTests(unittest.TestCase):
|
||||
self.assertEqual(runner.commands, [])
|
||||
self.assertIsNone(journal.get_managed_plugin("demo-plugin"))
|
||||
self.assertFalse(config.paths.include_path.exists())
|
||||
if os.name == "posix":
|
||||
self.assertEqual(stat.S_IMODE(config.paths.temp_dir.stat().st_mode), 0o711)
|
||||
|
||||
def test_install_is_disabled_and_uses_fixed_pip_argv(self) -> None:
|
||||
config = make_config(self.root, dry_run=False)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class SystemdUnitTests(unittest.TestCase):
|
||||
def test_socket_directory_is_traversable_but_socket_remains_restricted(self):
|
||||
socket_unit = Path("systemd/netbox-store-agent.socket").read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn("DirectoryMode=0755", socket_unit)
|
||||
self.assertIn("SocketMode=0660", socket_unit)
|
||||
self.assertIn("SocketGroup=netbox", socket_unit)
|
||||
|
||||
def test_state_traversal_does_not_relax_service_umask(self):
|
||||
service_unit = Path("systemd/netbox-store-agent.service").read_text(encoding="utf-8")
|
||||
|
||||
self.assertIn("StateDirectoryMode=0711", service_unit)
|
||||
self.assertIn("RuntimeDirectoryMode=0755", service_unit)
|
||||
self.assertIn("UMask=0077", service_unit)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user