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  }