# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation # # This file is part of the Nexus Sovereign Core. # See legal/LICENSE_SOVEREIGN.md for license terms. ## nimpak/namespace_subsystem.nim ## Namespace Subsystem for Nippels ## ## Provides Linux kernel namespace isolation for lightweight application environments. ## Implements different isolation levels from None to Quantum with zero overhead. ## ## Requirements: 1.1, 1.4, 1.5, 5.1-5.5 import std/[os, posix] import utils/resultutils import nippel_types # ============================================================================= # Linux Namespace Constants # ============================================================================= # Clone flags for namespace creation (from linux/sched.h) const CLONE_NEWNS* = 0x00020000.cint # Mount namespace CLONE_NEWPID* = 0x20000000.cint # PID namespace CLONE_NEWNET* = 0x40000000.cint # Network namespace CLONE_NEWIPC* = 0x08000000.cint # IPC namespace CLONE_NEWUSER* = 0x10000000.cint # User namespace CLONE_NEWUTS* = 0x04000000.cint # UTS namespace # Linux syscalls for namespace operations proc unshare(flags: cint): cint {.importc, header: "".} proc setns(fd: cint, nstype: cint): cint {.importc, header: "".} # ============================================================================= # Namespace Error Types # ============================================================================= type NamespaceError* = object of CatchableError ## Namespace-specific errors nippelName*: string operation*: string errno*: cint # ============================================================================= # Namespace Configuration (Requirement 5.1-5.5) # ============================================================================= proc getNamespaceHandle*(level: IsolationLevel): NamespaceHandle = ## Get namespace configuration for an isolation level ## ## Isolation Levels: ## - None: No isolation (full system access) ## - Standard: Mount + filesystem namespaces only ## - Strict: Mount + PID + network + IPC namespaces ## - Quantum: All namespaces including user and UTS case level: of None: # Requirement 5.1: No isolation NamespaceHandle( mountNS: false, pidNS: false, networkNS: false, ipcNS: false, userNS: false, utsNS: false, nsPath: "" ) of Standard: # Requirement 5.2: Standard isolation (mount + filesystem) NamespaceHandle( mountNS: true, pidNS: false, networkNS: false, ipcNS: false, userNS: false, utsNS: false, nsPath: "" ) of Strict: # Requirement 5.3: Strict isolation (mount + PID + network + IPC) NamespaceHandle( mountNS: true, pidNS: true, networkNS: true, ipcNS: true, userNS: false, utsNS: false, nsPath: "" ) of Quantum: # Requirement 5.4: Quantum isolation (all namespaces) NamespaceHandle( mountNS: true, pidNS: true, networkNS: true, ipcNS: true, userNS: true, utsNS: true, nsPath: "" ) proc validateNamespaceHandle*(config: NamespaceHandle): Result[bool, string] = ## Validate namespace configuration ## Ensures that namespace combinations are valid and supported try: # User namespace requires other namespaces to be useful if config.userNS and not (config.mountNS or config.pidNS): return err[bool]("User namespace requires at least mount or PID namespace") # PID namespace is most useful with mount namespace if config.pidNS and not config.mountNS: echo "âš ī¸ Warning: PID namespace without mount namespace may have limited isolation" # Network namespace requires root or user namespace if config.networkNS and not config.userNS: if getuid() != 0: echo "âš ī¸ Warning: Network namespace requires root privileges or user namespace" echo " Continuing without network namespace" # Don't fail validation, just warn - we'll handle it in createNamespaces # return err[bool]("Network namespace requires root privileges or user namespace") return ok(true) except Exception as e: return err[bool]("Failed to validate namespace config: " & e.msg) # ============================================================================= # Namespace Operations (Requirement 1.1, 1.5) # ============================================================================= proc createNamespaces*(config: NamespaceHandle): Result[NamespaceHandle, string] = ## Create Linux kernel namespaces based on configuration (Requirement 1.1) ## ## This uses the unshare() syscall to create new namespaces for the current process. ## The namespaces are created but not yet entered - use enterNamespace() for that. try: # Validate configuration first let validResult = validateNamespaceHandle(config) if validResult.isErr: return err[NamespaceHandle](validResult.error) # Build flags for unshare() syscall var flags: cint = 0 if config.mountNS: flags = flags or CLONE_NEWNS echo " 📁 Mount namespace enabled" if config.pidNS: flags = flags or CLONE_NEWPID echo " đŸ”ĸ PID namespace enabled" if config.networkNS: flags = flags or CLONE_NEWNET echo " 🌐 Network namespace enabled" if config.ipcNS: flags = flags or CLONE_NEWIPC echo " đŸ’Ŧ IPC namespace enabled" if config.userNS: flags = flags or CLONE_NEWUSER echo " 👤 User namespace enabled" if config.utsNS: flags = flags or CLONE_NEWUTS echo " đŸ–Ĩī¸ UTS namespace enabled" # If no namespaces requested, return empty handle if flags == 0: echo " â„šī¸ No namespace isolation (None level)" return ok(NamespaceHandle( mountNS: false, pidNS: false, networkNS: false, ipcNS: false, userNS: false, utsNS: false, nsPath: "" )) # Create namespaces using unshare() let unshareResult = unshare(flags) if unshareResult != 0: let errNo = errno # Check if it's a permission error if errNo == EPERM: echo "âš ī¸ Warning: Namespace creation requires root privileges" echo " Continuing without namespace isolation" # Return empty handle to allow operation to continue return ok(NamespaceHandle( mountNS: false, pidNS: false, networkNS: false, ipcNS: false, userNS: false, utsNS: false, nsPath: "" )) return err[NamespaceHandle]("Failed to create namespaces: " & $strerror(errNo)) # Store namespace file descriptors for later use let pid = getpid() let nsPath = "/proc/" & $pid & "/ns" echo "✅ Created namespaces successfully" echo " Namespace path: ", nsPath return ok(NamespaceHandle( mountNS: config.mountNS, pidNS: config.pidNS, networkNS: config.networkNS, ipcNS: config.ipcNS, userNS: config.userNS, utsNS: config.utsNS, nsPath: nsPath )) except Exception as e: return err[NamespaceHandle]("Failed to create namespaces: " & e.msg) proc enterNamespace*(handle: NamespaceHandle): Result[bool, string] = ## Enter an existing namespace (Requirement 1.5) ## ## This uses the setns() syscall to enter namespaces that were previously created. ## Useful for activating a Nippel that has existing namespaces. try: if handle.nsPath.len == 0: # No namespaces to enter return ok(true) echo "🔄 Entering namespaces..." # Enter each namespace type if enabled if handle.mountNS: let nsFile = handle.nsPath / "mnt" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open mount namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWNS) != 0: discard close(fd) return err[bool]("Failed to enter mount namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered mount namespace" if handle.pidNS: let nsFile = handle.nsPath / "pid" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open PID namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWPID) != 0: discard close(fd) return err[bool]("Failed to enter PID namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered PID namespace" if handle.networkNS: let nsFile = handle.nsPath / "net" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open network namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWNET) != 0: discard close(fd) return err[bool]("Failed to enter network namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered network namespace" if handle.ipcNS: let nsFile = handle.nsPath / "ipc" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open IPC namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWIPC) != 0: discard close(fd) return err[bool]("Failed to enter IPC namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered IPC namespace" if handle.userNS: let nsFile = handle.nsPath / "user" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open user namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWUSER) != 0: discard close(fd) return err[bool]("Failed to enter user namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered user namespace" if handle.utsNS: let nsFile = handle.nsPath / "uts" if fileExists(nsFile): let fd = open(cstring(nsFile), O_RDONLY) if fd < 0: return err[bool]("Failed to open UTS namespace: " & $strerror(errno)) if setns(fd, CLONE_NEWUTS) != 0: discard close(fd) return err[bool]("Failed to enter UTS namespace: " & $strerror(errno)) discard close(fd) echo " ✅ Entered UTS namespace" echo "✅ Successfully entered all namespaces" return ok(true) except Exception as e: return err[bool]("Failed to enter namespaces: " & e.msg) proc exitNamespace*(handle: NamespaceHandle): Result[bool, string] = ## Exit namespace and return to host namespace (Requirement 1.5) ## ## Note: In practice, exiting namespaces is done by the kernel when the process exits. ## This function is mainly for cleanup and documentation purposes. try: echo "🔄 Exiting namespaces..." # Namespaces are automatically cleaned up when the process exits # or when all references to them are closed # For now, we just log that we're exiting if handle.nsPath.len > 0: echo " â„šī¸ Namespace cleanup will occur on process exit" echo " Namespace path: ", handle.nsPath echo "✅ Namespace exit prepared" return ok(true) except Exception as e: return err[bool]("Failed to exit namespaces: " & e.msg) proc destroyNamespaces*(handle: NamespaceHandle): Result[bool, string] = ## Destroy namespaces and clean up resources (Requirement 1.5) ## ## Namespaces are automatically destroyed by the kernel when all processes ## using them have exited and all file descriptors are closed. try: echo "đŸ—‘ī¸ Destroying namespaces..." if handle.nsPath.len == 0: echo " â„šī¸ No namespaces to destroy" return ok(true) # Namespaces are reference-counted by the kernel # They will be automatically destroyed when: # 1. All processes in the namespace have exited # 2. All file descriptors referencing the namespace are closed echo " â„šī¸ Namespaces will be destroyed by kernel when no longer referenced" echo " Namespace path: ", handle.nsPath echo "✅ Namespace destruction initiated" return ok(true) except Exception as e: return err[bool]("Failed to destroy namespaces: " & e.msg) # ============================================================================= # Namespace Information # ============================================================================= proc getNamespaceInfo*(handle: NamespaceHandle): string = ## Get human-readable information about namespace configuration result = "Namespace Configuration:\n" result.add(" Mount: " & $handle.mountNS & "\n") result.add(" PID: " & $handle.pidNS & "\n") result.add(" Network: " & $handle.networkNS & "\n") result.add(" IPC: " & $handle.ipcNS & "\n") result.add(" User: " & $handle.userNS & "\n") result.add(" UTS: " & $handle.utsNS & "\n") if handle.nsPath.len > 0: result.add(" Path: " & handle.nsPath) proc getIsolationLevelInfo*(level: IsolationLevel): string = ## Get human-readable information about an isolation level let config = getNamespaceHandle(level) result = "Isolation Level: " & $level & "\n" result.add(" Description: ") case level: of None: result.add("No isolation - full system access\n") of Standard: result.add("Standard isolation - mount + filesystem namespaces\n") of Strict: result.add("Strict isolation - mount + PID + network + IPC namespaces\n") of Quantum: result.add("Quantum isolation - all namespaces + cryptographic boundaries\n") result.add("\n" & getNamespaceInfo(NamespaceHandle( mountNS: config.mountNS, pidNS: config.pidNS, networkNS: config.networkNS, ipcNS: config.ipcNS, userNS: config.userNS, utsNS: config.utsNS, nsPath: "" ))) # ============================================================================= # Namespace Verification # ============================================================================= proc verifyNamespaces*(handle: NamespaceHandle): Result[bool, string] = ## Verify that namespaces are correctly set up and accessible try: if handle.nsPath.len == 0: # No namespaces to verify return ok(true) # Check if namespace directory exists if not dirExists(handle.nsPath): return err[bool]("Namespace directory does not exist: " & handle.nsPath) # Verify each enabled namespace var verified = 0 if handle.mountNS: let nsFile = handle.nsPath / "mnt" if not fileExists(nsFile): return err[bool]("Mount namespace file does not exist: " & nsFile) verified.inc if handle.pidNS: let nsFile = handle.nsPath / "pid" if not fileExists(nsFile): return err[bool]("PID namespace file does not exist: " & nsFile) verified.inc if handle.networkNS: let nsFile = handle.nsPath / "net" if not fileExists(nsFile): return err[bool]("Network namespace file does not exist: " & nsFile) verified.inc if handle.ipcNS: let nsFile = handle.nsPath / "ipc" if not fileExists(nsFile): return err[bool]("IPC namespace file does not exist: " & nsFile) verified.inc if handle.userNS: let nsFile = handle.nsPath / "user" if not fileExists(nsFile): return err[bool]("User namespace file does not exist: " & nsFile) verified.inc if handle.utsNS: let nsFile = handle.nsPath / "uts" if not fileExists(nsFile): return err[bool]("UTS namespace file does not exist: " & nsFile) verified.inc echo "✅ Verified ", verified, " namespace(s)" return ok(true) except Exception as e: return err[bool]("Failed to verify namespaces: " & e.msg) # ============================================================================= # Exports # ============================================================================= export NamespaceError export getNamespaceHandle, validateNamespaceHandle export createNamespaces, enterNamespace, exitNamespace, destroyNamespaces export getNamespaceInfo, getIsolationLevelInfo, verifyNamespaces