nekomata / socket /policy.lisp
SNAPKITTYWEST's picture
push from SNAPKITTYWEST/nekomata
3b70664 verified
Raw
History Blame Contribute Delete
3.03 kB
(in-package #:nekod.socket)
;;; Security boundary — capability-based access control for Docker socket operations
(defvar *capability-registry* (make-hash-table :test #'equal))
(defstruct capability
(name "" :type string)
(method "" :type string)
(path-prefix "" :type string)
(granted nil :type boolean))
(defun register-capability (name method path-prefix &optional (granted t))
"Register a named capability for Docker API access."
(setf (gethash name *capability-registry*)
(make-capability :name name
:method method
:path-prefix path-prefix
:granted granted)))
(defun init-default-capabilities ()
"Initialize the default capability set."
(register-capability "container.read" "GET" "/containers")
(register-capability "container.start" "POST" "/containers")
(register-capability "container.stop" "POST" "/containers")
(register-capability "network.read" "GET" "/networks")
(register-capability "network.create" "POST" "/networks/create")
(register-capability "network.delete" "DELETE" "/networks")
(register-capability "events.subscribe" "GET" "/events")
(register-capability "system.ping" "GET" "/_ping")
(register-capability "system.version" "GET" "/version"))
(defun authorize-operation (method path)
"Check if an operation (method + path) is authorized by any granted capability.
Returns the matching capability name or signals policy-denied."
(maphash (lambda (name cap)
(when (and (capability-granted cap)
(string= method (capability-method cap))
(or (string= path (capability-path-prefix cap))
(and (> (length path) (length (capability-path-prefix cap)))
(string= (capability-path-prefix cap)
(subseq path 0 (length (capability-path-prefix cap)))))))
(return-from authorize-operation name)))
*capability-registry*)
(error 'nekod:policy-denied
:message (format nil "~a ~a" method path)
:operation (format nil "~a ~a" method path)))
(defun revoke-capability (name)
"Revoke a previously granted capability."
(let ((cap (gethash name *capability-registry*)))
(when cap
(setf (capability-granted cap) nil))))
(defun grant-capability (name)
"Grant a capability."
(let ((cap (gethash name *capability-registry*)))
(when cap
(setf (capability-granted cap) t))))
(defun validate-socket-path (path)
"Ensure socket path is a known safe value. Rejects arbitrary paths."
(unless (member path '("/var/run/docker.sock"
"/run/docker.sock"
"/var/run/podman/podman.sock")
:test #'string=)
(error 'nekod:policy-denied
:message (format nil "Untrusted socket path: ~a" path)
:operation "socket-connect")))