Skip to content
Merged
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
27 changes: 27 additions & 0 deletions api/v1/calico_webhooks_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ type CalicoWebhooksDeploymentContainer struct {
// If omitted, the calico-webhooks Deployment will use its default value for this container's resources.
// +optional
Resources *v1.ResourceRequirements `json:"resources,omitempty"`

// Ports allows customization of the calico-webhooks container's ports.
// If specified, this overrides the default container port configuration.
// If omitted, the calico-webhooks Deployment will use its default port (6443).
// +optional
Ports []CalicoWebhooksDeploymentContainerPort `json:"ports,omitempty"`
}

// CalicoWebhooksDeploymentContainerPort defines a port override for a calico-webhooks container.
type CalicoWebhooksDeploymentContainerPort struct {
// Name is an enum which identifies the calico-webhooks Deployment container port by name.
// Supported values are: calico-webhooks
// +kubebuilder:validation:Enum=calico-webhooks
Name string `json:"name"`

// Number of port to expose on the pod's IP address.
// This must be a valid port number, 0 < x < 65536.
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
ContainerPort int32 `json:"containerPort"`
}

// CalicoWebhooksDeploymentPodSpec is the calico-webhooks Deployment's PodSpec.
Expand Down Expand Up @@ -65,6 +85,13 @@ type CalicoWebhooksDeploymentPodSpec struct {
// WARNING: Please note that this field will override the default calico-webhooks Deployment tolerations.
// +optional
Tolerations []v1.Toleration `json:"tolerations"`

// HostNetwork forces the webhook pod to use the host's network namespace.
// When true, the webhook pod will run with hostNetwork=true and DNSPolicy=ClusterFirstWithHostNet.
// When nil or omitted, the operator auto-detects whether host networking is required
// (e.g., for EKS/TKG with Calico CNI).
// +optional
HostNetwork *bool `json:"hostNetwork,omitempty"`
}

// CalicoWebhooksDeploymentPodTemplateSpec is the calico-webhooks Deployment's PodTemplateSpec
Expand Down
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.

38 changes: 38 additions & 0 deletions pkg/imports/crds/operator/operator.tigera.io_apiservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2592,6 +2592,37 @@ spec:
enum:
- calico-webhooks
type: string
ports:
description: |-
Ports allows customization of the calico-webhooks container's ports.
If specified, this overrides the default container port configuration.
If omitted, the calico-webhooks Deployment will use its default port (6443).
items:
description:
CalicoWebhooksDeploymentContainerPort
defines a port override for a calico-webhooks
container.
properties:
containerPort:
description: |-
Number of port to expose on the pod's IP address.
This must be a valid port number, 0 < x < 65536.
format: int32
maximum: 65535
minimum: 1
type: integer
name:
description: |-
Name is an enum which identifies the calico-webhooks Deployment container port by name.
Supported values are: calico-webhooks
enum:
- calico-webhooks
type: string
required:
- containerPort
- name
type: object
type: array
resources:
description: |-
Resources allows customization of limits and requests for compute resources such as cpu and memory.
Expand Down Expand Up @@ -2658,6 +2689,13 @@ spec:
- name
type: object
type: array
hostNetwork:
description: |-
HostNetwork forces the webhook pod to use the host's network namespace.
When true, the webhook pod will run with hostNetwork=true and DNSPolicy=ClusterFirstWithHostNet.
When nil or omitted, the operator auto-detects whether host networking is required
(e.g., for EKS/TKG with Calico CNI).
type: boolean
nodeSelector:
additionalProperties:
type: string
Expand Down
31 changes: 24 additions & 7 deletions pkg/render/common/components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ func GetContainers(overrides any) []corev1.Container {
return valueToContainers(value)
}

func GetHostNetwork(overrides any) *bool {
value := getField(overrides, "Spec", "Template", "Spec", "HostNetwork")
if !value.IsValid() || value.IsNil() {
return nil
}
return value.Interface().(*bool)
}

