mirror of https://github.com/status-im/consul.git
Add stricter validation and some normalization code for API Gateway ConfigEntries (#16304)
* Add stricter validation and some normalization code for API Gateway ConfigEntries
This commit is contained in:
parent
ee99d5c3a0
commit
58801cc8aa
|
@ -713,6 +713,18 @@ type APIGatewayConfigEntry struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetKind() string { return APIGateway }
|
||||
func (e *APIGatewayConfigEntry) GetName() string { return e.Name }
|
||||
func (e *APIGatewayConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||
func (e *APIGatewayConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
|
||||
func (e *APIGatewayConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta { return &e.EnterpriseMeta }
|
||||
|
||||
var _ ControlledConfigEntry = (*APIGatewayConfigEntry)(nil)
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetStatus() Status { return e.Status }
|
||||
func (e *APIGatewayConfigEntry) SetStatus(status Status) { e.Status = status }
|
||||
func (e *APIGatewayConfigEntry) DefaultStatus() Status { return Status{} }
|
||||
|
||||
func (e *APIGatewayConfigEntry) ListenerIsReady(name string) bool {
|
||||
for _, condition := range e.Status.Conditions {
|
||||
if !condition.Resource.IsSame(&ResourceReference{
|
||||
|
@ -732,34 +744,28 @@ func (e *APIGatewayConfigEntry) ListenerIsReady(name string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetKind() string {
|
||||
return APIGateway
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) Normalize() error {
|
||||
for i, listener := range e.Listeners {
|
||||
protocol := strings.ToLower(string(listener.Protocol))
|
||||
listener.Protocol = APIGatewayListenerProtocol(protocol)
|
||||
e.Listeners[i] = listener
|
||||
|
||||
for i, cert := range listener.TLS.Certificates {
|
||||
if cert.Kind == "" {
|
||||
cert.Kind = InlineCertificate
|
||||
}
|
||||
listener.TLS.Certificates[i] = cert
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) Validate() error {
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.validateListenerNames(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -843,34 +849,6 @@ func (e *APIGatewayConfigEntry) CanWrite(authz acl.Authorizer) error {
|
|||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
var _ ControlledConfigEntry = (*APIGatewayConfigEntry)(nil)
|
||||
|
||||
func (e *APIGatewayConfigEntry) GetStatus() Status {
|
||||
return e.Status
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) SetStatus(status Status) {
|
||||
e.Status = status
|
||||
}
|
||||
|
||||
func (e *APIGatewayConfigEntry) DefaultStatus() Status {
|
||||
return Status{}
|
||||
}
|
||||
|
||||
// APIGatewayListenerProtocol is the protocol that an APIGateway listener uses
|
||||
type APIGatewayListenerProtocol string
|
||||
|
||||
|
@ -991,27 +969,10 @@ func (e *BoundAPIGatewayConfigEntry) IsInitializedForGateway(gateway *APIGateway
|
|||
return true
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetKind() string {
|
||||
return BoundAPIGateway
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) Normalize() error {
|
||||
return nil
|
||||
}
|
||||
func (e *BoundAPIGatewayConfigEntry) GetKind() string { return BoundAPIGateway }
|
||||
func (e *BoundAPIGatewayConfigEntry) GetName() string { return e.Name }
|
||||
func (e *BoundAPIGatewayConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||
func (e *BoundAPIGatewayConfigEntry) Normalize() error { return nil }
|
||||
|
||||
func (e *BoundAPIGatewayConfigEntry) Validate() error {
|
||||
allowedCertificateKinds := map[string]bool{
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// InlineCertificateConfigEntry manages the configuration for an inline certificate
|
||||
|
@ -39,6 +40,10 @@ func (e *InlineCertificateConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
|||
func (e *InlineCertificateConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
|
||||
|
||||
func (e *InlineCertificateConfigEntry) Validate() error {
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeyBlock, _ := pem.Decode([]byte(e.PrivateKey))
|
||||
if privateKeyBlock == nil {
|
||||
return errors.New("failed to parse private key PEM")
|
||||
|
@ -61,6 +66,18 @@ func (e *InlineCertificateConfigEntry) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// validate that each host referenced in the CN, DNSSans, and IPSans
|
||||
// are valid hostnames
|
||||
hosts, err := e.Hosts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, host := range hosts {
|
||||
if _, ok := dns.IsDomainName(host); !ok {
|
||||
return fmt.Errorf("host %q must be a valid DNS hostname", host)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,73 @@ import "testing"
|
|||
const (
|
||||
// generated via openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout private.key -out certificate.crt
|
||||
validPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0wzZeonUklhOvJ0AxcdDdCTiMwR9tsm/6IGcw9Jm50xVY+qg
|
||||
5GFg1RWrQaODq7Gjqd/JDUAwtTBnQMs1yt6nbsHe2QhbD4XeqtZ+6fTv1ZpG3k8F
|
||||
eB/M01xFqovczRV/ie77wd4vqoPD+AcfD8NDAFJt3htwUgGIqkQHP329Sh3TtLga
|
||||
9ZMCs1MoTT+POYGUPL8bwt9R6ClNrucbH4Bs6OnX2ZFbKF75O9OHKNxWTmpDSodv
|
||||
OFbFyKps3BfnPuF0Z6mj5M5yZeCjmtfS25PrsM3pMBGK5YHb0MlFfZIrIGboMbrz
|
||||
9F/BMQJ64pMe43KwqHvTnbKWhp6PzLhEkPGLnwIDAQABAoIBADBEJAiONPszDu67
|
||||
yU1yAM8zEDgysr127liyK7PtDnOfVXgAVMNmMcsJpZzhVF+TxKY487YAFCOb6kE7
|
||||
OBYpTYla9SgVbR3js8TGQUgoKCFlowd8cvfB7gn4dEZIrjqIzB4zdYgk1Cne8JZs
|
||||
qoHkWhJcx5ugEtPuXd7yp+WxT/T+6uOro06scp67NhP5t9yoAGFv5Vdb577RuzRo
|
||||
Wkd9higQ9A20+GtjCY0EYxdgRviWvW7mM5/F+Lzcaui86ME+ga754gX8zgW3+NJ5
|
||||
LMsz5OLSnh291Uyjmr77HWBv/xvpq01Fls0LyJcgxFVZuJs5GQz+l3otSqv4FTP6
|
||||
Ua9w/YECgYEA8To3dgUK1QhzX5rwhWtlst3pItGTvmEdNzXmjgSylu7uKM13i+xg
|
||||
llhp2uXrOEtuL+xtBZdeFNaijusbyqjg0xj6e4o31c19okuuDkJD5/sfQq22bvrn
|
||||
gVJMGuESprIiPePrEyrXCHOdxH6eDgR2dIzAeO5vz0nnKGFAWrJJbvECgYEA3/mJ
|
||||
eacXOJznw4Sa8jGWS2FtZLKxDHph7uDKMJmuG0ukb3aHJ9dMHrPleCLo8mhpoObA
|
||||
hueoIbIP7swGrQx79+nZbnQpF6rMp6FAU5bF3gSrj1eWbaeh8pn9mrv4hal9USmn
|
||||
orTbXMxDp3XSh7voR8Fqy5tMQqwZ+Lz74ccbw48CgYEA5cEhGdNrocPOv3x/IVRN
|
||||
JLOfXX5nTaiJfxBja1imEIO5ajtoZWjaBdhn2gmqo4+UfyicHfsxrH9RjPX5HmkC
|
||||
2Yys5gWbcJOr2Wxjd0k+DDFucL+rRsDKxq1vtxov/X0kh/YQ68ydynr0BTbjq04s
|
||||
1I1KtOPEspYdCKS3+qpcrsECgYBtvYeVesBO9do9G0kMKC26y4bdEwzaz1ASykNn
|
||||
IrWDHEH6dznr1HqwhHaHsZsvwucWdlmZAAKKWAOkfoU63uYS55qomvPTa9WQwNqS
|
||||
2koi6Wjh+Al1uvAHvVncKgOwAgar8Nv5ReJBirgPYhSAexpppiRclL/93vNuw7Iq
|
||||
wvMgkwKBgQC5wnb6SUUrzzKKSRgyusHM/XrjiKgVKq7lvFE9/iJkcw+BEXpjjbEe
|
||||
RyD0a7PRtCfR39SMVrZp4KXVNNK5ln0WhuLvraMDwOpH9JDWHQiAhuJ3ooSwBylK
|
||||
+QCLjyOtWAGZAIBRJyb1txfTXZ++dldkOjBi3bmEiadOa48ksvDsNQ==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
validCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIIDQjCCAioCCQC6cMRYsE+ahDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAkxBMQ0wCwYDVQQKDARUZXN0MQ0wCwYD
|
||||
VQQLDARTdHViMRwwGgYDVQQDDBNob3N0LmNvbnN1bC5leGFtcGxlMB4XDTIzMDIx
|
||||
NzAyMTA1MloXDTI4MDIxNjAyMTA1MlowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
|
||||
AkNBMQswCQYDVQQHDAJMQTENMAsGA1UECgwEVGVzdDENMAsGA1UECwwEU3R1YjEc
|
||||
MBoGA1UEAwwTaG9zdC5jb25zdWwuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBANMM2XqJ1JJYTrydAMXHQ3Qk4jMEfbbJv+iBnMPSZudMVWPq
|
||||
oORhYNUVq0Gjg6uxo6nfyQ1AMLUwZ0DLNcrep27B3tkIWw+F3qrWfun079WaRt5P
|
||||
BXgfzNNcRaqL3M0Vf4nu+8HeL6qDw/gHHw/DQwBSbd4bcFIBiKpEBz99vUod07S4
|
||||
GvWTArNTKE0/jzmBlDy/G8LfUegpTa7nGx+AbOjp19mRWyhe+TvThyjcVk5qQ0qH
|
||||
bzhWxciqbNwX5z7hdGepo+TOcmXgo5rX0tuT67DN6TARiuWB29DJRX2SKyBm6DG6
|
||||
8/RfwTECeuKTHuNysKh7052yloaej8y4RJDxi58CAwEAATANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAHF10odRNJ7TKvcD2JPtR8wMacfldSiPcQnn+rhMUyBaKOoSrALxOev+N
|
||||
L8N+RtEV+KXkyBkvT71OZzEpY9ROwqOQ/acnMdbfG0IBPbg3c/7WDD2sjcdr1zvc
|
||||
U3T7WJ7G3guZ5aWCuAGgOyT6ZW8nrDa4yFbKZ1PCJkvUQ2ttO1lXmyGPM533Y2pi
|
||||
SeXP6LL7z5VNqYO3oz5IJEstt10IKxdmb2gKFhHjgEmHN2gFL0jaPi4mjjaINrxq
|
||||
MdqcM9IzLr26AjZ45NuI9BCcZWO1mraaQTOIb3QL5LyqaC7CRJXLYPSGARthyDhq
|
||||
J3TrQE3YVrL4D9xnklT86WDnZKApJg==
|
||||
-----END CERTIFICATE-----`
|
||||
mismatchedCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIIDQjCCAioCCQC2H6+PYz23xDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAkxBMQ0wCwYDVQQKDARUZXN0MQwwCgYD
|
||||
VQQLDANGb28xHTAbBgNVBAMMFG90aGVyLmNvbnN1bC5leGFtcGxlMB4XDTIzMDIx
|
||||
NzAyMTM0OVoXDTI4MDIxNjAyMTM0OVowYzELMAkGA1UEBhMCVVMxCzAJBgNVBAgM
|
||||
AkNBMQswCQYDVQQHDAJMQTENMAsGA1UECgwEVGVzdDEMMAoGA1UECwwDRm9vMR0w
|
||||
GwYDVQQDDBRvdGhlci5jb25zdWwuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAO0IH/dzmWJaTPVL32xQVHivrnQk38vskW0ymILYuaismUMJ
|
||||
0+xrcaTcVljU+3nKhmSW9wcYSFY02GcGWAdcw8x8xO801cna020T+DIWiYaljXT3
|
||||
agrbYfULF9q+ihT6IL1D2mFa0AW1x6Bk1XAmZRSTpRBhp7iFNnCXGRK8sSSr95ge
|
||||
DxaRyj/2F8t6kG+ANPkRBiPd2rRgsYQjuTLuZYBvseeJygnSF8ty1QMg6koz7kdN
|
||||
bPon3Q5GFH71WNwzm9G3DWjMIu+dhpHz7rsbCnhwLB5lh1jsZBYkAMt3kiyY0g4I
|
||||
ReuiVWesMe+AMG/DQZvZ5mE252QFJ92dLTeo5RcCAwEAATANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAijm6blixjl+pMRAj7EajoPjU+GqhooZayJrvdwvofwcPxQYpkPuh7Uc6
|
||||
l2z494b75cRzMw7wS+iW/ad8NYrfw1JwHMsUfncxs5LDO5GsKl9Krg/39goDl3wC
|
||||
ywTcl00y+FMYfldNPjKDLunENmn+yPa2pKuBVQ0yOKALp+oUeJFVzRNPV5fohlBi
|
||||
HjypkO0KaVmCG6P01cqCgVkNzxnX9qQYP3YXX1yt5iOcI7QcoOa5WnRhOuD8WqJ1
|
||||
v3AZGYNvKyXf9E5nD0y2Cmz6t1awjFjzMlXMx6AdHrjWqxtHhYQ1xz4P4NfzK27m
|
||||
cCtURSzXMgcrSeZLepBfdICf+0/0+Q==
|
||||
-----END CERTIFICATE-----`
|
||||
emptyCNPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAx95Opa6t4lGEpiTUogEBptqOdam2ch4BHQGhNhX/MrDwwuZQ
|
||||
httBwMfngQ/wd9NmYEPAwj0dumUoAITIq6i2jQlhqTodElkbsd5vWY8R/bxJWQSo
|
||||
NvVE12TlzECxGpJEiHt4W0r8pGffk+rvpljiUyCfnT1kGF3znOSjK1hRMTn6RKWC
|
||||
|
@ -31,7 +98,7 @@ T0+9gwKBgHDoerX7NTskg0H0t8O+iSMevdxpEWp34ZYa9gHiftTQGyrRgERCa7Gj
|
|||
nZPAxKb2JoWyfnu3v7G5gZ8fhDFsiOxLbZv6UZJBbUIh1MjJISpXrForDrC2QNLX
|
||||
kHrHfwBFDB3KMudhQknsJzEJKCL/KmFH6o0MvsoaT9yzEl3K+ah/
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
validCertificate = `-----BEGIN CERTIFICATE-----
|
||||
emptyCNCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQCQMDsYO8FrPjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV
|
||||
UzAeFw0yMjEyMjAxNzUwMjVaFw0yNzEyMTkxNzUwMjVaMA0xCzAJBgNVBAYTAlVT
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx95Opa6t4lGEpiTUogEB
|
||||
|
@ -46,26 +113,12 @@ RahYIzNLRBTLrwadLAZkApUpZvB8qDK4knsTWFYujNsylCww2A6ajzIMFNU4GkUK
|
|||
NtyHRuD+KYRmjXtyX1yHNqfGN3vOQmwavHq2R8wHYuBSc6LAHHV9vG+j0VsgMELO
|
||||
qwxn8SmLkSKbf2+MsQVzLCXXN5u+D8Yv+4py+oKP4EQ5aFZuDEx+r/G/31rTthww
|
||||
AAJAMaoXmoYVdgXV+CPuBb2M4XCpuzLu3bcA2PXm5ipSyIgntMKwXV7r
|
||||
-----END CERTIFICATE-----`
|
||||
mismatchedCertificate = `-----BEGIN CERTIFICATE-----
|
||||
MIICljCCAX4CCQC49bq8e0QgLDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV
|
||||
UzAeFw0yMjEyMjAxNzUyMzJaFw0yNzEyMTkxNzUyMzJaMA0xCzAJBgNVBAYTAlVT
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk7Are9ulVDY0IqaG5Pt/
|
||||
OVuS0kmDhgVUfQBM5JDGRfIsu1ebn68kn5JGCTQ+nC8nU9QXRJS7vG6As5GWm08W
|
||||
FpkOyIbHLjOhWtYCYzQ+0R+sSSoMnczgl8l6wIUIkR3Vpoy6QUsSZbvo4/xDi3Uk
|
||||
1CF+JMTM2oFDLD8PNrNzW/txRyTugK36W1G1ofUhvP6EHsTjmVcZwBcLOKToov6L
|
||||
Ai758MLztl1/X/90DNdZwuHC9fGIgx52Ojz3+XIocXFttr+J8xZglMCtqL4n40bh
|
||||
5b1DE+hC3NHQmA+7Chc99z28baj2cU1woNk/TO+ewqpyvj+WPWwGOQt3U63ZoPaw
|
||||
yQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCMF3JlrDdcSv2KYrxEp1tWB/GglI8a
|
||||
JiSvrf3hePaRz59099bg4DoHzTn0ptOcOPOO9epDPbCJrUqLuPlwvrQRvll6GaW1
|
||||
y3TcbnE1AbwTAjbOTgpLhvuj6IVlyNNLoKbjZqs4A8N8i6UkQ7Y8qg77lwxD3QoH
|
||||
pWLwGZKJifKPa7ObVWmKj727kbU59nA2Hx+Y4qa/MyiPWxJM9Y0JsFGxSBxp4kmQ
|
||||
q4ikzSWaPv/TvtV+d4mO1H44aggdNMCYIQd/5BXQzG40l+ecHnBueJyG312ax/Zp
|
||||
NsYUAKQT864cGlxrnWVgT4sW/tsl9Qen7g9iAdeBAPvLO7cQjAjtc7KZ
|
||||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
||||
func TestInlineCertificate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := map[string]configEntryTestcase{
|
||||
"invalid private key": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
|
@ -101,6 +154,15 @@ func TestInlineCertificate(t *testing.T) {
|
|||
Certificate: validCertificate,
|
||||
},
|
||||
},
|
||||
"empty cn certificate": {
|
||||
entry: &InlineCertificateConfigEntry{
|
||||
Kind: InlineCertificate,
|
||||
Name: "cert-five",
|
||||
PrivateKey: emptyCNPrivateKey,
|
||||
Certificate: emptyCNCertificate,
|
||||
},
|
||||
validateErr: "host \"\" must be a valid DNS hostname",
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// BoundRoute indicates a route that has parent gateways which
|
||||
|
@ -41,15 +42,22 @@ type HTTPRouteConfigEntry struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetServices() []HTTPService {
|
||||
targets := []HTTPService{}
|
||||
for _, rule := range e.Rules {
|
||||
for _, service := range rule.Services {
|
||||
targets = append(targets, service)
|
||||
}
|
||||
}
|
||||
return targets
|
||||
}
|
||||
func (e *HTTPRouteConfigEntry) GetKind() string { return HTTPRoute }
|
||||
func (e *HTTPRouteConfigEntry) GetName() string { return e.Name }
|
||||
func (e *HTTPRouteConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||
func (e *HTTPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta { return &e.EnterpriseMeta }
|
||||
func (e *HTTPRouteConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
|
||||
|
||||
var _ ControlledConfigEntry = (*HTTPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetStatus() Status { return e.Status }
|
||||
func (e *HTTPRouteConfigEntry) SetStatus(status Status) { e.Status = status }
|
||||
func (e *HTTPRouteConfigEntry) DefaultStatus() Status { return Status{} }
|
||||
|
||||
var _ BoundRoute = (*HTTPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetParents() []ResourceReference { return e.Parents }
|
||||
func (e *HTTPRouteConfigEntry) GetProtocol() APIGatewayListenerProtocol { return ListenerProtocolHTTP }
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetServiceNames() []ServiceName {
|
||||
services := []ServiceName{}
|
||||
|
@ -59,36 +67,209 @@ func (e *HTTPRouteConfigEntry) GetServiceNames() []ServiceName {
|
|||
return services
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetKind() string {
|
||||
return HTTPRoute
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
func (e *HTTPRouteConfigEntry) GetServices() []HTTPService {
|
||||
targets := []HTTPService{}
|
||||
for _, rule := range e.Rules {
|
||||
targets = append(targets, rule.Services...)
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetParents() []ResourceReference {
|
||||
if e == nil {
|
||||
return []ResourceReference{}
|
||||
}
|
||||
return e.Parents
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetProtocol() APIGatewayListenerProtocol {
|
||||
return ListenerProtocolHTTP
|
||||
return targets
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) Normalize() error {
|
||||
for i, parent := range e.Parents {
|
||||
if parent.Kind == "" {
|
||||
parent.Kind = APIGateway
|
||||
e.Parents[i] = parent
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range e.Rules {
|
||||
for j, match := range rule.Matches {
|
||||
rule.Matches[j] = normalizeHTTPMatch(match)
|
||||
}
|
||||
e.Rules[i] = rule
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeHTTPMatch(match HTTPMatch) HTTPMatch {
|
||||
method := string(match.Method)
|
||||
method = strings.ToUpper(method)
|
||||
match.Method = HTTPMatchMethod(method)
|
||||
|
||||
pathMatch := match.Path.Match
|
||||
if string(pathMatch) == "" {
|
||||
match.Path.Match = HTTPPathMatchPrefix
|
||||
match.Path.Value = "/"
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) Validate() error {
|
||||
for _, host := range e.Hostnames {
|
||||
// validate that each host referenced in a valid dns name and has
|
||||
// no wildcards in it
|
||||
if _, ok := dns.IsDomainName(host); !ok {
|
||||
return fmt.Errorf("host %q must be a valid DNS hostname", host)
|
||||
}
|
||||
|
||||
if strings.ContainsRune(host, '*') {
|
||||
return fmt.Errorf("host %q must not be a wildcard", host)
|
||||
}
|
||||
}
|
||||
|
||||
validParentKinds := map[string]bool{
|
||||
APIGateway: true,
|
||||
}
|
||||
|
||||
for _, parent := range e.Parents {
|
||||
if !validParentKinds[parent.Kind] {
|
||||
return fmt.Errorf("unsupported parent kind: %q, must be 'api-gateway'", parent.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, rule := range e.Rules {
|
||||
if err := validateRule(rule); err != nil {
|
||||
return fmt.Errorf("Rule[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRule(rule HTTPRouteRule) error {
|
||||
if err := validateFilters(rule.Filters); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, match := range rule.Matches {
|
||||
if err := validateMatch(match); err != nil {
|
||||
return fmt.Errorf("Match[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, service := range rule.Services {
|
||||
if err := validateHTTPService(service); err != nil {
|
||||
return fmt.Errorf("Service[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateMatch(match HTTPMatch) error {
|
||||
if match.Method != HTTPMatchMethodAll {
|
||||
if !isValidHTTPMethod(string(match.Method)) {
|
||||
return fmt.Errorf("Method contains an invalid method %q", match.Method)
|
||||
}
|
||||
}
|
||||
|
||||
for i, query := range match.Query {
|
||||
if err := validateHTTPQueryMatch(query); err != nil {
|
||||
return fmt.Errorf("Query[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, header := range match.Headers {
|
||||
if err := validateHTTPHeaderMatch(header); err != nil {
|
||||
return fmt.Errorf("Headers[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateHTTPPathMatch(match.Path); err != nil {
|
||||
return fmt.Errorf("Path, %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHTTPService(service HTTPService) error {
|
||||
return validateFilters(service.Filters)
|
||||
}
|
||||
|
||||
func validateFilters(filter HTTPFilters) error {
|
||||
for i, header := range filter.Headers {
|
||||
if err := validateHeaderFilter(header); err != nil {
|
||||
return fmt.Errorf("HTTPFilters, Headers[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
for i, rewrite := range filter.URLRewrites {
|
||||
if err := validateURLRewrite(rewrite); err != nil {
|
||||
return fmt.Errorf("HTTPFilters, URLRewrite[%d], %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateURLRewrite(rewrite URLRewrite) error {
|
||||
// TODO: we don't really have validation of the actual params
|
||||
// passed as "PrefixRewrite" in our discoverychain config
|
||||
// entries, figure out if we should have something here
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHeaderFilter(filter HTTPHeaderFilter) error {
|
||||
// TODO: we don't really have validation of the values
|
||||
// passed as header modifiers in our current discoverychain
|
||||
// config entries, figure out if we need to
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHTTPQueryMatch(query HTTPQueryMatch) error {
|
||||
if query.Name == "" {
|
||||
return fmt.Errorf("missing required Name field")
|
||||
}
|
||||
|
||||
switch query.Match {
|
||||
case HTTPQueryMatchExact,
|
||||
HTTPQueryMatchPresent,
|
||||
HTTPQueryMatchRegularExpression:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("match type should be one of present, exact, or regex")
|
||||
}
|
||||
}
|
||||
|
||||
func validateHTTPHeaderMatch(header HTTPHeaderMatch) error {
|
||||
if header.Name == "" {
|
||||
return fmt.Errorf("missing required Name field")
|
||||
}
|
||||
|
||||
switch header.Match {
|
||||
case HTTPHeaderMatchExact,
|
||||
HTTPHeaderMatchPrefix,
|
||||
HTTPHeaderMatchRegularExpression,
|
||||
HTTPHeaderMatchSuffix,
|
||||
HTTPHeaderMatchPresent:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("match type should be one of present, exact, prefix, suffix, or regex")
|
||||
}
|
||||
}
|
||||
|
||||
func validateHTTPPathMatch(path HTTPPathMatch) error {
|
||||
switch path.Match {
|
||||
case HTTPPathMatchExact,
|
||||
HTTPPathMatchPrefix:
|
||||
if !strings.HasPrefix(path.Value, "/") {
|
||||
return fmt.Errorf("%s type match doesn't start with '/': %q", path.Match, path.Value)
|
||||
}
|
||||
fallthrough
|
||||
case HTTPPathMatchRegularExpression:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("match type should be one of exact, prefix, or regex")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||
var authzContext acl.AuthorizerContext
|
||||
e.FillAuthzContext(&authzContext)
|
||||
|
@ -101,27 +282,6 @@ func (e *HTTPRouteConfigEntry) CanWrite(authz acl.Authorizer) error {
|
|||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) FilteredHostnames(listenerHostname string) []string {
|
||||
if len(e.Hostnames) == 0 {
|
||||
// we have no hostnames specified here, so treat it like a wildcard
|
||||
|
@ -279,20 +439,6 @@ func (s HTTPService) ServiceName() ServiceName {
|
|||
return NewServiceName(s.Name, &s.EnterpriseMeta)
|
||||
}
|
||||
|
||||
var _ ControlledConfigEntry = (*HTTPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *HTTPRouteConfigEntry) GetStatus() Status {
|
||||
return e.Status
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) SetStatus(status Status) {
|
||||
e.Status = status
|
||||
}
|
||||
|
||||
func (e *HTTPRouteConfigEntry) DefaultStatus() Status {
|
||||
return Status{}
|
||||
}
|
||||
|
||||
// TCPRouteConfigEntry manages the configuration for a TCP route
|
||||
// with the given name.
|
||||
type TCPRouteConfigEntry struct {
|
||||
|
@ -317,9 +463,22 @@ type TCPRouteConfigEntry struct {
|
|||
RaftIndex
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetServices() []TCPService {
|
||||
return e.Services
|
||||
}
|
||||
func (e *TCPRouteConfigEntry) GetKind() string { return TCPRoute }
|
||||
func (e *TCPRouteConfigEntry) GetName() string { return e.Name }
|
||||
func (e *TCPRouteConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||
func (e *TCPRouteConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
|
||||
func (e *TCPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta { return &e.EnterpriseMeta }
|
||||
|
||||
var _ ControlledConfigEntry = (*TCPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetStatus() Status { return e.Status }
|
||||
func (e *TCPRouteConfigEntry) SetStatus(status Status) { e.Status = status }
|
||||
func (e *TCPRouteConfigEntry) DefaultStatus() Status { return Status{} }
|
||||
|
||||
var _ BoundRoute = (*TCPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetParents() []ResourceReference { return e.Parents }
|
||||
func (e *TCPRouteConfigEntry) GetProtocol() APIGatewayListenerProtocol { return ListenerProtocolTCP }
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetServiceNames() []ServiceName {
|
||||
services := []ServiceName{}
|
||||
|
@ -329,34 +488,7 @@ func (e *TCPRouteConfigEntry) GetServiceNames() []ServiceName {
|
|||
return services
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetKind() string {
|
||||
return TCPRoute
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetName() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetParents() []ResourceReference {
|
||||
if e == nil {
|
||||
return []ResourceReference{}
|
||||
}
|
||||
return e.Parents
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetProtocol() APIGatewayListenerProtocol {
|
||||
return ListenerProtocolTCP
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetMeta() map[string]string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e.Meta
|
||||
}
|
||||
func (e *TCPRouteConfigEntry) GetServices() []TCPService { return e.Services }
|
||||
|
||||
func (e *TCPRouteConfigEntry) Normalize() error {
|
||||
for i, parent := range e.Parents {
|
||||
|
@ -381,6 +513,11 @@ func (e *TCPRouteConfigEntry) Validate() error {
|
|||
return fmt.Errorf("unsupported parent kind: %q, must be 'api-gateway'", parent.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateConfigEntryMeta(e.Meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -396,34 +533,6 @@ func (e *TCPRouteConfigEntry) CanWrite(authz acl.Authorizer) error {
|
|||
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetRaftIndex() *RaftIndex {
|
||||
if e == nil {
|
||||
return &RaftIndex{}
|
||||
}
|
||||
return &e.RaftIndex
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
var _ ControlledConfigEntry = (*TCPRouteConfigEntry)(nil)
|
||||
|
||||
func (e *TCPRouteConfigEntry) GetStatus() Status {
|
||||
return e.Status
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) SetStatus(status Status) {
|
||||
e.Status = status
|
||||
}
|
||||
|
||||
func (e *TCPRouteConfigEntry) DefaultStatus() Status {
|
||||
return Status{}
|
||||
}
|
||||
|
||||
// TCPService is a service reference for a TCPRoute
|
||||
type TCPService struct {
|
||||
Name string
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
)
|
||||
|
||||
func TestTCPRoute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := map[string]configEntryTestcase{
|
||||
"multiple services": {
|
||||
entry: &TCPRouteConfigEntry{
|
||||
|
@ -56,3 +58,207 @@ func TestTCPRoute(t *testing.T) {
|
|||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
||||
func TestHTTPRoute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := map[string]configEntryTestcase{
|
||||
"normalize parent kind": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-one",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
},
|
||||
normalizeOnly: true,
|
||||
check: func(t *testing.T, entry ConfigEntry) {
|
||||
expectedParent := ResourceReference{
|
||||
Kind: APIGateway,
|
||||
Name: "gateway",
|
||||
}
|
||||
route := entry.(*HTTPRouteConfigEntry)
|
||||
require.Len(t, route.Parents, 1)
|
||||
require.Equal(t, expectedParent, route.Parents[0])
|
||||
},
|
||||
},
|
||||
"invalid parent kind": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Kind: "route",
|
||||
Name: "gateway",
|
||||
}},
|
||||
},
|
||||
validateErr: "unsupported parent kind",
|
||||
},
|
||||
"wildcard hostnames": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Hostnames: []string{"*"},
|
||||
},
|
||||
validateErr: "host \"*\" must not be a wildcard",
|
||||
},
|
||||
"wildcard subdomain": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Hostnames: []string{"*.consul.example"},
|
||||
},
|
||||
validateErr: "host \"*.consul.example\" must not be a wildcard",
|
||||
},
|
||||
"valid dns hostname": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Hostnames: []string{"...not legal"},
|
||||
},
|
||||
validateErr: "host \"...not legal\" must be a valid DNS hostname",
|
||||
},
|
||||
"rule matches invalid header match type": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Headers: []HTTPHeaderMatch{{
|
||||
Match: HTTPHeaderMatchType("foo"),
|
||||
Name: "foo",
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Headers[0], match type should be one of present, exact, prefix, suffix, or regex",
|
||||
},
|
||||
"rule matches invalid header match name": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Headers: []HTTPHeaderMatch{{
|
||||
Match: HTTPHeaderMatchPresent,
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Headers[0], missing required Name field",
|
||||
},
|
||||
"rule matches invalid query match type": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Query: []HTTPQueryMatch{{
|
||||
Match: HTTPQueryMatchType("foo"),
|
||||
Name: "foo",
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Query[0], match type should be one of present, exact, or regex",
|
||||
},
|
||||
"rule matches invalid query match name": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Query: []HTTPQueryMatch{{
|
||||
Match: HTTPQueryMatchPresent,
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Query[0], missing required Name field",
|
||||
},
|
||||
"rule matches invalid path match type": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Path: HTTPPathMatch{
|
||||
Match: HTTPPathMatchType("foo"),
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Path, match type should be one of exact, prefix, or regex",
|
||||
},
|
||||
"rule matches invalid path match prefix": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Path: HTTPPathMatch{
|
||||
Match: HTTPPathMatchPrefix,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Path, prefix type match doesn't start with '/': \"\"",
|
||||
},
|
||||
"rule matches invalid method": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Method: HTTPMatchMethod("foo"),
|
||||
}},
|
||||
}},
|
||||
},
|
||||
validateErr: "Rule[0], Match[0], Method contains an invalid method \"FOO\"",
|
||||
},
|
||||
"rule normalizes method casing and path matches": {
|
||||
entry: &HTTPRouteConfigEntry{
|
||||
Kind: HTTPRoute,
|
||||
Name: "route-two",
|
||||
Parents: []ResourceReference{{
|
||||
Name: "gateway",
|
||||
}},
|
||||
Rules: []HTTPRouteRule{{
|
||||
Matches: []HTTPMatch{{
|
||||
Method: HTTPMatchMethod("trace"),
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
|
|
@ -10,47 +10,51 @@ import (
|
|||
const (
|
||||
// generated via openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout private.key -out certificate.crt
|
||||
validPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
validCertificate = `-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
J3TrQE3YVrL4D9xnklT86WDnZKApJg==
|
||||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue