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