Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions api/v1/manager_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type ManagerSpec struct {
// RBACUI configures the RBAC management UI feature.
// +optional
RBACUI *RBACUI `json:"rbacUI,omitempty"`

// WAFUI configures the WAF management UI feature. This controls visibility of
// the WAF management UI only; it does not enable WAF enforcement on traffic
// (that is configured separately via GatewayAPI.spec.extensions.waf).
// +optional
WAFUI *WAFUI `json:"wafUI,omitempty"`
}

// +kubebuilder:validation:Enum=Enabled;Disabled
Expand Down Expand Up @@ -59,6 +65,34 @@ func (m *Manager) RBACManagementEnabled() bool {
return *m.Spec.RBACUI.State == RBACUIEnabled
}

// +kubebuilder:validation:Enum=Enabled;Disabled
type WAFUIStatusType string

const (
WAFUIDisabled WAFUIStatusType = "Disabled"
WAFUIEnabled WAFUIStatusType = "Enabled"
)

// WAFUI configures the WAF management UI. This turns the WAF management surface
// in the Calico Enterprise manager on or off. It controls UI visibility only,
// and does not enable WAF enforcement on traffic, which is configured
// separately via GatewayAPI.spec.extensions.waf.
type WAFUI struct {
// State turns the WAF management UI on or off. Defaults to Disabled.
// +optional
State *WAFUIStatusType `json:"state,omitempty"`
}

// WAFManagementEnabled returns true when the Manager CR enables the WAF
// management UI. Safe to call on a nil receiver; returns false for a nil
// Manager, an unset WAFUI, or any state other than Enabled.
func (m *Manager) WAFManagementEnabled() bool {
if m == nil || m.Spec.WAFUI == nil || m.Spec.WAFUI.State == nil {
return false
}
return *m.Spec.WAFUI.State == WAFUIEnabled
}

// ManagerDeployment is the configuration for the Manager Deployment.
type ManagerDeployment struct {
// Spec is the specification of the Manager Deployment.
Expand Down
34 changes: 34 additions & 0 deletions api/v1/manager_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,37 @@ func TestRBACManagementEnabled(t *testing.T) {
})
}
}

func TestWAFManagementEnabled(t *testing.T) {
state := func(s WAFUIStatusType) *WAFUIStatusType { return &s }

for _, tc := range []struct {
name string
m *Manager
want bool
}{
// Nil paths: WAFManagementEnabled is called as managerCR.WAFManagementEnabled()
// where managerCR is nil when no Manager CR exists, so a nil receiver (and each
// nil field below) must return false rather than panic.
{name: "nil Manager", m: nil, want: false},
{name: "nil WAFUI", m: &Manager{}, want: false},
{name: "nil State", m: &Manager{Spec: ManagerSpec{WAFUI: &WAFUI{}}}, want: false},

{name: "State Enabled", m: &Manager{Spec: ManagerSpec{WAFUI: &WAFUI{State: state(WAFUIEnabled)}}}, want: true},
{name: "State Disabled", m: &Manager{Spec: ManagerSpec{WAFUI: &WAFUI{State: state(WAFUIDisabled)}}}, want: false},
// Any value other than Enabled is off (the Enum marker rejects this at the
// apiserver, but the helper must not treat a non-empty value as enabled).
{name: "State unrecognized value", m: &Manager{Spec: ManagerSpec{WAFUI: &WAFUI{State: state("SomethingElse")}}}, want: false},
} {
t.Run(tc.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Fatalf("WAFManagementEnabled panicked on %s: %v", tc.name, r)
}
}()
if got := tc.m.WAFManagementEnabled(); got != tc.want {
t.Errorf("WAFManagementEnabled() = %v, want %v", got, tc.want)
}
})
}
}
25 changes: 25 additions & 0 deletions api/v1/zz_generated.deepcopy.go

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

15 changes: 15 additions & 0 deletions pkg/imports/crds/operator/operator.tigera.io_managers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,21 @@ spec:
- Disabled
type: string
type: object
wafUI:
description: |-
WAFUI configures the WAF management UI feature. This controls visibility of
the WAF management UI only; it does not enable WAF enforcement on traffic
(that is configured separately via GatewayAPI.spec.extensions.waf).
properties:
state:
description:
State turns the WAF management UI on or off. Defaults
to Disabled.
enum:
- Enabled
- Disabled
type: string
type: object
type: object
status:
description: Most recently observed state for the Calico Enterprise manager.
Expand Down
1 change: 1 addition & 0 deletions pkg/render/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ func (c *managerComponent) managerUIAPIsContainer() corev1.Container {
{Name: "ELASTIC_KIBANA_DISABLED", Value: strconv.FormatBool(c.cfg.Tenant.MultiTenant())},
{Name: "VOLTRON_URL", Value: ManagerService(c.cfg.Tenant)},
{Name: "RBAC_UI_ENABLED", Value: strconv.FormatBool(c.cfg.Manager.RBACManagementEnabled() && !c.cfg.Tenant.MultiTenant())},
{Name: "WAF_UI_ENABLED", Value: strconv.FormatBool(c.cfg.Manager.WAFManagementEnabled() && !c.cfg.Tenant.MultiTenant())},
}

