From 4b0b29a898495200ea1c9a70f266048f508107e3 Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Mon, 19 Jan 2026 13:15:37 +0000 Subject: [PATCH 1/2] Add secure input channel for searcher data streaming Implement a hardened FIFO-based data feed system that allows authenticated searchers to stream data directly into containers with security controls: - Root-owned FIFO prevents tampering and symlink attacks - Rate limiting (100MB/s) prevents accidental DoS - Read-only container mount prevents modification - Continuous streaming support without timeouts - SSH authentication + sudo escalation for secure access --- bob-common/mkosi.conf | 1 + .../mkosi.extra/etc/sudoers.d/99-searcher | 2 +- .../system/searcher-input-fifo.service | 15 ++++ .../mkosi.extra/usr/bin/feed-data-helper | 78 +++++++++++++++++++ .../mkosi.extra/usr/bin/init-container.sh | 1 + .../mkosi.extra/usr/bin/setup-input-fifo.sh | 62 +++++++++++++++ bob-common/mkosi.postinst | 1 + bob-common/searchersh.c | 16 +++- 8 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 bob-common/mkosi.extra/etc/systemd/system/searcher-input-fifo.service create mode 100644 bob-common/mkosi.extra/usr/bin/feed-data-helper create mode 100644 bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh diff --git a/bob-common/mkosi.conf b/bob-common/mkosi.conf index c1d8f8c9..4abc46be 100644 --- a/bob-common/mkosi.conf +++ b/bob-common/mkosi.conf @@ -28,6 +28,7 @@ Packages=podman openssh-sftp-server udev libsnappy1v5 + pv BuildPackages=build-essential git diff --git a/bob-common/mkosi.extra/etc/sudoers.d/99-searcher b/bob-common/mkosi.extra/etc/sudoers.d/99-searcher index 2b83e142..a226c1c9 100644 --- a/bob-common/mkosi.extra/etc/sudoers.d/99-searcher +++ b/bob-common/mkosi.extra/etc/sudoers.d/99-searcher @@ -1 +1 @@ -searcher ALL=(root) NOPASSWD: /usr/bin/toggle, /usr/bin/tdx-init set-passphrase, /usr/bin/systemctl restart lighthouse, /usr/local/bin/reboot +searcher ALL=(root) NOPASSWD: /usr/bin/toggle, /usr/bin/tdx-init set-passphrase, /usr/bin/systemctl restart lighthouse, /usr/local/bin/reboot, /usr/bin/feed-data-helper diff --git a/bob-common/mkosi.extra/etc/systemd/system/searcher-input-fifo.service b/bob-common/mkosi.extra/etc/systemd/system/searcher-input-fifo.service new file mode 100644 index 00000000..85f66d9a --- /dev/null +++ b/bob-common/mkosi.extra/etc/systemd/system/searcher-input-fifo.service @@ -0,0 +1,15 @@ +[Unit] +Description=Searcher Input FIFO Setup +After=persistent-mount.service +Requires=persistent-mount.service +Before=searcher-container.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/setup-input-fifo.sh +RemainAfterExit=yes +User=root +Group=root + +[Install] +WantedBy=minimal.target diff --git a/bob-common/mkosi.extra/usr/bin/feed-data-helper b/bob-common/mkosi.extra/usr/bin/feed-data-helper new file mode 100644 index 00000000..123c6de2 --- /dev/null +++ b/bob-common/mkosi.extra/usr/bin/feed-data-helper @@ -0,0 +1,78 @@ +#!/bin/bash +# Safely pipes authenticated data from searcher into container +# Security-hardened version with strict FIFO validation +# Supports continuous streaming without timeout +set -eu -o pipefail + +FIFO_PATH="/persistent/input/data.fifo" +LOG_DIR="/persistent/delayed_logs" +LOG_FILE="$LOG_DIR/output.log" +MAX_RATE="100M" # 100MB/s rate limit to prevent accidental DoS + +# Create log directory if it doesn't exist +mkdir -p "$LOG_DIR" + +log_error() { + echo "[$(date -Iseconds)] feed-data ERROR: $1" >> "$LOG_FILE" + echo "Error: $1" >&2 +} + +log_info() { + echo "[$(date -Iseconds)] feed-data INFO: $1" >> "$LOG_FILE" +} + +# Check system is initialized +if [ ! -f /etc/searcher-network.state ]; then + log_error "System not initialized. Run 'initialize' first." + exit 1 +fi + +# CRITICAL SECURITY VALIDATION: Verify FIFO exists and is not a symlink +if [ -L "$FIFO_PATH" ]; then + log_error "SECURITY VIOLATION: $FIFO_PATH is a symlink!" + exit 1 +fi + +if [ ! -e "$FIFO_PATH" ]; then + log_error "Input FIFO does not exist at $FIFO_PATH." + exit 1 +fi + +if [ ! -p "$FIFO_PATH" ]; then + log_error "SECURITY VIOLATION: $FIFO_PATH is not a FIFO!" + exit 1 +fi + +# Security: Verify ownership MUST be root (no exceptions in production) +FIFO_OWNER=$(stat -c %u "$FIFO_PATH") +if [ "$FIFO_OWNER" -ne 0 ]; then + log_error "SECURITY VIOLATION: FIFO not owned by root!" + exit 1 +fi + +# Verify we can write to the FIFO +if [ ! -w "$FIFO_PATH" ]; then + log_error "Cannot write to FIFO at $FIFO_PATH." + exit 1 +fi + +# Log start of data feed +log_info "Starting secure data feed to container (continuous stream)" + +# Rate limit and pipe stdin to FIFO +# No timeout - this is a continuous stream that runs until: +# - The sender closes the connection (EOF) +# - The container stops reading (SIGPIPE) +# - The SSH connection drops +if pv -q -L "$MAX_RATE" > "$FIFO_PATH" 2>/dev/null; then + log_info "Data feed completed successfully" + exit 0 +else + EXIT_CODE=$? + if [ $EXIT_CODE -eq 141 ]; then + log_info "Container disconnected (SIGPIPE)" + else + log_error "Feed terminated with exit code $EXIT_CODE" + fi + exit $EXIT_CODE +fi diff --git a/bob-common/mkosi.extra/usr/bin/init-container.sh b/bob-common/mkosi.extra/usr/bin/init-container.sh index 5375ace7..140f7782 100755 --- a/bob-common/mkosi.extra/usr/bin/init-container.sh +++ b/bob-common/mkosi.extra/usr/bin/init-container.sh @@ -27,6 +27,7 @@ su -s /bin/sh searcher -c "cd ~ && podman run -d \ -v /persistent/searcher:/persistent:rw \ -v /etc/searcher/ssh_hostkey:/etc/searcher/ssh_hostkey:rw \ -v /persistent/searcher_logs:/var/log/searcher:rw \ + -v /persistent/input:/persistent/input:ro \ -v /etc/searcher-logrotate.conf:/tmp/searcher.conf:ro \ $BOB_SEARCHER_EXTRA_PODMAN_FLAGS \ docker.io/library/ubuntu:24.04 \ diff --git a/bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh b/bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh new file mode 100644 index 00000000..73a1236b --- /dev/null +++ b/bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Sets up the input FIFO for searcher data feed with hardened security +# MUST run as root to ensure proper ownership and prevent tampering +set -eu -o pipefail + +# Use /persistent/input to match existing /persistent/searcher pattern +INPUT_DIR="/persistent/input" +FIFO_PATH="$INPUT_DIR/data.fifo" + +echo "Setting up hardened searcher input FIFO..." + +# Ensure we're running as root for security +if [ "$EUID" -ne 0 ]; then + echo "ERROR: This script must be run as root for security reasons" >&2 + exit 1 +fi + +# Create directory on persistent storage with secure ownership +mkdir -p "$INPUT_DIR" + +# Set directory ownership to root with read/execute for searcher group +# Directory must be root-owned to prevent tampering +chown root:root "$INPUT_DIR" +chmod 755 "$INPUT_DIR" # rwxr-xr-x - searcher can traverse but not modify + +# Remove any existing FIFO/symlink (security check) +if [ -e "$FIFO_PATH" ] || [ -L "$FIFO_PATH" ]; then + echo "Removing existing file at $FIFO_PATH for security..." + rm -f "$FIFO_PATH" +fi + +# Create FIFO with secure permissions +mkfifo "$FIFO_PATH" +echo "Created FIFO at $FIFO_PATH" + +# Set FIFO ownership: root owns it, searcher group can read +# This prevents the container from modifying/replacing the FIFO +chown root:1000 "$FIFO_PATH" # root:searcher +chmod 640 "$FIFO_PATH" # rw-r----- (root write, group read, others none) + +# Verify the FIFO was created correctly (security validation) +if [ ! -p "$FIFO_PATH" ]; then + echo "ERROR: Failed to create FIFO at $FIFO_PATH" >&2 + exit 1 +fi + +# Verify no symlinks (extra security check) +if [ -L "$FIFO_PATH" ]; then + echo "ERROR: Security violation - FIFO is a symlink!" >&2 + exit 1 +fi + +echo "Hardened searcher input FIFO ready at $FIFO_PATH" +echo "Security features enabled:" +echo " - Root-owned directory (prevents tampering)" +echo " - Root-owned FIFO (prevents replacement)" +echo " - Read-only mount in container (prevents modification)" +echo " - Group read permission only (searcher UID 1000)" +echo "" +echo "Container will access it READ-ONLY at /persistent/input/data.fifo" +echo "Usage: cat data.json | ssh searcher@host feed-data" +exit 0 diff --git a/bob-common/mkosi.postinst b/bob-common/mkosi.postinst index 227beea5..0bc0568f 100755 --- a/bob-common/mkosi.postinst +++ b/bob-common/mkosi.postinst @@ -27,6 +27,7 @@ for service in \ wait-for-key.service \ searcher-firewall.service \ dropbear.service \ + searcher-input-fifo.service \ searcher-container.service \ ssh-pubkey-server.service \ cvm-reverse-proxy.service diff --git a/bob-common/searchersh.c b/bob-common/searchersh.c index 25fe78d5..7e4fc26f 100644 --- a/bob-common/searchersh.c +++ b/bob-common/searchersh.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { if (command == NULL) { // If there's no token at all (e.g., empty or whitespace-only string), // we print an error and quit. - fprintf(stderr, "No command provided. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, reboot [force], initialize\n"); + fprintf(stderr, "No command provided. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, feed-data, reboot [force], initialize\n"); free(arg_copy); // free the memory return 1; // return error code 1 } @@ -186,6 +186,18 @@ int main(int argc, char *argv[]) { return 1; } + // If command == "feed-data", pipe stdin to container's input FIFO + else if (strcmp(command, "feed-data") == 0) { + // Pipes authenticated data from searcher into container + // Security: SSH authenticates searcher, sudo escalates to write to root-owned FIFO + // FIFO is root-owned (only root can write) and mounted read-only in container + execl("/usr/bin/sudo", "sudo", "/usr/bin/feed-data-helper", "feed-data-helper", NULL); + + perror("execl failed (feed-data)"); + free(arg_copy); + return 1; + } + // If command == "reboot", reboot the host machine using graceful shutdown wrapper else if (strcmp(command, "reboot") == 0) { // Check if force flag is provided @@ -201,7 +213,7 @@ int main(int argc, char *argv[]) { } // If we reach here, the command didn't match any of the valid commands - fprintf(stderr, "Invalid command. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, reboot [force], initialize\n"); + fprintf(stderr, "Invalid command. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, feed-data, reboot [force], initialize\n"); free(arg_copy); // Clean up allocated memory return 1; // Return error code 1 } From eed63e57ad1ee6f38e970a9932bccd2f9c6c679c Mon Sep 17 00:00:00 2001 From: MoeMahhouk Date: Tue, 20 Jan 2026 11:18:28 +0000 Subject: [PATCH 2/2] fix execution permissions --- bob-common/mkosi.extra/usr/bin/feed-data-helper | 0 bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bob-common/mkosi.extra/usr/bin/feed-data-helper mode change 100644 => 100755 bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh diff --git a/bob-common/mkosi.extra/usr/bin/feed-data-helper b/bob-common/mkosi.extra/usr/bin/feed-data-helper old mode 100644 new mode 100755 diff --git a/bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh b/bob-common/mkosi.extra/usr/bin/setup-input-fifo.sh old mode 100644 new mode 100755