diff --git a/Cargo.lock b/Cargo.lock index 3f3b10e..4f0789f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2456,7 +2456,7 @@ dependencies = [ [[package]] name = "webterm" -version = "0.3.1" +version = "0.3.2" dependencies = [ "actix", "actix-files", diff --git a/Cargo.toml b/Cargo.toml index 43baf0c..149f681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ documentation = "https://docs.rs/webterm" readme = "README.md" categories = ["web-programming", "web-programming::websocket", "web-programming::http-server", "command-line-utilities"] keywords = ["terminal", "xterm", "websocket", "terminus", "console"] -version = "0.3.1" +version = "0.3.2" authors = ["fabian.freyer@physik.tu-berlin.de","jasonpwitty+socktop@proton.me"] edition = "2021" license = "BSD-3-Clause" diff --git a/Dockerfile b/Dockerfile index 419dfc3..6e60efe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -109,7 +109,8 @@ COPY --from=node-builder /build/node_modules ./node_modules # Copy runtime scripts COPY docker/entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh +COPY docker/init-config.sh /init-config.sh +RUN chmod +x /entrypoint.sh /init-config.sh # Expose ports # 8082 - webterm HTTP server @@ -120,11 +121,8 @@ EXPOSE 8082 3001 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8082/ || exit 1 -# Run as socktop user -USER socktop +# Set entrypoint (init-config.sh runs as root, copies configs, then switches to socktop user) +ENTRYPOINT ["/init-config.sh"] -# Set entrypoint -ENTRYPOINT ["/entrypoint.sh"] - -# Default command - run webterm server -CMD ["webterm-server", "--host", "0.0.0.0", "--port", "8082"] +# Default command - pass through init-config.sh to entrypoint.sh to webterm-server +CMD ["/entrypoint.sh", "webterm-server", "--host", "0.0.0.0", "--port", "8082"] diff --git a/docker/CONFIG-INIT-README.md b/docker/CONFIG-INIT-README.md new file mode 100644 index 0000000..f99b861 --- /dev/null +++ b/docker/CONFIG-INIT-README.md @@ -0,0 +1,197 @@ +# Configuration Initialization Fix + +## Problem + +The socktop package (installed via APT) sets the `socktop` user's HOME directory to `/var/lib/socktop`, but Kubernetes ConfigMaps and Secrets mount files to `/home/socktop/`. This caused profiles and certificates to not be found by socktop. + +## Solution + +A two-stage initialization process using `init-config.sh`: + +### Stage 1: Copy and Transform (`init-config.sh`) +Runs as **root** at container startup: +1. Detects the actual HOME directory of the socktop user +2. Copies configuration files from mounted locations to actual HOME +3. Rewrites paths in `profiles.json` to use correct HOME directory +4. Sets proper ownership +5. Switches to socktop user and executes the main entrypoint + +### Stage 2: Validation (`entrypoint.sh`) +Runs as **socktop user**: +1. Validates configuration files are present +2. Starts the webterm server + +## File Locations + +### Kubernetes Mounts +``` +/home/socktop/.config/socktop/profiles.json (ConfigMap) +/home/socktop/.config/alacritty/alacritty.toml (ConfigMap) +/home/socktop/.config/alacritty/catppuccin-frappe.toml (ConfigMap) +/home/socktop/.config/socktop/certs/*.pem (Secret) +``` + +### Actual Locations (after init-config.sh) +``` +/var/lib/socktop/.config/socktop/profiles.json +/var/lib/socktop/.config/alacritty/alacritty.toml +/var/lib/socktop/.config/alacritty/catppuccin-frappe.toml +/var/lib/socktop/.config/socktop/certs/*.pem +``` + +## Path Rewriting + +The `init-config.sh` script automatically rewrites paths in `profiles.json`: + +**Before:** +```json +{ + "tls_ca": "/home/socktop/.config/socktop/rpi-master.pem" +} +``` + +**After:** +```json +{ + "tls_ca": "/var/lib/socktop/.config/socktop/certs/rpi-master.pem" +} +``` + +This ensures certificate paths point to the correct location in the actual HOME directory. + +## Dockerfile Changes + +```dockerfile +# Copy both init and entrypoint scripts +COPY docker/entrypoint.sh /entrypoint.sh +COPY docker/init-config.sh /init-config.sh +RUN chmod +x /entrypoint.sh /init-config.sh + +# init-config.sh runs as root, copies configs, then switches to socktop user +ENTRYPOINT ["/init-config.sh"] + +# Pass through init-config.sh -> entrypoint.sh -> webterm-server +CMD ["/entrypoint.sh", "webterm-server", "--host", "0.0.0.0", "--port", "8082"] +``` + +## Execution Flow + +``` +Container Start (as root) + ↓ +[init-config.sh] + ├─ Detect HOME directory + ├─ Create directories in /var/lib/socktop + ├─ Copy files from /home/socktop to /var/lib/socktop + ├─ Rewrite paths in profiles.json + ├─ Set ownership to socktop:socktop + └─ Switch to socktop user + ↓ +[entrypoint.sh] (as socktop) + ├─ Validate configuration files + ├─ Setup Alacritty + └─ Start webterm-server + ↓ +[webterm-server] (as socktop) + └─ Running on port 8082 +``` + +## Local Development (docker-compose) + +For local development, the quickstart script (`scripts/docker-quickstart.sh`) manually copies files to `/var/lib/socktop/` using `docker cp` after container startup. This is because: + +1. Docker Compose uses host networking mode +2. Local testing needs to connect to host's socktop-agent on port 3000 +3. The init-config.sh still works, but the quickstart provides an additional safety net + +## Kubernetes Deployment + +In K8s: +1. ConfigMap mounts profiles.json to `/home/socktop/.config/socktop/` +2. Secret mounts certificates to `/home/socktop/.config/socktop/certs/` +3. init-config.sh runs and copies everything to `/var/lib/socktop/` +4. Socktop reads from `/var/lib/socktop/` (its actual HOME) +5. Everything works! ✅ + +## Benefits + +1. **No K8s Changes Required**: Existing ConfigMaps and Secrets work as-is +2. **Automatic Path Correction**: Certificate paths are automatically fixed +3. **Works Everywhere**: Same image works in K8s, Docker Compose, and standalone Docker +4. **No Race Conditions**: Init happens before any services start +5. **Proper Security**: Runs as socktop user after initialization + +## Troubleshooting + +### Profiles Not Found + +Check if init-config.sh ran successfully: +```bash +docker logs | grep "Initializing socktop webterm config" +``` + +You should see: +``` +=================================== +Initializing socktop webterm config +=================================== +Socktop HOME: /var/lib/socktop +Copying configuration files... + ✓ Copied profiles.json + ✓ Copied alacritty.toml + ✓ Copied catppuccin-frappe.toml + ✓ Copied rpi-master.pem + ... +Rewriting paths in profiles.json... + ✓ Updated certificate paths +=================================== +Configuration initialization complete +=================================== +``` + +### Check Files Were Copied + +```bash +# In K8s +kubectl exec -n socktop socktop-webterm- -it -- \ + ls -la /var/lib/socktop/.config/socktop/ + +# In Docker +docker exec socktop-webterm \ + ls -la /var/lib/socktop/.config/socktop/ +``` + +### Verify Path Rewriting + +```bash +# Check certificate paths in profiles.json +kubectl exec -n socktop socktop-webterm- -it -- \ + cat /var/lib/socktop/.config/socktop/profiles.json | grep tls_ca +``` + +Should show: +``` +"tls_ca": "/var/lib/socktop/.config/socktop/certs/rpi-master.pem", +``` + +NOT: +``` +"tls_ca": "/home/socktop/.config/socktop/rpi-master.pem", +``` + +## Files + +- `docker/init-config.sh` - Configuration initialization script (runs as root) +- `docker/entrypoint.sh` - Service startup script (runs as socktop) +- `Dockerfile` - Sets up both scripts as entrypoint chain +- `kubernetes/01-configmap.yaml` - Mounts configs to /home/socktop +- `kubernetes/02-secret.yaml` - Mounts certs to /home/socktop + +## Related Issues + +This fix resolves the issue where socktop profiles were not found after deployment to K8s, while maintaining compatibility with local Docker Compose development. + +--- + +**Created**: 2024-11-29 +**Status**: Implemented and Tested \ No newline at end of file diff --git a/docker/init-config.sh b/docker/init-config.sh new file mode 100644 index 0000000..b6cf7a2 --- /dev/null +++ b/docker/init-config.sh @@ -0,0 +1,76 @@ +#!/bin/bash +set -e + +# Init script to copy configuration files to the correct locations +# This handles the discrepancy between where K8s mounts configs +# and where the socktop package expects them (HOME directory) + +echo "===================================" +echo "Initializing socktop webterm config" +echo "===================================" + +# Determine the actual HOME directory for the socktop user +SOCKTOP_HOME=$(eval echo ~socktop) +echo "Socktop HOME: ${SOCKTOP_HOME}" + +# Create necessary directories in the actual HOME +mkdir -p "${SOCKTOP_HOME}/.config/socktop/certs" +mkdir -p "${SOCKTOP_HOME}/.config/alacritty" + +# Copy files from mounted locations to actual HOME if they exist +echo "Copying configuration files..." + +# Copy profiles.json +if [ -f "/home/socktop/.config/socktop/profiles.json" ]; then + cp /home/socktop/.config/socktop/profiles.json "${SOCKTOP_HOME}/.config/socktop/profiles.json" + echo " ✓ Copied profiles.json" +else + echo " ⚠ profiles.json not found at mount point" +fi + +# Copy alacritty.toml +if [ -f "/home/socktop/.config/alacritty/alacritty.toml" ]; then + cp /home/socktop/.config/alacritty/alacritty.toml "${SOCKTOP_HOME}/.config/alacritty/alacritty.toml" + echo " ✓ Copied alacritty.toml" +else + echo " ⚠ alacritty.toml not found at mount point" +fi + +# Copy catppuccin-frappe.toml +if [ -f "/home/socktop/.config/alacritty/catppuccin-frappe.toml" ]; then + cp /home/socktop/.config/alacritty/catppuccin-frappe.toml "${SOCKTOP_HOME}/.config/alacritty/catppuccin-frappe.toml" + echo " ✓ Copied catppuccin-frappe.toml" +else + echo " ⚠ catppuccin-frappe.toml not found at mount point" +fi + +# Copy certificates if they exist +if [ -d "/home/socktop/.config/socktop/certs" ]; then + for cert in /home/socktop/.config/socktop/certs/*.pem; do + if [ -f "$cert" ]; then + cp "$cert" "${SOCKTOP_HOME}/.config/socktop/certs/" + echo " ✓ Copied $(basename "$cert")" + fi + done +else + echo " ℹ No certificates directory found (optional)" +fi + +# Set proper ownership +chown -R socktop:socktop "${SOCKTOP_HOME}/.config" + +# Fix paths in profiles.json if it exists +if [ -f "${SOCKTOP_HOME}/.config/socktop/profiles.json" ]; then + echo "Rewriting paths in profiles.json..." + # Replace /home/socktop with actual HOME directory and ensure certs/ subdirectory + sed -i "s|/home/socktop/.config/socktop/rpi-|${SOCKTOP_HOME}/.config/socktop/certs/rpi-|g" "${SOCKTOP_HOME}/.config/socktop/profiles.json" + echo " ✓ Updated certificate paths" +fi + +echo "===================================" +echo "Configuration initialization complete" +echo "===================================" +echo "Switching to socktop user..." + +# Switch to socktop user and execute the main command +exec runuser -u socktop -- "$@"