Skip to content

Commit 0728956

Browse files
committed
git-clone: Set up SSH authentication
Assisted-by: Claude Opus 4.6 Signed-off-by: Tomáš Nevrlka <tnevrlka@redhat.com>
1 parent 368d1a5 commit 0728956

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

pkg/commands/git_clone/git_clone.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@ func (c *GitClone) Run() error {
6868
return fmt.Errorf("failed to create internal directory: %w", err)
6969
}
7070
c.internalDir = internalDir
71+
72+
// Save and restore all environment variables that may be modified during Run().
73+
restoreEnv := saveAndRestoreEnv("GIT_CONFIG_GLOBAL", "GIT_SSH_COMMAND", "GIT_SSL_NO_VERIFY", "GIT_SSL_CAINFO")
7174
defer func() {
7275
_ = os.RemoveAll(c.internalDir)
73-
_ = os.Unsetenv("GIT_CONFIG_GLOBAL")
76+
restoreEnv()
7477
}()
7578

7679
if err := c.setupGitConfig(); err != nil {
@@ -82,6 +85,10 @@ func (c *GitClone) Run() error {
8285
return err
8386
}
8487

88+
if err := c.setupSSH(); err != nil {
89+
return err
90+
}
91+
8592
// Clean checkout directory if requested
8693
if c.Params.DeleteExisting {
8794
if err := c.cleanCheckoutDir(); err != nil {
@@ -228,3 +235,30 @@ func (c *GitClone) validateParams() error {
228235
func (c *GitClone) getCheckoutDir() string {
229236
return filepath.Join(c.Params.OutputDir, c.Params.Subdirectory)
230237
}
238+
239+
// saveAndRestoreEnv saves the current values of the given environment variables
240+
// and returns a function that restores them to their original state.
241+
func saveAndRestoreEnv(keys ...string) func() {
242+
type envEntry struct {
243+
val string
244+
existed bool
245+
}
246+
saved := make(map[string]envEntry, len(keys))
247+
for _, key := range keys {
248+
val, existed := os.LookupEnv(key)
249+
saved[key] = envEntry{val, existed}
250+
}
251+
return func() {
252+
for key, e := range saved {
253+
if e.existed {
254+
if err := os.Setenv(key, e.val); err != nil {
255+
l.Logger.Debugf("failed to restore env var %s: %v", key, err)
256+
}
257+
} else {
258+
if err := os.Unsetenv(key); err != nil {
259+
l.Logger.Debugf("failed to unset env var %s: %v", key, err)
260+
}
261+
}
262+
}
263+
}
264+
}

pkg/commands/git_clone/setup.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,76 @@ func rewriteGitConfigCredentialHelper(configContent, credentialsPath string) str
179179
return strings.Join(lines, "\n")
180180
}
181181

182+
// setupSSH sets up SSH keys from an ssh-directory workspace.
183+
// SSH files are copied to c.internalDir/.ssh/ and GIT_SSH_COMMAND is set
184+
// with explicit flags so that git uses the custom SSH config without modifying $HOME.
185+
func (c *GitClone) setupSSH() error {
186+
if c.Params.SSHDirectory == "" {
187+
return nil
188+
}
189+
190+
sshDir := c.Params.SSHDirectory
191+
192+
if _, err := os.Stat(sshDir); os.IsNotExist(err) {
193+
l.Logger.Infof("SSH directory not found: %s", sshDir)
194+
return nil
195+
}
196+
197+
l.Logger.Infof("Setting up SSH keys from %s", sshDir)
198+
199+
destSSHDir := filepath.Join(c.internalDir, ".ssh")
200+
201+
if err := os.MkdirAll(destSSHDir, 0700); err != nil {
202+
return fmt.Errorf("failed to create .ssh directory: %w", err)
203+
}
204+
205+
entries, err := os.ReadDir(sshDir)
206+
if err != nil {
207+
return fmt.Errorf("failed to read SSH directory: %w", err)
208+
}
209+
210+
for _, entry := range entries {
211+
if entry.IsDir() {
212+
continue // Skip subdirectories
213+
}
214+
215+
srcPath := filepath.Join(sshDir, entry.Name())
216+
destPath := filepath.Join(destSSHDir, entry.Name())
217+
218+
if err := copyFile(srcPath, destPath, 0400); err != nil {
219+
return fmt.Errorf("failed to copy SSH file %s: %w", entry.Name(), err)
220+
}
221+
}
222+
223+
sshCmd := "ssh"
224+
225+
configPath := filepath.Join(destSSHDir, "config")
226+
if fileExists(configPath) {
227+
sshCmd += fmt.Sprintf(` -F "%s"`, configPath)
228+
} else {
229+
sshCmd += " -F /dev/null"
230+
}
231+
232+
for _, entry := range entries {
233+
name := entry.Name()
234+
if strings.HasPrefix(name, "id_") && !strings.HasSuffix(name, ".pub") && !entry.IsDir() {
235+
sshCmd += fmt.Sprintf(` -i "%s"`, filepath.Join(destSSHDir, name))
236+
}
237+
}
238+
239+
knownHostsPath := filepath.Join(destSSHDir, "known_hosts")
240+
if fileExists(knownHostsPath) {
241+
sshCmd += fmt.Sprintf(` -o UserKnownHostsFile="%s"`, knownHostsPath)
242+
}
243+
244+
if err := os.Setenv("GIT_SSH_COMMAND", sshCmd); err != nil {
245+
return err
246+
}
247+
248+
l.Logger.Infof("SSH keys configured (GIT_SSH_COMMAND=%s)", sshCmd)
249+
return nil
250+
}
251+
182252
// fileExists checks if a file exists and is not a directory.
183253
func fileExists(path string) bool {
184254
info, err := os.Stat(path)

0 commit comments

Comments
 (0)