3.2 Configure Firewall Rules¶
Firewall rules are implemented as AWS Security Groups, defined in modules/security_groups.pkl. This module creates two security groups — one for control plane nodes and one for workers — with all the ingress and egress rules required for Talos, Kubernetes, and Cilium to communicate.
You do not run this module independently — it is composed into main.pkl and deployed as part of formae apply.
Step 1: Configure Security Variables¶
Open vars.pkl (or your environment override) and review the security-related variables:
/// CIDR range allowed to reach the K8s API (6443) and Talos API (50000)
/// from outside the VPC. Set to a specific IP (e.g. "41.x.x.x/32") to
/// restrict access, or null to allow only VPC-internal access.
allowedAdminCidr: String? = null
To allow external access (e.g. from your workstation):
// In envs/demo.pkl — restrict to your public IP
// Find your IP: curl -s ifconfig.me
allowedAdminCidr = "196.45.28.20/32"
Other security variables:
/// Container Network Interface plugin (cilium or calico).
cniType: String = "cilium"
/// Open NodePort range (30000-32767) in worker security groups.
enableNodePort: Boolean = false
Step 2: Understand the Module¶
The module is at modules/security_groups.pkl. It imports the network module to reference the VPC ID and CIDR:
Security Group Definitions¶
Two security groups are created, both attached to the VPC:
local controlPlaneSg = new sg.SecurityGroup {
label = "\(vars.environment)-talos-control-plane-sg"
groupName = "\(vars.environment)-talos-control-plane-sg"
groupDescription = "Security group for Talos control plane nodes"
vpcId = network.vpcId
tags = makeTags(new Mapping {
["Name"] = "\(vars.environment)-talos-control-plane-sg"
["NodeType"] = "control-plane"
})
}
local workerSg = new sg.SecurityGroup {
label = "\(vars.environment)-talos-worker-sg"
groupName = "\(vars.environment)-talos-worker-sg"
groupDescription = "Security group for Talos worker nodes"
vpcId = network.vpcId
tags = makeTags(new Mapping {
["Name"] = "\(vars.environment)-talos-worker-sg"
["NodeType"] = "worker"
})
}
Ingress Rule Pattern¶
Each rule is a separate SecurityGroupIngress resource that references its parent security group via groupId. The source can be a CIDR block or another security group:
CIDR-based rule (allows traffic from anywhere in the VPC):
local cpK8sApi = new sgIngress.SecurityGroupIngress {
label = "cp-k8s-api"
groupId = controlPlaneSg.res.groupId
description = "Kubernetes API server"
fromPort = 6443
toPort = 6443
ipProtocol = "tcp"
cidrIp = network.vpcCidr // e.g. "10.0.0.0/16"
}
Security group-based rule (allows traffic from a specific node type):
local cpEtcd = new sgIngress.SecurityGroupIngress {
label = "cp-etcd"
groupId = controlPlaneSg.res.groupId
description = "etcd peer communication"
fromPort = 2379
toPort = 2380
ipProtocol = "tcp"
sourceSecurityGroupId = controlPlaneSg.res.groupId // CP-to-CP only
}
Conditional External Access Rules¶
When allowedAdminCidr is set, two additional rules are created for external access to the control plane:
local cpK8sApiExternal = if (vars.allowedAdminCidr != null)
new sgIngress.SecurityGroupIngress {
label = "cp-k8s-api-external"
groupId = controlPlaneSg.res.groupId
description = "Kubernetes API (external admin)"
fromPort = 6443
toPort = 6443
ipProtocol = "tcp"
cidrIp = vars.allowedAdminCidr // e.g. "196.45.28.20/32"
}
else null
When allowedAdminCidr = null, these rules are not created and the control plane is only accessible from within the VPC.
Control Plane Ingress Rules¶
| Rule | Port(s) | Protocol | Source | Purpose |
|---|---|---|---|---|
cp-k8s-api |
6443 | TCP | VPC CIDR | Kubernetes API server |
cp-talos-api |
50000 | TCP | VPC CIDR | Talos API (apid) |
cp-talos-trustd |
50001 | TCP | Worker SG | Talos trustd service |
cp-etcd |
2379-2380 | TCP | CP SG (self) | etcd peer communication |
cp-kubelet-self |
10250 | TCP | CP SG (self) | Kubelet API (CP-to-CP) |
cp-kubelet-vpc |
10250 | TCP | VPC CIDR | Kubelet API (admin access) |
cp-controller-manager |
10257 | TCP | CP SG (self) | kube-controller-manager |
cp-scheduler |
10259 | TCP | CP SG (self) | kube-scheduler |
cp-cilium-geneve-cp |
6081 | UDP | CP SG (self) | Cilium GENEVE overlay (CP-to-CP) |
cp-cilium-geneve-worker |
6081 | UDP | Worker SG | Cilium GENEVE overlay (workers-to-CP) |
cp-cilium-health-cp |
4240 | TCP | CP SG (self) | Cilium health checks (CP-to-CP) |
cp-cilium-health-worker |
4240 | TCP | Worker SG | Cilium health checks (workers-to-CP) |
cp-icmp |
ICMP | ICMP | VPC CIDR | Ping for connectivity validation |
cp-k8s-api-external |
6443 | TCP | allowedAdminCidr |
Kubernetes API (external) — conditional |
cp-talos-api-external |
50000 | TCP | allowedAdminCidr |
Talos API (external) — conditional |
Worker Ingress Rules¶
| Rule | Port(s) | Protocol | Source | Purpose |
|---|---|---|---|---|
wk-talos-api-cp |
50000 | TCP | CP SG | Talos API from control plane |
wk-talos-api-vpc |
50000 | TCP | VPC CIDR | Talos API (admin access) |
wk-kubelet-cp |
10250 | TCP | CP SG | Kubelet API from control plane |
wk-kubelet-self |
10250 | TCP | Worker SG (self) | Kubelet API (worker-to-worker) |
wk-kubelet-vpc |
10250 | TCP | VPC CIDR | Kubelet API (admin access) |
wk-nodeport-self |
30000-32767 | TCP | Worker SG (self) | NodePort services (worker-to-worker) |
wk-nodeport-vpc |
30000-32767 | TCP | VPC CIDR | NodePort services (admin access) |
wk-cilium-geneve-cp |
6081 | UDP | CP SG | Cilium GENEVE overlay (CP-to-workers) |
wk-cilium-geneve-worker |
6081 | UDP | Worker SG (self) | Cilium GENEVE overlay (worker-to-worker) |
wk-cilium-health-cp |
4240 | TCP | CP SG | Cilium health checks (CP-to-workers) |
wk-cilium-health-worker |
4240 | TCP | Worker SG (self) | Cilium health checks (worker-to-worker) |
wk-icmp |
ICMP | ICMP | VPC CIDR | Ping for connectivity validation |
Egress Rules¶
Both security groups allow all outbound traffic:
local cpEgressAll = new sgEgress.SecurityGroupEgress {
label = "cp-egress-all"
groupId = controlPlaneSg.res.groupId
description = "Allow all outbound traffic"
ipProtocol = "-1"
cidrIp = "0.0.0.0/0"
}
Step 3: Module Exports¶
/// Control plane security group ID for use by compute module.
controlPlaneSecurityGroupId = controlPlaneSg.res.groupId
/// Worker security group ID for use by compute module.
workerSecurityGroupId = workerSg.res.groupId
| Export | Consumed By |
|---|---|
controlPlaneSecurityGroupId |
compute.pkl (CP instances) |
workerSecurityGroupId |
compute.pkl (worker instances) |
Customisation Summary¶
| What to Change | Where | Variable |
|---|---|---|
| Allow external admin access | vars.pkl |
allowedAdminCidr = "x.x.x.x/32" |
| Restrict to VPC only | vars.pkl |
allowedAdminCidr = null |
| Enable NodePort range on workers | vars.pkl |
enableNodePort = true |
| Change CNI type | vars.pkl |
cniType = "calico" (Cilium rules would need updating) |