Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support mTLS with client certificate when configured. #3302

Merged
merged 9 commits into from Aug 12, 2022
52 changes: 36 additions & 16 deletions internal/http/http.go
Expand Up @@ -112,6 +112,19 @@ func CheckConfig(ctx *context.Context, upload *config.Upload, kind string) error
return misconfigured(kind, upload, "no certificate could be added from the specified trusted_certificates configuration")
}

if upload.ClientX509Cert != "" && upload.ClientX509Key == "" {
return misconfigured(kind, upload, "'client_x509_key' must be set when 'client_x509_cert' is set")
}
if upload.ClientX509Key != "" && upload.ClientX509Cert == "" {
return misconfigured(kind, upload, "'client_x509_cert' must be set when 'client_x509_key' is set")
}
if upload.ClientX509Cert != "" && upload.ClientX509Key != "" {
if _, err := tls.LoadX509KeyPair(upload.ClientX509Cert, upload.ClientX509Key); err != nil {
return misconfigured(kind, upload,
"client x509 certificate could not be loaded from the specified 'client_x509_cert' and 'client_x509_key'")
}
}

return nil
}

Expand Down Expand Up @@ -306,27 +319,34 @@ func newUploadRequest(ctx *context.Context, method, target, username, secret str
}

func getHTTPClient(upload *config.Upload) (*h.Client, error) {
if upload.TrustedCerts == "" {
if upload.TrustedCerts == "" && upload.ClientX509Cert == "" && upload.ClientX509Key == "" {
return h.DefaultClient, nil
}
pool, err := x509.SystemCertPool()
if err != nil {
if runtime.GOOS == "windows" {
// on windows ignore errors until golang issues #16736 & #18609 get fixed
pool = x509.NewCertPool()
} else {
transport := &h.Transport{
Proxy: h.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{},
}
if upload.TrustedCerts != "" {
pool, err := x509.SystemCertPool()
if err != nil {
if runtime.GOOS == "windows" {
// on windows ignore errors until golang issues #16736 & #18609 get fixed
pool = x509.NewCertPool()
} else {
return nil, err
}
}
pool.AppendCertsFromPEM([]byte(upload.TrustedCerts)) // already validated certs checked by CheckConfig
transport.TLSClientConfig.RootCAs = pool
}
if upload.ClientX509Cert != "" && upload.ClientX509Key != "" {
cert, err := tls.LoadX509KeyPair(upload.ClientX509Cert, upload.ClientX509Key)
if err != nil {
return nil, err
}
transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
}
pool.AppendCertsFromPEM([]byte(upload.TrustedCerts)) // already validated certs checked by CheckConfig
return &h.Client{
Transport: &h.Transport{
Proxy: h.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{ // nolint: gosec
RootCAs: pool,
},
},
}, nil
return &h.Client{Transport: transport}, nil
}

// executeHTTPRequest processes the http call with respect of context ctx.
Expand Down
53 changes: 38 additions & 15 deletions internal/http/http_test.go
Expand Up @@ -2,6 +2,7 @@ package http

import (
"bytes"
"crypto/tls"
"encoding/pem"
"errors"
"fmt"
Expand Down Expand Up @@ -566,6 +567,26 @@ func TestUpload(t *testing.T) {
check{"/blah/2.1.0/a.deb", "u3", "x", content, map[string]string{}},
),
},
{
name: "client cert",
tryTLS: true,
setup: func(s *httptest.Server) (*context.Context, config.Upload) {
s.TLS.ClientAuth = tls.RequireAnyClientCert
return ctx, config.Upload{
Mode: ModeArchive,
Name: "a",
Target: s.URL + "/{{.ProjectName}}/{{.Version}}/",
Username: "u3",
TrustedCerts: cert(s),
ClientX509Cert: "testcert.pem",
ClientX509Key: "testkey.pem",
Exts: []string{"deb", "rpm"},
}
},
check: checks(
check{"/blah/2.1.0/a.deb", "u3", "x", content, map[string]string{}},
),
},
}

uploadAndCheck := func(t *testing.T, setup func(*httptest.Server) (*context.Context, config.Upload), wantErrPlain, wantErrTLS bool, check func(r []*h.Request) error, srv *httptest.Server) {
Expand All @@ -585,21 +606,23 @@ func TestUpload(t *testing.T) {
}

for _, tt := range tests {
if tt.tryPlain {
t.Run(tt.name, func(t *testing.T) {
srv := httptest.NewServer(mux)
defer srv.Close()
uploadAndCheck(t, tt.setup, tt.wantErrPlain, tt.wantErrTLS, tt.check, srv)
})
}
if tt.tryTLS {
t.Run(tt.name+"-tls", func(t *testing.T) {
srv := httptest.NewUnstartedServer(mux)
srv.StartTLS()
defer srv.Close()
uploadAndCheck(t, tt.setup, tt.wantErrPlain, tt.wantErrTLS, tt.check, srv)
})
}
t.Run(tt.name, func(t *testing.T) {
if tt.tryPlain {
t.Run(tt.name, func(t *testing.T) {
srv := httptest.NewServer(mux)
defer srv.Close()
uploadAndCheck(t, tt.setup, tt.wantErrPlain, tt.wantErrTLS, tt.check, srv)
})
}
if tt.tryTLS {
t.Run(tt.name+"-tls", func(t *testing.T) {
srv := httptest.NewUnstartedServer(mux)
srv.StartTLS()
defer srv.Close()
uploadAndCheck(t, tt.setup, tt.wantErrPlain, tt.wantErrTLS, tt.check, srv)
})
}
})
}
}

Expand Down
32 changes: 32 additions & 0 deletions internal/http/testcert.pem
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFgTCCA2mgAwIBAgIUScqw7e1i0RlxSe+l4VSg6cJXjSAwDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCHRlc3RjZXJ0MB4XDTIyMDgxMDA3
NTMzN1oXDTIyMDkwOTA3NTMzN1owUDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxETAPBgNVBAMMCHRl
c3RjZXJ0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0I10EpRJapUo
hQrvTvK12utZ1L6UASW7twzN2l4alM/hRi9qyrUUWIhD8uB0oEaPxa8ErAJFuK2e
paNMB7j0bS4iMaaTZx7hh9IoEz8iROiBILt+68LM9zEVN19YbLws6m7IQ7LAcjj8
imshswzVIDyOArLSko8z81nEE8fAbzXzBRfG5+x4T5JnVTy/B4qNC3Rk5McNfsOj
bTUklsVOeOmsWoNsMZXUgPMKXZbwQ1fJlNEcXalfxWXF7MBVuZD59eKeHZdFCvg9
otVqdaD4tEDcm9rjd7osNEasdGPSGG6kNIbUE8LYmSTR3OL1oPoQiqi+ic9NUOer
lsUgjbHwH8B0arw7QIbNDLNgIsKJX5FuGfb6BgfWoItrGc2wqFvXAWVn0EmTGCd6
x7IioW/U4TI+WHKlSZ2PdwtGnEmp0JXzwx3n6XTja30DMZXXTZ4MXd+YdLn7Lajz
33BXm6UYiVpxkCD/3QN2+32SwYWYa/js7rf2gZ3G3lpt4Iqb82v4/p4wxRcUl5sj
ws8yteV15iecXP1ow/wmUfzLBmHQkwy9WD1poKGWL7fVzWpTe4U+lYM47mXznvzu
WkXI2K1/70L+IablW6USDdMolf4ZR8IOS8cXl30z1XP7c+u0V9SGHOLVToOEfGIx
zxgT2P8wfkAiDY/qhNT2R5nMVrSymm8CAwEAAaNTMFEwHQYDVR0OBBYEFABsCi9C
i5QxArCJBWh1R1Tw4ntLMB8GA1UdIwQYMBaAFABsCi9Ci5QxArCJBWh1R1Tw4ntL
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAEC/KYL0s/GYKtEY
PsDjF+JwJ2dAKf8ZzfykTUTH4MWA1aGVFlRs5K/pbr1r4nPNumhPyw0+q7YNE8oK
0rhL4LarZQNIPWfSybjxyKD4jQJE8qTFnu0imn9r8Lmqjwz6xoz5JPt89dzk8ysR
et7Yv4q0aFDffSTnx2JbabR53TXm6JqTFkEBn66GGyq7ZTVU0yaHUsWsWRBsLFLY
F/gQp+l8uJlEi9MQh0gWPeIUJf+uGryOsOTEpFvYQ/9kaMHaHDQQ4FdKMDpGP4xi
YNvCzn4xQkEX8r5+Kff0Nr9dflsg6TMzPUJwPKqfi6s+mj5AkB2rAiBKO/Yvu5sb
ZLfYiRP73TIj2PSi0OxhADhtBwkhYFzhnNlAF76QieXsRMnwDG0oztTxMKIcIXaW
cCwB637h+BypnR22ye8ObzCRvh7CW841Xb/qNaPsHiviEtPsejeMIXcSwVbk3cU+
zYPUYrg7+S6/BUQIcYX8sVuPHxmZlDe4Zt/wHn7PhvO7RkHKmEM+WH1snJ/0mxd7
V++YDMxBdThi33cNsfBT6ug9xEcLydSD+Q+VqOTS+YsrnvpdKl2l/A9pODcCpazn
xR/LjDsbbsHgCP+90tM46ZIS7HV5uT6Gyek/UEergtaNjmA6gFU1C8BPI2NZjn+n
BsplATncX8RY04Q6e6x4FMxLEGCK
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions internal/http/testkey.pem
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
xCjEgdOkFCQ01FTbkkb4n7xPsleEas0=
-----END PRIVATE KEY-----
4 changes: 3 additions & 1 deletion pkg/config/config.go
Expand Up @@ -864,8 +864,10 @@ type Upload struct {
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Method string `yaml:"method,omitempty" json:"method,omitempty"`
ChecksumHeader string `yaml:"checksum_header,omitempty" json:"checksum_header,omitempty"`
ClientX509Cert string `yaml:"client_x509_cert" json:"client_x509_cert"`
ClientX509Key string `yaml:"client_x509_key" json:"client_x509_key"`
TrustedCerts string `yaml:"trusted_certificates,omitempty" json:"trusted_certificates,omitempty"`
Checksum bool `yaml:"checksum,omitempty" json:"checksum,omitempty"`
Checksum bool `yaml:"checksum,omitqempty" json:"checksum,omitempty"`
scr-oath marked this conversation as resolved.
Show resolved Hide resolved
Signature bool `yaml:"signature,omitempty" json:"signature,omitempty"`
CustomArtifactName bool `yaml:"custom_artifact_name,omitempty" json:"custom_artifact_name,omitempty"`
CustomHeaders map[string]string `yaml:"custom_headers,omitempty" json:"custom_headers,omitempty"`
Expand Down
12 changes: 11 additions & 1 deletion www/docs/static/schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.