mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-08-29 22:17:16 +00:00
Compare commits
3
Commits
master
..
js/gateway
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be3401cc9d | ||
|
|
8f61e0c68a | ||
|
|
b860eb798b |
@@ -24,6 +24,15 @@ key | default | description
|
||||
`hostNetworkSet` | `warning` | Fails when `hostNetwork` attribute is configured.
|
||||
`hostPortSet` | `warning` | Fails when `hostPort` attribute is configured.
|
||||
`tlsSettingsMissing` | `warning` | Fails when an Ingress lacks TLS settings.
|
||||
`gatewayTLSMissing` | `warning` | Fails when an HTTPS, GRPC, or terminating TLS Gateway listener lacks certificate references.
|
||||
`gatewayAllowedRoutesAll` | `warning` | Fails when a Gateway listener allows Routes from every namespace.
|
||||
`gatewayInsecureFrontendValidation` | `warning` | Fails when Gateway frontend client certificate validation allows insecure fallback.
|
||||
`gatewayCrossNamespaceCertificateRef` | `warning` | Fails when a Gateway references a certificate in another namespace without a matching ReferenceGrant. Cluster audits only.
|
||||
`httpRouteWildcardOrEmptyHost` | `warning` | Fails when an HTTPRoute omits hostnames or uses a wildcard hostname.
|
||||
`httpRouteInsecureListener` | `warning` | Fails when an HTTPRoute serves application traffic over HTTP without a full HTTPS redirect. Cluster audits only.
|
||||
`httpRouteCrossNamespaceBackendRef` | `warning` | Fails when an HTTPRoute references a backend in another namespace without a matching ReferenceGrant. Cluster audits only.
|
||||
`httpRouteBackendTLSMissing` | `warning` | Fails when an HTTPRoute TLS backend lacks a BackendTLSPolicy or kgateway BackendConfigPolicy. Cluster audits only.
|
||||
`kgatewayBackendTLSVerificationDisabled` | `warning` | Fails when a kgateway BackendConfigPolicy disables TLS certificate verification.
|
||||
`sensitiveContainerEnvVar` | `danger` | Fails when the container sets potentially sensitive environment variables.
|
||||
`sensitiveConfigmapContent` | `danger` | Fails when potentially sensitive content is detected in the ConfigMap keys or values.
|
||||
`missingNetworkPolicy` | `warning`
|
||||
@@ -40,6 +49,14 @@ key | default | description
|
||||
|
||||
Securing workloads in Kubernetes is an important part of overall cluster security. The overall goal should be to ensure that containers are running with as minimal privileges as possible. This includes avoiding privilege escalation, not running containers with a root user, not giving excessive access to the host network, and using read only file systems wherever possible.
|
||||
|
||||
### Gateway API
|
||||
|
||||
Gateway API separates listeners, routes, and backend TLS policy across different resources. Polaris checks standard `Gateway` and `HTTPRoute` resources for listener TLS, namespace isolation, host specificity, HTTPS redirects, cross-namespace authorization, and backend TLS. These checks work with conformant implementations such as kgateway.
|
||||
|
||||
`httpRouteBackendTLSMissing` also recognizes kgateway's `Backend` and `BackendConfigPolicy` resources. It identifies TLS backends from ports 443 and 8443, Service port names and `appProtocol`, and kgateway static Backend ports. `kgatewayBackendTLSVerificationDisabled` checks the kgateway-specific `insecureSkipVerify` setting. Authentication, authorization, and rate-limiting requirements are organization-specific and should be implemented as custom checks.
|
||||
|
||||
Checks marked "Cluster audits only" need related resources that are not available when Polaris evaluates a single admission request. They pass without a resource provider rather than rejecting an object without enough context.
|
||||
|
||||
A pod running with the `hostNetwork` attribute enabled will have access to the loopback device, services listening on localhost, and could be used to snoop on network activity of other pods on the same node. There are certain examples where setting `hostNetwork` to true is required, such as deploying a networking plugin like Flannel.
|
||||
|
||||
Setting the `hostPort` attribute on a container will ensure that it is accessible on that specific port on each node it is deployed to. Unfortunately when this is specified, it limits where a pod can actually be scheduled in a cluster.
|
||||
|
||||
@@ -57,6 +57,15 @@ var (
|
||||
"sensitiveContainerEnvVar",
|
||||
// Other checks
|
||||
"tlsSettingsMissing",
|
||||
"gatewayTLSMissing",
|
||||
"gatewayAllowedRoutesAll",
|
||||
"gatewayInsecureFrontendValidation",
|
||||
"gatewayCrossNamespaceCertificateRef",
|
||||
"httpRouteWildcardOrEmptyHost",
|
||||
"httpRouteInsecureListener",
|
||||
"httpRouteCrossNamespaceBackendRef",
|
||||
"httpRouteBackendTLSMissing",
|
||||
"kgatewayBackendTLSVerificationDisabled",
|
||||
"pdbDisruptionsIsZero",
|
||||
"metadataAndInstanceMismatched",
|
||||
"missingPodDisruptionBudget",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
successMessage: Gateway listeners restrict route attachment by namespace
|
||||
failureMessage: Gateway listeners should not allow routes from all namespaces
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
listeners:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
allowedRoutes:
|
||||
type: object
|
||||
properties:
|
||||
namespaces:
|
||||
type: object
|
||||
properties:
|
||||
from:
|
||||
not:
|
||||
const: All
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: Gateway cross-namespace certificate references are authorized
|
||||
failureMessage: Gateway cross-namespace certificate references should have a matching ReferenceGrant
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/ReferenceGrant
|
||||
@@ -0,0 +1,40 @@
|
||||
successMessage: Gateway frontend client certificate validation fails closed
|
||||
failureMessage: Gateway frontend client certificate validation should not allow insecure fallback
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
frontend:
|
||||
type: object
|
||||
properties:
|
||||
default:
|
||||
type: object
|
||||
properties:
|
||||
validation:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: AllowInsecureFallback
|
||||
perPort:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
validation:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: AllowInsecureFallback
|
||||
@@ -0,0 +1,41 @@
|
||||
successMessage: Gateway TLS listeners have certificates configured
|
||||
failureMessage: Gateway HTTPS, GRPC, and terminating TLS listeners should configure certificateRefs
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/Gateway
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
listeners:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
if:
|
||||
anyOf:
|
||||
- required: [protocol]
|
||||
properties:
|
||||
protocol:
|
||||
enum: [HTTPS, GRPC]
|
||||
- required: [protocol]
|
||||
properties:
|
||||
protocol:
|
||||
const: TLS
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
mode:
|
||||
not:
|
||||
const: Passthrough
|
||||
then:
|
||||
required: [tls]
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
required: [certificateRefs]
|
||||
properties:
|
||||
certificateRefs:
|
||||
type: array
|
||||
minItems: 1
|
||||
@@ -0,0 +1,9 @@
|
||||
successMessage: HTTPRoute TLS backends have TLS origination configured
|
||||
failureMessage: HTTPRoute backends on TLS ports should have a BackendTLSPolicy or kgateway BackendConfigPolicy
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- Service
|
||||
- gateway.networking.k8s.io/BackendTLSPolicy
|
||||
- gateway.kgateway.dev/Backend
|
||||
- gateway.kgateway.dev/BackendConfigPolicy
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: HTTPRoute cross-namespace backend references are authorized
|
||||
failureMessage: HTTPRoute cross-namespace backend references should have a matching ReferenceGrant
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/ReferenceGrant
|
||||
@@ -0,0 +1,6 @@
|
||||
successMessage: HTTPRoute uses secure listeners or redirects HTTP to HTTPS
|
||||
failureMessage: HTTPRoute should not serve application traffic over an HTTP listener
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
relatedKinds:
|
||||
- gateway.networking.k8s.io/Gateway
|
||||
@@ -0,0 +1,19 @@
|
||||
successMessage: HTTPRoute uses explicit hostnames
|
||||
failureMessage: HTTPRoute should use explicit hostnames instead of matching every hostname
|
||||
category: Security
|
||||
target: gateway.networking.k8s.io/HTTPRoute
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [hostnames]
|
||||
properties:
|
||||
hostnames:
|
||||
type: array
|
||||
minItems: 1
|
||||
items:
|
||||
type: string
|
||||
pattern: '^[^*]+$'
|
||||
@@ -0,0 +1,17 @@
|
||||
successMessage: kgateway backend TLS certificate verification is enabled
|
||||
failureMessage: kgateway BackendConfigPolicy should not disable TLS certificate verification
|
||||
category: Security
|
||||
target: gateway.kgateway.dev/BackendConfigPolicy
|
||||
schema:
|
||||
'$schema': https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
tls:
|
||||
type: object
|
||||
properties:
|
||||
insecureSkipVerify:
|
||||
not:
|
||||
const: true
|
||||
@@ -38,6 +38,15 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
gatewayTLSMissing: warning
|
||||
gatewayAllowedRoutesAll: warning
|
||||
gatewayInsecureFrontendValidation: warning
|
||||
gatewayCrossNamespaceCertificateRef: warning
|
||||
httpRouteWildcardOrEmptyHost: warning
|
||||
httpRouteInsecureListener: warning
|
||||
httpRouteCrossNamespaceBackendRef: warning
|
||||
httpRouteBackendTLSMissing: warning
|
||||
kgatewayBackendTLSVerificationDisabled: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
|
||||
@@ -38,6 +38,15 @@ checks:
|
||||
hostNetworkSet: danger
|
||||
hostPortSet: warning
|
||||
tlsSettingsMissing: warning
|
||||
gatewayTLSMissing: warning
|
||||
gatewayAllowedRoutesAll: warning
|
||||
gatewayInsecureFrontendValidation: warning
|
||||
gatewayCrossNamespaceCertificateRef: warning
|
||||
httpRouteWildcardOrEmptyHost: warning
|
||||
httpRouteInsecureListener: warning
|
||||
httpRouteCrossNamespaceBackendRef: warning
|
||||
httpRouteBackendTLSMissing: warning
|
||||
kgatewayBackendTLSVerificationDisabled: warning
|
||||
sensitiveContainerEnvVar: danger
|
||||
sensitiveConfigmapContent: danger
|
||||
clusterrolePodExecAttach: danger
|
||||
|
||||
@@ -80,6 +80,7 @@ type SchemaCheck struct {
|
||||
AdditionalSchemas map[string]map[string]any `yaml:"additionalSchemas" json:"additionalSchemas"`
|
||||
AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"`
|
||||
AdditionalValidators map[string]jsonschema.Schema `yaml:"-" json:"-"`
|
||||
RelatedKinds []TargetKind `yaml:"relatedKinds" json:"relatedKinds"`
|
||||
Mutations []Mutation `yaml:"mutations" json:"mutations"`
|
||||
}
|
||||
|
||||
|
||||
+15
-5
@@ -325,16 +325,22 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
}
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(resources)
|
||||
allChecks := []conf.SchemaCheck{}
|
||||
for _, check := range c.CustomChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
for checkID, check := range c.CustomChecks {
|
||||
severity, enabled := c.Checks[checkID]
|
||||
if enabled && severity.IsActionable() {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
}
|
||||
for _, check := range conf.BuiltInChecks {
|
||||
allChecks = append(allChecks, check)
|
||||
for checkID, check := range conf.BuiltInChecks {
|
||||
severity, enabled := c.Checks[checkID]
|
||||
if enabled && severity.IsActionable() {
|
||||
allChecks = append(allChecks, check)
|
||||
}
|
||||
}
|
||||
|
||||
var additionalKinds []conf.TargetKind
|
||||
for _, check := range allChecks {
|
||||
neededKinds := []conf.TargetKind{check.Target}
|
||||
neededKinds := append([]conf.TargetKind{check.Target}, check.RelatedKinds...)
|
||||
for key := range check.AdditionalSchemas {
|
||||
neededKinds = append(neededKinds, conf.TargetKind(key))
|
||||
}
|
||||
@@ -353,6 +359,10 @@ func CreateResourceProviderFromAPI(ctx context.Context, kube kubernetes.Interfac
|
||||
groupKind := parseGroupKind(maybeTransformKindIntoGroupKind(string(kind)))
|
||||
mapping, err := restMapper.RESTMapping(groupKind)
|
||||
if err != nil {
|
||||
if meta.IsNoMatchError(err) {
|
||||
logrus.Infof("Skipping unavailable Kind %s", kind)
|
||||
continue
|
||||
}
|
||||
logrus.Warnf("error retrieving mapping of Kind %s because of error: %v", kind, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -176,3 +176,45 @@ func TestGetResourceFromAPI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdditionalKindLoading(t *testing.T) {
|
||||
ingress := test.MockIngress()
|
||||
k8s, dynamicInterface := test.SetupTestAPI(append(test.GetMockControllers("test"), &ingress)...)
|
||||
|
||||
enabled := conf.Configuration{
|
||||
Checks: map[string]conf.Severity{
|
||||
"customIngress": conf.SeverityWarning,
|
||||
},
|
||||
CustomChecks: map[string]conf.SchemaCheck{
|
||||
"customIngress": {Target: "networking.k8s.io/Ingress"},
|
||||
},
|
||||
}
|
||||
resources, err := CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, enabled)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, resources.Resources["networking.k8s.io/Ingress"], 1)
|
||||
}
|
||||
|
||||
ignored := enabled
|
||||
ignored.Checks = map[string]conf.Severity{
|
||||
"customIngress": conf.SeverityIgnore,
|
||||
}
|
||||
resources, err = CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, ignored)
|
||||
if assert.NoError(t, err) {
|
||||
assert.Empty(t, resources.Resources["networking.k8s.io/Ingress"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnavailableAdditionalKindDoesNotFailAudit(t *testing.T) {
|
||||
k8s, dynamicInterface := test.SetupTestAPI(test.GetMockControllers("test")...)
|
||||
config := conf.Configuration{
|
||||
Checks: map[string]conf.Severity{
|
||||
"optionalCRD": conf.SeverityWarning,
|
||||
},
|
||||
CustomChecks: map[string]conf.SchemaCheck{
|
||||
"optionalCRD": {Target: "example.com/OptionalResource"},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := CreateResourceProviderFromAPI(context.Background(), k8s, "test", dynamicInterface, config)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fairwindsops/polaris/pkg/kube"
|
||||
"github.com/qri-io/jsonschema"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
const (
|
||||
gatewayAPIGroup = "gateway.networking.k8s.io"
|
||||
kgatewayAPIGroup = "gateway.kgateway.dev"
|
||||
)
|
||||
|
||||
type objectReference struct {
|
||||
group string
|
||||
kind string
|
||||
name string
|
||||
namespace string
|
||||
sectionName string
|
||||
port int64
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerCustomChecks("httpRouteInsecureListener", httpRouteInsecureListener)
|
||||
registerCustomChecks("gatewayCrossNamespaceCertificateRef", gatewayCrossNamespaceCertificateRef)
|
||||
registerCustomChecks("httpRouteCrossNamespaceBackendRef", httpRouteCrossNamespaceBackendRef)
|
||||
registerCustomChecks("httpRouteBackendTLSMissing", httpRouteBackendTLSMissing)
|
||||
}
|
||||
|
||||
func httpRouteInsecureListener(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if isFullHTTPSRedirect(test.Resource.Resource.Object) || test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
routeNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, parent := range referencesAt(test.Resource.Resource.Object, "spec", "parentRefs") {
|
||||
parent = withDefaults(parent, gatewayAPIGroup, "Gateway", routeNamespace)
|
||||
if parent.group != gatewayAPIGroup || parent.kind != "Gateway" {
|
||||
continue
|
||||
}
|
||||
|
||||
gateway := findResource(test.ResourceProvider.Resources[gatewayAPIGroup+"/Gateway"], parent.namespace, parent.name)
|
||||
if gateway == nil {
|
||||
continue
|
||||
}
|
||||
listeners := nestedSlice(gateway.Resource.Object, "spec", "listeners")
|
||||
for _, rawListener := range listeners {
|
||||
listener, ok := rawListener.(map[string]any)
|
||||
if !ok || (parent.sectionName != "" && stringValue(listener["name"]) != parent.sectionName) {
|
||||
continue
|
||||
}
|
||||
if stringValue(listener["protocol"]) == "HTTP" && listenerAcceptsHTTPRoute(listener, parent.namespace, test.Resource, test.ResourceProvider) {
|
||||
return gatewayFailure("spec.parentRefs", fmt.Sprintf("HTTPRoute references HTTP listener %q on Gateway %s/%s without a full HTTPS redirect", stringValue(listener["name"]), parent.namespace, parent.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func gatewayCrossNamespaceCertificateRef(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
sourceNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
listeners := nestedSlice(test.Resource.Resource.Object, "spec", "listeners")
|
||||
for _, rawListener := range listeners {
|
||||
listener, ok := rawListener.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, ref := range referencesAt(listener, "tls", "certificateRefs") {
|
||||
ref = withDefaults(ref, "", "Secret", sourceNamespace)
|
||||
if ref.namespace != sourceNamespace && !hasReferenceGrant(test.ResourceProvider, sourceNamespace, "Gateway", ref) {
|
||||
return gatewayFailure("spec.listeners.tls.certificateRefs", fmt.Sprintf("Gateway %s/%s references %s %s/%s without a matching ReferenceGrant", sourceNamespace, test.Resource.ObjectMeta.GetName(), ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func httpRouteCrossNamespaceBackendRef(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
sourceNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, ref := range httpRouteBackendRefs(test.Resource.Resource.Object) {
|
||||
ref = withDefaults(ref, "", "Service", sourceNamespace)
|
||||
if ref.namespace != sourceNamespace && !hasReferenceGrant(test.ResourceProvider, sourceNamespace, "HTTPRoute", ref) {
|
||||
return gatewayFailure("spec.rules.backendRefs", fmt.Sprintf("HTTPRoute %s/%s references %s %s/%s without a matching ReferenceGrant", sourceNamespace, test.Resource.ObjectMeta.GetName(), ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func httpRouteBackendTLSMissing(test schemaTestCase) (bool, []jsonschema.KeyError, error) {
|
||||
if test.ResourceProvider == nil {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
routeNamespace := test.Resource.ObjectMeta.GetNamespace()
|
||||
for _, ref := range httpRouteBackendRefs(test.Resource.Resource.Object) {
|
||||
ref = withDefaults(ref, "", "Service", routeNamespace)
|
||||
if !backendUsesTLS(test.ResourceProvider, ref) {
|
||||
continue
|
||||
}
|
||||
if hasBackendTLSPolicy(test.ResourceProvider, ref) || hasKgatewayBackendTLSPolicy(test.ResourceProvider, ref) {
|
||||
continue
|
||||
}
|
||||
return gatewayFailure("spec.rules.backendRefs", fmt.Sprintf("HTTPRoute backend %s %s/%s appears to use TLS but has no BackendTLSPolicy or kgateway BackendConfigPolicy", ref.kind, ref.namespace, ref.name))
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
func isFullHTTPSRedirect(object map[string]any) bool {
|
||||
rules := nestedSlice(object, "spec", "rules")
|
||||
if len(rules) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, rawRule := range rules {
|
||||
rule, ok := rawRule.(map[string]any)
|
||||
if !ok || len(referencesAt(rule, "backendRefs")) > 0 || !ruleMatchesAllTraffic(rule) || !hasHTTPSRedirect(rule) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ruleMatchesAllTraffic(rule map[string]any) bool {
|
||||
matches := nestedSlice(rule, "matches")
|
||||
if len(matches) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, rawMatch := range matches {
|
||||
match, ok := rawMatch.(map[string]any)
|
||||
if !ok || len(match) != 1 {
|
||||
continue
|
||||
}
|
||||
path, ok := match["path"].(map[string]any)
|
||||
if ok && (stringValue(path["type"]) == "" || stringValue(path["type"]) == "PathPrefix") && stringValue(path["value"]) == "/" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasHTTPSRedirect(rule map[string]any) bool {
|
||||
filters := nestedSlice(rule, "filters")
|
||||
for _, rawFilter := range filters {
|
||||
filter, ok := rawFilter.(map[string]any)
|
||||
if !ok || stringValue(filter["type"]) != "RequestRedirect" {
|
||||
continue
|
||||
}
|
||||
redirect, ok := filter["requestRedirect"].(map[string]any)
|
||||
if ok && strings.EqualFold(stringValue(redirect["scheme"]), "https") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listenerAcceptsHTTPRoute(listener map[string]any, gatewayNamespace string, route kube.GenericResource, provider *kube.ResourceProvider) bool {
|
||||
if !listenerHostnameIntersectsRoute(listener, route.Resource.Object) {
|
||||
return false
|
||||
}
|
||||
|
||||
allowedRoutes, ok := listener["allowedRoutes"].(map[string]any)
|
||||
if !ok {
|
||||
return route.ObjectMeta.GetNamespace() == gatewayNamespace
|
||||
}
|
||||
if kinds := nestedSlice(allowedRoutes, "kinds"); len(kinds) > 0 {
|
||||
allowsHTTPRoute := false
|
||||
for _, rawKind := range kinds {
|
||||
kind, ok := rawKind.(map[string]any)
|
||||
if ok && withDefaultString(stringValue(kind["group"]), gatewayAPIGroup) == gatewayAPIGroup && stringValue(kind["kind"]) == "HTTPRoute" {
|
||||
allowsHTTPRoute = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allowsHTTPRoute {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
namespaces, ok := allowedRoutes["namespaces"].(map[string]any)
|
||||
if !ok || stringValue(namespaces["from"]) == "" || stringValue(namespaces["from"]) == "Same" {
|
||||
return route.ObjectMeta.GetNamespace() == gatewayNamespace
|
||||
}
|
||||
if stringValue(namespaces["from"]) == "All" {
|
||||
return true
|
||||
}
|
||||
if stringValue(namespaces["from"]) != "Selector" {
|
||||
return false
|
||||
}
|
||||
selectorMap, ok := namespaces["selector"].(map[string]any)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
selector := &metav1.LabelSelector{}
|
||||
selectorJSON, err := json.Marshal(selectorMap)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err := json.Unmarshal(selectorJSON, selector); err != nil {
|
||||
return false
|
||||
}
|
||||
compiled, err := metav1.LabelSelectorAsSelector(selector)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, namespace := range provider.Namespaces {
|
||||
if namespace.Name == route.ObjectMeta.GetNamespace() {
|
||||
return compiled.Matches(labels.Set(namespace.Labels))
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listenerHostnameIntersectsRoute(listener, route map[string]any) bool {
|
||||
listenerHostname := stringValue(listener["hostname"])
|
||||
routeHostnames := nestedSlice(route, "spec", "hostnames")
|
||||
if len(routeHostnames) == 0 || listenerHostname == "" {
|
||||
return true
|
||||
}
|
||||
for _, routeHostname := range routeHostnames {
|
||||
if hostnamesIntersect(listenerHostname, stringValue(routeHostname)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hostnamesIntersect(left, right string) bool {
|
||||
if left == "" || right == "" || left == "*" || right == "*" || strings.EqualFold(left, right) {
|
||||
return true
|
||||
}
|
||||
leftSuffix, leftWildcard := strings.CutPrefix(strings.ToLower(left), "*.")
|
||||
rightSuffix, rightWildcard := strings.CutPrefix(strings.ToLower(right), "*.")
|
||||
switch {
|
||||
case leftWildcard && rightWildcard:
|
||||
return leftSuffix == rightSuffix || strings.HasSuffix(leftSuffix, "."+rightSuffix) || strings.HasSuffix(rightSuffix, "."+leftSuffix)
|
||||
case leftWildcard:
|
||||
return strings.HasSuffix(strings.ToLower(right), "."+leftSuffix)
|
||||
case rightWildcard:
|
||||
return strings.HasSuffix(strings.ToLower(left), "."+rightSuffix)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func httpRouteBackendRefs(object map[string]any) []objectReference {
|
||||
var refs []objectReference
|
||||
rules := nestedSlice(object, "spec", "rules")
|
||||
for _, rawRule := range rules {
|
||||
rule, ok := rawRule.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
refs = append(refs, referencesAt(rule, "backendRefs")...)
|
||||
filters := nestedSlice(rule, "filters")
|
||||
for _, rawFilter := range filters {
|
||||
filter, ok := rawFilter.(map[string]any)
|
||||
if !ok || stringValue(filter["type"]) != "RequestMirror" {
|
||||
continue
|
||||
}
|
||||
mirror, ok := filter["requestMirror"].(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if backend, ok := mirror["backendRef"].(map[string]any); ok {
|
||||
refs = append(refs, referenceFromMap(backend))
|
||||
}
|
||||
}
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
func referencesAt(object map[string]any, fields ...string) []objectReference {
|
||||
items := nestedSlice(object, fields...)
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
refs := make([]objectReference, 0, len(items))
|
||||
for _, item := range items {
|
||||
if ref, ok := item.(map[string]any); ok {
|
||||
refs = append(refs, referenceFromMap(ref))
|
||||
}
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
func referenceFromMap(ref map[string]any) objectReference {
|
||||
return objectReference{
|
||||
group: stringValue(ref["group"]),
|
||||
kind: stringValue(ref["kind"]),
|
||||
name: stringValue(ref["name"]),
|
||||
namespace: stringValue(ref["namespace"]),
|
||||
sectionName: stringValue(ref["sectionName"]),
|
||||
port: int64Value(ref["port"]),
|
||||
}
|
||||
}
|
||||
|
||||
func withDefaults(ref objectReference, group, kind, namespace string) objectReference {
|
||||
if ref.group == "" {
|
||||
ref.group = group
|
||||
}
|
||||
if ref.kind == "" {
|
||||
ref.kind = kind
|
||||
}
|
||||
if ref.namespace == "" {
|
||||
ref.namespace = namespace
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func withDefaultString(value, defaultValue string) string {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func hasReferenceGrant(provider *kube.ResourceProvider, sourceNamespace, sourceKind string, target objectReference) bool {
|
||||
for _, grant := range provider.Resources[gatewayAPIGroup+"/ReferenceGrant"] {
|
||||
if grant.ObjectMeta.GetNamespace() != target.namespace {
|
||||
continue
|
||||
}
|
||||
fromMatches := false
|
||||
for _, from := range referencesAt(grant.Resource.Object, "spec", "from") {
|
||||
if from.group == gatewayAPIGroup && from.kind == sourceKind && from.namespace == sourceNamespace {
|
||||
fromMatches = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !fromMatches {
|
||||
continue
|
||||
}
|
||||
for _, to := range referencesAt(grant.Resource.Object, "spec", "to") {
|
||||
if to.group == target.group && to.kind == target.kind && (to.name == "" || to.name == target.name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func backendUsesTLS(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
// ponytail: infer TLS from conventional ports and backend metadata; replace
|
||||
// this with controller status or an implementation graph when Polaris has one.
|
||||
if ref.port == 443 || ref.port == 8443 {
|
||||
return true
|
||||
}
|
||||
groupKind := ref.kind
|
||||
if ref.group != "" {
|
||||
groupKind = ref.group + "/" + ref.kind
|
||||
}
|
||||
backend := findResource(provider.Resources[groupKind], ref.namespace, ref.name)
|
||||
if backend == nil {
|
||||
return false
|
||||
}
|
||||
if ref.group == "" && ref.kind == "Service" {
|
||||
ports := nestedSlice(backend.Resource.Object, "spec", "ports")
|
||||
for _, rawPort := range ports {
|
||||
port, ok := rawPort.(map[string]any)
|
||||
if !ok || (ref.port != 0 && int64Value(port["port"]) != ref.port) {
|
||||
continue
|
||||
}
|
||||
name := strings.ToLower(stringValue(port["name"]))
|
||||
appProtocol := strings.ToLower(stringValue(port["appProtocol"]))
|
||||
if name == "https" || strings.HasPrefix(name, "https-") || appProtocol == "https" || strings.HasSuffix(appProtocol, "/https") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if ref.group == kgatewayAPIGroup && ref.kind == "Backend" {
|
||||
hosts := nestedSlice(backend.Resource.Object, "spec", "static", "hosts")
|
||||
for _, rawHost := range hosts {
|
||||
host, ok := rawHost.(map[string]any)
|
||||
if ok && (int64Value(host["port"]) == 443 || int64Value(host["port"]) == 8443) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasBackendTLSPolicy(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
if ref.group != "" || ref.kind != "Service" {
|
||||
return false
|
||||
}
|
||||
for _, policy := range provider.Resources[gatewayAPIGroup+"/BackendTLSPolicy"] {
|
||||
if policy.ObjectMeta.GetNamespace() == ref.namespace && policyTargets(policy, ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasKgatewayBackendTLSPolicy(provider *kube.ResourceProvider, ref objectReference) bool {
|
||||
for _, policy := range provider.Resources[kgatewayAPIGroup+"/BackendConfigPolicy"] {
|
||||
if policy.ObjectMeta.GetNamespace() != ref.namespace {
|
||||
continue
|
||||
}
|
||||
if _, found := nestedValue(policy.Resource.Object, "spec", "tls"); found && policyTargets(policy, ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func policyTargets(policy kube.GenericResource, target objectReference) bool {
|
||||
for _, ref := range referencesAt(policy.Resource.Object, "spec", "targetRefs") {
|
||||
ref = withDefaults(ref, "", "Service", policy.ObjectMeta.GetNamespace())
|
||||
if ref.group == target.group && ref.kind == target.kind && ref.name == target.name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func findResource(resources []kube.GenericResource, namespace, name string) *kube.GenericResource {
|
||||
for i := range resources {
|
||||
if resources[i].ObjectMeta.GetNamespace() == namespace && resources[i].ObjectMeta.GetName() == name {
|
||||
return &resources[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringValue(value any) string {
|
||||
valueString, _ := value.(string)
|
||||
return valueString
|
||||
}
|
||||
|
||||
func int64Value(value any) int64 {
|
||||
switch number := value.(type) {
|
||||
case int:
|
||||
return int64(number)
|
||||
case int32:
|
||||
return int64(number)
|
||||
case int64:
|
||||
return number
|
||||
case float64:
|
||||
return int64(number)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func nestedSlice(object map[string]any, fields ...string) []any {
|
||||
value, found := nestedValue(object, fields...)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
items, _ := value.([]any)
|
||||
return items
|
||||
}
|
||||
|
||||
func nestedValue(object map[string]any, fields ...string) (any, bool) {
|
||||
var current any = object
|
||||
for _, field := range fields {
|
||||
currentMap, ok := current.(map[string]any)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
current, ok = currentMap[field]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return current, true
|
||||
}
|
||||
|
||||
func gatewayFailure(path, message string) (bool, []jsonschema.KeyError, error) {
|
||||
return false, []jsonschema.KeyError{{
|
||||
PropertyPath: path,
|
||||
Message: message,
|
||||
}}, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: unrestricted
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: restricted
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: Selector
|
||||
selector:
|
||||
matchLabels:
|
||||
gateway-access: "true"
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: wildcard
|
||||
namespace: certificates
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: wildcard
|
||||
namespace: certificates
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: ReferenceGrant
|
||||
metadata:
|
||||
name: allow-infra-gateway
|
||||
namespace: certificates
|
||||
spec:
|
||||
from:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
namespace: infra
|
||||
to:
|
||||
- group: ""
|
||||
kind: Secret
|
||||
name: wildcard
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: fail-open
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
tls:
|
||||
frontend:
|
||||
default:
|
||||
validation:
|
||||
mode: AllowInsecureFallback
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: fail-closed
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
tls:
|
||||
frontend:
|
||||
default:
|
||||
validation:
|
||||
mode: AllowValidOnly
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: missing-tls
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: insecure
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
mode: Terminate
|
||||
certificateRefs: []
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: secure
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
tls:
|
||||
mode: Terminate
|
||||
certificateRefs:
|
||||
- name: example-tls
|
||||
- name: passthrough
|
||||
protocol: TLS
|
||||
port: 8443
|
||||
tls:
|
||||
mode: Passthrough
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: plaintext-to-tls-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 443
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-kgateway-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- group: gateway.kgateway.dev
|
||||
kind: Backend
|
||||
name: external-api
|
||||
---
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: Backend
|
||||
metadata:
|
||||
name: external-api
|
||||
namespace: app
|
||||
spec:
|
||||
type: Static
|
||||
static:
|
||||
hosts:
|
||||
- host: api.example.com
|
||||
port: 443
|
||||
---
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: external-api-tls
|
||||
namespace: app
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: gateway.kgateway.dev
|
||||
kind: Backend
|
||||
name: external-api
|
||||
tls:
|
||||
sni: api.example.com
|
||||
wellKnownCACertificates: System
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-backend
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 443
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: BackendTLSPolicy
|
||||
metadata:
|
||||
name: api-tls
|
||||
namespace: app
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
validation:
|
||||
hostname: api.app.svc.cluster.local
|
||||
wellKnownCACertificates: System
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: shared-api
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
namespace: shared
|
||||
port: 8080
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: shared-api
|
||||
namespace: app
|
||||
spec:
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
namespace: shared
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: ReferenceGrant
|
||||
metadata:
|
||||
name: allow-app-route
|
||||
namespace: shared
|
||||
spec:
|
||||
from:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: HTTPRoute
|
||||
namespace: app
|
||||
to:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: plaintext
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
namespace: infra
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: app
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,31 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: secure-host-only
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: app
|
||||
port: 8080
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: app
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: unrelated-http
|
||||
hostname: other.example.com
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
- name: app-https
|
||||
hostname: app.example.com
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: redirect
|
||||
namespace: app
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: public
|
||||
namespace: infra
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- app.example.com
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
filters:
|
||||
- type: RequestRedirect
|
||||
requestRedirect:
|
||||
scheme: https
|
||||
statusCode: 301
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: public
|
||||
namespace: infra
|
||||
spec:
|
||||
gatewayClassName: kgateway
|
||||
listeners:
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: every-host
|
||||
spec:
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: wildcard-host
|
||||
spec:
|
||||
hostnames:
|
||||
- "*.example.com"
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: explicit-host
|
||||
spec:
|
||||
hostnames:
|
||||
- api.example.com
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: api
|
||||
port: 8080
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: unverified
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
tls:
|
||||
insecureSkipVerify: true
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: gateway.kgateway.dev/v1alpha1
|
||||
kind: BackendConfigPolicy
|
||||
metadata:
|
||||
name: verified
|
||||
spec:
|
||||
targetRefs:
|
||||
- group: ""
|
||||
kind: Service
|
||||
name: api
|
||||
tls:
|
||||
sni: api.example.com
|
||||
wellKnownCACertificates: System
|
||||
Reference in New Issue
Block a user