Skip to content

Commit 3368a1f

Browse files
committed
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
1 parent d17ff97 commit 3368a1f

File tree

7 files changed

+172
-2
lines changed

7 files changed

+172
-2
lines changed

bob-common/mkosi.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Packages=podman
2828
openssh-sftp-server
2929
udev
3030
libsnappy1v5
31+
pv
3132

3233
BuildPackages=build-essential
3334
git
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[Unit]
2+
Description=Searcher Input FIFO Setup
3+
After=persistent-mount.service
4+
Requires=persistent-mount.service
5+
Before=searcher-container.service
6+
7+
[Service]
8+
Type=oneshot
9+
ExecStart=/usr/bin/setup-input-fifo.sh
10+
RemainAfterExit=yes
11+
User=root
12+
Group=root
13+
14+
[Install]
15+
WantedBy=minimal.target
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/bash
2+
# Safely pipes authenticated data from searcher into container
3+
# Security-hardened version with strict FIFO validation
4+
# Supports continuous streaming without timeout
5+
set -eu -o pipefail
6+
7+
FIFO_PATH="/persistent/input/data.fifo"
8+
LOG_DIR="/persistent/delayed_logs"
9+
LOG_FILE="$LOG_DIR/output.log"
10+
MAX_RATE="100M" # 100MB/s rate limit to prevent accidental DoS
11+
12+
# Create log directory if it doesn't exist
13+
mkdir -p "$LOG_DIR"
14+
15+
log_error() {
16+
echo "[$(date -Iseconds)] feed-data ERROR: $1" >> "$LOG_FILE"
17+
echo "Error: $1" >&2
18+
}
19+
20+
log_info() {
21+
echo "[$(date -Iseconds)] feed-data INFO: $1" >> "$LOG_FILE"
22+
}
23+
24+
# Check system is initialized
25+
if [ ! -f /etc/searcher-network.state ]; then
26+
log_error "System not initialized. Run 'initialize' first."
27+
exit 1
28+
fi
29+
30+
# CRITICAL SECURITY VALIDATION: Verify FIFO exists and is not a symlink
31+
if [ -L "$FIFO_PATH" ]; then
32+
log_error "SECURITY VIOLATION: $FIFO_PATH is a symlink!"
33+
exit 1
34+
fi
35+
36+
if [ ! -e "$FIFO_PATH" ]; then
37+
log_error "Input FIFO does not exist at $FIFO_PATH."
38+
exit 1
39+
fi
40+
41+
if [ ! -p "$FIFO_PATH" ]; then
42+
log_error "SECURITY VIOLATION: $FIFO_PATH is not a FIFO!"
43+
exit 1
44+
fi
45+
46+
# Security: Verify ownership MUST be root (no exceptions in production)
47+
FIFO_OWNER=$(stat -c %u "$FIFO_PATH")
48+
if [ "$FIFO_OWNER" -ne 0 ]; then
49+
log_error "SECURITY VIOLATION: FIFO not owned by root!"
50+
exit 1
51+
fi
52+
53+
# Verify we can write to the FIFO
54+
if [ ! -w "$FIFO_PATH" ]; then
55+
log_error "Cannot write to FIFO at $FIFO_PATH."
56+
exit 1
57+
fi
58+
59+
# Log start of data feed
60+
log_info "Starting secure data feed to container (continuous stream)"
61+
62+
# Rate limit and pipe stdin to FIFO
63+
# No timeout - this is a continuous stream that runs until:
64+
# - The sender closes the connection (EOF)
65+
# - The container stops reading (SIGPIPE)
66+
# - The SSH connection drops
67+
if pv -q -L "$MAX_RATE" > "$FIFO_PATH" 2>/dev/null; then
68+
log_info "Data feed completed successfully"
69+
exit 0
70+
else
71+
EXIT_CODE=$?
72+
if [ $EXIT_CODE -eq 141 ]; then
73+
log_info "Container disconnected (SIGPIPE)"
74+
else
75+
log_error "Feed terminated with exit code $EXIT_CODE"
76+
fi
77+
exit $EXIT_CODE
78+
fi

