From 7d6560b921accbd7ecae7588c911ea9d9f504e94 Mon Sep 17 00:00:00 2001 From: Hugo Bjork Date: Wed, 8 Apr 2026 17:47:55 +0200 Subject: [PATCH] fix: use $HOME/.ssh instead of /root/.ssh for non-root pod execution The addons download job runs as non-root user (uid 1000) but the SSH setup script was trying to create /root/.ssh which fails with 'Permission denied'. Changes: - Only setup SSH directories when sshSecrets are provided - Use $HOME/.ssh instead of /root/.ssh to work with non-root users - Separate HTTPS and SSH clone logic to avoid unnecessary SSH config - Use GIT_SSH_COMMAND env var for cleaner SSH key handling Fixes cloning of private repositories when running with restricted pod security context (RunAsNonRoot: true). --- internal/controller/odoo_controller.go | 55 ++++++++++++++++++-------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/internal/controller/odoo_controller.go b/internal/controller/odoo_controller.go index 8a3944b..b65665e 100644 --- a/internal/controller/odoo_controller.go +++ b/internal/controller/odoo_controller.go @@ -1441,17 +1441,20 @@ func (r *OdooReconciler) jobForAddonsDownload(odoo *odoov1alpha1.Odoo, repositor var scriptBuilder strings.Builder _, _ = scriptBuilder.WriteString("#!/bin/sh\nset -e\n\n") - // Setup SSH for all provided secrets - _, _ = scriptBuilder.WriteString("mkdir -p /root/.ssh\n") - _, _ = scriptBuilder.WriteString("chmod 700 /root/.ssh\n") - _, _ = scriptBuilder.WriteString("echo \"StrictHostKeyChecking no\" >> /root/.ssh/config\n") - for i := range sshSecrets { - // Mount path for each secret is unique, /etc/ssh-key- - _, _ = scriptBuilder.WriteString(fmt.Sprintf("cp /etc/ssh-key-%d/ssh-privatekey /root/.ssh/id_rsa_%d\n", i, i)) - _, _ = scriptBuilder.WriteString(fmt.Sprintf("chmod 600 /root/.ssh/id_rsa_%d\n", i)) - _, _ = scriptBuilder.WriteString(fmt.Sprintf("echo \"IdentityFile /root/.ssh/id_rsa_%d\" >> /root/.ssh/config\n", i)) - } - _, _ = scriptBuilder.WriteString("\n") + // Setup SSH only if there are SSH secrets to configure + if len(sshSecrets) > 0 { + // Use $HOME/.ssh to work with non-root users (pod runs as user 1000) + _, _ = scriptBuilder.WriteString("mkdir -p $HOME/.ssh\n") + _, _ = scriptBuilder.WriteString("chmod 700 $HOME/.ssh\n") + _, _ = scriptBuilder.WriteString("echo \"StrictHostKeyChecking no\" >> $HOME/.ssh/config\n") + for i := range sshSecrets { + // Mount path for each secret is unique, /etc/ssh-key- + _, _ = scriptBuilder.WriteString(fmt.Sprintf("cp /etc/ssh-key-%d/ssh-privatekey $HOME/.ssh/id_rsa_%d\n", i, i)) + _, _ = scriptBuilder.WriteString(fmt.Sprintf("chmod 600 $HOME/.ssh/id_rsa_%d\n", i)) + _, _ = scriptBuilder.WriteString(fmt.Sprintf("echo \"IdentityFile $HOME/.ssh/id_rsa_%d\" >> $HOME/.ssh/config\n", i)) + } + _, _ = scriptBuilder.WriteString("\n") + } // Loop through repositories and clone/update for _, repo := range repositories { @@ -1466,28 +1469,48 @@ func (r *OdooReconciler) jobForAddonsDownload(odoo *odoov1alpha1.Odoo, repositor // Determine if it's an SSH URL for adding to known_hosts isSSH := strings.HasPrefix(repo.URL, "git@") + sshKeyIndex := indexOf(sshSecrets, repo.SSHKeySecretRef) if isSSH { host := strings.Split(strings.Split(repo.URL, "@")[1], ":")[0] - _, _ = scriptBuilder.WriteString(fmt.Sprintf("ssh-keyscan %s >> /root/.ssh/known_hosts\n", host)) + _, _ = scriptBuilder.WriteString(fmt.Sprintf("ssh-keyscan %s >> $HOME/.ssh/known_hosts\n", host)) } - _, _ = scriptBuilder.WriteString(fmt.Sprintf(` + // Generate clone/update script - only set sshCommand if this repo uses SSH + if isSSH && sshKeyIndex >= 0 { + _, _ = scriptBuilder.WriteString(fmt.Sprintf(` +TARGET_DIR="/mnt/extra-addons/%s" # Each repo gets its own subdirectory + +if [ -d "$TARGET_DIR/.git" ]; then + echo "Repo %s exists, updating..." + cd "$TARGET_DIR" + git config core.sshCommand "ssh -i $HOME/.ssh/id_rsa_%d" + git fetch origin + git checkout "%s" + git pull origin "%s" +else + echo "Cloning repo %s..." + GIT_SSH_COMMAND="ssh -i $HOME/.ssh/id_rsa_%d" git clone -b "%s" "%s" "$TARGET_DIR" +fi + +`, repo.Name, repo.Name, sshKeyIndex, repoVersion, repoVersion, repo.Name, sshKeyIndex, repoVersion, repo.URL)) + } else { + // HTTPS or public repo - no SSH configuration needed + _, _ = scriptBuilder.WriteString(fmt.Sprintf(` TARGET_DIR="/mnt/extra-addons/%s" # Each repo gets its own subdirectory if [ -d "$TARGET_DIR/.git" ]; then echo "Repo %s exists, updating..." cd "$TARGET_DIR" - git config core.sshCommand "ssh -i /root/.ssh/id_rsa_%d" git fetch origin git checkout "%s" git pull origin "%s" else echo "Cloning repo %s..." - git config core.sshCommand "ssh -i /root/.ssh/id_rsa_%d" git clone -b "%s" "%s" "$TARGET_DIR" fi -`, repo.Name, repo.Name, indexOf(sshSecrets, repo.SSHKeySecretRef), repoVersion, repoVersion, repo.Name, indexOf(sshSecrets, repo.SSHKeySecretRef), repoVersion, repo.URL)) +`, repo.Name, repo.Name, repoVersion, repoVersion, repo.Name, repoVersion, repo.URL)) + } } // VolumeMounts for addons PVC