func GetDNSPolicy(overrides any) (corev1.DNSPolicy, bool) {
value := getField(overrides, "Spec", "Template", "Spec", "DNSPolicy")

Expand Down Expand Up @@ -162,13 +170,16 @@ func valueToContainerPorts(v reflect.Value) []corev1.ContainerPort {
if !portOverrides.IsValid() || portOverrides.IsNil() {
return nil
}
customPorts := portOverrides.Interface().([]operator.APIServerDeploymentContainerPort)
ports := make([]corev1.ContainerPort, 0, len(customPorts))
for _, p := range customPorts {
ports = append(ports, corev1.ContainerPort{
Name: p.Name,
ContainerPort: p.ContainerPort,
})
ports := make([]corev1.ContainerPort, 0, portOverrides.Len())
for i := 0; i < portOverrides.Len(); i++ {
p := portOverrides.Index(i)
port := corev1.ContainerPort{
ContainerPort: int32(p.FieldByName("ContainerPort").Int()),
}
if name := p.FieldByName("Name"); name.IsValid() && name.String() != "" {
port.Name = name.String()
}
ports = append(ports, port)
}
return ports
}
Expand Down Expand Up @@ -355,6 +366,12 @@ func applyReplicatedPodResourceOverrides(r *replicatedPodResource, overrides any
r.podTemplateSpec.Spec.PriorityClassName = priorityClassName
}

// If `overrides` has a Spec.Template.Spec.HostNetwork field, and it's non-nil, it sets
// `r.podTemplateSpec.Spec.HostNetwork`.
if hostNetwork := GetHostNetwork(overrides); hostNetwork != nil {
r.podTemplateSpec.Spec.HostNetwork = *hostNetwork
}

// If `overrides` has a Spec.Template.Spec.DNSPolicy field, and it's non-empty, it sets
// `r.podTemplateSpec.Spec.DNSPolicy`.
if dnsPolicy, ok := GetDNSPolicy(overrides); ok {
Expand Down
95 changes: 56 additions & 39 deletions pkg/render/webhooks/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,50 +97,14 @@ func (c *component) Objects() ([]client.Object, []client.Object) {
},
}

// Network policy to allow traffic to/from the webhook pod.
egressRules := networkpolicy.AppendDNSEgressRules(nil, c.cfg.OpenShift)
egressRules = append(egressRules,
v3.Rule{
Action: v3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: networkpolicy.KubeAPIServerEntityRule,
},
v3.Rule{
Action: v3.Pass,
},
)
np := &v3.NetworkPolicy{
TypeMeta: metav1.TypeMeta{Kind: "NetworkPolicy", APIVersion: "projectcalico.org/v3"},
ObjectMeta: metav1.ObjectMeta{
Name: WebhooksPolicyName,
Namespace: common.CalicoNamespace,
},
Spec: v3.NetworkPolicySpec{
Order: &networkpolicy.HighPrecedenceOrder,
Tier: networkpolicy.TigeraComponentTierName,
Selector: networkpolicy.KubernetesAppSelector(WebhooksName),
Types: []v3.PolicyType{v3.PolicyTypeIngress, v3.PolicyTypeEgress},
Ingress: []v3.Rule{
{
Action: v3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: v3.EntityRule{
Ports: networkpolicy.Ports(6443),
},
},
},
Egress: egressRules,
},
}

// Create the correct security context for the webhook container. By default, it should run as non-root, but in Enterprise
// we need to run as root to be able to write audit logs to the host filesystem.
securtyContext := securitycontext.NewNonRootContext()
if c.cfg.Installation.Variant == operatorv1.TigeraSecureEnterprise {
securtyContext = securitycontext.NewRootContext(c.cfg.Installation.KubernetesProvider.IsOpenShift())
}

// Create the Deployment for the webhook.
// Create the Deployment for the webhook with defaults, then apply overrides.
dep := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -213,6 +177,54 @@ func (c *component) Objects() ([]client.Object, []client.Object) {
rcomp.ApplyDeploymentOverrides(dep, overrides)
}

// Set DNSPolicy based on the final HostNetwork value (after overrides).
if dep.Spec.Template.Spec.HostNetwork {
dep.Spec.Template.Spec.DNSPolicy = corev1.DNSClusterFirstWithHostNet
}

// Read the final container port from the deployment (after overrides) for use in the Service.
containerPort := dep.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort

// Network policy to allow traffic to/from the webhook pod. Skip if host networking is
// enabled, since network policy is ineffective for host-networked pods.
var np *v3.NetworkPolicy
if !dep.Spec.Template.Spec.HostNetwork {
egressRules := networkpolicy.AppendDNSEgressRules(nil, c.cfg.OpenShift)
egressRules = append(egressRules,
v3.Rule{
Action: v3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: networkpolicy.KubeAPIServerEntityRule,
},
v3.Rule{
Action: v3.Pass,
},
)
np = &v3.NetworkPolicy{
TypeMeta: metav1.TypeMeta{Kind: "NetworkPolicy", APIVersion: "projectcalico.org/v3"},
ObjectMeta: metav1.ObjectMeta{
Name: WebhooksPolicyName,
Namespace: common.CalicoNamespace,
},
Spec: v3.NetworkPolicySpec{
Order: &networkpolicy.HighPrecedenceOrder,
Tier: networkpolicy.TigeraComponentTierName,
Selector: networkpolicy.KubernetesAppSelector(WebhooksName),
Types: []v3.PolicyType{v3.PolicyTypeIngress, v3.PolicyTypeEgress},
Ingress: []v3.Rule{
{
Action: v3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: v3.EntityRule{
Ports: networkpolicy.Ports(uint16(containerPort)),
},
},
},
Egress: egressRules,
},
}
}

// Create the Service for the webhook.
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -224,7 +236,7 @@ func (c *component) Objects() ([]client.Object, []client.Object) {
{
Port: 443,
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromInt(6443),
TargetPort: intstr.FromInt32(containerPort),
},
},
Type: corev1.ServiceTypeClusterIP,
Expand Down Expand Up @@ -361,7 +373,12 @@ func (c *component) Objects() ([]client.Object, []client.Object) {
},
}

return []client.Object{sa, np, dep, svc, vwc, cr, crb}, nil
objs := []client.Object{sa}
if np != nil {
objs = append(objs, np)
}
objs = append(objs, dep, svc, vwc, cr, crb)
return objs, nil
}

func (c *component) Ready() bool {
Expand Down
Loading
Loading