bob-common/mkosi.extra/usr/bin/init-container.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ su -s /bin/sh searcher -c "cd ~ && podman run -d \
2727
-v /persistent/searcher:/persistent:rw \
2828
-v /etc/searcher/ssh_hostkey:/etc/searcher/ssh_hostkey:rw \
2929
-v /persistent/searcher_logs:/var/log/searcher:rw \
30+
-v /persistent/input:/persistent/input:ro \
3031
-v /etc/searcher-logrotate.conf:/tmp/searcher.conf:ro \
3132
$BOB_SEARCHER_EXTRA_PODMAN_FLAGS \
3233
docker.io/library/ubuntu:24.04 \
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
# Sets up the input FIFO for searcher data feed with hardened security
3+
# MUST run as root to ensure proper ownership and prevent tampering
4+
set -eu -o pipefail
5+
6+
# Use /persistent/input to match existing /persistent/searcher pattern
7+
INPUT_DIR="/persistent/input"
8+
FIFO_PATH="$INPUT_DIR/data.fifo"
9+
10+
echo "Setting up hardened searcher input FIFO..."
11+
12+
# Ensure we're running as root for security
13+
if [ "$EUID" -ne 0 ]; then
14+
echo "ERROR: This script must be run as root for security reasons" >&2
15+
exit 1
16+
fi
17+
18+
# Create directory on persistent storage with secure ownership
19+
mkdir -p "$INPUT_DIR"
20+
21+
# Set directory ownership to root with read/execute for searcher group
22+
# Directory must be root-owned to prevent tampering
23+
chown root:root "$INPUT_DIR"
24+
chmod 755 "$INPUT_DIR" # rwxr-xr-x - searcher can traverse but not modify
25+
26+
# Remove any existing FIFO/symlink (security check)
27+
if [ -e "$FIFO_PATH" ] || [ -L "$FIFO_PATH" ]; then
28+
echo "Removing existing file at $FIFO_PATH for security..."
29+
rm -f "$FIFO_PATH"
30+
fi
31+
32+
# Create FIFO with secure permissions
33+
mkfifo "$FIFO_PATH"
34+
echo "Created FIFO at $FIFO_PATH"
35+
36+
# Set FIFO ownership: root owns it, searcher group can read
37+
# This prevents the container from modifying/replacing the FIFO
38+
chown root:1000 "$FIFO_PATH" # root:searcher
39+
chmod 640 "$FIFO_PATH" # rw-r----- (root write, group read, others none)
40+
41+
# Verify the FIFO was created correctly (security validation)
42+
if [ ! -p "$FIFO_PATH" ]; then
43+
echo "ERROR: Failed to create FIFO at $FIFO_PATH" >&2
44+
exit 1
45+
fi
46+
47+
# Verify no symlinks (extra security check)
48+
if [ -L "$FIFO_PATH" ]; then
49+
echo "ERROR: Security violation - FIFO is a symlink!" >&2
50+
exit 1
51+
fi
52+
53+
echo "Hardened searcher input FIFO ready at $FIFO_PATH"
54+
echo "Security features enabled:"
55+
echo " - Root-owned directory (prevents tampering)"
56+
echo " - Root-owned FIFO (prevents replacement)"
57+
echo " - Read-only mount in container (prevents modification)"
58+
echo " - Group read permission only (searcher UID 1000)"
59+
echo ""
60+
echo "Container will access it READ-ONLY at /persistent/input/data.fifo"
61+
echo "Usage: cat data.json | ssh searcher@host feed-data"
62+
exit 0

bob-common/mkosi.postinst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ for service in \
2727
wait-for-key.service \
2828
searcher-firewall.service \
2929
dropbear.service \
30+
searcher-input-fifo.service \
3031
searcher-container.service \
3132
ssh-pubkey-server.service \
3233
cvm-reverse-proxy.service

bob-common/searchersh.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ int main(int argc, char *argv[]) {
6666
if (command == NULL) {
6767
// If there's no token at all (e.g., empty or whitespace-only string),
6868
// we print an error and quit.
69-
fprintf(stderr, "No command provided. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, reboot [force], initialize\n");
69+
fprintf(stderr, "No command provided. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, feed-data, reboot [force], initialize\n");
7070
free(arg_copy); // free the memory
7171
return 1; // return error code 1
7272
}
@@ -186,6 +186,18 @@ int main(int argc, char *argv[]) {
186186
return 1;
187187
}
188188

189+
// If command == "feed-data", pipe stdin to container's input FIFO
190+
else if (strcmp(command, "feed-data") == 0) {
191+
// Pipes authenticated data from searcher into container
192+
// Security: SSH authenticates searcher, sudo escalates to write to root-owned FIFO
193+
// FIFO is root-owned (only root can write) and mounted read-only in container
194+
execl("/usr/bin/sudo", "sudo", "/usr/bin/feed-data-helper", "feed-data-helper", NULL);
195+
196+
perror("execl failed (feed-data)");
197+
free(arg_copy);
198+
return 1;
199+
}
200+
189201
// If command == "reboot", reboot the host machine using graceful shutdown wrapper
190202
else if (strcmp(command, "reboot") == 0) {
191203
// Check if force flag is provided
@@ -201,7 +213,7 @@ int main(int argc, char *argv[]) {
201213
}
202214

203215
// If we reach here, the command didn't match any of the valid commands
204-
fprintf(stderr, "Invalid command. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, reboot [force], initialize\n");
216+
fprintf(stderr, "Invalid command. Valid commands are: toggle, status, logs, tail-the-logs, restart-lighthouse, feed-data, reboot [force], initialize\n");
205217
free(arg_copy); // Clean up allocated memory
206218
return 1; // Return error code 1
207219
}

0 commit comments

Comments
 (0)