github.com/argoproj/argo-cd@v1.8.7/util/git/creds.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "strings" 9 10 argoio "github.com/argoproj/gitops-engine/pkg/utils/io" 11 log "github.com/sirupsen/logrus" 12 13 certutil "github.com/argoproj/argo-cd/util/cert" 14 ) 15 16 type Creds interface { 17 Environ() (io.Closer, []string, error) 18 } 19 20 // nop implementation 21 type NopCloser struct { 22 } 23 24 func (c NopCloser) Close() error { 25 return nil 26 } 27 28 type NopCreds struct { 29 } 30 31 func (c NopCreds) Environ() (io.Closer, []string, error) { 32 return NopCloser{}, nil, nil 33 } 34 35 // HTTPS creds implementation 36 type HTTPSCreds struct { 37 // Username for authentication 38 username string 39 // Password for authentication 40 password string 41 // Whether to ignore invalid server certificates 42 insecure bool 43 // Client certificate to use 44 clientCertData string 45 // Client certificate key to use 46 clientCertKey string 47 } 48 49 func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool) HTTPSCreds { 50 return HTTPSCreds{ 51 username, 52 password, 53 insecure, 54 clientCertData, 55 clientCertKey, 56 } 57 } 58 59 // Get additional required environment variables for executing git client to 60 // access specific repository via HTTPS. 61 func (c HTTPSCreds) Environ() (io.Closer, []string, error) { 62 env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), fmt.Sprintf("GIT_USERNAME=%s", c.username), fmt.Sprintf("GIT_PASSWORD=%s", c.password)} 63 httpCloser := authFilePaths(make([]string, 0)) 64 65 // GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at 66 // all. 67 if c.insecure { 68 env = append(env, "GIT_SSL_NO_VERIFY=true") 69 } 70 71 // In case the repo is configured for using a TLS client cert, we need to make 72 // sure git client will use it. The certificate's key must not be password 73 // protected. 74 if c.clientCertData != "" && c.clientCertKey != "" { 75 var certFile, keyFile *os.File 76 77 // We need to actually create two temp files, one for storing cert data and 78 // another for storing the key. If we fail to create second fail, the first 79 // must be removed. 80 certFile, err := ioutil.TempFile(argoio.TempDir, "") 81 if err == nil { 82 defer certFile.Close() 83 keyFile, err = ioutil.TempFile(argoio.TempDir, "") 84 if err != nil { 85 removeErr := os.Remove(certFile.Name()) 86 if removeErr != nil { 87 log.Errorf("Could not remove previously created tempfile %s: %v", certFile.Name(), removeErr) 88 } 89 return NopCloser{}, nil, err 90 } 91 defer keyFile.Close() 92 } else { 93 return NopCloser{}, nil, err 94 } 95 96 // We should have both temp files by now 97 httpCloser = authFilePaths([]string{certFile.Name(), keyFile.Name()}) 98 99 _, err = certFile.WriteString(c.clientCertData) 100 if err != nil { 101 httpCloser.Close() 102 return NopCloser{}, nil, err 103 } 104 // GIT_SSL_CERT is the full path to a client certificate to be used 105 env = append(env, fmt.Sprintf("GIT_SSL_CERT=%s", certFile.Name())) 106 107 _, err = keyFile.WriteString(c.clientCertKey) 108 if err != nil { 109 httpCloser.Close() 110 return NopCloser{}, nil, err 111 } 112 // GIT_SSL_KEY is the full path to a client certificate's key to be used 113 env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name())) 114 115 } 116 return httpCloser, env, nil 117 } 118 119 // SSH implementation 120 type SSHCreds struct { 121 sshPrivateKey string 122 caPath string 123 insecure bool 124 } 125 126 func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool) SSHCreds { 127 return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey} 128 } 129 130 type sshPrivateKeyFile string 131 132 type authFilePaths []string 133 134 func (f sshPrivateKeyFile) Close() error { 135 return os.Remove(string(f)) 136 } 137 138 // Remove a list of files that have been created as temp files while creating 139 // HTTPCreds object above. 140 func (f authFilePaths) Close() error { 141 var retErr error = nil 142 for _, path := range f { 143 err := os.Remove(path) 144 if err != nil { 145 log.Errorf("HTTPSCreds.Close(): Could not remove temp file %s: %v", path, err) 146 retErr = err 147 } 148 } 149 return retErr 150 } 151 152 func (c SSHCreds) Environ() (io.Closer, []string, error) { 153 // use the SHM temp dir from util, more secure 154 file, err := ioutil.TempFile(argoio.TempDir, "") 155 if err != nil { 156 return nil, nil, err 157 } 158 defer file.Close() 159 160 _, err = file.WriteString(c.sshPrivateKey + "\n") 161 if err != nil { 162 return nil, nil, err 163 } 164 165 args := []string{"ssh", "-i", file.Name()} 166 var env []string 167 if c.caPath != "" { 168 env = append(env, fmt.Sprintf("GIT_SSL_CAINFO=%s", c.caPath)) 169 } 170 if c.insecure { 171 log.Warn("temporarily disabling strict host key checking (i.e. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'), please don't use in production") 172 // StrictHostKeyChecking will add the host to the knownhosts file, we don't want that - a security issue really, 173 // UserKnownHostsFile=/dev/null is therefore used so we write the new insecure host to /dev/null 174 args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null") 175 } else { 176 knownHostsFile := certutil.GetSSHKnownHostsDataPath() 177 args = append(args, "-o", "StrictHostKeyChecking=yes", "-o", fmt.Sprintf("UserKnownHostsFile=%s", knownHostsFile)) 178 } 179 env = append(env, []string{fmt.Sprintf("GIT_SSH_COMMAND=%s", strings.Join(args, " "))}...) 180 return sshPrivateKeyFile(file.Name()), env, nil 181 }