// Determine the Linseed location. Use code default unless in multi-tenant mode,
Expand Down
95 changes: 95 additions & 0 deletions pkg/render/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() {
{Name: "ELASTIC_KIBANA_DISABLED", Value: "false"},
{Name: "VOLTRON_URL", Value: render.ManagerService(nil)},
{Name: "RBAC_UI_ENABLED", Value: "false"},
{Name: "WAF_UI_ENABLED", Value: "false"},
}
Expect(uiAPIs.Env).To(Equal(uiAPIsExpectedEnvVars))

Expand Down Expand Up @@ -1962,6 +1963,100 @@ var _ = Describe("Tigera Secure Manager rendering tests", func() {
})
})
})

Context("WAF management UI", func() {
var installation *operatorv1.InstallationSpec
BeforeEach(func() {
replicas := int32(1)
installation = &operatorv1.InstallationSpec{
ControlPlaneReplicas: &replicas,
Variant: operatorv1.CalicoEnterprise,
Registry: "testregistry.com/",
}
})

wafUIEnabledEnv := func(d *appsv1.Deployment) corev1.EnvVar {
for _, c := range d.Spec.Template.Spec.Containers {
if c.Name == render.UIAPIsName {
for _, e := range c.Env {
if e.Name == "WAF_UI_ENABLED" {
return e
}
}
}
}
return corev1.EnvVar{}
}

It("renders WAF_UI_ENABLED=false when the Manager is unset", func() {
resources, _ := renderObjects(renderConfig{
installation: installation,
ns: render.ManagerNamespace,
})
d := rtest.GetResource(resources, render.ManagerDeploymentName, render.ManagerNamespace, appsv1.GroupName, "v1", "Deployment").(*appsv1.Deployment)
Expect(wafUIEnabledEnv(d)).To(Equal(corev1.EnvVar{Name: "WAF_UI_ENABLED", Value: "false"}))
})

It("renders WAF_UI_ENABLED=false when the Manager exists but wafUI is unset", func() {
resources, _ := renderObjects(renderConfig{
installation: installation,
ns: render.ManagerNamespace,
manager: &operatorv1.Manager{Spec: operatorv1.ManagerSpec{}},
})
d := rtest.GetResource(resources, render.ManagerDeploymentName, render.ManagerNamespace, appsv1.GroupName, "v1", "Deployment").(*appsv1.Deployment)
Expect(wafUIEnabledEnv(d)).To(Equal(corev1.EnvVar{Name: "WAF_UI_ENABLED", Value: "false"}))
})

It("renders WAF_UI_ENABLED=true when wafUI.state is Enabled", func() {
resources, _ := renderObjects(renderConfig{
installation: installation,
ns: render.ManagerNamespace,
manager: &operatorv1.Manager{
Spec: operatorv1.ManagerSpec{
WAFUI: &operatorv1.WAFUI{State: ptr.To(operatorv1.WAFUIEnabled)},
},
},
})
d := rtest.GetResource(resources, render.ManagerDeploymentName, render.ManagerNamespace, appsv1.GroupName, "v1", "Deployment").(*appsv1.Deployment)
Expect(wafUIEnabledEnv(d)).To(Equal(corev1.EnvVar{Name: "WAF_UI_ENABLED", Value: "true"}))
})

It("renders WAF_UI_ENABLED=false when wafUI.state is Disabled", func() {
resources, _ := renderObjects(renderConfig{
installation: installation,
ns: render.ManagerNamespace,
manager: &operatorv1.Manager{
Spec: operatorv1.ManagerSpec{
WAFUI: &operatorv1.WAFUI{State: ptr.To(operatorv1.WAFUIDisabled)},
},
},
})
d := rtest.GetResource(resources, render.ManagerDeploymentName, render.ManagerNamespace, appsv1.GroupName, "v1", "Deployment").(*appsv1.Deployment)
Expect(wafUIEnabledEnv(d)).To(Equal(corev1.EnvVar{Name: "WAF_UI_ENABLED", Value: "false"}))
})

It("renders WAF_UI_ENABLED=false in multi-tenant mode even when wafUI.state is Enabled", func() {
resources, _ := renderObjects(renderConfig{
installation: installation,
ns: "tenant-a",
bindingNamespaces: []string{"tenant-a"},
tenant: &operatorv1.Tenant{
ObjectMeta: metav1.ObjectMeta{Name: "tenantA", Namespace: "tenant-a"},
Spec: operatorv1.TenantSpec{
ID: "tenant-a",
ManagedClusterVariant: &operatorv1.Calico,
},
},
manager: &operatorv1.Manager{
Spec: operatorv1.ManagerSpec{
WAFUI: &operatorv1.WAFUI{State: ptr.To(operatorv1.WAFUIEnabled)},
},
},
})
d := rtest.GetResource(resources, render.ManagerDeploymentName, "tenant-a", appsv1.GroupName, "v1", "Deployment").(*appsv1.Deployment)
Expect(wafUIEnabledEnv(d)).To(Equal(corev1.EnvVar{Name: "WAF_UI_ENABLED", Value: "false"}))
})
})
})

type renderConfig struct {
Expand Down
Loading