mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
x509 request authenticator
This commit is contained in:
parent
7f2d0c0f71
commit
09ba404fb7
@ -19,6 +19,7 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -222,6 +223,11 @@ func main() {
|
||||
ReadTimeout: 5 * time.Minute,
|
||||
WriteTimeout: 5 * time.Minute,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
TLSConfig: &tls.Config{
|
||||
// Populate PeerCertificates in requests, but don't reject connections without certificates
|
||||
// This allows certificates to be validated by authenticators, while still allowing other auth types
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
},
|
||||
}
|
||||
glog.Infof("Serving securely on %s", secureLocation)
|
||||
go func() {
|
||||
|
19
plugin/pkg/auth/authenticator/request/x509/doc.go
Normal file
19
plugin/pkg/auth/authenticator/request/x509/doc.go
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package x509 provides a request authenticator that validates and
|
||||
// extracts user information from client certificates
|
||||
package x509
|
111
plugin/pkg/auth/authenticator/request/x509/x509.go
Normal file
111
plugin/pkg/auth/authenticator/request/x509/x509.go
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"net/http"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
// UserConversion defines an interface for extracting user info from a client certificate chain
|
||||
type UserConversion interface {
|
||||
User(chain []*x509.Certificate) (user.Info, bool, error)
|
||||
}
|
||||
|
||||
// UserConversionFunc is a function that implements the UserConversion interface.
|
||||
type UserConversionFunc func(chain []*x509.Certificate) (user.Info, bool, error)
|
||||
|
||||
// User implements x509.UserConversion
|
||||
func (f UserConversionFunc) User(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
return f(chain)
|
||||
}
|
||||
|
||||
// Authenticator implements request.Authenticator by extracting user info from verified client certificates
|
||||
type Authenticator struct {
|
||||
opts x509.VerifyOptions
|
||||
user UserConversion
|
||||
}
|
||||
|
||||
// New returns a request.Authenticator that verifies client certificates using the provided
|
||||
// VerifyOptions, and converts valid certificate chains into user.Info using the provided UserConversion
|
||||
func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
|
||||
return &Authenticator{opts, user}
|
||||
}
|
||||
|
||||
// AuthenticateRequest authenticates the request using presented client certificates
|
||||
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
|
||||
if req.TLS == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
var errors util.ErrorList
|
||||
for _, cert := range req.TLS.PeerCertificates {
|
||||
chains, err := cert.Verify(a.opts)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, chain := range chains {
|
||||
user, ok, err := a.user.User(chain)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok {
|
||||
return user, ok, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false, errors.ToError()
|
||||
}
|
||||
|
||||
// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
|
||||
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
|
||||
func DefaultVerifyOptions() x509.VerifyOptions {
|
||||
return x509.VerifyOptions{
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
}
|
||||
|
||||
// CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName
|
||||
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
if len(chain[0].Subject.CommonName) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &user.DefaultInfo{Name: chain[0].Subject.CommonName}, true, nil
|
||||
})
|
||||
|
||||
// DNSNameUserConversion builds user info from a certificate chain using the first DNSName on the certificate
|
||||
var DNSNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
if len(chain[0].DNSNames) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &user.DefaultInfo{Name: chain[0].DNSNames[0]}, true, nil
|
||||
})
|
||||
|
||||
// EmailAddressUserConversion builds user info from a certificate chain using the first EmailAddress on the certificate
|
||||
var EmailAddressUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
if len(chain[0].EmailAddresses) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &user.DefaultInfo{Name: chain[0].EmailAddresses[0]}, true, nil
|
||||
})
|
575
plugin/pkg/auth/authenticator/request/x509/x509_test.go
Normal file
575
plugin/pkg/auth/authenticator/request/x509/x509_test.go
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
|
||||
)
|
||||
|
||||
const (
|
||||
rootCACert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAqKgAwIBAgIJAOoObf5kuGgZMA0GCSqGSIb3DQEBBQUAMGcxCzAJBgNV
|
||||
BAYTAlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0G
|
||||
A1UEChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MRAwDgYDVQQDEwdST09UIENB
|
||||
MB4XDTE0MTIwODIwMjU1N1oXDTI0MTIwNTIwMjU1N1owZzELMAkGA1UEBhMCVVMx
|
||||
ETAPBgNVBAgTCE15IFN0YXRlMRAwDgYDVQQHEwdNeSBDaXR5MQ8wDQYDVQQKEwZN
|
||||
eSBPcmcxEDAOBgNVBAsTB015IFVuaXQxEDAOBgNVBAMTB1JPT1QgQ0EwgZ8wDQYJ
|
||||
KoZIhvcNAQEBBQADgY0AMIGJAoGBAMfcayGpuF4vwrP8SXKDMCTJ9HV1cvb1NYEc
|
||||
UgKF0RtcWpK+i0jvhcEs0TPDZIwLSwFw6UMEt5xy4LUlv1K/SHGY3Ym3m/TXMnB9
|
||||
gkfrbWlY9LBIm4oVXwrPWyNIe74qAh1Oi03J1492uUPdHhcEmf01RIP6IIqIDuDL
|
||||
xNNggeIrAgMBAAGjgewwgekwHQYDVR0OBBYEFD3w9zA9O+s6VWj69UPJx6zhPxB4
|
||||
MIGZBgNVHSMEgZEwgY6AFD3w9zA9O+s6VWj69UPJx6zhPxB4oWukaTBnMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENpdHkxDzAN
|
||||
BgNVBAoTBk15IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMHUk9PVCBD
|
||||
QYIJAOoObf5kuGgZMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMBEGCWCGSAGG
|
||||
+EIBAQQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQBSrJjMevHUgBKkjaSyeKhOqd8V
|
||||
XlbA//N/mtJTD3eD/HUZBgyMcBH+sk6hnO8N9ICHtndkTrCElME9N3JA+wg2fHLW
|
||||
Lj09yrFm7u/0Wd+lcnBnczzoMDhlOjyVqsgIMhisFEw1VVaMoHblYnzY0B+oKNnu
|
||||
H9oc7u5zhTGXeV8WPg==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
selfSignedCert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDEzCCAnygAwIBAgIJAMaPaFbGgJN+MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNV
|
||||
BAYTAlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0G
|
||||
A1UEChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MQ4wDAYDVQQDEwVzZWxmMTAe
|
||||
Fw0xNDEyMDgyMDI1NThaFw0yNDEyMDUyMDI1NThaMGUxCzAJBgNVBAYTAlVTMREw
|
||||
DwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UEChMGTXkg
|
||||
T3JnMRAwDgYDVQQLEwdNeSBVbml0MQ4wDAYDVQQDEwVzZWxmMTCBnzANBgkqhkiG
|
||||
9w0BAQEFAAOBjQAwgYkCgYEA2NAe5AE//Uccy/HSqr4TBhzSe4QD5NYOWuTSKVeX
|
||||
LLJ0IK2SD3PfnFM/Y0wERx6ORZPGxM0ByPO1RgZe14uFSPEdnD2WTx4lcALK9Jci
|
||||
IrsvGRyMH0ZT6Q+35ScchAOdOJJYcvXEWf/heZauogzNQAGskwZdYxQB4zwC/es/
|
||||
EE0CAwEAAaOByjCBxzAdBgNVHQ4EFgQUfKsCqEU/sCgvcZFSonHu2UArQ3EwgZcG
|
||||
A1UdIwSBjzCBjIAUfKsCqEU/sCgvcZFSonHu2UArQ3GhaaRnMGUxCzAJBgNVBAYT
|
||||
AlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UE
|
||||
ChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MQ4wDAYDVQQDEwVzZWxmMYIJAMaP
|
||||
aFbGgJN+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAxpo9Nyp4d3TT
|
||||
FnEC4erqQGgbc15fOF47J7bgXxsKK8o8oR/CzQ+08KhoDn3WgV39rEfX2jENDdWp
|
||||
ze3kOoP+iWSmTySHMSKVMppp0Xnls6t38mrsXtPuY8fGD2GS6VllaizMqc3wShNK
|
||||
4HADGF3q5z8hZYSV9ICQYHu5T9meF8M=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
clientCNCert = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: Dec 8 20:25:58 2014 GMT
|
||||
Not After : Dec 5 20:25:58 2024 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=client_cn
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:a5:30:b3:2b:c0:bd:cb:29:cf:e2:d8:fd:68:b0:
|
||||
03:c3:a6:3b:1b:ec:36:73:a1:52:5d:27:ee:02:35:
|
||||
5c:51:ed:3d:3b:54:d7:11:f5:38:94:ee:fd:cc:0c:
|
||||
22:a8:f8:8e:11:2f:7c:43:5a:aa:07:3f:95:4f:50:
|
||||
22:7d:aa:e2:5d:2a:90:3d:02:1a:5b:d2:cf:3f:fb:
|
||||
dc:58:32:c5:ce:2f:81:58:31:20:eb:35:d3:53:d3:
|
||||
42:47:c2:13:68:93:62:58:b6:46:60:48:17:df:d2:
|
||||
8c:c3:40:47:cf:67:ea:27:0f:09:78:e9:d5:2a:64:
|
||||
1e:c4:33:5a:d6:0d:7a:79:93
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
E7:FB:1F:45:F0:71:77:AF:8C:10:4A:0A:42:03:F5:1F:1F:07:CF:DF
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78
|
||||
DirName:/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=ROOT CA
|
||||
serial:EA:0E:6D:FE:64:B8:68:19
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
<EMPTY>
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
Netscape Cert Type:
|
||||
SSL Client
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
08:bc:b4:80:a5:3b:be:9a:78:f9:47:3f:c0:2d:75:e3:10:89:
|
||||
61:b1:6a:dd:f4:a4:c4:6a:d3:6f:27:30:7f:2d:07:78:d9:12:
|
||||
03:bc:a5:44:68:f3:10:bc:aa:32:e3:3f:6a:16:12:25:eb:82:
|
||||
ac:ae:30:ef:0d:be:87:11:13:e7:2f:78:69:67:36:62:ba:aa:
|
||||
51:8a:ee:6e:1e:ca:35:75:95:25:2d:db:e6:cb:71:70:95:25:
|
||||
76:99:13:02:57:99:56:25:a3:33:55:a2:6a:30:87:8b:97:e6:
|
||||
68:f3:c1:37:3c:c1:14:26:90:a0:dd:d3:02:3a:e9:c2:9e:59:
|
||||
d2:44
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDczCCAtygAwIBAgIBATANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENpdHkxDzANBgNVBAoTBk15
|
||||
IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMHUk9PVCBDQTAeFw0xNDEy
|
||||
MDgyMDI1NThaFw0yNDEyMDUyMDI1NThaMGkxCzAJBgNVBAYTAlVTMREwDwYDVQQI
|
||||
EwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UEChMGTXkgT3JnMRAw
|
||||
DgYDVQQLEwdNeSBVbml0MRIwEAYDVQQDFAljbGllbnRfY24wgZ8wDQYJKoZIhvcN
|
||||
AQEBBQADgY0AMIGJAoGBAKUwsyvAvcspz+LY/WiwA8OmOxvsNnOhUl0n7gI1XFHt
|
||||
PTtU1xH1OJTu/cwMIqj4jhEvfENaqgc/lU9QIn2q4l0qkD0CGlvSzz/73Fgyxc4v
|
||||
gVgxIOs101PTQkfCE2iTYli2RmBIF9/SjMNAR89n6icPCXjp1SpkHsQzWtYNenmT
|
||||
AgMBAAGjggErMIIBJzAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM
|
||||
IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU5/sfRfBxd6+MEEoKQgP1
|
||||
Hx8Hz98wgZkGA1UdIwSBkTCBjoAUPfD3MD076zpVaPr1Q8nHrOE/EHiha6RpMGcx
|
||||
CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0
|
||||
eTEPMA0GA1UEChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MRAwDgYDVQQDEwdS
|
||||
T09UIENBggkA6g5t/mS4aBkwCQYDVR0RBAIwADATBgNVHSUEDDAKBggrBgEFBQcD
|
||||
AjARBglghkgBhvhCAQEEBAMCB4AwDQYJKoZIhvcNAQELBQADgYEACLy0gKU7vpp4
|
||||
+Uc/wC114xCJYbFq3fSkxGrTbycwfy0HeNkSA7ylRGjzELyqMuM/ahYSJeuCrK4w
|
||||
7w2+hxET5y94aWc2YrqqUYrubh7KNXWVJS3b5stxcJUldpkTAleZViWjM1WiajCH
|
||||
i5fmaPPBNzzBFCaQoN3TAjrpwp5Z0kQ=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
clientDNSCert = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 4 (0x4)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: Dec 8 20:25:58 2014 GMT
|
||||
Not After : Dec 5 20:25:58 2024 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=client_dns
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:b0:6d:16:6a:fc:28:f7:dc:da:2c:a8:e4:0c:27:
|
||||
3c:27:ce:ae:d5:72:d9:3c:eb:af:3d:a3:83:98:5b:
|
||||
85:d8:68:f4:bd:53:57:d2:ad:e8:71:b1:18:8e:ae:
|
||||
37:8e:02:9c:b2:6c:92:09:cc:5e:e6:74:a1:4b:e1:
|
||||
50:41:08:9a:5e:d4:20:0b:6f:c7:c0:34:a8:e6:be:
|
||||
77:1d:43:1f:2c:df:dc:ca:9d:1a:0a:9f:a3:6e:0a:
|
||||
60:f1:6d:d9:7f:f0:f1:ea:66:9d:4c:f3:de:62:af:
|
||||
b1:92:70:f1:bb:8a:81:f4:9c:3c:b8:c9:e8:04:18:
|
||||
70:2f:77:74:48:d9:cd:e5:af
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
6E:A3:F6:01:52:79:4D:46:78:3C:D0:AB:4A:75:96:AC:7D:6C:08:BE
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78
|
||||
DirName:/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=ROOT CA
|
||||
serial:EA:0E:6D:FE:64:B8:68:19
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:client_dns.example.com
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
Netscape Cert Type:
|
||||
SSL Client
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
69:20:83:0f:16:f8:b6:f5:04:98:56:a4:b2:67:32:e0:82:80:
|
||||
da:8e:54:06:94:96:cd:56:eb:90:4c:f4:3c:50:80:6a:25:ac:
|
||||
3d:e2:81:05:e4:89:2b:55:63:9a:2d:4a:da:3b:c4:97:5e:1a:
|
||||
e9:6f:83:b8:05:4a:dc:bd:ab:b0:a0:75:d0:1e:b5:c5:8d:f3:
|
||||
f6:92:f1:52:d2:81:67:fc:6f:74:ee:49:37:73:08:bc:f5:26:
|
||||
86:67:f5:82:04:ff:db:5a:9f:f9:6b:df:2f:f5:75:61:f2:a5:
|
||||
91:0b:05:56:5b:e8:d1:36:d7:56:7a:ed:7d:e5:5f:2a:08:87:
|
||||
c2:48
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjDCCAvWgAwIBAgIBBDANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENpdHkxDzANBgNVBAoTBk15
|
||||
IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMHUk9PVCBDQTAeFw0xNDEy
|
||||
MDgyMDI1NThaFw0yNDEyMDUyMDI1NThaMGoxCzAJBgNVBAYTAlVTMREwDwYDVQQI
|
||||
EwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UEChMGTXkgT3JnMRAw
|
||||
DgYDVQQLEwdNeSBVbml0MRMwEQYDVQQDFApjbGllbnRfZG5zMIGfMA0GCSqGSIb3
|
||||
DQEBAQUAA4GNADCBiQKBgQCwbRZq/Cj33NosqOQMJzwnzq7Vctk86689o4OYW4XY
|
||||
aPS9U1fSrehxsRiOrjeOApyybJIJzF7mdKFL4VBBCJpe1CALb8fANKjmvncdQx8s
|
||||
39zKnRoKn6NuCmDxbdl/8PHqZp1M895ir7GScPG7ioH0nDy4yegEGHAvd3RI2c3l
|
||||
rwIDAQABo4IBQzCCAT8wCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
|
||||
TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFG6j9gFSeU1GeDzQq0p1
|
||||
lqx9bAi+MIGZBgNVHSMEgZEwgY6AFD3w9zA9O+s6VWj69UPJx6zhPxB4oWukaTBn
|
||||
MQswCQYDVQQGEwJVUzERMA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENp
|
||||
dHkxDzANBgNVBAoTBk15IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMH
|
||||
Uk9PVCBDQYIJAOoObf5kuGgZMCEGA1UdEQQaMBiCFmNsaWVudF9kbnMuZXhhbXBs
|
||||
ZS5jb20wEwYDVR0lBAwwCgYIKwYBBQUHAwIwEQYJYIZIAYb4QgEBBAQDAgeAMA0G
|
||||
CSqGSIb3DQEBCwUAA4GBAGkggw8W+Lb1BJhWpLJnMuCCgNqOVAaUls1W65BM9DxQ
|
||||
gGolrD3igQXkiStVY5otSto7xJdeGulvg7gFSty9q7CgddAetcWN8/aS8VLSgWf8
|
||||
b3TuSTdzCLz1JoZn9YIE/9tan/lr3y/1dWHypZELBVZb6NE211Z67X3lXyoIh8JI
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
clientEmailCert = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 2 (0x2)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: Dec 8 20:25:58 2014 GMT
|
||||
Not After : Dec 5 20:25:58 2024 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=client_email
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:bf:f3:c3:d7:50:d5:64:d6:d2:e3:6c:bb:7e:5d:
|
||||
4b:41:63:76:9c:c4:c8:33:9a:37:ee:68:24:1e:26:
|
||||
cf:de:57:79:d6:dc:53:b6:da:12:c6:c0:95:7d:69:
|
||||
b8:af:1d:4e:8f:a5:83:8b:22:78:e3:94:cc:6e:fe:
|
||||
24:e2:05:91:ed:1c:01:b7:e1:53:91:aa:51:53:7a:
|
||||
55:6e:fe:0c:ef:c1:66:70:12:0c:85:94:95:c6:3e:
|
||||
f5:35:58:4d:3f:11:b1:5a:d6:ec:a1:f5:21:c1:e6:
|
||||
1f:c1:91:5b:67:89:25:2a:e3:86:27:6b:d8:31:7b:
|
||||
f1:0d:83:c7:f2:68:70:f0:23
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
76:22:99:CD:3D:BA:90:62:0F:BE:E7:5B:57:8D:31:1D:25:27:C6:6A
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78
|
||||
DirName:/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=ROOT CA
|
||||
serial:EA:0E:6D:FE:64:B8:68:19
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
email:client_email@example.com
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Client Authentication
|
||||
Netscape Cert Type:
|
||||
SSL Client
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
80:70:19:d2:5c:c1:cf:d2:b6:e5:0e:76:cd:8f:c2:8d:a8:19:
|
||||
07:86:22:3f:a4:b1:98:c6:98:c1:dc:f8:99:5b:20:5c:6d:17:
|
||||
6b:fa:8b:4c:1b:86:14:b4:71:f7:41:22:03:ca:ec:2c:cd:ae:
|
||||
77:93:bd:08:06:8c:3c:06:ce:04:2c:b1:ce:79:20:0d:d5:01:
|
||||
1c:bd:66:60:38:db:4f:ad:dc:a6:33:8f:07:af:e6:bd:1c:27:
|
||||
4b:93:6a:4f:59:e3:cf:df:ff:87:f1:af:02:ad:50:06:f9:50:
|
||||
c7:59:87:bc:0c:e6:66:cd:d1:c8:df:e6:15:b2:21:b3:04:86:
|
||||
8c:89
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkDCCAvmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENpdHkxDzANBgNVBAoTBk15
|
||||
IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMHUk9PVCBDQTAeFw0xNDEy
|
||||
MDgyMDI1NThaFw0yNDEyMDUyMDI1NThaMGwxCzAJBgNVBAYTAlVTMREwDwYDVQQI
|
||||
EwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UEChMGTXkgT3JnMRAw
|
||||
DgYDVQQLEwdNeSBVbml0MRUwEwYDVQQDFAxjbGllbnRfZW1haWwwgZ8wDQYJKoZI
|
||||
hvcNAQEBBQADgY0AMIGJAoGBAL/zw9dQ1WTW0uNsu35dS0FjdpzEyDOaN+5oJB4m
|
||||
z95XedbcU7baEsbAlX1puK8dTo+lg4sieOOUzG7+JOIFke0cAbfhU5GqUVN6VW7+
|
||||
DO/BZnASDIWUlcY+9TVYTT8RsVrW7KH1IcHmH8GRW2eJJSrjhidr2DF78Q2Dx/Jo
|
||||
cPAjAgMBAAGjggFFMIIBQTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
|
||||
U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdiKZzT26kGIPvudb
|
||||
V40xHSUnxmowgZkGA1UdIwSBkTCBjoAUPfD3MD076zpVaPr1Q8nHrOE/EHiha6Rp
|
||||
MGcxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkg
|
||||
Q2l0eTEPMA0GA1UEChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MRAwDgYDVQQD
|
||||
EwdST09UIENBggkA6g5t/mS4aBkwIwYDVR0RBBwwGoEYY2xpZW50X2VtYWlsQGV4
|
||||
YW1wbGUuY29tMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBEGCWCGSAGG+EIBAQQEAwIH
|
||||
gDANBgkqhkiG9w0BAQsFAAOBgQCAcBnSXMHP0rblDnbNj8KNqBkHhiI/pLGYxpjB
|
||||
3PiZWyBcbRdr+otMG4YUtHH3QSIDyuwsza53k70IBow8Bs4ELLHOeSAN1QEcvWZg
|
||||
ONtPrdymM48Hr+a9HCdLk2pPWePP3/+H8a8CrVAG+VDHWYe8DOZmzdHI3+YVsiGz
|
||||
BIaMiQ==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
serverCert = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 7 (0x7)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=ROOT CA
|
||||
Validity
|
||||
Not Before: Dec 8 20:25:58 2014 GMT
|
||||
Not After : Dec 5 20:25:58 2024 GMT
|
||||
Subject: C=US, ST=My State, L=My City, O=My Org, OU=My Unit, CN=127.0.0.1
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:e2:50:d9:1c:ff:03:34:0d:f8:b4:0c:08:70:fc:
|
||||
2a:27:2f:42:c9:4b:90:f2:a7:f2:7c:8c:ec:58:a5:
|
||||
0f:49:29:0c:77:b5:aa:0a:aa:b7:71:e7:2d:0e:fb:
|
||||
73:2c:88:de:70:69:df:d1:b0:7f:3b:2d:28:99:2d:
|
||||
f1:43:93:13:aa:c9:98:16:05:05:fb:80:64:7b:11:
|
||||
19:44:b7:5a:8c:83:20:6f:68:73:4f:ec:78:c2:73:
|
||||
de:96:68:30:ce:2a:04:03:22:80:21:26:cc:7e:d6:
|
||||
ec:b5:58:a7:41:bb:ae:fc:2c:29:6a:d1:3a:aa:b9:
|
||||
2f:88:f5:62:d8:8e:69:f4:19
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Netscape Comment:
|
||||
OpenSSL Generated Certificate
|
||||
X509v3 Subject Key Identifier:
|
||||
36:A1:0C:B2:28:0C:77:6C:7F:96:90:11:CA:19:AF:67:1E:92:17:08
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78
|
||||
DirName:/C=US/ST=My State/L=My City/O=My Org/OU=My Unit/CN=ROOT CA
|
||||
serial:EA:0E:6D:FE:64:B8:68:19
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
<EMPTY>
|
||||
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
Netscape Cert Type:
|
||||
SSL Server
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
a9:dd:3d:64:e5:e2:fb:7e:2e:ce:52:7a:85:1d:62:0b:ec:ca:
|
||||
1d:78:51:d1:f7:13:36:1c:27:3f:69:59:27:5f:89:ac:41:5e:
|
||||
65:c6:ae:dc:18:60:18:85:5b:bb:9a:76:93:df:60:47:96:97:
|
||||
58:61:34:98:59:46:ea:d4:ad:01:6c:f7:4e:6c:9d:72:26:4d:
|
||||
76:21:1b:7a:a1:f0:e6:e6:88:61:68:f5:cc:2e:40:76:f1:57:
|
||||
04:5b:9e:d2:88:c8:ac:9e:49:b5:b4:d6:71:c1:fd:d8:b8:0f:
|
||||
c7:1a:9c:f3:3f:cc:11:60:ef:54:3a:3d:b8:8d:09:80:fe:be:
|
||||
f9:ef
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDczCCAtygAwIBAgIBBzANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJVUzER
|
||||
MA8GA1UECBMITXkgU3RhdGUxEDAOBgNVBAcTB015IENpdHkxDzANBgNVBAoTBk15
|
||||
IE9yZzEQMA4GA1UECxMHTXkgVW5pdDEQMA4GA1UEAxMHUk9PVCBDQTAeFw0xNDEy
|
||||
MDgyMDI1NThaFw0yNDEyMDUyMDI1NThaMGkxCzAJBgNVBAYTAlVTMREwDwYDVQQI
|
||||
EwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0eTEPMA0GA1UEChMGTXkgT3JnMRAw
|
||||
DgYDVQQLEwdNeSBVbml0MRIwEAYDVQQDEwkxMjcuMC4wLjEwgZ8wDQYJKoZIhvcN
|
||||
AQEBBQADgY0AMIGJAoGBAOJQ2Rz/AzQN+LQMCHD8KicvQslLkPKn8nyM7FilD0kp
|
||||
DHe1qgqqt3HnLQ77cyyI3nBp39GwfzstKJkt8UOTE6rJmBYFBfuAZHsRGUS3WoyD
|
||||
IG9oc0/seMJz3pZoMM4qBAMigCEmzH7W7LVYp0G7rvwsKWrROqq5L4j1YtiOafQZ
|
||||
AgMBAAGjggErMIIBJzAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM
|
||||
IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNqEMsigMd2x/lpARyhmv
|
||||
Zx6SFwgwgZkGA1UdIwSBkTCBjoAUPfD3MD076zpVaPr1Q8nHrOE/EHiha6RpMGcx
|
||||
CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNeSBTdGF0ZTEQMA4GA1UEBxMHTXkgQ2l0
|
||||
eTEPMA0GA1UEChMGTXkgT3JnMRAwDgYDVQQLEwdNeSBVbml0MRAwDgYDVQQDEwdS
|
||||
T09UIENBggkA6g5t/mS4aBkwCQYDVR0RBAIwADATBgNVHSUEDDAKBggrBgEFBQcD
|
||||
ATARBglghkgBhvhCAQEEBAMCBkAwDQYJKoZIhvcNAQELBQADgYEAqd09ZOXi+34u
|
||||
zlJ6hR1iC+zKHXhR0fcTNhwnP2lZJ1+JrEFeZcau3BhgGIVbu5p2k99gR5aXWGE0
|
||||
mFlG6tStAWz3TmydciZNdiEbeqHw5uaIYWj1zC5AdvFXBFue0ojIrJ5JtbTWccH9
|
||||
2LgPxxqc8z/MEWDvVDo9uI0JgP6++e8=
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
)
|
||||
|
||||
func TestX509(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
Insecure bool
|
||||
Certs []*x509.Certificate
|
||||
|
||||
Opts x509.VerifyOptions
|
||||
User UserConversion
|
||||
|
||||
ExpectUserName string
|
||||
ExpectOK bool
|
||||
ExpectErr bool
|
||||
}{
|
||||
"non-tls": {
|
||||
Insecure: true,
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"tls, no certs": {
|
||||
ExpectOK: false,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"self signed": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, selfSignedCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectErr: true,
|
||||
},
|
||||
|
||||
"server cert": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, serverCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectErr: true,
|
||||
},
|
||||
"server cert allowing non-client cert usages": {
|
||||
Opts: x509.VerifyOptions{Roots: getRootCertPool(t)},
|
||||
Certs: getCerts(t, serverCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectUserName: "127.0.0.1",
|
||||
ExpectOK: true,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"common name": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectUserName: "client_cn",
|
||||
ExpectOK: true,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"empty dns": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: DNSNameUserConversion,
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: false,
|
||||
},
|
||||
"dns": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientDNSCert),
|
||||
User: DNSNameUserConversion,
|
||||
|
||||
ExpectUserName: "client_dns.example.com",
|
||||
ExpectOK: true,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"empty email": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: EmailAddressUserConversion,
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: false,
|
||||
},
|
||||
"email": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientEmailCert),
|
||||
User: EmailAddressUserConversion,
|
||||
|
||||
ExpectUserName: "client_email@example.com",
|
||||
ExpectOK: true,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"custom conversion error": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
return nil, false, errors.New("custom error")
|
||||
}),
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
},
|
||||
"custom conversion success": {
|
||||
Opts: getDefaultVerifyOptions(t),
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
|
||||
return &user.DefaultInfo{Name: "custom"}, true, nil
|
||||
}),
|
||||
|
||||
ExpectUserName: "custom",
|
||||
ExpectOK: true,
|
||||
ExpectErr: false,
|
||||
},
|
||||
|
||||
"future cert": {
|
||||
Opts: x509.VerifyOptions{
|
||||
CurrentTime: time.Now().Add(time.Duration(-100 * time.Hour * 24 * 365)),
|
||||
Roots: getRootCertPool(t),
|
||||
},
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
},
|
||||
"expired cert": {
|
||||
Opts: x509.VerifyOptions{
|
||||
CurrentTime: time.Now().Add(time.Duration(100 * time.Hour * 24 * 365)),
|
||||
Roots: getRootCertPool(t),
|
||||
},
|
||||
Certs: getCerts(t, clientCNCert),
|
||||
User: CommonNameUserConversion,
|
||||
|
||||
ExpectOK: false,
|
||||
ExpectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, testCase := range testCases {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
if !testCase.Insecure {
|
||||
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
|
||||
}
|
||||
|
||||
a := New(testCase.Opts, testCase.User)
|
||||
|
||||
user, ok, err := a.AuthenticateRequest(req)
|
||||
|
||||
if testCase.ExpectErr && err == nil {
|
||||
t.Errorf("%s: Expected error, got none", k)
|
||||
continue
|
||||
}
|
||||
if !testCase.ExpectErr && err != nil {
|
||||
t.Errorf("%s: Got unexpected error: %v", k, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if testCase.ExpectOK != ok {
|
||||
t.Errorf("%s: Expected ok=%v, got %v", testCase.ExpectOK, ok)
|
||||
continue
|
||||
}
|
||||
|
||||
if testCase.ExpectOK {
|
||||
if testCase.ExpectUserName != user.GetName() {
|
||||
t.Errorf("%s: Expected user.name=%v, got %v", testCase.ExpectUserName, user.GetName())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultVerifyOptions(t *testing.T) x509.VerifyOptions {
|
||||
options := DefaultVerifyOptions()
|
||||
options.Roots = getRootCertPool(t)
|
||||
return options
|
||||
}
|
||||
|
||||
func getRootCertPool(t *testing.T) *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
pool.AddCert(getCert(t, rootCACert))
|
||||
return pool
|
||||
}
|
||||
|
||||
func getCert(t *testing.T, pemData string) *x509.Certificate {
|
||||
pemBlock, _ := pem.Decode([]byte(pemData))
|
||||
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing cert: %v", err)
|
||||
return nil
|
||||
}
|
||||
return cert
|
||||
}
|
||||
|
||||
func getCerts(t *testing.T, pemData ...string) []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, 0)
|
||||
for _, pemData := range pemData {
|
||||
certs = append(certs, getCert(t, pemData))
|
||||
}
|
||||
return certs
|
||||
}
|
Loading…
Reference in New Issue
Block a user