Add platform infrastructure configuration

This commit is contained in:
donghyeon-ka
2026-08-28 17:35:41 +09:00
parent fa76531e5b
commit 16c337bcc9
302 changed files with 83259 additions and 1 deletions
@@ -0,0 +1,243 @@
#!/usr/bin/env bash
# Direct-exec root helper for one staged A1 transaction.
_k3slra1h_command() {
"$@"
}
_k3slra1h_euid_is_root() {
(( EUID == 0 ))
}
_k3slra1h_output_name_is_safe() {
local output_name="${1-}"
(( $# == 1 )) || return 1
[[ "$output_name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || return 1
[[ "$output_name" != _k3slra1h_* ]]
}
_k3slra1h_capture_metadata() {
local target="${1-}" output_name="${2-}" line=''
(( $# == 2 )) || return 1
_k3slra1h_output_name_is_safe "$output_name" || return 1
case "$output_name" in target|output_name|line|record) return 1 ;; esac
_k3slra1h_packet_line metadata "$target" line || return 1
printf -v "$output_name" '%s' "$line"
}
_k3slra1h_capture_canonical() {
local target="${1-}" output_name="${2-}" line=''
(( $# == 2 )) || return 1
_k3slra1h_output_name_is_safe "$output_name" || return 1
case "$output_name" in target|output_name|line) return 1 ;; esac
_k3slra1h_packet_line readlink "$target" line || return 1
[[ "$line" == "$target" ]] || return 1
printf -v "$output_name" '%s' "$line"
}
_k3slra1h_stderr_marker() {
local chunk='' read_rc=0
if IFS= read -r -d '' -n 1 chunk; then
read_rc=0
else
read_rc=$?
fi
if (( read_rc == 0 )); then
printf '\0STDERR\0'
while IFS= read -r -d '' -n 4096 chunk; do
:
done
else
if [[ -n "$chunk" ]]; then
printf '\0STDERR\0'
fi
fi
}
_k3slra1h_metadata_packet() {
local target="${1-}" rc=0
(( $# == 1 )) || return 1
if _k3slra1h_command /usr/bin/stat --format='%d:%i|%h|%s|%y|%F|%u:%g|%a' -- "$target" 2> >(_k3slra1h_stderr_marker); then
rc=0
else
rc=$?
fi
printf '\0RC=%s\0' "$rc"
}
_k3slra1h_readlink_packet() {
local target="${1-}" rc=0
(( $# == 1 )) || return 1
if _k3slra1h_command /usr/bin/readlink --canonicalize-existing -- "$target" 2> >(_k3slra1h_stderr_marker); then
rc=0
else
rc=$?
fi
printf '\0RC=%s\0' "$rc"
}
_k3slra1h_packet_value() {
local producer="${1-}" argument="${2-}" output_name="${3-}"
local -a records=()
(( $# == 3 )) || return 1
_k3slra1h_output_name_is_safe "$output_name" || return 1
case "$output_name" in producer|argument|output_name|records) return 1 ;; esac
case "$producer" in
metadata) mapfile -d '' -t records < <(_k3slra1h_metadata_packet "$argument") ;;
readlink) mapfile -d '' -t records < <(_k3slra1h_readlink_packet "$argument") ;;
*) return 1 ;;
esac
(( ${#records[@]} == 2 )) || return 1
[[ "${records[1]}" == RC=0 ]] || return 1
printf -v "$output_name" '%s' "${records[0]}"
}
_k3slra1h_packet_line() {
local producer="${1-}" argument="${2-}" output_name="${3-}" value=''
(( $# == 3 )) || return 1
_k3slra1h_output_name_is_safe "$output_name" || return 1
case "$output_name" in producer|argument|output_name|value) return 1 ;; esac
_k3slra1h_packet_value "$producer" "$argument" value || return 1
[[ "$value" == *$'\n' ]] || return 1
[[ "$value" != *$'\r'* ]] || return 1
value="${value%$'\n'}"
[[ -n "$value" ]] || return 1
[[ "$value" != *$'\n'* ]] || return 1
printf -v "$output_name" '%s' "$value"
}
_k3slra1h_output_names_are_distinct() {
local name
local -A seen=()
(( $# >= 1 )) || return 1
for name in "$@"; do
_k3slra1h_output_name_is_safe "$name" || return 1
[[ "${seen[$name]+set}" != set ]] || return 1
printf -v 'seen[$name]' '%s' 1
done
}
_k3slra1h_metadata_fields() {
local target="${1-}" out_identity="${2-}" out_nlink="${3-}" out_size="${4-}"
local out_mtime="${5-}" out_type="${6-}" out_owner="${7-}" out_mode="${8-}"
local metadata='' normalized_type=''
(( $# == 8 )) || return 1
_k3slra1h_output_names_are_distinct "$out_identity" "$out_nlink" "$out_size" "$out_mtime" "$out_type" "$out_owner" "$out_mode" || return 1
case "$out_identity|$out_nlink|$out_size|$out_mtime|$out_type|$out_owner|$out_mode" in
*'|target|'*|target\|*|*\|target|*'|out_identity|'*|out_identity\|*|*\|out_identity|*'|out_nlink|'*|out_nlink\|*|*\|out_nlink|*'|out_size|'*|out_size\|*|*\|out_size|*'|out_mtime|'*|out_mtime\|*|*\|out_mtime|*'|out_type|'*|out_type\|*|*\|out_type|*'|out_owner|'*|out_owner\|*|*\|out_owner|*'|out_mode|'*|out_mode\|*|*\|out_mode|*'|metadata|'*|metadata\|*|*\|metadata|*'|normalized_type|'*|normalized_type\|*|*\|normalized_type) return 1 ;;
esac
_k3slra1h_capture_metadata "$target" metadata || return 1
[[ "$metadata" =~ ^([0-9]+:[0-9]+)\|([0-9]+)\|([0-9]+)\|([^|]+)\|(directory|regular[[:space:]]file|regular[[:space:]]empty[[:space:]]file|symbolic[[:space:]]link)\|([0-9]+:[0-9]+)\|([0-9]+)$ ]] || return 1
normalized_type="${BASH_REMATCH[5]}"
if [[ "$normalized_type" == 'regular empty file' ]]; then
printf -v normalized_type '%s' 'regular file'
fi
printf -v "$out_identity" '%s' "${BASH_REMATCH[1]}"
printf -v "$out_nlink" '%s' "${BASH_REMATCH[2]}"
printf -v "$out_size" '%s' "${BASH_REMATCH[3]}"
printf -v "$out_mtime" '%s' "${BASH_REMATCH[4]}"
printf -v "$out_type" '%s' "$normalized_type"
printf -v "$out_owner" '%s' "${BASH_REMATCH[6]}"
printf -v "$out_mode" '%s' "${BASH_REMATCH[7]}"
}
_k3slra1h_regular_root_file_is_safe() {
local target="${1-}" expected_device="${2-}" canonical=''
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
(( $# == 2 )) || return 1
_k3slra1h_capture_canonical "$target" canonical || return 1
[[ "$canonical" == "$target" ]] || return 1
_k3slra1h_metadata_fields "$target" identity nlink size mtime type owner mode || return 1
[[ "${identity%%:*}" == "$expected_device" ]] || return 1
[[ "$nlink" == 1 ]] || return 1
[[ "$type" == 'regular file' ]] || return 1
[[ "$owner" == 0:0 ]] || return 1
[[ "$mode" == 600 ]]
}
_k3slra1h_root_directory_is_safe() {
local target="${1-}" expected_device="${2-}" canonical=''
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
(( $# == 2 )) || return 1
_k3slra1h_capture_canonical "$target" canonical || return 1
[[ "$canonical" == "$target" ]] || return 1
_k3slra1h_metadata_fields "$target" identity nlink size mtime type owner mode || return 1
[[ "${identity%%:*}" == "$expected_device" ]] || return 1
[[ "$type" == directory ]] || return 1
[[ "$owner" == 0:0 ]] || return 1
[[ "$mode" == 700 ]]
}
_k3slra1h_staged_root_is_safe() {
local capture_root="${1-}" canonical='' device=''
local identity='' nlink='' size='' mtime='' type='' owner='' mode=''
(( $# == 1 )) || return 1
[[ "$capture_root" == /* ]] || return 1
[[ "$capture_root" != */ ]] || return 1
[[ "$capture_root" != *//* ]] || return 1
_k3slra1h_capture_canonical "$capture_root" canonical || return 1
[[ "$canonical" == "$capture_root" ]] || return 1
_k3slra1h_metadata_fields "$capture_root" identity nlink size mtime type owner mode || return 1
[[ "$type" == directory ]] || return 1
[[ "$owner" == 0:0 ]] || return 1
[[ "$mode" == 700 ]] || return 1
device="${identity%%:*}"
[[ "$device" =~ ^[0-9]+$ ]] || return 1
_k3slra1h_root_directory_is_safe "${capture_root}/lib" "$device" || return 1
_k3slra1h_root_directory_is_safe "${capture_root}/libexec" "$device" || return 1
_k3slra1h_root_directory_is_safe "${capture_root}/contract" "$device" || return 1
_k3slra1h_regular_root_file_is_safe "${capture_root}/lib/k3s-local-recovery.sh" "$device" || return 1
_k3slra1h_regular_root_file_is_safe "${capture_root}/lib/k3s-local-recovery-a1.sh" "$device" || return 1
_k3slra1h_regular_root_file_is_safe "${capture_root}/contract/local-recovery.env" "$device" || return 1
_k3slra1h_regular_root_file_is_safe "${capture_root}/libexec/k3s-local-recovery-a1-check.sh" "$device"
}
_k3slra1h_load_staged_dependencies() {
local capture_root="${1-}" shared_library='' a1_library='' contract_file=''
(( $# == 1 )) || return 1
_k3slra1h_staged_root_is_safe "$capture_root" || return 1
shared_library="${capture_root}/lib/k3s-local-recovery.sh"
a1_library="${capture_root}/lib/k3s-local-recovery-a1.sh"
contract_file="${capture_root}/contract/local-recovery.env"
source "$shared_library" || return 1
source "$a1_library" || return 1
_k3slr_load_contract "$contract_file" || return 1
[[ "${K3SLR_MAPPING_NAME-}" == "$K3SLRA1_MAIN_MAPPING_NAME" ]] || return 1
[[ "${K3SLR_PROOF_MAPPING_NAME-}" == k3s-recovery-proof ]]
}
_k3slra1h_check_main_impl() {
local mount_root="${1-}" role="${2-}" capture_root="${3-}"
(( $# == 3 )) || return 1
_k3slra1h_euid_is_root || return 1
[[ "$-" != *x* ]] || return 1
case "$role" in
original-baseline) [[ "$mount_root" == "${capture_root}/original-baseline-mount" ]] || return 1 ;;
proof) [[ "$mount_root" == "${capture_root}/proof-mount" ]] || return 1 ;;
original-final) [[ "$mount_root" == "${capture_root}/original-final-mount" ]] || return 1 ;;
*) return 1 ;;
esac
_k3slra1h_load_staged_dependencies "$capture_root" || return 1
_k3slra1_verify_payload_root "$mount_root" "$role" "$capture_root"
}
k3slra1_check_main() {
_k3slra1h_check_main_impl "$@" >/dev/null 2>&1
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
_k3slra1h_direct_signal_rc=0
_k3slra1h_direct_rc=0
trap '_k3slra1h_direct_signal_rc=130; if (( _k3slra1h_direct_rc == 0 )); then _k3slra1h_direct_rc=130; fi' INT
trap '_k3slra1h_direct_signal_rc=143; if (( _k3slra1h_direct_rc == 0 )); then _k3slra1h_direct_rc=143; fi' TERM
if k3slra1_check_main "$@"; then
_k3slra1h_direct_rc=0
else
_k3slra1h_direct_rc=$?
fi
if (( _k3slra1h_direct_signal_rc != 0 && _k3slra1h_direct_rc == 0 )); then
_k3slra1h_direct_rc=$_k3slra1h_direct_signal_rc
fi
exit "$_k3slra1h_direct_rc"
fi
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,947 @@
#!/usr/bin/python3
from __future__ import annotations
import hmac
import ctypes
import os
import re
import signal
import socket
import stat
import struct
import subprocess
import sys
import termios
from typing import Callable, Sequence
KEEPASSXC_CLI = "/usr/bin/keepassxc-cli"
PYTHON3 = "/usr/bin/python3"
SOCKET_NAME = "vault.sock"
BASELINE_NAME = "baseline.kdbx"
CANDIDATE_NAME = "candidate.kdbx"
ENTRY_GROUP = "/Platform/Observability/Slack"
ENTRY_TITLE = "Alertmanager webhook - desktop-infra-전체"
ENTRY_PATH = ENTRY_GROUP + "/" + ENTRY_TITLE
ENTRY_USERNAME = "desktop-infra"
ENTRY_URL = ""
MAX_WEBHOOK_BYTES = 4096
MAX_INPUT_BYTES = 4096
MAX_PROTOCOL_BYTES = 4096
MAX_CHILD_OUTPUT_BYTES = 64 * 1024
MAX_CHILD_STDERR_BYTES = 8192
DEFAULT_ACCEPT_TIMEOUT = 900.0
DEFAULT_IO_TIMEOUT = 15.0
DEFAULT_CHILD_TIMEOUT = 30.0
WEBHOOK_RE = re.compile(
rb"https://hooks[.]slack[.]com/services/"
rb"[A-Za-z0-9_-]+/[A-Za-z0-9_-]+/[A-Za-z0-9_-]+"
)
class VaultContractError(RuntimeError):
pass
class VaultProtocolError(VaultContractError):
pass
class VaultSignal(BaseException):
def __init__(self, signum: int) -> None:
super().__init__(signum)
self.signum = signum
HANDLED_SIGNALS = (signal.SIGHUP, signal.SIGINT, signal.SIGTERM)
def install_process_signal_handlers() -> None:
def raise_first_signal(signum: int, _frame: object) -> None:
for handled in HANDLED_SIGNALS:
signal.signal(handled, signal.SIG_IGN)
raise VaultSignal(signum)
for signum in HANDLED_SIGNALS:
signal.signal(signum, raise_first_signal)
class KeepassResult:
def __init__(self, returncode: int, stdout: bytes, stderr: bytes) -> None:
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
def wipe_mutable_buffer(buffer: bytearray) -> None:
for index in range(len(buffer)):
buffer[index] = 0
def _regular_metadata(fd: int, *, allow_unlinked: bool = False) -> tuple[int, ...]:
info = os.fstat(fd)
if not stat.S_ISREG(info.st_mode):
raise VaultContractError("expected regular file")
if info.st_uid != os.getuid():
raise VaultContractError("file owner mismatch")
if stat.S_IMODE(info.st_mode) != 0o600:
raise VaultContractError("file mode mismatch")
if info.st_nlink != 1 and not (allow_unlinked and info.st_nlink == 0):
raise VaultContractError("file link count mismatch")
return (
info.st_dev,
info.st_ino,
info.st_uid,
stat.S_IMODE(info.st_mode),
info.st_nlink,
info.st_size,
)
class RetainedWebhook:
def __init__(self, fd: int, metadata: tuple[int, ...], content: bytearray) -> None:
self._fd = fd
self._metadata = metadata
self._content = bytearray(content)
self._closed = False
@property
def fd(self) -> int:
if self._closed:
raise VaultContractError("webhook descriptor is closed")
return self._fd
def read_bytes(self) -> bytes:
if self._closed:
raise VaultContractError("webhook descriptor is closed")
before = _regular_metadata(self._fd)
if before != self._metadata:
raise VaultContractError("webhook metadata drift")
collected = bytearray()
confirmation = bytearray()
try:
for target in (collected, confirmation):
os.lseek(self._fd, 0, os.SEEK_SET)
while len(target) <= MAX_WEBHOOK_BYTES:
chunk = os.read(
self._fd,
min(1024, MAX_WEBHOOK_BYTES + 1 - len(target)),
)
if not chunk:
break
target.extend(chunk)
after = _regular_metadata(self._fd)
if after != before:
raise VaultContractError("webhook metadata drift")
if len(collected) > MAX_WEBHOOK_BYTES or len(confirmation) > MAX_WEBHOOK_BYTES:
raise VaultContractError("webhook content is oversized")
if not hmac.compare_digest(bytes(collected), bytes(confirmation)) or not hmac.compare_digest(
bytes(confirmation), self._content
):
raise VaultContractError("webhook content drift")
return bytes(collected)
finally:
wipe_mutable_buffer(collected)
wipe_mutable_buffer(confirmation)
def close(self) -> None:
if not self._closed:
try:
os.close(self._fd)
finally:
wipe_mutable_buffer(self._content)
self._closed = True
def _validate_webhook_content(content: bytes) -> None:
if not content or len(content) > MAX_WEBHOOK_BYTES:
raise VaultContractError("invalid webhook content")
if b"\r" in content or b"\n" in content or b"\0" in content:
raise VaultContractError("invalid webhook content")
if content.strip() != content:
raise VaultContractError("invalid webhook content")
if WEBHOOK_RE.fullmatch(content) is None:
raise VaultContractError("invalid webhook content")
def open_validated_webhook(path: str) -> RetainedWebhook:
if not os.path.isabs(path):
raise VaultContractError("absolute webhook path required")
flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC
fd = os.open(path, flags)
try:
before = _regular_metadata(fd)
content_buffer = bytearray()
confirmation_buffer = bytearray()
try:
for target in (content_buffer, confirmation_buffer):
if _regular_metadata(fd) != before:
raise VaultContractError("webhook metadata drift")
os.lseek(fd, 0, os.SEEK_SET)
while len(target) <= MAX_WEBHOOK_BYTES:
chunk = os.read(
fd,
min(1024, MAX_WEBHOOK_BYTES + 1 - len(target)),
)
if not chunk:
break
target.extend(chunk)
if _regular_metadata(fd) != before:
raise VaultContractError("webhook metadata drift")
after = _regular_metadata(fd)
if before != after:
raise VaultContractError("webhook metadata drift")
if not hmac.compare_digest(
bytes(content_buffer), bytes(confirmation_buffer)
):
raise VaultContractError("webhook content drift")
_validate_webhook_content(confirmation_buffer)
return RetainedWebhook(fd, before, confirmation_buffer)
finally:
wipe_mutable_buffer(content_buffer)
wipe_mutable_buffer(confirmation_buffer)
except BaseException:
os.close(fd)
raise
def _read_line_fd(fd: int, limit: int) -> bytearray:
value = bytearray()
try:
while len(value) <= limit:
chunk = os.read(fd, 1)
if not chunk:
raise VaultContractError("operator input ended early")
if chunk == b"\n":
return value
if chunk in {b"\r", b"\0"}:
raise VaultContractError("invalid operator input")
value.extend(chunk)
raise VaultContractError("operator input is oversized")
except BaseException:
wipe_mutable_buffer(value)
raise
def _validate_app_name(raw: bytes) -> str:
try:
app = raw.decode("utf-8", "strict")
except UnicodeDecodeError as exc:
raise VaultContractError("invalid app name") from exc
if not 1 <= len(app) <= 80 or app.strip() != app:
raise VaultContractError("invalid app name")
if any(character in ";=\\\r\n" or not character.isprintable() for character in app):
raise VaultContractError("invalid app name")
return app
def read_operator_secrets(
*, input_fd: int | None = None, output_fd: int | None = None
) -> tuple[str, bytearray]:
owned_fd = False
if input_fd is None:
input_fd = os.open("/dev/tty", os.O_RDWR | os.O_CLOEXEC)
output_fd = input_fd
owned_fd = True
if output_fd is None:
raise VaultContractError("operator output descriptor required")
master = bytearray()
old_termios = None
try:
os.write(output_fd, b"Slack app name: ")
raw_app = _read_line_fd(input_fd, MAX_INPUT_BYTES)
try:
app = _validate_app_name(bytes(raw_app))
finally:
wipe_mutable_buffer(raw_app)
if os.isatty(input_fd):
previous_mask = signal.pthread_sigmask(signal.SIG_BLOCK, HANDLED_SIGNALS)
try:
old_termios = termios.tcgetattr(input_fd)
new_termios = list(old_termios)
new_termios[3] &= ~termios.ECHO
termios.tcsetattr(input_fd, termios.TCSAFLUSH, new_termios)
os.write(output_fd, b"KeePassXC master password: ")
finally:
signal.pthread_sigmask(signal.SIG_SETMASK, previous_mask)
else:
os.write(output_fd, b"KeePassXC master password: ")
master = _read_line_fd(input_fd, MAX_INPUT_BYTES)
if not master:
raise VaultContractError("empty master password")
if old_termios is not None:
previous_mask = signal.pthread_sigmask(signal.SIG_BLOCK, HANDLED_SIGNALS)
try:
termios.tcsetattr(input_fd, termios.TCSAFLUSH, old_termios)
old_termios = None
os.write(output_fd, b"\n")
finally:
signal.pthread_sigmask(signal.SIG_SETMASK, previous_mask)
return app, master
except BaseException:
wipe_mutable_buffer(master)
raise
finally:
if old_termios is not None:
previous_mask = signal.pthread_sigmask(signal.SIG_BLOCK, HANDLED_SIGNALS)
try:
termios.tcsetattr(input_fd, termios.TCSAFLUSH, old_termios)
os.write(output_fd, b"\n")
finally:
signal.pthread_sigmask(signal.SIG_SETMASK, previous_mask)
if owned_fd:
os.close(input_fd)
def _validate_child_stderr(stderr: bytes) -> None:
if len(stderr) > MAX_CHILD_STDERR_BYTES or b"\0" in stderr or b"\r" in stderr:
raise VaultContractError("invalid KeePass diagnostic")
try:
text = stderr.decode("utf-8", "strict")
except UnicodeDecodeError as exc:
raise VaultContractError("invalid KeePass diagnostic") from exc
if any(character not in "\t\n" and not character.isprintable() for character in text):
raise VaultContractError("invalid KeePass diagnostic")
def run_keepass(
arguments: Sequence[str],
master_buffer: bytearray,
private_home: str,
*,
webhook: RetainedWebhook | None = None,
timeout: float = DEFAULT_CHILD_TIMEOUT,
check: bool = True,
) -> KeepassResult:
if not arguments or any(not isinstance(value, str) or "\0" in value for value in arguments):
raise VaultContractError("invalid KeePass arguments")
if not os.path.isabs(private_home):
raise VaultContractError("private home must be absolute")
child_input = bytearray(master_buffer)
child_input.extend(b"\n")
if webhook is not None:
protected = bytearray(webhook.read_bytes())
try:
child_input.extend(protected)
child_input.extend(b"\n")
finally:
wipe_mutable_buffer(protected)
environment = {
"HOME": os.devnull,
"XDG_CONFIG_HOME": os.devnull,
"LC_ALL": "C.UTF-8",
"PATH": "/usr/bin:/bin",
}
expected_parent_pid = os.getpid()
def prepare_child() -> None:
os.umask(0o077)
arm_parent_death_signal(expected_parent_pid)
process = subprocess.Popen(
[KEEPASSXC_CLI, *arguments],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=environment,
close_fds=True,
start_new_session=True,
preexec_fn=prepare_child,
)
try:
try:
stdout, stderr = process.communicate(bytes(child_input), timeout=timeout)
except subprocess.TimeoutExpired as exc:
try:
os.killpg(process.pid, 15)
except ProcessLookupError:
pass
try:
process.communicate(timeout=1.0)
except subprocess.TimeoutExpired:
try:
os.killpg(process.pid, 9)
except ProcessLookupError:
pass
process.communicate()
raise VaultContractError("KeePass child timeout") from exc
if len(stdout) > MAX_CHILD_OUTPUT_BYTES:
raise VaultContractError("KeePass output is oversized")
_validate_child_stderr(stderr)
result = KeepassResult(process.returncode, stdout, stderr)
if check and process.returncode != 0:
raise VaultContractError("KeePass command failed")
return result
finally:
wipe_mutable_buffer(child_input)
def arm_parent_death_signal(expected_parent_pid: int) -> None:
"""Ensure a vault death cannot orphan a secret-bearing KeePass child."""
if expected_parent_pid <= 1:
raise VaultContractError("invalid expected parent PID")
libc = ctypes.CDLL(None, use_errno=True)
prctl = libc.prctl
rc = prctl(1, 9, 0, 0, 0) # PR_SET_PDEATHSIG, SIGKILL
if rc != 0:
err = ctypes.get_errno()
raise OSError(err, os.strerror(err))
if os.getppid() != expected_parent_pid:
os.kill(os.getpid(), 9)
def expected_notes(app_name: str) -> str:
_validate_app_name(app_name.encode("utf-8"))
return (
"channel=desktop-infra-전체;"
f"app={app_name};"
"recovery=revoke-and-reissue-in-slack"
)
def _entry_count(
database: str,
master: bytearray,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> tuple[int, set[str]]:
result = run_keepass(
["ls", "-q", "-R", "-f", database], master, private_home,
check=False, timeout=child_timeout,
)
if result.returncode != 0:
raise VaultContractError("KeePass database unlock failed")
try:
text = result.stdout.decode("utf-8", "strict")
except UnicodeDecodeError as exc:
raise VaultContractError("invalid KeePass listing") from exc
lines = text.splitlines()
exact = ENTRY_PATH.lstrip("/")
return sum(line == exact for line in lines), set(lines)
def _shown_attributes(
database: str,
master: bytearray,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> tuple[str, str, str, str]:
result = run_keepass(
[
"show", "-q", "-a", "Title", "-a", "UserName", "-a", "URL",
"-a", "Notes", database, ENTRY_PATH,
],
master,
private_home,
timeout=child_timeout,
)
try:
text = result.stdout.decode("utf-8", "strict")
except UnicodeDecodeError as exc:
raise VaultContractError("invalid KeePass attributes") from exc
lines = text.split("\n")
if len(lines) != 5 or lines[-1] != "" or any("\r" in line for line in lines[:-1]):
raise VaultContractError("invalid KeePass attribute framing")
return lines[0], lines[1], lines[2], lines[3]
def protected_value_matches(producer_rc: int, shown: bytes, expected: bytes) -> bool:
comparison = bytearray(expected)
comparison.extend(b"\n")
try:
return producer_rc == 0 and hmac.compare_digest(shown, bytes(comparison))
finally:
wipe_mutable_buffer(comparison)
def _password_matches(
database: str,
master: bytearray,
webhook: RetainedWebhook,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> bool:
result = run_keepass(
["show", "-q", "-s", "-a", "Password", database, ENTRY_PATH],
master,
private_home,
check=False,
timeout=child_timeout,
)
expected = bytearray(webhook.read_bytes())
shown = bytearray(result.stdout)
try:
return protected_value_matches(result.returncode, bytes(shown), bytes(expected))
finally:
wipe_mutable_buffer(expected)
wipe_mutable_buffer(shown)
def classify_database(
database: str,
master_buffer: bytearray,
webhook: RetainedWebhook,
app_name: str,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> str:
try:
count, _ = _entry_count(database, master_buffer, private_home, child_timeout)
if count == 0:
return "absent"
if count > 1:
return "ambiguous"
expected = (ENTRY_TITLE, ENTRY_USERNAME, ENTRY_URL, expected_notes(app_name))
if _shown_attributes(database, master_buffer, private_home, child_timeout) != expected:
return "mismatch"
if not _password_matches(
database, master_buffer, webhook, private_home, child_timeout
):
return "mismatch"
return "exact-noop"
except (OSError, VaultContractError):
return "failed"
def _copy_private_database(source: str, destination: str) -> None:
source_fd = os.open(source, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC)
destination_fd = -1
try:
_regular_metadata(source_fd)
destination_fd = os.open(
destination,
os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW | os.O_CLOEXEC,
0o600,
)
while True:
chunk = os.read(source_fd, 1024 * 1024)
if not chunk:
break
view = memoryview(chunk)
while view:
written = os.write(destination_fd, view)
view = view[written:]
os.fsync(destination_fd)
_regular_metadata(destination_fd)
finally:
if destination_fd >= 0:
os.close(destination_fd)
os.close(source_fd)
def _ensure_groups(
database: str,
master: bytearray,
private_home: str,
existing_lines: set[str],
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> None:
for group in ("/Platform", "/Platform/Observability", ENTRY_GROUP):
flattened = group.lstrip("/") + "/"
if flattened not in existing_lines:
run_keepass(
["mkdir", "-q", database, group], master, private_home,
timeout=child_timeout,
)
existing_lines.add(flattened)
def build_candidate(
baseline: str,
candidate: str,
mode: str,
master_buffer: bytearray,
webhook: RetainedWebhook,
app_name: str,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> None:
if mode not in {"ADD", "UPDATE"}:
raise VaultContractError("invalid candidate mode")
classification = classify_database(
baseline, master_buffer, webhook, app_name, private_home, child_timeout
)
if (mode == "ADD" and classification != "absent") or (
mode == "UPDATE" and classification != "mismatch"
):
raise VaultContractError("candidate mode does not match classification")
_copy_private_database(baseline, candidate)
try:
_, existing_lines = _entry_count(
candidate, master_buffer, private_home, child_timeout
)
notes = expected_notes(app_name)
if mode == "ADD":
_ensure_groups(
candidate, master_buffer, private_home, existing_lines, child_timeout
)
arguments = [
"add", "-q", "-p", "-u", ENTRY_USERNAME, "--url", ENTRY_URL,
"--notes", notes, candidate, ENTRY_PATH,
]
else:
arguments = [
"edit", "-q", "-p", "-t", ENTRY_TITLE, "-u", ENTRY_USERNAME,
"--url", ENTRY_URL, "--notes", notes, candidate, ENTRY_PATH,
]
run_keepass(
arguments, master_buffer, private_home, webhook=webhook,
timeout=child_timeout,
)
if not verify_database(
candidate, master_buffer, webhook, app_name, private_home, child_timeout
):
raise VaultContractError("candidate verification failed")
except BaseException:
try:
os.unlink(candidate)
except OSError:
pass
raise
def verify_database(
database: str,
master_buffer: bytearray,
webhook: RetainedWebhook,
app_name: str,
private_home: str,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
) -> bool:
return classify_database(
database, master_buffer, webhook, app_name, private_home, child_timeout
) == "exact-noop"
def _attest_runtime_root(runtime_root: str) -> str:
if not os.path.isabs(runtime_root):
raise VaultContractError("absolute runtime root required")
canonical = os.path.realpath(runtime_root)
if canonical != os.path.abspath(runtime_root):
raise VaultContractError("runtime root must be canonical")
expected_parent = f"/run/user/{os.getuid()}"
if os.path.commonpath((canonical, expected_parent)) != expected_parent or canonical == expected_parent:
raise VaultContractError("runtime root is outside the private runtime")
fd = os.open(canonical, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC)
try:
info = os.fstat(fd)
if info.st_uid != os.getuid() or stat.S_IMODE(info.st_mode) != 0o700:
raise VaultContractError("runtime root metadata mismatch")
finally:
os.close(fd)
mount_type = None
best_length = -1
with open("/proc/self/mountinfo", "r", encoding="utf-8") as stream:
for line in stream:
left, right = line.rstrip("\n").split(" - ", 1)
mount_point = left.split()[4].replace("\\040", " ")
if canonical == mount_point or canonical.startswith(mount_point.rstrip("/") + "/"):
if len(mount_point) > best_length:
mount_type = right.split()[0]
best_length = len(mount_point)
if mount_type != "tmpfs":
raise VaultContractError("runtime root is not on tmpfs")
return canonical
def _retained_runtime_path(runtime_fd: int) -> str:
info = os.fstat(runtime_fd)
if not stat.S_ISDIR(info.st_mode):
raise VaultContractError("retained runtime is not a directory")
proc_path = f"/proc/{os.getpid()}/fd/{runtime_fd}"
link_info = os.lstat(proc_path)
if not stat.S_ISLNK(link_info.st_mode):
raise VaultContractError("retained runtime proc anchor is unavailable")
resolved = os.stat(proc_path)
if (resolved.st_dev, resolved.st_ino) != (info.st_dev, info.st_ino):
raise VaultContractError("retained runtime proc anchor mismatch")
return proc_path
def _open_snapshot_below_runtime(
path: str, runtime_root: str, runtime_fd: int
) -> int:
if not os.path.isabs(path) or os.path.normpath(path) != path:
raise VaultProtocolError("snapshot must be canonical")
relative = os.path.relpath(path, runtime_root)
parts = relative.split(os.sep)
if not parts or any(
not part or part in {".", ".."} or os.sep in part for part in parts
):
raise VaultProtocolError("snapshot is outside runtime root")
current_fd = os.dup(runtime_fd)
try:
for part in parts[:-1]:
next_fd = os.open(
part,
os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC,
dir_fd=current_fd,
)
os.close(current_fd)
current_fd = next_fd
directory_info = os.fstat(current_fd)
if (
directory_info.st_uid != os.getuid()
or stat.S_IMODE(directory_info.st_mode) != 0o700
):
raise VaultProtocolError("snapshot directory metadata mismatch")
return os.open(
parts[-1],
os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC,
dir_fd=current_fd,
)
finally:
os.close(current_fd)
def _attest_snapshot(
path: str, runtime_root: str, runtime_fd: int | None = None
) -> None:
if runtime_fd is not None:
fd = _open_attested_snapshot(path, runtime_root, runtime_fd)
try:
return
finally:
os.close(fd)
if not os.path.isabs(path) or os.path.realpath(path) != os.path.abspath(path):
raise VaultProtocolError("snapshot must be canonical")
if os.path.commonpath((path, runtime_root)) != runtime_root or path == runtime_root:
raise VaultProtocolError("snapshot is outside runtime root")
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC)
try:
_regular_metadata(fd)
finally:
os.close(fd)
def _open_attested_snapshot(path: str, runtime_root: str, runtime_fd: int) -> int:
fd = _open_snapshot_below_runtime(path, runtime_root, runtime_fd)
try:
_regular_metadata(fd)
return fd
except BaseException:
os.close(fd)
raise
def read_protocol_line(connection: socket.socket, *, timeout: float) -> str:
connection.settimeout(timeout)
data = bytearray()
try:
while len(data) <= MAX_PROTOCOL_BYTES:
chunk = connection.recv(MAX_PROTOCOL_BYTES + 2 - len(data))
if not chunk:
raise VaultProtocolError("partial protocol EOF")
data.extend(chunk)
if b"\n" in data:
break
if len(data) > MAX_PROTOCOL_BYTES + 1:
raise VaultProtocolError("protocol request is oversized")
if not data.endswith(b"\n") or data.count(b"\n") != 1:
raise VaultProtocolError("invalid protocol framing")
line = data[:-1]
if b"\r" in line or b"\0" in line:
raise VaultProtocolError("invalid protocol bytes")
try:
return line.decode("utf-8", "strict")
except UnicodeDecodeError as exc:
raise VaultProtocolError("invalid protocol UTF-8") from exc
finally:
wipe_mutable_buffer(data)
def parse_protocol_command(line: str) -> tuple[str, str | None]:
if line in {"PREPARE", "VERIFY_CANDIDATE", "SHUTDOWN"}:
return line, None
if line in {"BUILD ADD", "BUILD UPDATE"}:
return "BUILD", line.split(" ", 1)[1]
prefix = "VERIFY_COMMITTED "
if line.startswith(prefix):
path = line[len(prefix):]
if not path or " " in path or not os.path.isabs(path):
raise VaultProtocolError("invalid committed snapshot command")
return "VERIFY_COMMITTED", path
raise VaultProtocolError("unknown protocol command")
def peer_uid(connection: socket.socket) -> int:
credentials = connection.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, 12)
_, uid, _ = struct.unpack("3i", credentials)
return uid
def _write_reply(connection: socket.socket, reply: str, timeout: float) -> None:
payload = reply.encode("ascii") + b"\n"
connection.settimeout(timeout)
connection.sendall(payload)
def serve_private_socket(
runtime_root: str,
webhook_file: str,
*,
input_fd: int | None = None,
output_fd: int | None = None,
accept_timeout: float = DEFAULT_ACCEPT_TIMEOUT,
io_timeout: float = DEFAULT_IO_TIMEOUT,
child_timeout: float = DEFAULT_CHILD_TIMEOUT,
wipe_hook: Callable[[bytearray], None] | None = None,
) -> None:
if child_timeout <= 0:
raise VaultContractError("invalid child timeout")
runtime = ""
runtime_fd = -1
runtime_access = ""
webhook = None
master = bytearray()
listener = None
socket_identity = None
try:
runtime = _attest_runtime_root(runtime_root)
runtime_fd = os.open(
runtime, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC
)
runtime_access = _retained_runtime_path(runtime_fd)
webhook = open_validated_webhook(webhook_file)
listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socket_path = os.path.join(runtime_access, SOCKET_NAME)
private_home = os.devnull
baseline = os.path.join(runtime_access, BASELINE_NAME)
candidate = os.path.join(runtime_access, CANDIDATE_NAME)
app_name, master = read_operator_secrets(input_fd=input_fd, output_fd=output_fd)
previous_mask = signal.pthread_sigmask(signal.SIG_BLOCK, HANDLED_SIGNALS)
try:
listener.bind(socket_path)
os.chmod(SOCKET_NAME, 0o600, dir_fd=runtime_fd, follow_symlinks=False)
socket_info = os.stat(
SOCKET_NAME, dir_fd=runtime_fd, follow_symlinks=False
)
socket_identity = (socket_info.st_dev, socket_info.st_ino)
listener.listen(1)
finally:
signal.pthread_sigmask(signal.SIG_SETMASK, previous_mask)
listener.settimeout(accept_timeout)
connection, _ = listener.accept()
listener.close()
if peer_uid(connection) != os.getuid():
connection.close()
raise VaultContractError("socket peer UID mismatch")
state = "initial"
classification = None
with connection:
while True:
try:
command, argument = parse_protocol_command(
read_protocol_line(connection, timeout=io_timeout)
)
if command == "PREPARE":
if state != "initial":
raise VaultProtocolError("PREPARE out of state")
classification = classify_database(
baseline, master, webhook, app_name, private_home,
child_timeout,
)
state = "prepared"
_write_reply(connection, classification, io_timeout)
elif command == "BUILD":
if state != "prepared" or (
argument == "ADD" and classification != "absent"
) or (
argument == "UPDATE" and classification != "mismatch"
):
raise VaultProtocolError("BUILD out of state")
try:
build_candidate(
baseline, candidate, argument or "", master,
webhook, app_name, private_home, child_timeout,
)
except (OSError, VaultContractError):
_write_reply(connection, "failed", io_timeout)
continue
state = "candidate-ready"
_write_reply(connection, "candidate-ready", io_timeout)
elif command == "VERIFY_CANDIDATE":
if state != "candidate-ready":
raise VaultProtocolError("VERIFY_CANDIDATE out of state")
verified = verify_database(
candidate, master, webhook, app_name, private_home,
child_timeout,
)
if verified:
state = "candidate-verified"
_write_reply(connection, "verified" if verified else "failed", io_timeout)
elif command == "VERIFY_COMMITTED":
if state != "candidate-verified" or argument is None:
raise VaultProtocolError("VERIFY_COMMITTED out of state")
snapshot_fd = _open_attested_snapshot(
argument, runtime, runtime_fd
)
try:
verified = verify_database(
f"/proc/{os.getpid()}/fd/{snapshot_fd}",
master,
webhook,
app_name,
private_home,
child_timeout,
)
finally:
os.close(snapshot_fd)
_write_reply(connection, "verified" if verified else "failed", io_timeout)
elif command == "SHUTDOWN":
_write_reply(connection, "stopped", io_timeout)
break
except VaultProtocolError:
try:
_write_reply(connection, "failed", io_timeout)
except OSError:
pass
raise
finally:
try:
if listener is not None:
listener.close()
finally:
try:
if webhook is not None:
webhook.close()
finally:
try:
wipe_mutable_buffer(master)
if wipe_hook is not None:
wipe_hook(master)
finally:
try:
if socket_identity is not None:
try:
current = os.stat(
SOCKET_NAME, dir_fd=runtime_fd,
follow_symlinks=False,
)
if (current.st_dev, current.st_ino) == socket_identity:
os.unlink(SOCKET_NAME, dir_fd=runtime_fd)
except FileNotFoundError:
pass
finally:
if runtime_fd >= 0:
os.close(runtime_fd)
def _main(argv: Sequence[str]) -> int:
if len(argv) != 5 or argv[0] != "--serve" or argv[1] != "--runtime-root" or argv[3] != "--webhook-file":
return 2
runtime_root, webhook_file = argv[2], argv[4]
if not os.path.isabs(runtime_root) or not os.path.isabs(webhook_file):
return 2
install_process_signal_handlers()
try:
serve_private_socket(runtime_root, webhook_file)
return 0
except VaultSignal as exc:
return 128 + exc.signum
except (OSError, VaultContractError):
os.write(2, b"vault helper failed\n")
return 1
if __name__ == "__main__":
raise SystemExit(_main(sys.argv[1:]))