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-----`)