Skip to content

Commit 94a1422

Browse files
authored
Fix local privilege escalation via AZURE_CONFIG_DIR and document RBAC over-grant (#144)
1 parent 0a130b5 commit 94a1422

File tree

4 files changed

+39
-33
lines changed

4 files changed

+39
-33
lines changed

aks-flex-node-agent.service

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,14 @@ TimeoutStopSec=60
1515
# Restart configuration for daemon resilience
1616
Restart=on-failure
1717
RestartSec=30
18-
# TODO: review the settings and permission here
19-
User=root
20-
Group=root
21-
SupplementaryGroups=PLACEHOLDER_USER_GROUP
22-
Environment=AZURE_CONFIG_DIR=PLACEHOLDER_AZURE_CONFIG_DIR
18+
# AZURE_CONFIG_DIR points to a root-owned copy under /etc/aks-flex-node/azure,
19+
# NOT the installing user's ~/.azure (which would allow local privilege escalation
20+
# via malicious CLI extensions). See scripts/install.sh for the copy logic.
21+
Environment=AZURE_CONFIG_DIR=/etc/aks-flex-node/azure
2322
RuntimeDirectory=aks-flex-node
24-
RuntimeDirectoryMode=0755
23+
RuntimeDirectoryMode=0750
2524
StandardOutput=journal
2625
StandardError=journal
2726

28-
# Security hardening (runs as root for system-level Kubernetes node operations)
29-
NoNewPrivileges=false
30-
ProtectSystem=false
31-
ProtectHome=false
32-
PrivateTmp=false
33-
PrivateDevices=false
34-
ProtectHostname=false
35-
ProtectClock=false
36-
ProtectKernelTunables=false
37-
ProtectKernelModules=false
38-
ProtectKernelLogs=false
39-
ProtectControlGroups=false
40-
RestrictNamespaces=false
41-
LockPersonality=false
42-
MemoryDenyWriteExecute=false
43-
RestrictRealtime=false
44-
RestrictSUIDSGID=false
45-
RemoveIPC=false
46-
47-
# Allow access to specific paths that need modification (- prefix makes paths optional)
48-
ReadWritePaths=-/etc/kubernetes -/var/lib/kubelet -/var/lib/containerd -/etc/containerd -/opt/cni -/etc/cni -/etc/systemd/system -/etc/sysctl.d -/etc/modules-load.d -/var/log/aks-flex-node -/tmp -/etc/aks-flex-node -/run/aks-flex-node
49-
5027
[Install]
51-
WantedBy=multi-user.target
28+
WantedBy=multi-user.target

components/arc/v20260301/arc_rbac.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ func (a *installArcAction) getRoleAssignments(spec *arc.InstallArcSpec) []roleAs
6565
clusterScope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ContainerService/managedClusters/%s", subscriptionID, resourceGroup, clusterName)
6666

6767
// Define required role assignments
68+
//
69+
// TODO(security): These roles are over-privileged for a worker node identity and should be
70+
// restricted once the design is confirmed. Current grants defeat Kubernetes NodeRestriction
71+
// because the node MSI is mapped to cluster-admin by Azure RBAC. Planned changes:
72+
// - Replace "AKS RBAC Cluster Admin" with "AKS RBAC Writer" (or a custom role) scoped to
73+
// the cluster, sufficient for kubelet node/pod/lease operations.
74+
// - Remove "AKS Cluster Admin Role" — listClusterAdminCredentials is only needed by the
75+
// operator's bootstrap credential, not the node identity.
76+
// - Replace RG-wide "Contributor" with "Reader" scoped to the Arc machine resource.
77+
// Note: "Reader" is intentionally scoped at the subscription level to avoid hitting
78+
// Azure's per-resource role assignment count limits when many flex nodes are registered.
79+
// See: https://learn.microsoft.com/en-us/azure/aks/manage-azure-rbac
80+
// See: https://learn.microsoft.com/en-us/azure/role-based-access-control/troubleshoot-limits?tabs=default#symptom---no-more-role-assignments-can-be-created
6881
assignments := []roleAssignment{
6982
{
7083
roleName: "Reader",

pkg/components/arc/arc_base.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ func (ab *base) getArcMachine(ctx context.Context) (*armhybridcompute.Machine, e
8989
return &result.Machine, nil
9090
}
9191

92+
// TODO(security): These roles are over-privileged — see components/arc/v20260301/arc_rbac.go for details.
93+
// Will be restricted to least-privilege once the node auth design is confirmed.
9294
func (ab *base) getRoleAssignments() []roleAssignment {
9395
return []roleAssignment{
9496
{"Reader (Target Cluster)", ab.config.GetTargetClusterID(), roleDefinitionIDs["Reader"]},

scripts/install.sh

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,15 +325,29 @@ setup_systemd_service() {
325325
cp "$temp_dir/aks-flex-node-agent.service" /etc/systemd/system/
326326
chmod 644 /etc/systemd/system/aks-flex-node-agent.service
327327

328-
# Update the service file with the correct user path for Azure CLI access
328+
# Copy Azure CLI config to a root-owned directory so the root service never reads
329+
# from an unprivileged user's home directory (prevents local privilege escalation
330+
# via malicious CLI extensions planted in ~/.azure/cliextensions/).
329331
local current_user
330332
current_user=$(logname 2>/dev/null || echo "${SUDO_USER:-$USER}")
331333
local current_user_home
332334
current_user_home=$(eval echo "~$current_user")
333335

334-
log_info "Configuring service file for current user ($current_user)..."
335-
sed -i "s|PLACEHOLDER_AZURE_CONFIG_DIR|$current_user_home/.azure|g" /etc/systemd/system/aks-flex-node-agent.service
336-
sed -i "s|PLACEHOLDER_USER_GROUP|$current_user|g" /etc/systemd/system/aks-flex-node-agent.service
336+
local azure_config_dir="/etc/aks-flex-node/azure"
337+
mkdir -p "$azure_config_dir"
338+
chmod 700 "$azure_config_dir"
339+
chown root:root "$azure_config_dir"
340+
341+
# Copy only the authentication-related files (token cache, profile), never extensions
342+
for f in azureProfile.json msal_token_cache.json msal_token_cache.bin clouds.config; do
343+
if [ -f "$current_user_home/.azure/$f" ]; then
344+
cp "$current_user_home/.azure/$f" "$azure_config_dir/"
345+
chmod 600 "$azure_config_dir/$f"
346+
chown root:root "$azure_config_dir/$f"
347+
fi
348+
done
349+
350+
log_info "Azure CLI auth files copied to root-owned $azure_config_dir"
337351

338352
# Reload systemd
339353
systemctl daemon-reload

0 commit comments

Comments
 (0)