Skip to content
This repository was archived by the owner on Jan 12, 2023. It is now read-only.

Commit 13df1ef

Browse files
authored
Support all ingress versions (#128)
* Support all ingress versions Signed-off-by: Mingfei Huang <[email protected]>
1 parent 4cd7c85 commit 13df1ef

8 files changed

+262
-71
lines changed

charts/k-rail/Chart.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v1
22
name: k-rail
33
description: Kubernetes security tool for policy enforcement
44
home: https://github.com/cruise-automation/k-rail
5-
version: v3.6.0
5+
version: v3.6.1
66
maintainers:
77
- name: cruise-automation
88
url: https://cruise-automation.github.io/k-rail/

policies/ingress/disallow_nginx_snippet.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ func (p PolicyDisallowNGINXSnippet) Validate(ctx context.Context, config policie
4040
return resourceViolations, nil
4141
}
4242

43-
if ingressResource.IngressExt.Annotations == nil {
44-
return resourceViolations, nil
45-
}
46-
for key := range ingressResource.IngressExt.Annotations {
43+
for key := range ingressResource.GetAnnotations() {
4744
if nginxSnippetAnnotationRegex.MatchString(key) {
4845
resourceViolations = append(resourceViolations, policies.ResourceViolation{
4946
Namespace: ar.Namespace,

policies/ingress/disallow_nginx_snippet_test.go

+40-15
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020

2121
admissionv1 "k8s.io/api/admission/v1"
2222
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
23+
networkingv1 "k8s.io/api/networking/v1"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/apimachinery/pkg/runtime/schema"
2527

2628
"github.com/cruise-automation/k-rail/v3/policies"
2729
)
@@ -30,14 +32,19 @@ func TestPolicyDisallowNGINXSnippet(t *testing.T) {
3032
ctx := context.Background()
3133

3234
tests := []struct {
33-
name string
34-
ingressExt *extensionsv1beta1.Ingress
35+
name string
36+
ingress interface {
37+
GetObjectKind() schema.ObjectKind
38+
}
3539
violations int
3640
}{
3741
{
3842
name: "deny 1",
3943
violations: 1,
40-
ingressExt: &extensionsv1beta1.Ingress{
44+
ingress: &extensionsv1beta1.Ingress{
45+
TypeMeta: metav1.TypeMeta{
46+
APIVersion: "extensions/v1beta1",
47+
},
4148
ObjectMeta: metav1.ObjectMeta{
4249
Annotations: map[string]string{
4350
"nginx.ingress.kubernetes.io/server-snippet": "i'm malicious",
@@ -48,7 +55,25 @@ func TestPolicyDisallowNGINXSnippet(t *testing.T) {
4855
{
4956
name: "deny 2",
5057
violations: 2,
51-
ingressExt: &extensionsv1beta1.Ingress{
58+
ingress: &extensionsv1beta1.Ingress{
59+
TypeMeta: metav1.TypeMeta{
60+
APIVersion: "networking.k8s.io/v1beta1",
61+
},
62+
ObjectMeta: metav1.ObjectMeta{
63+
Annotations: map[string]string{
64+
"nginx.ingress.kubernetes.io/server-snippet": "i'm malicious",
65+
"nginx.ingress.kubernetes.io/auth-snippet": "me too",
66+
},
67+
},
68+
},
69+
},
70+
{
71+
name: "deny 3",
72+
violations: 2,
73+
ingress: &networkingv1.Ingress{
74+
TypeMeta: metav1.TypeMeta{
75+
APIVersion: "networking.k8s.io/v1",
76+
},
5277
ObjectMeta: metav1.ObjectMeta{
5378
Annotations: map[string]string{
5479
"nginx.ingress.kubernetes.io/server-snippet": "i'm malicious",
@@ -60,7 +85,7 @@ func TestPolicyDisallowNGINXSnippet(t *testing.T) {
6085
{
6186
name: "allow",
6287
violations: 0,
63-
ingressExt: &extensionsv1beta1.Ingress{
88+
ingress: &extensionsv1beta1.Ingress{
6489
ObjectMeta: metav1.ObjectMeta{
6590
Annotations: map[string]string{
6691
"foo": "bar",
@@ -71,16 +96,16 @@ func TestPolicyDisallowNGINXSnippet(t *testing.T) {
7196
}
7297
for _, tt := range tests {
7398
t.Run(tt.name, func(t *testing.T) {
74-
var ar = &admissionv1.AdmissionRequest{}
75-
76-
if tt.ingressExt != nil {
77-
raw, _ := json.Marshal(tt.ingressExt)
78-
ar = &admissionv1.AdmissionRequest{
79-
Namespace: "namespace",
80-
Name: "name",
81-
Object: runtime.RawExtension{Raw: raw},
82-
Resource: metav1.GroupVersionResource{Group: "extensions", Version: "v1beta1", Resource: "ingresses"},
83-
}
99+
raw, _ := json.Marshal(tt.ingress)
100+
ar := &admissionv1.AdmissionRequest{
101+
Namespace: "namespace",
102+
Name: "name",
103+
Object: runtime.RawExtension{Raw: raw},
104+
Resource: metav1.GroupVersionResource{
105+
Group: tt.ingress.GetObjectKind().GroupVersionKind().Group,
106+
Version: tt.ingress.GetObjectKind().GroupVersionKind().Version,
107+
Resource: "ingresses",
108+
},
84109
}
85110

86111
v := PolicyDisallowNGINXSnippet{}

policies/ingress/require_ingress_exemption.go

+1-14
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,7 @@ func (p PolicyRequireIngressExemption) Validate(ctx context.Context, config poli
3838
violationText := "Require Ingress Exemption: Using certain Ingress classes requires an exemption"
3939

4040
for _, ingressClass := range config.PolicyRequireIngressExemptionClasses {
41-
for annotation, value := range ingressResource.IngressExt.ObjectMeta.GetAnnotations() {
42-
if annotation == "kubernetes.io/ingress.class" {
43-
if value == ingressClass {
44-
resourceViolations = append(resourceViolations, policies.ResourceViolation{
45-
Namespace: ar.Namespace,
46-
ResourceName: ingressResource.ResourceName,
47-
ResourceKind: ingressResource.ResourceKind,
48-
Violation: violationText,
49-
Policy: p.Name(),
50-
})
51-
}
52-
}
53-
}
54-
for annotation, value := range ingressResource.IngressNet.ObjectMeta.GetAnnotations() {
41+
for annotation, value := range ingressResource.GetAnnotations() {
5542
if annotation == "kubernetes.io/ingress.class" {
5643
if value == ingressClass {
5744
resourceViolations = append(resourceViolations, policies.ResourceViolation{

policies/ingress/require_ingress_exemption_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestPolicyRequireIngressExemption(t *testing.T) {
100100
Namespace: "namespace",
101101
Name: "name",
102102
Object: runtime.RawExtension{Raw: raw},
103-
Resource: metav1.GroupVersionResource{Group: "networking", Version: "v1beta1", Resource: "ingresses"},
103+
Resource: metav1.GroupVersionResource{Group: "networking.k8s.io", Version: "v1beta1", Resource: "ingresses"},
104104
}
105105
}
106106

policies/ingress/unique_ingress_host.go

+2-30
Original file line numberDiff line numberDiff line change
@@ -86,36 +86,8 @@ func (p PolicyRequireUniqueHost) Validate(ctx context.Context, config policies.C
8686

8787
violationText := "Requires Unique Ingress Host: Ingress Host should not point to multiple namespaces. Host already in:"
8888

89-
for _, rule := range ingressResource.IngressExt.Spec.Rules {
90-
ingressNamespacesMap, err := p.CheckIngressNamespaces(ctx, rule.Host)
91-
if err != nil {
92-
log.Error(err)
93-
return nil, nil
94-
}
95-
foundNamespace := false
96-
_, ok := ingressNamespacesMap[ar.Namespace]
97-
if ok {
98-
foundNamespace = true
99-
}
100-
if (len(ingressNamespacesMap) == 0) || (len(ingressNamespacesMap) == 1 && foundNamespace) {
101-
return resourceViolations, nil
102-
} else {
103-
namespacesStr := ""
104-
for k := range ingressNamespacesMap {
105-
namespacesStr = namespacesStr + " " + k
106-
}
107-
resourceViolations = append(resourceViolations, policies.ResourceViolation{
108-
Namespace: ar.Namespace,
109-
ResourceName: ingressResource.ResourceName,
110-
ResourceKind: ingressResource.ResourceKind,
111-
Violation: violationText + ": " + namespacesStr,
112-
Policy: p.Name(),
113-
})
114-
}
115-
}
116-
117-
for _, rule := range ingressResource.IngressNet.Spec.Rules {
118-
ingressNamespacesMap, err := p.CheckIngressNamespaces(ctx, rule.Host)
89+
for _, host := range ingressResource.GetHosts() {
90+
ingressNamespacesMap, err := p.CheckIngressNamespaces(ctx, host)
11991
if err != nil {
12092
log.Error(err)
12193
return nil, nil

resource/ingress.go

+45-6
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ import (
1717

1818
admissionv1 "k8s.io/api/admission/v1"
1919
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
20+
networkingv1 "k8s.io/api/networking/v1"
2021
networkingv1beta1 "k8s.io/api/networking/v1beta1"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223
)
2324

2425
// IngressResource contains the information needed for processing by a Policy
2526
type IngressResource struct {
26-
IngressExt extensionsv1beta1.Ingress
27-
IngressNet networkingv1beta1.Ingress
28-
ResourceName string
29-
ResourceKind string
27+
IngressExt extensionsv1beta1.Ingress
28+
IngressNetV1Beta1 networkingv1beta1.Ingress
29+
IngressNetV1 networkingv1.Ingress
30+
ResourceName string
31+
ResourceKind string
3032
}
3133

3234
// GetIngressResource extracts and IngressResource from an AdmissionRequest
@@ -49,17 +51,54 @@ func decodeIngressResource(ar *admissionv1.AdmissionRequest) *IngressResource {
4951
ResourceName: GetResourceName(ing.ObjectMeta),
5052
ResourceKind: "Ingress",
5153
}
52-
case metav1.GroupVersionResource{Group: "networking", Version: "v1beta1", Resource: "ingresses"}:
54+
case metav1.GroupVersionResource{Group: "networking.k8s.io", Version: "v1beta1", Resource: "ingresses"}:
5355
ing := networkingv1beta1.Ingress{}
5456
if err := decodeObject(ar.Object.Raw, &ing); err != nil {
5557
return nil
5658
}
5759
return &IngressResource{
58-
IngressNet: ing,
60+
IngressNetV1Beta1: ing,
61+
ResourceName: GetResourceName(ing.ObjectMeta),
62+
ResourceKind: "Ingress",
63+
}
64+
case metav1.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "ingresses"}:
65+
ing := networkingv1.Ingress{}
66+
if err := decodeObject(ar.Object.Raw, &ing); err != nil {
67+
return nil
68+
}
69+
return &IngressResource{
70+
IngressNetV1: ing,
5971
ResourceName: GetResourceName(ing.ObjectMeta),
6072
ResourceKind: "Ingress",
6173
}
6274
default:
6375
return nil
6476
}
6577
}
78+
79+
// GetAnnotations returns ingress annotations, across all available ingress versions
80+
func (ir IngressResource) GetAnnotations() map[string]string {
81+
if ir.IngressExt.Annotations != nil {
82+
return ir.IngressExt.Annotations
83+
} else if ir.IngressNetV1Beta1.Annotations != nil {
84+
return ir.IngressNetV1Beta1.Annotations
85+
} else if ir.IngressNetV1.Annotations != nil {
86+
return ir.IngressNetV1.Annotations
87+
}
88+
return nil
89+
}
90+
91+
// GetHosts returns list of all hosts in ingress spec, across all available ingress versions
92+
func (ir IngressResource) GetHosts() []string {
93+
hosts := []string{}
94+
for _, rule := range ir.IngressExt.Spec.Rules {
95+
hosts = append(hosts, rule.Host)
96+
}
97+
for _, rule := range ir.IngressNetV1Beta1.Spec.Rules {
98+
hosts = append(hosts, rule.Host)
99+
}
100+
for _, rule := range ir.IngressNetV1.Spec.Rules {
101+
hosts = append(hosts, rule.Host)
102+
}
103+
return hosts
104+
}

0 commit comments

Comments
 (0)