mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
fs: tls: add --client-pass support for encrypted --client-key files
This also widens the supported types
- Unencrypted PKCS#1 ("BEGIN RSA PRIVATE KEY")
- Unencrypted PKCS#8 ("BEGIN PRIVATE KEY")
- Encrypted PKCS#8 ("BEGIN ENCRYPTED PRIVATE KEY")
- Legacy PEM encryption (e.g., DEK-Info headers), which are automatically detected.
This commit is contained in:
119
bin/make-test-certs.sh
Executable file
119
bin/make-test-certs.sh
Executable file
@@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create test TLS certificates for use with rclone.
|
||||||
|
|
||||||
|
OUT_DIR="${OUT_DIR:-./tls-test}"
|
||||||
|
CA_SUBJ="${CA_SUBJ:-/C=US/ST=Test/L=Test/O=Test Org/OU=Test Unit/CN=Test Root CA}"
|
||||||
|
SERVER_CN="${SERVER_CN:-localhost}"
|
||||||
|
CLIENT_CN="${CLIENT_CN:-Test Client}"
|
||||||
|
CLIENT_KEY_PASS="${CLIENT_KEY_PASS:-testpassword}"
|
||||||
|
|
||||||
|
CA_DAYS=${CA_DAYS:-3650}
|
||||||
|
SERVER_DAYS=${SERVER_DAYS:-825}
|
||||||
|
CLIENT_DAYS=${CLIENT_DAYS:-825}
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
cd "$OUT_DIR"
|
||||||
|
|
||||||
|
# Create OpenSSL config
|
||||||
|
|
||||||
|
# CA extensions
|
||||||
|
cat > ca_openssl.cnf <<'EOF'
|
||||||
|
[ ca_ext ]
|
||||||
|
basicConstraints = critical, CA:true, pathlen:1
|
||||||
|
keyUsage = critical, keyCertSign, cRLSign
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid:always,issuer
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Server extensions (SAN includes localhost + loopback IP)
|
||||||
|
cat > server_openssl.cnf <<EOF
|
||||||
|
[ server_ext ]
|
||||||
|
basicConstraints = critical, CA:false
|
||||||
|
keyUsage = critical, digitalSignature, keyEncipherment
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid,issuer
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
|
||||||
|
[ alt_names ]
|
||||||
|
DNS.1 = ${SERVER_CN}
|
||||||
|
IP.1 = 127.0.0.1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Client extensions (for mTLS client auth)
|
||||||
|
cat > client_openssl.cnf <<'EOF'
|
||||||
|
[ client_ext ]
|
||||||
|
basicConstraints = critical, CA:false
|
||||||
|
keyUsage = critical, digitalSignature, keyEncipherment
|
||||||
|
extendedKeyUsage = clientAuth
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid,issuer
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Create CA key, CSR, and self-signed CA cert"
|
||||||
|
if [ ! -f ca.key.pem ]; then
|
||||||
|
openssl genrsa -out ca.key.pem 4096
|
||||||
|
chmod 600 ca.key.pem
|
||||||
|
fi
|
||||||
|
|
||||||
|
openssl req -new -key ca.key.pem -subj "$CA_SUBJ" -out ca.csr.pem
|
||||||
|
|
||||||
|
openssl x509 -req -in ca.csr.pem -signkey ca.key.pem \
|
||||||
|
-sha256 -days "$CA_DAYS" \
|
||||||
|
-extfile ca_openssl.cnf -extensions ca_ext \
|
||||||
|
-out ca.cert.pem
|
||||||
|
|
||||||
|
echo "Create server key (NO PASSWORD) and cert signed by CA"
|
||||||
|
openssl genrsa -out server.key.pem 2048
|
||||||
|
chmod 600 server.key.pem
|
||||||
|
|
||||||
|
openssl req -new -key server.key.pem -subj "/CN=${SERVER_CN}" -out server.csr.pem
|
||||||
|
|
||||||
|
openssl x509 -req -in server.csr.pem \
|
||||||
|
-CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial \
|
||||||
|
-out server.cert.pem -days "$SERVER_DAYS" -sha256 \
|
||||||
|
-extfile server_openssl.cnf -extensions server_ext
|
||||||
|
|
||||||
|
echo "Create client key (PASSWORD-PROTECTED), CSR, and cert"
|
||||||
|
openssl genrsa -aes256 -passout pass:"$CLIENT_KEY_PASS" -out client.key.pem 2048
|
||||||
|
chmod 600 client.key.pem
|
||||||
|
|
||||||
|
openssl req -new -key client.key.pem -passin pass:"$CLIENT_KEY_PASS" \
|
||||||
|
-subj "/CN=${CLIENT_CN}" -out client.csr.pem
|
||||||
|
|
||||||
|
openssl x509 -req -in client.csr.pem \
|
||||||
|
-CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial \
|
||||||
|
-out client.cert.pem -days "$CLIENT_DAYS" -sha256 \
|
||||||
|
-extfile client_openssl.cnf -extensions client_ext
|
||||||
|
|
||||||
|
echo "Verify chain"
|
||||||
|
openssl verify -CAfile ca.cert.pem server.cert.pem client.cert.pem
|
||||||
|
|
||||||
|
echo "Done"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Summary"
|
||||||
|
echo "-------"
|
||||||
|
printf "%-22s %s\n" \
|
||||||
|
"CA key:" "ca.key.pem" \
|
||||||
|
"CA cert:" "ca.cert.pem" \
|
||||||
|
"Server key:" "server.key.pem (no password)" \
|
||||||
|
"Server CSR:" "server.csr.pem" \
|
||||||
|
"Server cert:" "server.cert.pem (SAN: ${SERVER_CN}, 127.0.0.1)" \
|
||||||
|
"Client key:" "client.key.pem (encrypted)" \
|
||||||
|
"Client CSR:" "client.csr.pem" \
|
||||||
|
"Client cert:" "client.cert.pem" \
|
||||||
|
"Client key password:" "$CLIENT_KEY_PASS"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Test rclone server"
|
||||||
|
echo
|
||||||
|
echo "rclone serve http -vv --addr :8080 --cert ${OUT_DIR}/server.cert.pem --key ${OUT_DIR}/server.key.pem --client-ca ${OUT_DIR}/ca.cert.pem ."
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Test rclone client"
|
||||||
|
echo
|
||||||
|
echo "rclone lsf :http: --http-url 'https://localhost:8080' --ca-cert ${OUT_DIR}/ca.cert.pem --client-cert ${OUT_DIR}/client.cert.pem --client-key ${OUT_DIR}/client.key.pem --client-pass \$(rclone obscure $CLIENT_KEY_PASS)"
|
||||||
|
echo
|
||||||
@@ -3000,6 +3000,20 @@ The `--client-key` flag is required too when using this.
|
|||||||
This loads the PEM encoded client side private key used for mutual TLS
|
This loads the PEM encoded client side private key used for mutual TLS
|
||||||
authentication. Used in conjunction with `--client-cert`.
|
authentication. Used in conjunction with `--client-cert`.
|
||||||
|
|
||||||
|
Supported types are:
|
||||||
|
|
||||||
|
- Unencrypted PKCS#1 ("BEGIN RSA PRIVATE KEY")
|
||||||
|
- Unencrypted PKCS#8 ("BEGIN PRIVATE KEY")
|
||||||
|
- Encrypted PKCS#8 ("BEGIN ENCRYPTED PRIVATE KEY")
|
||||||
|
- Legacy PEM encryption (e.g., DEK-Info headers), which are automatically detected.
|
||||||
|
|
||||||
|
### --client-pass string
|
||||||
|
|
||||||
|
This can be used to supply an optional password to decrypt the client key file.
|
||||||
|
|
||||||
|
**NB** the password should be obscured so it should be the output of
|
||||||
|
`rclone obscure YOURPASSWORD`.
|
||||||
|
|
||||||
### --no-check-certificate
|
### --no-check-certificate
|
||||||
|
|
||||||
`--no-check-certificate` controls whether a client verifies the
|
`--no-check-certificate` controls whether a client verifies the
|
||||||
|
|||||||
@@ -438,6 +438,12 @@ var ConfigOptionsInfo = Options{{
|
|||||||
Default: "",
|
Default: "",
|
||||||
Help: "Client SSL private key (PEM) for mutual TLS auth",
|
Help: "Client SSL private key (PEM) for mutual TLS auth",
|
||||||
Groups: "Networking",
|
Groups: "Networking",
|
||||||
|
}, {
|
||||||
|
Name: "client_pass",
|
||||||
|
Default: "",
|
||||||
|
Help: "Password for client SSL private key (PEM) for mutual TLS auth (obscured)",
|
||||||
|
Groups: "Networking",
|
||||||
|
IsPassword: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "multi_thread_cutoff",
|
Name: "multi_thread_cutoff",
|
||||||
Default: SizeSuffix(256 * 1024 * 1024),
|
Default: SizeSuffix(256 * 1024 * 1024),
|
||||||
@@ -644,6 +650,7 @@ type ConfigInfo struct {
|
|||||||
CaCert []string `config:"ca_cert"` // Client Side CA
|
CaCert []string `config:"ca_cert"` // Client Side CA
|
||||||
ClientCert string `config:"client_cert"` // Client Side Cert
|
ClientCert string `config:"client_cert"` // Client Side Cert
|
||||||
ClientKey string `config:"client_key"` // Client Side Key
|
ClientKey string `config:"client_key"` // Client Side Key
|
||||||
|
ClientPass string `config:"client_pass"` // Client Side Key Password (obscured)
|
||||||
MultiThreadCutoff SizeSuffix `config:"multi_thread_cutoff"`
|
MultiThreadCutoff SizeSuffix `config:"multi_thread_cutoff"`
|
||||||
MultiThreadStreams int `config:"multi_thread_streams"`
|
MultiThreadStreams int `config:"multi_thread_streams"`
|
||||||
MultiThreadSet bool `config:"multi_thread_set"` // whether MultiThreadStreams was set (set in fs/config/configflags)
|
MultiThreadSet bool `config:"multi_thread_set"` // whether MultiThreadStreams was set (set in fs/config/configflags)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -18,7 +20,9 @@ import (
|
|||||||
|
|
||||||
"github.com/rclone/rclone/fs"
|
"github.com/rclone/rclone/fs"
|
||||||
"github.com/rclone/rclone/fs/accounting"
|
"github.com/rclone/rclone/fs/accounting"
|
||||||
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
"github.com/rclone/rclone/lib/structs"
|
"github.com/rclone/rclone/lib/structs"
|
||||||
|
"github.com/youmark/pkcs8"
|
||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -48,6 +52,156 @@ func ResetTransport() {
|
|||||||
noTransport = new(sync.Once)
|
noTransport = new(sync.Once)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadKeyPair loads a TLS certificate and private key from PEM-encoded files,
|
||||||
|
// with extended support for encrypted private keys.
|
||||||
|
//
|
||||||
|
// This function is designed as a robust replacement for tls.X509KeyPair,
|
||||||
|
// providing the same core functionality but adding support for
|
||||||
|
// password-protected private keys.
|
||||||
|
//
|
||||||
|
// The certificate file (certFile) must contain one or more PEM-encoded
|
||||||
|
// certificates. The first certificate is treated as the leaf certificate, and
|
||||||
|
// any subsequent certificates are treated as its chain.
|
||||||
|
//
|
||||||
|
// The key file (keyFile) must contain a PEM-encoded private key. Supported
|
||||||
|
// formats are:
|
||||||
|
//
|
||||||
|
// - Unencrypted PKCS#1 ("BEGIN RSA PRIVATE KEY")
|
||||||
|
// - Unencrypted PKCS#8 ("BEGIN PRIVATE KEY")
|
||||||
|
// - Encrypted PKCS#8 ("BEGIN ENCRYPTED PRIVATE KEY")
|
||||||
|
// - Legacy PEM encryption (e.g., DEK-Info headers), which are automatically detected.
|
||||||
|
//
|
||||||
|
// The password parameter is used to decrypt the private key. If the
|
||||||
|
// key is not encrypted, this parameter is ignored and can be an empty
|
||||||
|
// string. The password should be an obscured string.
|
||||||
|
//
|
||||||
|
// On success, it returns a fully populated tls.Certificate struct, including the
|
||||||
|
// Leaf certificate field.
|
||||||
|
func LoadKeyPair(certFile, keyFile, password string) (cert tls.Certificate, err error) {
|
||||||
|
certPEM, err := os.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("read cert: %w", err)
|
||||||
|
}
|
||||||
|
keyPEM, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("read key: %w", err)
|
||||||
|
}
|
||||||
|
if password != "" {
|
||||||
|
password, err = obscure.Reveal(password)
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("reveal key password: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path: unencrypted PKCS#1/PKCS#8
|
||||||
|
cert, err = tls.X509KeyPair(certPEM, keyPEM)
|
||||||
|
if err == nil {
|
||||||
|
if len(cert.Certificate) == 0 {
|
||||||
|
return cert, errors.New("no certificates parsed")
|
||||||
|
}
|
||||||
|
leaf, err := x509.ParseCertificate(cert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("parse leaf: %w", err)
|
||||||
|
}
|
||||||
|
cert.Leaf = leaf
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt / parse key manually
|
||||||
|
block, rest := pem.Decode(keyPEM)
|
||||||
|
if block == nil {
|
||||||
|
return cert, errors.New("no PEM block in key")
|
||||||
|
}
|
||||||
|
if len(rest) != 0 {
|
||||||
|
fs.Debugf(nil, "Trailing data (%d bytes) in key PEM loaded from %q", len(rest), keyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
var privKey any
|
||||||
|
switch {
|
||||||
|
case block.Type == "ENCRYPTED PRIVATE KEY":
|
||||||
|
if password == "" {
|
||||||
|
return cert, errors.New("key is encrypted but no --client-pass provided")
|
||||||
|
}
|
||||||
|
privKey, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("parse encrypted PKCS#8: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case x509.IsEncryptedPEMBlock(block): //nolint:staticcheck // this is Legacy and insecure
|
||||||
|
if password == "" {
|
||||||
|
return cert, errors.New("key is encrypted but no --client-pass provided")
|
||||||
|
}
|
||||||
|
der, err := x509.DecryptPEMBlock(block, []byte(password)) //nolint:staticcheck // this is Legacy and insecure
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("decrypt PEM key: %w", err)
|
||||||
|
}
|
||||||
|
// Try PKCS#8, then RSA PKCS#1, then EC
|
||||||
|
if k, kerr1 := x509.ParsePKCS8PrivateKey(der); kerr1 == nil {
|
||||||
|
privKey = k
|
||||||
|
} else if k, kerr2 := x509.ParsePKCS1PrivateKey(der); kerr2 == nil {
|
||||||
|
privKey = k
|
||||||
|
} else if k, kerr3 := x509.ParseECPrivateKey(der); kerr3 == nil {
|
||||||
|
privKey = k
|
||||||
|
} else {
|
||||||
|
return cert, fmt.Errorf("parse decrypted key: pkcs8: %v, pkcs1: %v, ec: %v", kerr1, kerr2, kerr3)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Unencrypted specific types
|
||||||
|
switch block.Type {
|
||||||
|
case "PRIVATE KEY":
|
||||||
|
k, kerr := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if kerr != nil {
|
||||||
|
return cert, fmt.Errorf("parse PKCS#8: %w", kerr)
|
||||||
|
}
|
||||||
|
privKey = k
|
||||||
|
case "RSA PRIVATE KEY":
|
||||||
|
k, kerr := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if kerr != nil {
|
||||||
|
return cert, fmt.Errorf("parse PKCS#1 RSA: %w", kerr)
|
||||||
|
}
|
||||||
|
privKey = k
|
||||||
|
case "EC PRIVATE KEY":
|
||||||
|
k, kerr := x509.ParseECPrivateKey(block.Bytes)
|
||||||
|
if kerr != nil {
|
||||||
|
return cert, fmt.Errorf("parse EC: %w", kerr)
|
||||||
|
}
|
||||||
|
privKey = k
|
||||||
|
default:
|
||||||
|
return cert, fmt.Errorf("unsupported key type %q", block.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build cert chain from PEM
|
||||||
|
var certDERs [][]byte
|
||||||
|
for rest := certPEM; ; {
|
||||||
|
var b *pem.Block
|
||||||
|
b, rest = pem.Decode(rest)
|
||||||
|
if b == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if b.Type == "CERTIFICATE" {
|
||||||
|
certDERs = append(certDERs, b.Bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(certDERs) == 0 {
|
||||||
|
return cert, fmt.Errorf("no CERTIFICATE blocks in %s", certFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert = tls.Certificate{
|
||||||
|
Certificate: certDERs,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leaf is always the first certificate
|
||||||
|
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
return cert, fmt.Errorf("parse leaf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewTransportCustom returns an http.RoundTripper with the correct timeouts.
|
// NewTransportCustom returns an http.RoundTripper with the correct timeouts.
|
||||||
// The customize function is called if set to give the caller an opportunity to
|
// The customize function is called if set to give the caller an opportunity to
|
||||||
// customize any defaults in the Transport.
|
// customize any defaults in the Transport.
|
||||||
@@ -85,17 +239,10 @@ func NewTransportCustom(ctx context.Context, customize func(*http.Transport)) *T
|
|||||||
if ci.ClientCert == "" || ci.ClientKey == "" {
|
if ci.ClientCert == "" || ci.ClientKey == "" {
|
||||||
fs.Fatalf(nil, "Both --client-cert and --client-key must be set")
|
fs.Fatalf(nil, "Both --client-cert and --client-key must be set")
|
||||||
}
|
}
|
||||||
cert, err := tls.LoadX509KeyPair(ci.ClientCert, ci.ClientKey)
|
cert, err := LoadKeyPair(ci.ClientCert, ci.ClientKey, ci.ClientPass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Fatalf(nil, "Failed to load --client-cert/--client-key pair: %v", err)
|
fs.Fatalf(nil, "Failed to load --client-cert/--client-key pair: %v", err)
|
||||||
}
|
}
|
||||||
if cert.Leaf == nil {
|
|
||||||
// Leaf is always the first certificate
|
|
||||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
|
||||||
if err != nil {
|
|
||||||
fs.Fatalf(nil, "Failed to parse the certificate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.TLSClientConfig.Certificates = []tls.Certificate{cert}
|
t.TLSClientConfig.Certificates = []tls.Certificate{cert}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,14 +334,12 @@ func NewClientWithUnixSocket(ctx context.Context, path string) *http.Client {
|
|||||||
// * Updates metrics
|
// * Updates metrics
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
*http.Transport
|
*http.Transport
|
||||||
|
ci *fs.ConfigInfo
|
||||||
dump fs.DumpFlags
|
dump fs.DumpFlags
|
||||||
filterRequest func(req *http.Request)
|
filterRequest func(req *http.Request)
|
||||||
userAgent string
|
userAgent string
|
||||||
headers []*fs.HTTPOption
|
headers []*fs.HTTPOption
|
||||||
metrics *Metrics
|
metrics *Metrics
|
||||||
// Filename of the client cert in case we need to reload it
|
|
||||||
clientCert string
|
|
||||||
clientKey string
|
|
||||||
// Mutex for serializing attempts at reloading the certificates
|
// Mutex for serializing attempts at reloading the certificates
|
||||||
reloadMutex sync.Mutex
|
reloadMutex sync.Mutex
|
||||||
}
|
}
|
||||||
@@ -203,13 +348,12 @@ type Transport struct {
|
|||||||
// roundtrips including the body if logBody is set.
|
// roundtrips including the body if logBody is set.
|
||||||
func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport {
|
func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport {
|
||||||
return &Transport{
|
return &Transport{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
dump: ci.Dump,
|
ci: ci,
|
||||||
userAgent: ci.UserAgent,
|
dump: ci.Dump,
|
||||||
headers: ci.Headers,
|
userAgent: ci.UserAgent,
|
||||||
metrics: DefaultMetrics,
|
headers: ci.Headers,
|
||||||
clientCert: ci.ClientCert,
|
metrics: DefaultMetrics,
|
||||||
clientKey: ci.ClientKey,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,20 +453,10 @@ func (t *Transport) reloadCertificates() {
|
|||||||
if !isCertificateExpired(t.TLSClientConfig) {
|
if !isCertificateExpired(t.TLSClientConfig) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
cert, err := LoadKeyPair(t.ci.ClientCert, t.ci.ClientKey, t.ci.ClientPass)
|
||||||
cert, err := tls.LoadX509KeyPair(t.clientCert, t.clientKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Fatalf(nil, "Failed to load --client-cert/--client-key pair: %v", err)
|
fs.Fatalf(nil, "Failed to load --client-cert/--client-key pair: %v", err)
|
||||||
}
|
}
|
||||||
// Check if we need to parse the certificate again, we need it
|
|
||||||
// for checking the expiration date
|
|
||||||
if cert.Leaf == nil {
|
|
||||||
// Leaf is always the first certificate
|
|
||||||
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
|
||||||
if err != nil {
|
|
||||||
fs.Fatalf(nil, "Failed to parse the certificate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.TLSClientConfig.Certificates = []tls.Certificate{cert}
|
t.TLSClientConfig.Certificates = []tls.Certificate{cert}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user