8000 Merge pull request #1510 from hiddeco/mtls-support · go-git/go-git@763ce2e · GitHub
[go: up one dir, main page]

Skip to content

Commit 763ce2e

Browse files
authored
Merge pull request #1510 from hiddeco/mtls-support
[v5] plumbing: support mTLS for HTTPS protocol
2 parents 6f444d3 + 5320e1b commit 763ce2e

File tree

8 files changed

+111
-33
lines changed

8 files changed

+111
-33
lines changed

options.go

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/ProtonMail/go-crypto/openpgp"
11+
1112
"github.com/go-git/go-git/v5/config"
1213
"github.com/go-git/go-git/v5/plumbing"
1314
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
@@ -72,9 +73,16 @@ type CloneOptions struct {
7273
// Tags describe how the tags will be fetched from the remote repository,
7374
// by default is AllTags.
7475
Tags TagMode
75-
// InsecureSkipTLS skips ssl verify if protocol is https
76+
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
7677
InsecureSkipTLS bool
77-
// CABundle specify additional ca bundle with system cert pool
78+
// ClientCert is the client certificate to use for mutual TLS authentication
79+
// over the HTTPS protocol.
80+
ClientCert []byte
81+
// ClientKey is the client key to use for mutual TLS authentication over
82+
// the HTTPS protocol.
83+
ClientKey []byte
84+
// CABundle specifies an additional CA bundle to use together with the
85+
// system cert pool.
7886
CABundle []byte
7987
// ProxyOptions provides info required for connecting to a proxy.
8088
ProxyOptions transport.ProxyOptions
@@ -153,9 +161,16 @@ type PullOptions struct {
153161
// Force allows the pull to update a local branch even when the remote
154162
// branch does not descend from it.
155163
Force bool
156-
// InsecureSkipTLS skips ssl verify if protocol is https
164+
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
157165
InsecureSkipTLS bool
158-
// CABundle specify additional ca bundle with system cert pool
166+
// ClientCert is the client certificate to use for mutual TLS authentication
167+
// over the HTTPS protocol.
168+
ClientCert []byte
169+
// ClientKey is the client key to use for mutual TLS authentication over
170+
// the HTTPS protocol.
171+
ClientKey []byte
172+
// CABundle specifies an additional CA bundle to use together with the
173+
// system cert pool.
159174
CABundle []byte
160175
// ProxyOptions provides info required for connecting to a proxy.
161176
ProxyOptions transport.ProxyOptions
@@ -211,9 +226,16 @@ type FetchOptions struct {
211226
// Force allows the fetch to update a local branch even when the remote
212227
// branch does not descend from it.
213228
Force bool
214-
// InsecureSkipTLS skips ssl verify if protocol is https
229+
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
215230
InsecureSkipTLS bool
216-
// CABundle specify additional ca bundle with system cert pool
231+
// ClientCert is the client certificate to use for mutual TLS authentication
232+
// over the HTTPS protocol.
233+
ClientCert []byte
234+
// ClientKey is the client key to use for mutual TLS authentication over
235+
// the HTTPS protocol.
236+
ClientKey []byte
237+
// CABundle specifies an additional CA bundle to use together with the
238+
// system cert pool.
217239
CABundle []byte
218240
// ProxyOptions provides info required for connecting to a proxy.
219241
ProxyOptions transport.ProxyOptions
@@ -267,9 +289,16 @@ type PushOptions struct {
267289
// Force allows the push to update a remote branch even when the local
268290
// branch does not descend from it.
269291
Force bool
270-
// InsecureSkipTLS skips ssl verify if protocol is https
292+
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
271293
InsecureSkipTLS bool
272-
// CABundle specify additional ca bundle with system cert pool
294+
// ClientCert is the client certificate to use for mutual TLS authentication
295+
// over the HTTPS protocol.
296+
ClientCert []byte
297+
// ClientKey is the client key to use for mutual TLS authentication over
298+
// the HTTPS protocol.
299+
ClientKey []byte
300+
// CABundle specifies an additional CA bundle to use together with the
301+
// system cert pool.
273302
CABundle []byte
274303
// RequireRemoteRefs only allows a remote ref to be updated if its current
275304
// value is the one specified here.
@@ -693,9 +722,16 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
693722
type ListOptions struct {
694723
// Auth credentials, if required, to use with the remote repository.
695724
Auth transport.AuthMethod
696-
// InsecureSkipTLS skips ssl verify if protocol is https
725+
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
697726
InsecureSkipTLS bool
698-
// CABundle specify additional ca bundle with system cert pool
727+
// ClientCert is the client certificate to use for mutual TLS authentication
728+
// over the HTTPS protocol.
729+
ClientCert []byte
730+
// ClientKey is the client key to use for mutual TLS authentication over
731+
// the HTTPS protocol.
732+
ClientKey []byte
733+
// CABundle specifies an additional CA bundle to use together with the
734+
// system cert pool.
699735
CABundle []byte
700736
// PeelingOption defines how peeled objects are handled during a
701737
// remote list.

plumbing/transport/common.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,17 @@ type Endpoint struct {
113113
Port int
114114
// Path is the repository path.
115115
Path string
116-
// InsecureSkipTLS skips ssl verify if protocol is https
116+
// InsecureSkipTLS skips SSL verification if Protocol is HTTPS.
117117
InsecureSkipTLS bool
118-
// CaBundle specify additional ca bundle with system cert pool
118+
// ClientCert specifies an optional client certificate to use for mutual
119+
// TLS authentication if Protocol is HTTPS.
120+
ClientCert []byte
121+
// ClientKey specifies an optional client key to use for mutual TLS
122+
// authentication if Protocol is HTTPS.
123+
ClientKey []byte
124+
// CaBundle specifies an optional CA bundle to use for SSL verification
125+
// if Protocol is HTTPS. The bundle is added in addition to the system
126+
// CA bundle.
119127
CaBundle []byte
120128
// Proxy provides info required for connecting to a proxy.
121129
Proxy ProxyOptions

plumbing/transport/http/common.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ import (
1515
"strings"
1616
"sync"
1717

18+
"github.com/golang/groupcache/lru"
19+
1820
"github.com/go-git/go-git/v5/plumbing"
1921
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
2022
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
2123
"github.com/go-git/go-git/v5/plumbing/transport"
2224
"github.com/go-git/go-git/v5/utils/ioutil"
23-
"github.com/golang/groupcache/lru"
2425
)
2526

2627
// it requires a bytes.Buffer, because we need to know the length
@@ -185,6 +186,18 @@ func transportWithInsecureTLS(transport *http.Transport) {
185186
transport.TLSClientConfig.InsecureSkipVerify = true
186187
}
187188

189+
func transportWithClientCert(transport *http.Transport, cert, key []byte) error {
190+
keyPair, err := tls.X509KeyPair(cert, key)
191+
if err != nil {
192+
return err
193+
}
194+
if transport.TLSClientConfig == nil {
195+
transport.TLSClientConfig = &tls.Config{}
196+
}
197+
transport.TLSClientConfig.Certificates = []tls.Certificate{keyPair}
198+
return nil
199+
}
200+
188201
func transportWithCABundle(transport *http.Transport, caBundle []byte) error {
189202
rootCAs, err := x509.SystemCertPool()
190203
if err != nil {
@@ -206,6 +219,11 @@ func transportWithProxy(transport *http.Transport, proxyURL *url.URL) {
206219
}
207220

208221
func configureTransport(transport *http.Transport, ep *transport.Endpoint) error {
222+
if len(ep.ClientCert) > 0 && len(ep.ClientKey) > 0 {
223+
if err := transportWithClientCert(transport, ep.ClientCert, ep.ClientKey); err != nil {
224+
return err
225+
}
226+
}
209227
if len(ep.CaBundle) > 0 {
210228
if err := transportWithCABundle(transport, ep.CaBundle); err != nil {
211229
return err
@@ -230,7 +248,7 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
230248

231249
// We need to configure the http transport if there are transport specific
232250
// options present in the endpoint.
233-
if len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
251+
if len(ep.ClientKey) > 0 || len(ep.ClientCert) > 0 || len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
234252
var transport *http.Transport
235253
// if the client wasn't configured to have a cache for transports then just configure
236254
// the transport and use it directly, otherwise try to use the cache.
@@ -242,9 +260,13 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
242260
}
243261

244262
transport = tr.Clone()
245-
configureTransport(transport, ep)
263+
if err := configureTransport(transport, ep); err != nil {
264+
return nil, err
265+
}
246266
} else {
247267
transportOpts := transportOptions{
268+
clientCert: string(ep.ClientCert),
269+
clientKey: string(ep.ClientKey),
248270
caBundle: string(ep.CaBundle),
249271
insecureSkipTLS: ep.InsecureSkipTLS,
250272
}
@@ -260,7 +282,9 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
260282

261283
if !found {
262284
transport = c.client.Transport.(*http.Transport).Clone()
263-
configureTransport(transport, ep)
285+
if err := configureTransport(transport, ep); err != nil {
286+
return nil, err
287+
}
264288
c.addTransport(transportOpts, transport)
265289
}
266290
}

plumbing/transport/http/common_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ func (s *ClientSuite) TestNewUnexpectedError(c *C) {
111111

112112
func (s *ClientSuite) Test_newSession(c *C) {
113113
cl := NewClientWithOptions(nil, &ClientOptions{
114-
CacheMaxEntries: 2,
114+
CacheMaxEntries: 3,
115115
}).(*client)
116116

117-
insecureEP := s.Endpoint
117+
insecureEP := *s.Endpoint
118118
insecureEP.InsecureSkipTLS = true
119-
session, err := newSession(cl, insecureEP, nil)
119+
session, err := newSession(cl, &insecureEP, nil)
120120
c.Assert(err, IsNil)
121121

122122
sessionTransport := session.client.Transport.(*http.Transport)
@@ -131,7 +131,7 @@ func (s *ClientSuite) Test_newSession(c *C) {
131131

132132
caEndpoint := insecureEP
133133
caEndpoint.CaBundle = []byte("this is the way")
134-
session, err = newSession(cl, caEndpoint, nil)
134+
session, err = newSession(cl, &caEndpoint, nil)
135135
c.Assert(err, IsNil)
136136

137137
sessionTransport = session.client.Transport.(*http.Transport)
@@ -146,7 +146,7 @@ func (s *ClientSuite) Test_newSession(c *C) {
146146
// cached transport should be the one that's used.
147147
c.Assert(sessionTransport, Equals, t)
148148

149-
session, err = newSession(cl, caEndpoint, nil)
149+
session, err = newSession(cl, &caEndpoint, nil)
150150
c.Assert(err, IsNil)
151151
sessionTransport = session.client.Transport.(*http.Transport)
152152
// transport that's going to be used should be cached already.
@@ -156,7 +156,7 @@ func (s *ClientSuite) Test_newSession(c *C) {
156156

157157
// if the cache does not exist, the transport should still be correctly configured.
158158
cl.transports = nil
159-
session, err = newSession(cl, insecureEP, nil)
159+
session, err = newSession(cl, &insecureEP, nil)
160160
c.Assert(err, IsNil)
161161

162162
sessionTransport = session.client.Transport.(*http.Transport)

plumbing/transport/http/transport.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
type transportOptions struct {
1010
insecureSkipTLS bool
1111
// []byte is not comparable.
12-
caBundle string
13-
proxyURL url.URL
12+
clientCert string
13+
clientKey string
14+
caBundle string
15+
proxyURL url.URL
1416
}
1517

1618
func (c *client) addTransport(opts transportOptions, transport *http.Transport) {

remote.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
114114
o.RemoteURL = r.c.URLs[len(r.c.URLs)-1]
115115
}
116116

117-
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
117+
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
118118
if err != nil {
119119
return err
120120
}
@@ -416,7 +416,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
416416
o.RemoteURL = r.c.URLs[0]
417417
}
418418

419-
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
419+
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
420420
if err != nil {
421421
return nil, err
422422
}
@@ -532,31 +532,33 @@ func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) {
532532
return false, nil
533533
}
534534

535-
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
536-
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
535+
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
536+
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
537537
if err != nil {
538538
return nil, err
539539
}
540540

541541
return c.NewUploadPackSession(ep, auth)
542542
}
543543

544-
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
545-
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
544+
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
545+
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
546546
if err != nil {
547547
return nil, err
548548
}
549549

550550
return c.NewReceivePackSession(ep, auth)
551551
}
552552

553-
func newClient(url string, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
553+
func newClient(url string, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
554554
ep, err := transport.NewEndpoint(url)
555555
if err != nil {
556556
return nil, nil, err
557557
}
558558
ep.InsecureSkipTLS = insecure
559-
ep.CaBundle = cabundle
559+
ep.ClientCert = clientCert
560+
ep.ClientKey = clientKey
561+
ep.CaBundle = caBundle
560562
ep.Proxy = proxyOpts
561563

562564
c, err := client.NewClient(ep)
@@ -1356,7 +1358,7 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
13561358
return nil, ErrEmptyUrls
13571359
}
13581360

1359-
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
1361+
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
13601362
if err != nil {
13611363
return nil, err
13621364
}

repository.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/go-git/go-billy/v5"
2020
"github.com/go-git/go-billy/v5/osfs"
2121
"github.com/go-git/go-billy/v5/util"
22+
2223
"github.com/go-git/go-git/v5/config"
2324
"github.com/go-git/go-git/v5/internal/path_util"
2425
"github.com/go-git/go-git/v5/internal/revision"
@@ -930,6 +931,8 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
930931
Tags: o.Tags,
931932
RemoteName: o.RemoteName,
932933
InsecureSkipTLS: o.InsecureSkipTLS,
934+
ClientCert: o.ClientCert,
935+
ClientKey: o.ClientKey,
933936
CABundle: o.CABundle,
934937
ProxyOptions: o.ProxyOptions,
935938
}, o.ReferenceName)

worktree.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/go-git/go-billy/v5"
1414
"github.com/go-git/go-billy/v5/util"
15+
1516
"github.com/go-git/go-git/v5/config"
1617
"github.com/go-git/go-git/v5/plumbing"
1718
"github.com/go-git/go-git/v5/plumbing/filemode"
@@ -79,6 +80,8 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
7980 6027
Progress: o.Progress,
8081
Force: o.Force,
8182
InsecureSkipTLS: o.InsecureSkipTLS,
83+
ClientCert: o.ClientCert,
84+
ClientKey: o.ClientKey,
8285
CABundle: o.CABundle,
8386
ProxyOptions: o.ProxyOptions,
8487
})

0 commit comments

Comments
 (0)
0