Rootless podman deeply caches the UID in storage, subuid mappings, and systemd sessions. Changing it destroys all container state. Reference: log/2026-04-03-uid-incident.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
64 lines
2.0 KiB
Nix
64 lines
2.0 KiB
Nix
# MCP (Metacircular Control Plane) agent user and configuration.
|
|
#
|
|
# Creates a dedicated 'mcp' system user with rootless podman support
|
|
# and a systemd service for the agent daemon.
|
|
{ pkgs, ... }:
|
|
|
|
{
|
|
users.users.mcp = {
|
|
isSystemUser = true;
|
|
uid = 850; # NEVER CHANGE. Rootless podman caches the UID in storage, subuid mappings,
|
|
# and systemd sessions. Changing it destroys all container state.
|
|
# See log/2026-04-03-uid-incident.md.
|
|
group = "mcp";
|
|
home = "/srv/mcp";
|
|
shell = pkgs.shadow; # nologin equivalent
|
|
subUidRanges = [{ startUid = 100000; count = 65536; }];
|
|
subGidRanges = [{ startGid = 100000; count = 65536; }];
|
|
extraGroups = [ "systemd-journal" ];
|
|
linger = true;
|
|
};
|
|
|
|
users.groups.mcp = { gid = 850; };
|
|
|
|
# MCP Master runs as an MCP-managed container (not a systemd service).
|
|
# Deployed via: mcp deploy mcp-master --direct
|
|
# See ~/.config/mcp/services/mcp-master.toml
|
|
|
|
systemd.services.mcp-agent = {
|
|
description = "MCP Agent";
|
|
after = [ "network-online.target" ];
|
|
wants = [ "network-online.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "/srv/mcp/mcp-agent server --config /srv/mcp/mcp-agent.toml";
|
|
Restart = "on-failure";
|
|
RestartSec = 5;
|
|
|
|
User = "mcp";
|
|
Group = "mcp";
|
|
Environment = [
|
|
"HOME=/srv/mcp"
|
|
"XDG_RUNTIME_DIR=/run/user/850"
|
|
"PATH=/run/current-system/sw/bin:/usr/local/bin"
|
|
];
|
|
|
|
NoNewPrivileges = true;
|
|
ProtectSystem = "full"; # "strict" blocks /run/user; "full" protects /usr and /boot
|
|
# ProtectHome makes /run/user inaccessible, which breaks rootless podman.
|
|
# The agent's home is /srv/mcp (not /home), so this is acceptable.
|
|
ProtectHome = false;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true;
|
|
RestrictSUIDSGID = true;
|
|
LockPersonality = true;
|
|
RestrictRealtime = true;
|
|
ReadWritePaths = [ "/srv" ];
|
|
};
|
|
};
|
|
}
|