package validation
import (
"io/ioutil"
"os"
"strings"
"testing"
"github.com/openshift/origin/pkg/cmd/server/api"
)
func TestValidateServingInfo(t *testing.T) {
certFile, err := ioutil.TempFile("", "cert.crt")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer os.Remove(certFile.Name())
certFileName := certFile.Name()
ioutil.WriteFile(certFile.Name(), localhostCert, os.FileMode(0755))
keyFile, err := ioutil.TempFile("", "cert.key")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
defer os.Remove(keyFile.Name())
keyFileName := keyFile.Name()
ioutil.WriteFile(keyFile.Name(), localhostKey, os.FileMode(0755))
testcases := map[string]struct {
ServingInfo api.ServingInfo
ExpectedErrors []string
ExpectedWarnings []string
}{
"basic": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
},
},
"missing key": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{
CertFile: certFileName,
},
},
ExpectedErrors: []string{"keyFile: Required value"},
},
"namedCertificates valid": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"example.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
},
"namedCertificates valid wildcard spec": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"*.wildcard.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
},
"namedCertificates specific host for wildcard cert": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"www.wildcard.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
},
"namedCertificates without default cert": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
//ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"example.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedErrors: []string{"namedCertificates: Invalid value"},
},
"namedCertificates with missing names": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{ /*"example.com"*/ }, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedErrors: []string{"namedCertificates[0].names: Required value"},
},
"namedCertificates with missing key": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"example.com"}, CertInfo: api.CertInfo{CertFile: certFileName /*, KeyFile: keyFileName*/}},
},
},
ExpectedErrors: []string{"namedCertificates[0].keyFile: Required value"},
},
"namedCertificates with duplicate names": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"example.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
{Names: []string{"example.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedErrors: []string{"namedCertificates[1].names[0]: Invalid value"},
},
"namedCertificates with empty name": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{""}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedErrors: []string{"namedCertificates[0].names[0]: Required value"},
},
"namedCertificates with unmatched DNS name": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"badexample.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedWarnings: []string{"namedCertificates[0].names[0]: Invalid value"},
},
"namedCertificates with non-DNS names": {
ServingInfo: api.ServingInfo{
BindAddress: "0.0.0.0:1234",
BindNetwork: "tcp",
ServerCert: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName},
NamedCertificates: []api.NamedCertificate{
{Names: []string{"foo bar.com"}, CertInfo: api.CertInfo{CertFile: certFileName, KeyFile: keyFileName}},
},
},
ExpectedErrors: []string{
`namedCertificates[0].names[0]: Invalid value: "foo bar.com": must be a valid DNS name`,
},
},
}
for k, tc := range testcases {
result := ValidateServingInfo(tc.ServingInfo, nil)
if len(tc.ExpectedErrors) != len(result.Errors) {
t.Errorf("%s: Expected %d errors, got %d", k, len(tc.ExpectedErrors), len(result.Errors))
for _, e := range tc.ExpectedErrors {
t.Logf("\tExpected error: %s", e)
}
for _, r := range result.Errors {
t.Logf("\tActual error: %s", r.Error())
}
continue
}
for i, r := range result.Errors {
if !strings.Contains(r.Error(), tc.ExpectedErrors[i]) {
t.Errorf("%s: Expected error containing %s, got %s", k, tc.ExpectedErrors[i], r.Error())
}
}
if len(tc.ExpectedWarnings) != len(result.Warnings) {
t.Errorf("%s: Expected %d warning, got %d", k, len(tc.ExpectedWarnings), len(result.Warnings))
for _, e := range tc.ExpectedErrors {
t.Logf("\tExpected warning: %s", e)
}
for _, r := range result.Warnings {
t.Logf("\tActual warning: %s", r.Error())
}
continue
}
for i, r := range result.Warnings {
if !strings.Contains(r.Error(), tc.ExpectedWarnings[i]) {
t.Errorf("%s: Expected warning containing %s, got %s", k, tc.ExpectedWarnings[i], r.Error())
}
}
}
}
// localhostCert is a PEM-encoded TLS cert with SAN IPs
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
// of ASN.1 time).
// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com,*.wildcard.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBmjCCAUagAwIBAgIQNElZIQ+5sNqQ5FlhpXDzvzALBgkqhkiG9w0BAQswEjEQ
MA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2MDAw
MFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDX
oyZQ4OZGzWC+UqL+F671Gtv6wxyrQWbyu8z5KxrHCxObGTMG4fcSOTrJ5ApwIXuW
O6KuXL/QwbdI+0V43pNhAgMBAAGjeDB2MA4GA1UdDwEB/wQEAwIApDATBgNVHSUE
DDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MD4GA1UdEQQ3MDWCC2V4YW1w
bGUuY29tgg4qLndpbGRjYXJkLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAL
BgkqhkiG9w0BAQsDQQDHWUY1n4YZNm2Cuutg5NGaRefzzK9qgksi7bIs9bH0tYPH
/Vp4NKH+27aG54X5U+Vw1aXS9CKhqEky5CZMfHtn
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANejJlDg5kbNYL5Sov4XrvUa2/rDHKtBZvK7zPkrGscLE5sZMwbh
9xI5OsnkCnAhe5Y7oq5cv9DBt0j7RXjek2ECAwEAAQJBAIMFMma5/7DNYRbDBx30
Le3nX/nBS04S8wZRbX2H30FIL/PU4mezFiDoVlcIEHUBi1TAcwQux3FFg/8f+j6w
rAECIQDzWRsqow24qQL5nPCvA9RSkNgmZSCpog5hKSK1vgNS8QIhAOLZOJlLVo8v
IUaAt4uvQJVE/ClFi7sLq2hnduJjiGdxAiBCcldHqiQqAwRL8j2KHGqSbPiIa16i
0xxIDXpr08mGkQIgfV1CVCU4buTC5O2Zgc6WSGfZWw2eDP6D+azEHJSY+2ECIQCU
+w6O+Pa96Fi0XvY8wVsg1h1eNUjAumxThaf9Sp64lw==
-----END RSA PRIVATE KEY-----`)