Skip to content

Commit 2d033db

Browse files
tosin2013claude
andcommitted
fix(ssh): Sync non-root user SSH keys to /root/.ssh for Airflow container access
The Airflow container mounts /root/.ssh:/root/.ssh:ro but when running as a non-root admin user (e.g., github-runner), the SSH key exists at /home/<user>/.ssh/id_rsa which is not accessible inside the container. This fix: - E2E workflow: Copy admin user's SSH key to /root/.ssh/ after generation - docker-compose.yml: Add AIRFLOW_CONN_LOCALHOST_SSH with configurable SSH user via QUBINODE_SSH_USER env var (defaults to root) - deploy-qubinode.sh: Sync admin user's SSH key to /root/.ssh/ during configure_ssh() for production deployments The SSH connection now uses the mounted /root/.ssh/id_rsa key while connecting as the configured user (root or QUBINODE_SSH_USER). Fixes E2E test failure: FileNotFoundError: [Errno 2] No such file or directory: '/home/github-runner/.ssh/id_rsa' 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent d4a80d4 commit 2d033db

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

.github/workflows/e2e-test.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,40 @@ jobs:
258258
echo "SSH_USER=$SSH_USER" >> $GITHUB_ENV
259259
echo "SSH_HOME=$SSH_HOME" >> $GITHUB_ENV
260260
261+
# CRITICAL: Copy SSH key to /root/.ssh/ for Airflow container access
262+
# The docker-compose.yml mounts /root/.ssh:/root/.ssh:ro into containers
263+
# So the key MUST be available at /root/.ssh/id_rsa for the container to use it
264+
if [ "$SSH_USER" != "root" ]; then
265+
echo "[INFO] Syncing SSH key to /root/.ssh for Airflow container access..."
266+
sudo mkdir -p /root/.ssh
267+
sudo chmod 700 /root/.ssh
268+
269+
# Check if source key exists before copying
270+
if sudo test -f "$SSH_HOME/.ssh/id_rsa"; then
271+
# Only copy if /root/.ssh/id_rsa doesn't exist or is different
272+
if ! sudo test -f /root/.ssh/id_rsa || \
273+
! sudo diff -q "$SSH_HOME/.ssh/id_rsa" /root/.ssh/id_rsa >/dev/null 2>&1; then
274+
sudo cp "$SSH_HOME/.ssh/id_rsa" /root/.ssh/id_rsa
275+
sudo cp "$SSH_HOME/.ssh/id_rsa.pub" /root/.ssh/id_rsa.pub
276+
sudo chmod 600 /root/.ssh/id_rsa
277+
sudo chmod 644 /root/.ssh/id_rsa.pub
278+
echo "[OK] SSH key copied to /root/.ssh/"
279+
else
280+
echo "[OK] SSH key already in sync at /root/.ssh/"
281+
fi
282+
283+
# Also add the key to root's authorized_keys for container->host SSH
284+
sudo touch /root/.ssh/authorized_keys
285+
sudo chmod 600 /root/.ssh/authorized_keys
286+
if ! sudo grep -qF "$USER_PUB_KEY" /root/.ssh/authorized_keys 2>/dev/null; then
287+
echo "$USER_PUB_KEY" | sudo tee -a /root/.ssh/authorized_keys > /dev/null
288+
echo "[OK] SSH key added to /root/.ssh/authorized_keys"
289+
fi
290+
else
291+
echo "[WARN] Source SSH key not found at $SSH_HOME/.ssh/id_rsa"
292+
fi
293+
fi
294+
261295
- name: Deploy Qubinode Stack
262296
env:
263297
SSH_PASSWORD: ${{ secrets.SSH_PASSWORD }}

airflow/docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ x-airflow-common:
4242
QUBINODE_KCLI_ENABLED: 'true'
4343
QUBINODE_RAG_INTEGRATION: 'true'
4444
# SSH Connection Configuration (for SSHOperator in DAGs)
45+
# This connection is used by DAGs to SSH to the host for kcli/virsh commands
46+
# The key at /root/.ssh/id_rsa is mounted from the host (see volumes below)
47+
# For non-root users: set QUBINODE_SSH_USER and copy your key to /root/.ssh/
48+
# Or override the entire connection via AIRFLOW_CONN_LOCALHOST_SSH env var
49+
AIRFLOW_CONN_LOCALHOST_SSH: ${AIRFLOW_CONN_LOCALHOST_SSH:-ssh://${QUBINODE_SSH_USER:-root}@localhost?key_file=/root/.ssh/id_rsa&no_host_key_check=true}
4550
# Override default SSH connection ID if needed (default: localhost_ssh)
4651
# QUBINODE_SSH_CONN_ID: 'localhost_ssh'
4752
# See SSH-CONNECTION-SETUP.md for configuration instructions

scripts/development/deploy-qubinode.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,33 @@ configure_ssh() {
13711371
fi
13721372
fi
13731373

1374+
# Sync SSH key to /root/.ssh/ for Airflow container access
1375+
# The docker-compose.yml mounts /root/.ssh:/root/.ssh:ro into containers
1376+
# So the key MUST be available at /root/.ssh/id_rsa for the container to use it
1377+
if [[ -n "${QUBINODE_ADMIN_USER:-}" && "${QUBINODE_ADMIN_USER}" != "root" ]]; then
1378+
log_info "Syncing SSH key to /root/.ssh for Airflow container access..."
1379+
if [[ -f "$ssh_key_path" ]]; then
1380+
mkdir -p /root/.ssh
1381+
chmod 700 /root/.ssh
1382+
# Only copy if not already in sync
1383+
if [[ ! -f /root/.ssh/id_rsa ]] || ! diff -q "$ssh_key_path" /root/.ssh/id_rsa &>/dev/null; then
1384+
cp "$ssh_key_path" /root/.ssh/id_rsa
1385+
cp "${ssh_key_path}.pub" /root/.ssh/id_rsa.pub
1386+
chmod 600 /root/.ssh/id_rsa
1387+
chmod 644 /root/.ssh/id_rsa.pub
1388+
log_info "SSH key copied to /root/.ssh/"
1389+
fi
1390+
# Also add to root's authorized_keys for container->host SSH
1391+
touch /root/.ssh/authorized_keys
1392+
chmod 600 /root/.ssh/authorized_keys
1393+
local pub_key=$(cat "${ssh_key_path}.pub")
1394+
if ! grep -qF "$pub_key" /root/.ssh/authorized_keys 2>/dev/null; then
1395+
echo "$pub_key" >> /root/.ssh/authorized_keys
1396+
log_info "SSH key added to /root/.ssh/authorized_keys"
1397+
fi
1398+
fi
1399+
fi
1400+
13741401
log_success "SSH configuration completed"
13751402
return 0
13761403
}

0 commit comments

Comments
 (0)