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

Validate server cert against ca cert for registry mirror #2414

Merged
merged 1 commit into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/content/en/docs/reference/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ linkTitle: "What's New?"
weight: 35
---

## Unreleased

### Added

### Fixed
- Fix issue using self-signed certificates for registry mirror [#1857](https://github.com/aws/eks-anywhere/issues/1857)

## [v0.9.0](https://github.com/aws/eks-anywhere/releases/tag/v0.9.0)

### Added
Expand Down
23 changes: 13 additions & 10 deletions pkg/crypto/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
type DefaultTlsValidator struct{}

type TlsValidator interface {
ValidateCert(host, port, cert string) error
ValidateCert(host, port, caCertContent string) error
HasSelfSignedCert(host, port string) (bool, error)
}

Expand All @@ -37,9 +37,9 @@ func (tv *DefaultTlsValidator) HasSelfSignedCert(host, port string) (bool, error
}

// ValidateCert parses the cert, ensures that the cert format is valid and verifies that the cert is valid for the url
func (tv *DefaultTlsValidator) ValidateCert(host, port, cert string) error {
func (tv *DefaultTlsValidator) ValidateCert(host, port, caCertContent string) error {
// Validates that the cert format is valid
block, _ := pem.Decode([]byte(cert))
block, _ := pem.Decode([]byte(caCertContent))
if block == nil {
return fmt.Errorf("failed to parse certificate PEM")
}
Expand All @@ -50,15 +50,18 @@ func (tv *DefaultTlsValidator) ValidateCert(host, port, cert string) error {

roots := x509.NewCertPool()
roots.AddCert(providedCert)
opts := x509.VerifyOptions{
DNSName: host,
Roots: roots,
conf := &tls.Config{
InsecureSkipVerify: false,
RootCAs: roots,
}

// Verifies that the cert is valid
_, err = providedCert.Verify(opts)
// Verifies that the cert is valid by making a connection to the endpoint
endpoint := net.JoinHostPort(host, port)
conn, err := tls.Dial("tcp", endpoint, conf)
if err != nil {
return fmt.Errorf("failed to verify certificate: %v", err)
return fmt.Errorf("verifying tls connection to host with custom CA: %v", err)
}
if err = conn.Close(); err != nil {
return fmt.Errorf("closing tls connection to %v: %v", endpoint, err)
}
return nil
}
213 changes: 193 additions & 20 deletions pkg/crypto/validator_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,95 @@
package crypto_test

import (
"crypto/tls"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/aws/eks-anywhere/pkg/constants"
"github.com/aws/eks-anywhere/pkg/crypto"
)

const (
endpoint = "unit-test.local"
invalid_endpoint = "invalid-endpoint.local"
port = constants.DefaultHttpsPort
endpoint = "127.0.0.1"
invalidEndpoint = "invalid-endpoint.local"
/*
This certificate was generated using the following commands and is valid only for `unit-test.local`
This certificate was generated using the following commands and is valid only for `127.0.0.1`
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr
openssl x509 -req -extfile <(printf "subjectAltName=DNS:unit-test.local") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
openssl x509 -req -extfile <(printf "subjectAltName=IP:127.0.0.1") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
*/
cert = `
caCert = `
-----BEGIN CERTIFICATE-----
MIICrjCCAZYCCQD6stMfKEVAGTANBgkqhkiG9w0BAQsFADAZMQswCQYDVQQGEwJV
UzEKMAgGA1UEAwwBKjAeFw0yMjA2MTUwNzE4MDRaFw0zMjA2MTIwNzE4MDRaMBkx
CzAJBgNVBAYTAlVTMQowCAYDVQQDDAEqMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAv25FFUfMGKdF9wRb3GBBmE0F0ifMz8nUb52nEX0+BGFg+x6Q8gRD
xTCvCwZoMOo2uRZpaz6KtGQywFuuQ5h8VrYB3WcuDPcpg1OJueWrITRY0NHUBMjh
JbvvqZGs8I/DfKoQOyxcXoeIMfbSW2IHnMeHzTfE9lkGiZatQVqlRQAOkshFIed6
YZ2p3hgxrzkPmTTiDNoQQotluTN0FcNgUwiKYGINVlmH4LaDobranBaMLztulWsK
0tQiDw2e7aaAZmxqaUN6QRMzwc1g2mvj+PXNf/V3/WGymsXUShy9zCx4PiMDuINW
fA/EhAdkZk0MrpYbfk221gSfxxvnd1xFLwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
AQCwRcvdYlSPYypWh7B1gCvqoq4XNk8YiRPs8X4JLly41uXlgQLfDw9oJUnR0h8c
TNObdEZEdsneinC8Gwg9hscooB2R5iYyqDjST0NAS7jPDDx9x42iRY6naUW7zuVZ
2IC/9ss4BZYlS0zztUOBDax9YOJxV3dwROwnfFqGiiiYqWuDfhlfUaHj6q+NAX9/
xISi7YOoym7wHgMlMIEwGKfMLPOgvZBnU8vMqVqhbxMjAx42VdWyyV8AOcIZxasq
jXcaCczmsh5VMZHNocnTmivmY3KwmUXgC+/4w7E/fRGPblh+2CKxBO6Wlz+ZyN1n
CNgC8QQ1opc4yanhaAZgn/Yd
-----END CERTIFICATE-----
`
serverKey = `
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDZzMrH+mOavXSn
XHV9lMywwmZARE0TLpuyUTBDuYwVHG13Yx5X7br3uwEtyp2tTUBMHRh+2uVf/7lm
fQfy0AoE5O2qa4EvuOc4zS8eHr1gW4rFC++EzA2FrTMLGczIwKzPPa4VAMteQyCj
LYmfdrOSLSM0Fn7TPFJ1c3DBjj1pY/DdIs/GPItPnL7B2m+gNSQ9E5lRJ/DmP1j8
2+hnScbB5GiLqdZBASd7T+Dwp7gUqZInMKdW6JJjbUY9xNXcHrCS7GINuXHbo5CK
z9Z19nl7+v0fzQbcVP6fH2jWAo7vfFd9IR+Tz/7zJ76UescRS1ONEu6071KfTA7j
fW1dhgqLAgMBAAECggEAdbH0NsK5Boqwuiv9laJORoqWpM4D9ISwQFkdQsvGxjW5
ddWLNSrTaUGV7n/aWycuwrLKZOq3Hvxa3OZd4DnJ4EExqXE0u2wpDwnaF2W3IpX1
VGwRv+pguEcTGUGU5zsvZ0JGizUFsOeHgIaAIzsK6MgZiPFLEa08Rhne6cmKqCMI
tueV7Rqwbbd6+pvnvHFqSrA9KHaJax9+GY0rDr3KlzuJkjK0Wy1D13j/1fkDzalN
opsxF3rShEuTHAVEiwjS+nzD5g6KZ3q2WDTRSKVAbh50rBOEPE9ERwY0vlEQRgKP
4YX/UH1DLDRiDguM2Fr4nhIiTlL98nF6EVLnD7dugQKBgQD+8ZWrvgRlvcqVJmB8
BloKWsoyJp3BgBO77ys0j1fgmQe+TPLpsnVetGmjIK75EQmv/30/lFoLXIM9sfcY
4WgOsnndXFS8gKLfvG8+yMVoSwxNIR/2dNnqZuohqp5LCMlamb7Xvb2rkeSMOx4P
Q0OkTgd0YSl2l7VUwCmBs0VBSwKBgQDas89DAkBVN6q0NRd3iqrjcAN5kKchFO3W
IyDVwrrqhodnx7c7GncPULacMjOs1FPDrhiBoE7x5pDO4v30uVQKrKRZbmN4EHJC
lAUVGKBJFG5RFGzqWrCWf4Hdzynlv5na2SYtGfcmEV5F5ja9ZoDxsOdnjvy6yh0p
E44mtB3TwQKBgGVxASn2EM/e5eXVAF05Ncia+Ytc/DaLXM7RyrI+Oyw+F+uruJgu
jy8gwEvNbHHkSqOCGHcc83tD02DQGE8JGZuHfqAK5hifYq99zhIAVzQ5cGqcPJiX
REJVsuG0fwnCNERdmqdDc136TiNSPpK6JAcTmTnAk3wBv4A6egmGqI7jAoGAG4Vb
BIyo+dBKe+jebh2WCY7T8R1B2sjecP70p9GcYdzR9z5LkXVwHA5FHHy4wfvqGoqy
7MT2ijxAZrhryrrzl3BIMjTQ8Y/oQPaNeS0jJm8avrs6RXdqF1YuSnJCTHYC72Y6
Bpzo2/J9kYA5zTWz7jYbuI1mwj6i0sNyNO6ffkECgYBJ3xZ/h5zDsrbxPDiTsXCs
3liEvmzCibkoCexflz/sN524E1d3F5jj+gsTvcXTvYTaSKLkjRgvtGT9deSCJt76
wKprVZnZJeY+y21aOW1+HM22zfrGJ1GLt1Qjx33GLDzxPJDL9gsHzyRM0S2wLa8u
z9TAYr37qFPTWOJJtmRitg==
-----END PRIVATE KEY-----
`
serverCert = `
-----BEGIN CERTIFICATE-----
MIICyDCCAbCgAwIBAgIJAJA97n2wo1wBMA0GCSqGSIb3DQEBBQUAMBkxCzAJBgNV
BAYTAlVTMQowCAYDVQQDDAEqMB4XDTIyMDYxNTA3MTkwOVoXDTIzMDYxNTA3MTkw
OVowGTELMAkGA1UEBhMCVVMxCjAIBgNVBAMMASowggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDZzMrH+mOavXSnXHV9lMywwmZARE0TLpuyUTBDuYwVHG13
Yx5X7br3uwEtyp2tTUBMHRh+2uVf/7lmfQfy0AoE5O2qa4EvuOc4zS8eHr1gW4rF
C++EzA2FrTMLGczIwKzPPa4VAMteQyCjLYmfdrOSLSM0Fn7TPFJ1c3DBjj1pY/Dd
Is/GPItPnL7B2m+gNSQ9E5lRJ/DmP1j82+hnScbB5GiLqdZBASd7T+Dwp7gUqZIn
MKdW6JJjbUY9xNXcHrCS7GINuXHbo5CKz9Z19nl7+v0fzQbcVP6fH2jWAo7vfFd9
IR+Tz/7zJ76UescRS1ONEu6071KfTA7jfW1dhgqLAgMBAAGjEzARMA8GA1UdEQQI
MAaHBH8AAAEwDQYJKoZIhvcNAQEFBQADggEBAJTUv9eqrYJnF9ugKpC6QjsxQzJm
6A9Xwc13ORcno+NlYip1t0ITgW2ZebUboqvScy2B+IHM9HyQGkmVu8bXFJQ1clxy
HHA7Xp3xM+zB1KnvQ0ZPbaHnquOl39nULOCTxqSWCN8fqCuA/X3y9UiYL6P1cYRz
GFuoSgMoU+CrhkMQbF8c4dvl1Wr52Re9SYoX0o74N45pXhOMjeqUq2Mauapq7pFE
+21Y+KHEXojBWEufU8F6ihmWDMqu2vbbfODT63qp+6+VAiQOvMrBvsjW7Iq86VLE
8jddCYLRLwQjqaSn6APSSEocuzzP2J6CeGA4mxXHZz5gD22pugKbKLjHxwQ=
-----END CERTIFICATE-----
`
incorrectCert = `
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIUO/ncrEaWxLUqZ8IioBVCRl1P2R4wDQYJKoZIhvcNAQEL
BQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH
Expand All @@ -44,30 +115,132 @@ zwaAnAW1ZiriAbeFx+xOaO1fETVSm+5Poyl97r5Mmu97+3IpoWHFPO2z4Os9vn3q
XsKvL2lz2uQY+ZbrfvrL20p2
-----END CERTIFICATE-----
`
invalid_cert = `
invalidCert = `
-----BEGIN CERTIFICATE-----
invalidCert
-----END CERTIFICATE-----
`
)

func TestValidateCertValidCert(t *testing.T) {
tv := crypto.NewTlsValidator()
if err := tv.ValidateCert(endpoint, port, cert); err != nil {
t.Fatalf("Failed to validate cert: %v", err)
func TestHasSelfSignedCert(t *testing.T) {
certSvr, err := runTestServerWithCert(serverCert, serverKey)
if err != nil {
t.Fatalf("starting test server with certs: %v", err)
}
defer certSvr.Close()
certServerPort := strings.Split(certSvr.URL, ":")[2]
svr, err := runTestServer()
if err != nil {
t.Fatalf("starting test server: %v", err)
}
defer svr.Close()
serverPort := strings.Split(svr.URL, ":")[2]
tests := []struct {
testName string
endpoint string
port string
wantCert bool
wantError bool
}{
{
testName: "valid cert",
endpoint: endpoint,
port: certServerPort,
wantCert: true,
wantError: false,
},
{
testName: "invalid endpoint",
endpoint: invalidEndpoint,
port: serverPort,
wantCert: false,
wantError: true,
},
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
tv := crypto.NewTlsValidator()
hasCert, err := tv.HasSelfSignedCert(tc.endpoint, tc.port)
if (err != nil) != tc.wantError {
t.Fatalf("HasSelfSignedCert() error = %v, wantError %v", err, tc.wantError)
}
if hasCert != tc.wantCert {
t.Fatalf("HasSelfSignedCert() returned %v, want %v", hasCert, tc.wantCert)
}
})
}
}

func TestValidateCertInvalidEndpoint(t *testing.T) {
tv := crypto.NewTlsValidator()
if err := tv.ValidateCert(invalid_endpoint, port, cert); err == nil {
t.Fatalf("Certificate validation passed for invalid endpoint")
func TestValidateCert(t *testing.T) {
svr, err := runTestServerWithCert(serverCert, serverKey)
if err != nil {
t.Fatalf("starting test server with certs: %v", err)
}
defer svr.Close()
serverPort := strings.Split(svr.URL, ":")[2]
tests := []struct {
testName string
endpoint string
port string
caCert string
wantError bool
}{
{
testName: "valid cert",
endpoint: endpoint,
port: serverPort,
caCert: caCert,
wantError: false,
},
{
testName: "invalid endpoint",
endpoint: invalidEndpoint,
port: serverPort,
caCert: caCert,
wantError: true,
},
{
testName: "incorrect cert",
endpoint: endpoint,
port: serverPort,
caCert: incorrectCert,
wantError: true,
},
{
testName: "invalid cert format",
endpoint: endpoint,
port: serverPort,
caCert: invalidCert,
wantError: true,
},
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
tv := crypto.NewTlsValidator()
err := tv.ValidateCert(tc.endpoint, tc.port, tc.caCert)
if (err != nil) != tc.wantError {
t.Fatalf("ValidateCert() error = %v, wantError %v", err, tc.wantError)
}
})
}
}

func TestValidateCertInvalidCert(t *testing.T) {
tv := crypto.NewTlsValidator()
if err := tv.ValidateCert(endpoint, port, invalid_cert); err == nil {
t.Fatalf("Certificate validation passed for invalid cert")
func runTestServerWithCert(serverCert, serverKey string) (*httptest.Server, error) {
mux := http.NewServeMux()
svr := httptest.NewUnstartedServer(mux)
certificate, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
vivek-koppuru marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, fmt.Errorf("creating key pair: %v", err)
}
svr.TLS = &tls.Config{
Certificates: []tls.Certificate{certificate},
}
svr.StartTLS()
return svr, nil
}

func runTestServer() (*httptest.Server, error) {
mux := http.NewServeMux()
svr := httptest.NewServer(mux)
return svr, nil
}
3 changes: 1 addition & 2 deletions pkg/validations/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ func ValidateCertForRegistryMirror(clusterSpec *cluster.Spec, tlsValidator TlsVa
}

if certContent != "" {
err := tlsValidator.ValidateCert(host, port, certContent)
if err != nil {
if err = tlsValidator.ValidateCert(host, port, certContent); err != nil {
return fmt.Errorf("invalid registry certificate: %v", err)
}
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/validations/mocks/tls.go

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

2 changes: 1 addition & 1 deletion pkg/validations/tls.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package validations

type TlsValidator interface {
ValidateCert(host, port, cert string) error
ValidateCert(host, port, caCertContent string) error
HasSelfSignedCert(host, port string) (bool, error)
}