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

APIGateway BoundAPIGateway validation #15858

Merged
merged 13 commits into from
Dec 21, 2022
45 changes: 43 additions & 2 deletions agent/structs/config_entry_gateways.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ func (e *APIGatewayConfigEntry) validateListeners() error {

for _, listener := range e.Listeners {
if !validProtocols[listener.Protocol] {
return fmt.Errorf("unsupported listener protocol %q", listener.Protocol)
return fmt.Errorf("unsupported listener protocol %q, must be one of 'tcp', or 'http'", listener.Protocol)
}
if listener.Protocol == ListenerProtocolTCP && listener.Hostname != "" {
// TODO: once we have SNI matching we should be able to implement this
Expand All @@ -798,7 +798,7 @@ func (e *APIGatewayConfigEntry) validateListeners() error {
}
for _, certificate := range listener.TLS.Certificates {
if !allowedCertificateKinds[certificate.Kind] {
return fmt.Errorf("unsupported certificate kind: %q", certificate.Kind)
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind)
}
if certificate.Name == "" {
return fmt.Errorf("certificate reference must have a name")
Expand Down Expand Up @@ -892,6 +892,10 @@ type BoundAPIGatewayConfigEntry struct {
// gateway service definition.
Name string

// Listeners are the valid listeners of an APIGateway with information about
// what certificates and routes have successfully bound to it.
Listeners []BoundAPIGatewayListener

Meta map[string]string `json:",omitempty"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
Expand Down Expand Up @@ -920,6 +924,35 @@ func (e *BoundAPIGatewayConfigEntry) Normalize() error {
}

func (e *BoundAPIGatewayConfigEntry) Validate() error {
allowedCertificateKinds := map[string]bool{
InlineCertificate: true,
}
allowedRouteKinds := map[string]bool{
HTTPRoute: true,
TCPRoute: true,
}

// These should already be validated by upstream validation
// logic in the gateways/routes, but just in case we validate
// here as well.
for _, listener := range e.Listeners {
for _, certificate := range listener.Certificates {
if !allowedCertificateKinds[certificate.Kind] {
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind)
}
if certificate.Name == "" {
return fmt.Errorf("certificate reference must have a name")
}
}
for _, route := range listener.Routes {
if !allowedRouteKinds[route.Kind] {
return fmt.Errorf("unsupported route kind: %q, must be one of 'http-route', or 'tcp-route'", route.Kind)
}
if route.Name == "" {
return fmt.Errorf("route reference must have a name")
}
}
}
return nil
}

Expand All @@ -946,3 +979,11 @@ func (e *BoundAPIGatewayConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
}
return &e.EnterpriseMeta
}

// BoundAPIGatewayListener is an API gateway listener with information
// about the routes and certificates that have successfully bound to it.
type BoundAPIGatewayListener struct {
Name string
Routes []ResourceReference
Certificates []ResourceReference
}
66 changes: 66 additions & 0 deletions agent/structs/config_entry_gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1275,3 +1275,69 @@ func TestAPIGateway_Listeners(t *testing.T) {
}
testConfigEntryNormalizeAndValidate(t, cases)
}

func TestBoundAPIGateway(t *testing.T) {
cases := map[string]configEntryTestcase{
"invalid certificate, no name": {
entry: &BoundAPIGatewayConfigEntry{
Kind: BoundAPIGateway,
Name: "bound-api-gw-one",
Listeners: []BoundAPIGatewayListener{
{
Name: "one",
Certificates: []ResourceReference{{
Kind: InlineCertificate,
}},
},
},
},
validateErr: "certificate reference must have a name",
},
"invalid certificate, no kind": {
entry: &BoundAPIGatewayConfigEntry{
Kind: BoundAPIGateway,
Name: "bound-api-gw-two",
Listeners: []BoundAPIGatewayListener{
{
Name: "one",
Certificates: []ResourceReference{{
Name: "foo",
}},
},
},
},
validateErr: "unsupported certificate kind",
},
"invalid route, no name": {
entry: &BoundAPIGatewayConfigEntry{
Kind: BoundAPIGateway,
Name: "bound-api-gw-three",
Listeners: []BoundAPIGatewayListener{
{
Name: "one",
Routes: []ResourceReference{{
Kind: TCPRoute,
}},
},
},
},
validateErr: "route reference must have a name",
},
"invalid route, no kind": {
entry: &BoundAPIGatewayConfigEntry{
Kind: BoundAPIGateway,
Name: "bound-api-gw-four",
Listeners: []BoundAPIGatewayListener{
{
Name: "one",
Routes: []ResourceReference{{
Name: "foo",
}},
},
},
},
validateErr: "unsupported route kind",
},
}
testConfigEntryNormalizeAndValidate(t, cases)
}
66 changes: 66 additions & 0 deletions proto/pbconfigentry/config_entry.gen.go

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

10 changes: 10 additions & 0 deletions proto/pbconfigentry/config_entry.pb.binary.go

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

Loading