Network Security Groups Overview
Implement network security groups and firewall rules to enforce least privilege access, segment networks, and protect infrastructure from unauthorized access.
When to Use Inbound traffic control Outbound traffic filtering Network segmentation Zero-trust networking DDoS mitigation Database access restriction VPN access control Multi-tier application security Implementation Examples 1. AWS Security Groups
aws-security-groups.yaml
Resources: # VPC Security Group VPCSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: VPC security group VpcId: vpc-12345678 SecurityGroupIngress: # Allow HTTP from anywhere - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Description: "HTTP from anywhere"
# Allow HTTPS from anywhere
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: "HTTPS from anywhere"
# Allow SSH from admin network only
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.0.0/8
Description: "SSH from admin network"
SecurityGroupEgress:
# Allow all outbound
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Description: "All outbound traffic"
Tags:
- Key: Name
Value: vpc-security-group
# Database Security Group DatabaseSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Database security group VpcId: vpc-12345678 SecurityGroupIngress: # Allow PostgreSQL from app tier only - IpProtocol: tcp FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !Ref AppSecurityGroup Description: "PostgreSQL from app tier"
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: database-security-group
# Application Tier Security Group AppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Application tier security group VpcId: vpc-12345678 SecurityGroupIngress: # Allow traffic from load balancer - IpProtocol: tcp FromPort: 8080 ToPort: 8080 SourceSecurityGroupId: !Ref LBSecurityGroup Description: "App traffic from LB"
SecurityGroupEgress:
# Allow to databases
- IpProtocol: tcp
FromPort: 5432
ToPort: 5432
DestinationSecurityGroupId: !Ref DatabaseSecurityGroup
Description: "Database access"
# Allow to external APIs
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: "HTTPS external APIs"
Tags:
- Key: Name
Value: app-security-group
# Load Balancer Security Group LBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Load balancer security group VpcId: vpc-12345678 SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
DestinationSecurityGroupId: !Ref AppSecurityGroup
Tags:
- Key: Name
Value: lb-security-group
- Kubernetes Network Policies
kubernetes-network-policies.yaml
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: deny-all-ingress namespace: production spec: podSelector: {} policyTypes: - Ingress
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend namespace: production spec: podSelector: matchLabels: app: frontend policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: ingress-nginx ports: - protocol: TCP port: 8080
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-backend-to-database namespace: production spec: podSelector: matchLabels: tier: database policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: tier: backend ports: - protocol: TCP port: 5432
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-backend-to-cache namespace: production spec: podSelector: matchLabels: tier: cache policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: tier: backend ports: - protocol: TCP port: 6379
Egress policy
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: backend-egress namespace: production spec: podSelector: matchLabels: tier: backend policyTypes: - Egress egress: # Allow to database - to: - podSelector: matchLabels: tier: database ports: - protocol: TCP port: 5432
# Allow to cache
- to:
- podSelector:
matchLabels:
tier: cache
ports:
- protocol: TCP
port: 6379
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow external APIs
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
- GCP Firewall Rules
gcp-firewall-rules.yaml
apiVersion: compute.cnrm.cloud.google.com/v1beta1 kind: ComputeFirewall metadata: name: allow-http-https spec: network: name: default direction: INGRESS priority: 1000 sourceRanges: - 0.0.0.0/0 allowed: - IPProtocol: tcp ports: - "80" - "443" targetTags: - http-server - https-server
apiVersion: compute.cnrm.cloud.google.com/v1beta1 kind: ComputeFirewall metadata: name: allow-ssh-internal spec: network: name: default direction: INGRESS priority: 1000 sourceRanges: - 10.0.0.0/8 allowed: - IPProtocol: tcp ports: - "22" targetTags: - allow-ssh
apiVersion: compute.cnrm.cloud.google.com/v1beta1 kind: ComputeFirewall metadata: name: deny-all-ingress spec: network: name: default direction: INGRESS priority: 65534 denied: - IPProtocol: all
- Security Group Management Script
!/bin/bash
manage-security-groups.sh - Security group management utility
set -euo pipefail
ACTION="${1:-list}" REGION="${2:-us-east-1}"
List security groups
list_security_groups() { echo "Security Groups in $REGION:" aws ec2 describe-security-groups \ --region "$REGION" \ --query 'SecurityGroups[*].[GroupId,GroupName,VpcId]' \ --output table }
Show security group details
show_security_group() { local sg_id="$1" echo "Inbound Rules for $sg_id:" aws ec2 describe-security-groups \ --group-ids "$sg_id" \ --region "$REGION" \ --query 'SecurityGroups[0].IpPermissions' \ --output table
echo -e "\nOutbound Rules for $sg_id:"
aws ec2 describe-security-groups \
--group-ids "$sg_id" \
--region "$REGION" \
--query 'SecurityGroups[0].IpPermissionsEgress' \
--output table
}
Add inbound rule
add_inbound_rule() { local sg_id="$1" local protocol="$2" local port="$3" local cidr="$4" local description="${5:-}"
aws ec2 authorize-security-group-ingress \
--group-id "$sg_id" \
--protocol "$protocol" \
--port "$port" \
--cidr "$cidr" \
--region "$REGION" \
${description:+--description "$description"}
echo "Rule added to $sg_id"
}
Audit security groups for overly permissive rules
audit_security_groups() { echo "Auditing security groups for overly permissive rules..."
aws ec2 describe-security-groups \
--region "$REGION" \
--query 'SecurityGroups[*].[GroupId,IpPermissions]' \
--output text | while read sg_id; do
# Check for 0.0.0.0/0 on sensitive ports
if aws ec2 describe-security-groups \
--group-ids "$sg_id" \
--region "$REGION" \
--query "SecurityGroups[0].IpPermissions[?FromPort==\`22\` || FromPort==\`3306\` || FromPort==\`5432\`]" \
--output json | grep -q "0.0.0.0/0"; then
echo "WARNING: $sg_id has sensitive ports open to 0.0.0.0/0"
fi
done
}
Main
case "$ACTION" in list) list_security_groups ;; show) show_security_group "$3" ;; add-rule) add_inbound_rule "$3" "$4" "$5" "$6" "${7:-}" ;; audit) audit_security_groups ;; *) echo "Usage: $0 {list|show|add-rule|audit} [args]" exit 1 ;; esac
Best Practices ✅ DO Implement least privilege access Use security groups for segmentation Document rule purposes Regularly audit rules Separate inbound and outbound rules Use security group references Monitor rule changes Test access before enabling ❌ DON'T Allow 0.0.0.0/0 for databases Open all ports unnecessarily Mix environments in single SG Ignore egress rules Allow all protocols Forget to document rules Use single catch-all rule Deploy without firewall Common Rules Port Protocol Purpose 22 TCP SSH (Admin only) 80 TCP HTTP (Public) 443 TCP HTTPS (Public) 3306 TCP MySQL (App tier only) 5432 TCP PostgreSQL (App tier only) 6379 TCP Redis (App tier only) Resources AWS Security Groups Documentation Kubernetes Network Policies GCP Firewall Rules Zero Trust Networking