Skip to content

Commit 54991c0

Browse files
committed
add status to waf policy
1 parent 61185c7 commit 54991c0

File tree

3 files changed

+140
-49
lines changed

3 files changed

+140
-49
lines changed

internal/controller/state/conditions/conditions.go

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,8 @@ import (
1010
ngfAPI "github.com/nginx/nginx-gateway-fabric/apis/v1alpha1"
1111
)
1212

13+
// Conditions and Reasons for Route resources.
1314
const (
14-
// GatewayClassReasonGatewayClassConflict indicates there are multiple GatewayClass resources
15-
// that reference this controller, and we ignored the resource in question and picked the
16-
// GatewayClass that is referenced in the command-line argument.
17-
// This reason is used with GatewayClassConditionAccepted (false).
18-
GatewayClassReasonGatewayClassConflict v1.GatewayClassConditionReason = "GatewayClassConflict"
19-
20-
// GatewayClassMessageGatewayClassConflict is a message that describes GatewayClassReasonGatewayClassConflict.
21-
GatewayClassMessageGatewayClassConflict = "The resource is ignored due to a conflicting GatewayClass resource"
22-
23-
// ListenerReasonUnsupportedValue is used with the "Accepted" condition when a value of a field in a Listener
24-
// is invalid or not supported.
25-
ListenerReasonUnsupportedValue v1.ListenerConditionReason = "UnsupportedValue"
26-
27-
// ListenerMessageFailedNginxReload is a message used with ListenerConditionProgrammed (false)
28-
// when nginx fails to reload.
29-
ListenerMessageFailedNginxReload = "The Listener is not programmed due to a failure to " +
30-
"reload nginx with the configuration"
31-
3215
// RouteReasonBackendRefUnsupportedValue is used with the "ResolvedRefs" condition when one of the
3316
// Route rules has a backendRef with an unsupported value.
3417
RouteReasonBackendRefUnsupportedValue v1.RouteConditionReason = "UnsupportedValue"
@@ -61,6 +44,48 @@ const (
6144
// invalid. Used with ResolvedRefs (false).
6245
RouteReasonInvalidFilter v1.RouteConditionReason = "InvalidFilter"
6346

47+
// RouteMessageFailedNginxReload is a message used with RouteReasonGatewayNotProgrammed
48+
// when nginx fails to reload.
49+
RouteMessageFailedNginxReload = GatewayMessageFailedNginxReload + ". NGINX may still be configured " +
50+
"for this Route. However, future updates to this resource will not be configured until the Gateway " +
51+
"is programmed again"
52+
)
53+
54+
// Conditions and Reasons for GatewayClass, Gateway and Listener resources.
55+
const (
56+
// GatewayClassReasonGatewayClassConflict indicates there are multiple GatewayClass resources
57+
// that reference this controller, and we ignored the resource in question and picked the
58+
// GatewayClass that is referenced in the command-line argument.
59+
// This reason is used with GatewayClassConditionAccepted (false).
60+
GatewayClassReasonGatewayClassConflict v1.GatewayClassConditionReason = "GatewayClassConflict"
61+
62+
// GatewayClassMessageGatewayClassConflict is a message that describes GatewayClassReasonGatewayClassConflict.
63+
GatewayClassMessageGatewayClassConflict = "The resource is ignored due to a conflicting GatewayClass resource"
64+
65+
// ListenerReasonUnsupportedValue is used with the "Accepted" condition when a value of a field in a Listener
66+
// is invalid or not supported.
67+
ListenerReasonUnsupportedValue v1.ListenerConditionReason = "UnsupportedValue"
68+
69+
// ListenerMessageFailedNginxReload is a message used with ListenerConditionProgrammed (false)
70+
// when nginx fails to reload.
71+
ListenerMessageFailedNginxReload = "The Listener is not programmed due to a failure to " +
72+
"reload nginx with the configuration"
73+
74+
// GatewayResolvedRefs condition indicates whether the controller was able to resolve the
75+
// parametersRef on the Gateway.
76+
GatewayResolvedRefs v1.GatewayConditionType = "ResolvedRefs"
77+
78+
// GatewayReasonResolvedRefs is used with the "GatewayResolvedRefs" condition when the condition is true.
79+
GatewayReasonResolvedRefs v1.GatewayConditionReason = "ResolvedRefs"
80+
81+
// GatewayReasonParamsRefNotFound is used with the "GatewayResolvedRefs" condition when the
82+
// parametersRef resource does not exist.
83+
GatewayReasonParamsRefNotFound v1.GatewayConditionReason = "ParametersRefNotFound"
84+
85+
// GatewayReasonParamsRefInvalid is used with the "GatewayResolvedRefs" condition when the
86+
// parametersRef resource is invalid.
87+
GatewayReasonParamsRefInvalid v1.GatewayConditionReason = "ParametersRefInvalid"
88+
6489
// GatewayReasonUnsupportedValue is used with GatewayConditionAccepted (false) when a value of a field in a Gateway
6590
// is invalid or not supported.
6691
GatewayReasonUnsupportedValue v1.GatewayConditionReason = "UnsupportedValue"
@@ -70,12 +95,6 @@ const (
7095
GatewayMessageFailedNginxReload = "The Gateway is not programmed due to a failure to " +
7196
"reload nginx with the configuration"
7297

73-
// RouteMessageFailedNginxReload is a message used with RouteReasonGatewayNotProgrammed
74-
// when nginx fails to reload.
75-
RouteMessageFailedNginxReload = GatewayMessageFailedNginxReload + ". NGINX may still be configured " +
76-
"for this Route. However, future updates to this resource will not be configured until the Gateway " +
77-
"is programmed again"
78-
7998
// GatewayClassResolvedRefs condition indicates whether the controller was able to resolve the
8099
// parametersRef on the GatewayClass.
81100
GatewayClassResolvedRefs v1.GatewayClassConditionType = "ResolvedRefs"
@@ -90,7 +109,10 @@ const (
90109
// GatewayClassReasonParamsRefInvalid is used with the "GatewayClassResolvedRefs" condition when the
91110
// parametersRef resource is invalid.
92111
GatewayClassReasonParamsRefInvalid v1.GatewayClassConditionReason = "ParametersRefInvalid"
112+
)
93113

114+
// Conditions and Reasons for Policy resources.
115+
const (
94116
// PolicyReasonNginxProxyConfigNotSet is used with the "PolicyAccepted" condition when the
95117
// NginxProxy resource is missing or invalid.
96118
PolicyReasonNginxProxyConfigNotSet v1alpha2.PolicyConditionReason = "NginxProxyConfigNotSet"
@@ -107,6 +129,18 @@ const (
107129
// has an overlapping hostname:port/path combination with another Route.
108130
PolicyReasonTargetConflict v1alpha2.PolicyConditionReason = "TargetConflict"
109131

132+
// PolicyAffectedReason is used with the "PolicyAffected" condition when a
133+
// ObservabilityPolicy or ClientSettingsPolicy is applied to Gateways or Routes.
134+
PolicyAffectedReason v1alpha2.PolicyConditionReason = "PolicyAffected"
135+
136+
// PolicySourceInvalid is used with the "PolicySourceInvalid" condition when a
137+
// source of a WAFPolicy is invalid or incomplete.
138+
PolicySourceInvalid v1alpha2.PolicyConditionReason = "SourceInvalid"
139+
140+
// PolicyFetchError is used with the "PolicyFetchError" condition when a
141+
// WAFPolicy or LogProfileBundle cannot be fetched from the specified file location.
142+
PolicyFetchError v1alpha2.PolicyConditionReason = "FetchError"
143+
110144
// ClientSettingsPolicyAffected is used with the "PolicyAffected" condition when a
111145
// ClientSettingsPolicy is applied to a Gateway, HTTPRoute, or GRPCRoute.
112146
ClientSettingsPolicyAffected v1alpha2.PolicyConditionType = "ClientSettingsPolicyAffected"
@@ -115,24 +149,9 @@ const (
115149
// ObservabilityPolicy is applied to a HTTPRoute, or GRPCRoute.
116150
ObservabilityPolicyAffected v1alpha2.PolicyConditionType = "ObservabilityPolicyAffected"
117151

118-
// PolicyAffectedReason is used with the "PolicyAffected" condition when a
119-
// ObservabilityPolicy or ClientSettingsPolicy is applied to Gateways or Routes.
120-
PolicyAffectedReason v1alpha2.PolicyConditionReason = "PolicyAffected"
121-
122-
// GatewayResolvedRefs condition indicates whether the controller was able to resolve the
123-
// parametersRef on the Gateway.
124-
GatewayResolvedRefs v1.GatewayConditionType = "ResolvedRefs"
125-
126-
// GatewayReasonResolvedRefs is used with the "GatewayResolvedRefs" condition when the condition is true.
127-
GatewayReasonResolvedRefs v1.GatewayConditionReason = "ResolvedRefs"
128-
129-
// GatewayReasonParamsRefNotFound is used with the "GatewayResolvedRefs" condition when the
130-
// parametersRef resource does not exist.
131-
GatewayReasonParamsRefNotFound v1.GatewayConditionReason = "ParametersRefNotFound"
132-
133-
// GatewayReasonParamsRefInvalid is used with the "GatewayResolvedRefs" condition when the
134-
// parametersRef resource is invalid.
135-
GatewayReasonParamsRefInvalid v1.GatewayConditionReason = "ParametersRefInvalid"
152+
// WAFPolicyAffected is used with the "PolicyAffected" condition when an
153+
// WAFPolicyAffected is applied to a Gateway, HTTPRoute, or GRPCRoute.
154+
WAFPolicyAffected v1alpha2.PolicyConditionType = "WAFPolicyAffected"
136155
)
137156

138157
// Condition defines a condition to be reported in the status of resources.
@@ -998,3 +1017,34 @@ func NewClientSettingsPolicyAffected() Condition {
9981017
Message: "ClientSettingsPolicy is applied to the resource",
9991018
}
10001019
}
1020+
1021+
// NewWAFPolicyAffected returns a Condition that indicates that a WAFPolicy
1022+
// is applied to the resource.
1023+
func NewWAFPolicyAffected() Condition {
1024+
return Condition{
1025+
Type: string(WAFPolicyAffected),
1026+
Status: metav1.ConditionTrue,
1027+
Reason: string(PolicyAffectedReason),
1028+
Message: "WAFPolicy is applied to the resource",
1029+
}
1030+
}
1031+
1032+
// NewPolicySourceInvalid returns a Condition that indicates that the WAF policy source is invalid or incomplete.
1033+
func NewPolicySourceInvalid() Condition {
1034+
return Condition{
1035+
Type: string(PolicySourceInvalid),
1036+
Status: metav1.ConditionFalse,
1037+
Reason: string(PolicySourceInvalid),
1038+
Message: "The policy source is invalid or incomplete.",
1039+
}
1040+
}
1041+
1042+
// NewPolicyFetchError returns a Condition that indicates that there was an error fetching the WAF policy bundle.
1043+
func NewPolicyFetchError(msg string) Condition {
1044+
return Condition{
1045+
Type: string(PolicyFetchError),
1046+
Status: metav1.ConditionFalse,
1047+
Reason: string(PolicyFetchError),
1048+
Message: "Failed to fetch the policy bundle due to: " + msg,
1049+
}
1050+
}

internal/controller/state/graph/policies.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,11 @@ func addStatusToTargetRefs(policyKind string, conditionsList *[]conditions.Condi
532532
return
533533
}
534534
*conditionsList = append(*conditionsList, conditions.NewClientSettingsPolicyAffected())
535+
case kinds.WAFPolicy:
536+
if conditions.HasMatchingCondition(*conditionsList, conditions.NewWAFPolicyAffected()) {
537+
return
538+
}
539+
*conditionsList = append(*conditionsList, conditions.NewWAFPolicyAffected())
535540
}
536541
}
537542

@@ -562,7 +567,8 @@ func fetchWAFPolicyBundleData(
562567
if wafPolicy.Spec.PolicySource != nil && wafPolicy.Spec.PolicySource.FileLocation != "" {
563568
fetcher := createFetcher(buildFetchOptions(wafPolicy.Spec.PolicySource)...)
564569
if !fetchAndStoreBundle(wafPolicy.Spec.PolicySource.FileLocation, policy, refPolicyBundles, fetcher) {
565-
continue // Policy was marked invalid, skip security logs
570+
policy.Conditions = append(policy.Conditions, conditions.NewPolicySourceInvalid())
571+
continue
566572
}
567573
}
568574

@@ -573,7 +579,8 @@ func fetchWAFPolicyBundleData(
573579

574580
fetcher := createFetcher(buildFetchOptions(secLog.LogProfileBundle)...)
575581
if !fetchAndStoreBundle(secLog.LogProfileBundle.FileLocation, policy, refPolicyBundles, fetcher) {
576-
break // Policy was marked invalid, skip other security logs
582+
policy.Conditions = append(policy.Conditions, conditions.NewPolicySourceInvalid())
583+
break
577584
}
578585
}
579586
}
@@ -596,8 +603,7 @@ func fetchAndStoreBundle(
596603
data, err := fetcher.GetRemoteFile(fileLocation)
597604
if err != nil {
598605
policy.Valid = false
599-
// FIXME(ciarams87): Add appropriate condition when available.
600-
policy.Conditions = append(policy.Conditions, conditions.NewPolicyInvalid("Error fetching policy: "+err.Error()))
606+
policy.Conditions = append(policy.Conditions, conditions.NewPolicyFetchError(err.Error()))
601607
return false
602608
}
603609

internal/controller/state/graph/policies_test.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,6 +1720,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) {
17201720

17211721
cspGVK := schema.GroupVersionKind{Group: "Group", Version: "Version", Kind: "ClientSettingsPolicy"}
17221722
opGVK := schema.GroupVersionKind{Group: "Group", Version: "Version", Kind: "ObservabilityPolicy"}
1723+
wafPolicyGVK := schema.GroupVersionKind{Group: "Group", Version: "Version", Kind: "WAFPolicy"}
17231724

17241725
gw1Ref := createTestRef(kinds.Gateway, v1.GroupName, "gw1")
17251726
gw1TargetRef := createTestPolicyTargetRef(
@@ -1801,7 +1802,7 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) {
18011802
},
18021803
},
18031804
{
1804-
name: "gateway attached to csp and op policy",
1805+
name: "gateway attached to csp, op and waf policy",
18051806
policies: map[PolicyKey]*Policy{
18061807
createTestPolicyKey(cspGVK, "csp1"): {
18071808
Source: createTestPolicy(cspGVK, "csp1", gw2Ref),
@@ -1811,13 +1812,18 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) {
18111812
Source: createTestPolicy(opGVK, "observabilityPolicy1", gw2Ref),
18121813
TargetRefs: []PolicyTargetRef{gw2TargetRef},
18131814
},
1815+
createTestPolicyKey(wafPolicyGVK, "wafPolicy1"): {
1816+
Source: createTestPolicy(wafPolicyGVK, "wafPolicy1", gw2Ref),
1817+
TargetRefs: []PolicyTargetRef{gw2TargetRef},
1818+
},
18141819
},
18151820
gws: createGatewayMap(types.NamespacedName{Namespace: testNs, Name: "gw2"}),
18161821
routes: nil,
18171822
expectedConditions: map[types.NamespacedName][]conditions.Condition{
18181823
{Namespace: testNs, Name: "gw2"}: {
18191824
conditions.NewClientSettingsPolicyAffected(),
18201825
conditions.NewObservabilityPolicyAffected(),
1826+
conditions.NewWAFPolicyAffected(),
18211827
},
18221828
},
18231829
},
@@ -1875,6 +1881,10 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) {
18751881
Source: createTestPolicy(opGVK, "observabilityPolicy2", gw3Ref, gr2Ref),
18761882
TargetRefs: []PolicyTargetRef{gw3TargetRef, gr2TargetRef},
18771883
},
1884+
createTestPolicyKey(wafPolicyGVK, "wafPolicy1"): {
1885+
Source: createTestPolicy(wafPolicyGVK, "wafPolicy1", gw3Ref, hr2Ref),
1886+
TargetRefs: []PolicyTargetRef{gw3TargetRef, hr2TargetRef},
1887+
},
18781888
},
18791889
gws: createGatewayMap(
18801890
types.NamespacedName{Namespace: testNs, Name: "gw3"},
@@ -1901,10 +1911,12 @@ func TestAddPolicyAffectedStatusOnTargetRefs(t *testing.T) {
19011911
{Namespace: testNs, Name: "gw3"}: {
19021912
conditions.NewClientSettingsPolicyAffected(),
19031913
conditions.NewObservabilityPolicyAffected(),
1914+
conditions.NewWAFPolicyAffected(),
19041915
},
19051916
{Namespace: testNs, Name: "hr2"}: {
19061917
conditions.NewObservabilityPolicyAffected(),
19071918
conditions.NewClientSettingsPolicyAffected(),
1919+
conditions.NewWAFPolicyAffected(),
19081920
},
19091921
{Namespace: testNs, Name: "gr2"}: {
19101922
conditions.NewObservabilityPolicyAffected(),
@@ -2103,6 +2115,7 @@ func TestFetchPolicyBundleData(t *testing.T) {
21032115
expectedPolicyState map[string]bool
21042116
expectFetchConditions map[string]bool
21052117
name string
2118+
expectedConds []conditions.Condition
21062119
expectedBundleCount int
21072120
}{
21082121
{
@@ -2150,6 +2163,9 @@ func TestFetchPolicyBundleData(t *testing.T) {
21502163
expectFetchConditions: map[string]bool{
21512164
"invalid-waf": false,
21522165
},
2166+
expectedConds: []conditions.Condition{
2167+
conditions.NewPolicySourceInvalid(),
2168+
},
21532169
},
21542170
{
21552171
name: "WAF policy with empty FileLocation",
@@ -2304,6 +2320,9 @@ func TestFetchPolicyBundleData(t *testing.T) {
23042320
expectFetchConditions: map[string]bool{
23052321
"waf-fail": true,
23062322
},
2323+
expectedConds: []conditions.Condition{
2324+
conditions.NewPolicySourceInvalid(),
2325+
},
23072326
},
23082327
{
23092328
name: "WAF policy with PolicySource success but SecurityLog failure",
@@ -2338,6 +2357,10 @@ func TestFetchPolicyBundleData(t *testing.T) {
23382357
expectFetchConditions: map[string]bool{
23392358
"waf-mixed": true,
23402359
},
2360+
expectedConds: []conditions.Condition{
2361+
conditions.NewPolicySourceInvalid(),
2362+
conditions.NewPolicyFetchError("network error"),
2363+
},
23412364
},
23422365
{
23432366
name: "WAF policy with multiple SecurityLog bundles - partial failure",
@@ -2378,6 +2401,10 @@ func TestFetchPolicyBundleData(t *testing.T) {
23782401
expectFetchConditions: map[string]bool{
23792402
"waf-multi": true,
23802403
},
2404+
expectedConds: []conditions.Condition{
2405+
conditions.NewPolicySourceInvalid(),
2406+
conditions.NewPolicyFetchError("network error"),
2407+
},
23812408
},
23822409
}
23832410

@@ -2429,8 +2456,16 @@ func TestFetchPolicyBundleData(t *testing.T) {
24292456
if expectFetchConditions, exists := test.expectFetchConditions[policyName]; exists && expectFetchConditions {
24302457
g.Expect(policy.Conditions).ToNot(BeEmpty(),
24312458
fmt.Sprintf("Policy %s should have fetch error conditions", policyName))
2432-
g.Expect(policy.Conditions[0].Reason).To(Equal("Invalid"))
2433-
g.Expect(policy.Conditions[0].Message).To(ContainSubstring("Error fetching policy:"))
2459+
2460+
if len(policy.Conditions) > 1 {
2461+
g.Expect(policy.Conditions[0].Reason).To(Equal("FetchError"))
2462+
g.Expect(policy.Conditions[0].Message).To(ContainSubstring("Failed to fetch the policy bundle due to:"))
2463+
g.Expect(policy.Conditions[1].Reason).To(Equal("SourceInvalid"))
2464+
g.Expect(policy.Conditions[1].Message).To(ContainSubstring("policy source is invalid or incomplete."))
2465+
} else {
2466+
g.Expect(policy.Conditions[0].Reason).To(Equal("SourceInvalid"))
2467+
g.Expect(policy.Conditions[0].Message).To(ContainSubstring("policy source is invalid or incomplete."))
2468+
}
24342469
}
24352470
break
24362471
}

0 commit comments

Comments
 (0)