courses

Network Security

Controlling what traffic may go where

Network security controls what traffic can enter, traverse, and leave an environment — it is least privilege applied to the network itself. This page covers segmentation and VLANs, firewalls and secure zoning, DNS security, and DDoS mitigation. It builds on networking fundamentals (TCP/IP, routing, addressing) and connects to the deeper dives elsewhere in the course: firewalls and IDS/IPS, VPNs and IPSec, DNS security and privacy, and email security.

Network Segmentation

Network segmentation divides a network into isolated zones so that compromise of one zone does not automatically grant access to others. It is a foundational defense-in-depth control.

Segmentation principles

Zone model

A classic three-tier zone model:

Internet
    │
    ▼
[DMZ — Public-facing services]
    Web servers, load balancers, reverse proxies
    Allowed inbound: 80/tcp, 443/tcp from internet
    Allowed outbound: 8080/tcp to Application tier only
    │
    ▼
[Application tier — Business logic]
    API servers, microservices
    Allowed inbound: 8080/tcp from DMZ only
    Allowed outbound: 5432/tcp to Data tier only
    │
    ▼
[Data tier — Databases, storage]
    PostgreSQL, Redis, object storage
    Allowed inbound: 5432/tcp from Application tier only
    Allowed outbound: None (no internet access)

Linux firewall segmentation with nftables

nftables is the modern replacement for iptables on Linux (kernel 3.13+):

# Basic nftables ruleset enforcing the Application tier policy
# /etc/nftables.conf

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established connections
        ct state established,related accept

        # Allow loopback
        iif lo accept

        # Allow SSH from management subnet only
        ip saddr 10.0.10.0/24 tcp dport 22 accept

        # Allow API traffic from DMZ only
        ip saddr 10.0.1.0/24 tcp dport 8080 accept

        # Drop everything else
        log prefix "INPUT-DROP: " drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;

        # Allow only database traffic outbound
        ip daddr 10.0.3.0/24 tcp dport 5432 accept

        # Allow DNS
        udp dport 53 accept

        # Block all other outbound (comment out for less restrictive)
        # log prefix "OUTPUT-DROP: " drop
    }
}
# Apply nftables rules
sudo nft -f /etc/nftables.conf
sudo nft list ruleset          # verify active rules
sudo systemctl enable nftables

Kubernetes network segmentation

# Enforce strict segmentation between namespaces with NetworkPolicy
# Step 1: Default-deny all traffic in production namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
# Step 2: Allow frontend pods to reach backend pods on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: backend
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: frontend
      ports:
        - protocol: TCP
          port: 8080
---
# Step 3: Allow backend to reach database (separate namespace)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-egress-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      tier: backend
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: database
      ports:
        - protocol: TCP
          port: 5432
  policyTypes:
    - Egress

VLANs

VLANs (Virtual Local Area Networks) segment a physical network at Layer 2, creating separate broadcast domains on shared switch infrastructure. VLANs provide network segmentation without requiring separate physical hardware.

VLAN concepts

Concept Description
VLAN ID 12-bit number (1–4094) identifying a VLAN; IDs 1 and 4094 reserved
Access port Switch port assigned to one VLAN; carries untagged frames to the host
Trunk port Switch port that carries tagged frames for multiple VLANs (between switches, to routers)
802.1Q tagging IEEE standard for VLAN tags; inserts a 4-byte tag into the Ethernet frame
Inter-VLAN routing A router or Layer 3 switch routes between VLANs; acts as the enforcement point for segment policy

Linux VLAN interfaces

Linux can create VLAN-tagged interfaces using the 8021q kernel module:

# Load the 802.1Q module
sudo modprobe 8021q
echo 8021q | sudo tee -a /etc/modules

# Create a VLAN interface (VLAN 100 on eth0)
sudo ip link add link eth0 name eth0.100 type vlan id 100
sudo ip addr add 10.100.0.1/24 dev eth0.100
sudo ip link set eth0.100 up

# Verify
ip -d link show eth0.100          # shows vlan protocol 802.1Q id 100
cat /proc/net/vlan/eth0.100       # VLAN statistics

# Persistent configuration (systemd-networkd)
# /etc/systemd/network/10-eth0.100.netdev
# [NetDev]
# Name=eth0.100
# Kind=vlan
# [VLAN]
# Id=100

# /etc/systemd/network/10-eth0.100.network
# [Match]
# Name=eth0.100
# [Network]
# Address=10.100.0.1/24

VLAN security considerations


Firewalls

Firewalls inspect and control traffic based on rules. Modern firewalls are stateful — they track connection state — and many include application-layer inspection.

iptables (legacy, still common)

See Networking for full iptables coverage. Key security rules:

# Hardened INPUT chain baseline
iptables -F INPUT                               # flush existing rules
iptables -P INPUT DROP                          # default deny
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -m limit \
    --limit 1/second -j ACCEPT                 # rate-limit ping

# Log and drop everything else
iptables -A INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
iptables -A INPUT -j DROP

nftables (current standard)

# List all active rules
nft list ruleset

# Add a rule to an existing chain
nft add rule inet filter input tcp dport 8080 accept

# Delete a rule by handle
nft --handle list chain inet filter input    # find handle numbers
nft delete rule inet filter input handle 5

# Save and restore rules
nft list ruleset > /etc/nftables.conf
nft -f /etc/nftables.conf

Web Application Firewall (WAF)

A WAF operates at Layer 7 (HTTP) and blocks application-layer attacks that network firewalls cannot inspect:

# ModSecurity with OWASP Core Rule Set (nginx)
# Install
sudo apt install libnginx-mod-security2

# /etc/nginx/nginx.conf
# load_module modules/ngx_http_modsecurity_module.so;

# /etc/nginx/conf.d/modsecurity.conf
# modsecurity on;
# modsecurity_rules_file /etc/nginx/modsec/main.conf;
# /etc/nginx/modsec/main.conf
Include /etc/nginx/modsec/modsecurity.conf
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf

# Custom rule: block requests with SQL injection patterns
SecRule ARGS "@detectSQLi" \
    "id:1001,phase:2,deny,status:403,\
    msg:'SQL Injection Attempt',\
    logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
# Python: interact with AWS WAF to check blocked requests
import boto3
from datetime import datetime, timedelta

wafv2 = boto3.client('wafv2', region_name='us-east-1')

# Get sampled blocked requests for a web ACL
response = wafv2.get_sampled_requests(
    WebAclArn='arn:aws:wafv2:us-east-1:123456789012:regional/webacl/my-waf/abc123',
    RuleMetricName='AWSManagedRulesCommonRuleSet',
    Scope='REGIONAL',
    TimeWindow={
        'StartTime': datetime.utcnow() - timedelta(hours=1),
        'EndTime': datetime.utcnow(),
    },
    MaxItems=100,
)

for req in response.get('SampledRequests', []):
    http = req['Request']
    action = req.get('Action', 'UNKNOWN')
    print(f"{action}: {http.get('Method')} {http.get('URI')} "
          f"from {http.get('ClientIP')}")

DNS Security

DNS is a frequent attack vector: DNS poisoning redirects users to attacker-controlled servers, DNS exfiltration tunnels data out through DNS queries, and misconfigured DNS exposes internal infrastructure details.

DNSSEC

DNSSEC adds cryptographic signatures to DNS records, allowing resolvers to verify that responses have not been tampered with:

# Check if a domain has DNSSEC enabled
dig +dnssec example.com
dig DS example.com @8.8.8.8    # check for Delegation Signer records

# Validate DNSSEC chain of trust
dig +trace +dnssec example.com

# Check DNSSEC status with delv (DNS lookup and validation)
delv @8.8.8.8 example.com A +rtrace

DNS security monitoring

# Monitor DNS queries with tcpdump
sudo tcpdump -i eth0 port 53 -n

# Log DNS queries with systemd-resolved
resolvectl statistics                       # resolver stats
journalctl -u systemd-resolved --since today  # resolver log

# Check for DNS exfiltration patterns (unusually long or high-entropy subdomains)
# using Suricata rule:
# alert dns any any -> any any (msg:"DNS Exfiltration: Long subdomain";
#   dns_query; content:"."; pcre:"/^[a-z0-9]{30,}/i"; sid:2024001;)
# Python: detect high-entropy DNS queries (potential DNS tunneling)
import math
import re
from collections import Counter

def entropy(s: str) -> float:
    """Shannon entropy of a string."""
    counts = Counter(s)
    total = len(s)
    return -sum((c/total) * math.log2(c/total) for c in counts.values())

def analyze_dns_query(fqdn: str) -> dict:
    """Flag suspicious DNS queries."""
    labels = fqdn.split('.')
    subdomain = '.'.join(labels[:-2]) if len(labels) > 2 else ''
    result = {
        'fqdn': fqdn,
        'subdomain_length': len(subdomain),
        'entropy': entropy(subdomain) if subdomain else 0,
        'suspicious': False,
        'reason': [],
    }
    if len(subdomain) > 50:
        result['suspicious'] = True
        result['reason'].append(f"Long subdomain ({len(subdomain)} chars)")
    if result['entropy'] > 3.5:
        result['suspicious'] = True
        result['reason'].append(f"High entropy ({result['entropy']:.2f})")
    return result

test_queries = [
    "normal.example.com",
        "aGVsbG8gd29ybGQgdGhpcyBpcyBiYXNlNjQgZW5jb2RlZA.evil.com",
]
for q in test_queries:
    result = analyze_dns_query(q)
    if result['suspicious']:
        print(f"SUSPICIOUS: {q}{', '.join(result['reason'])}")
    else:
        print(f"OK: {q}")

DDoS Mitigation

Distributed Denial of Service (DDoS) attacks exhaust a target’s resources — bandwidth, CPU, connection table — making the service unavailable to legitimate users.

DDoS categories

Category Attack type Example Mitigation
Volumetric Bandwidth exhaustion UDP flood, ICMP flood Upstream scrubbing, anycast
Protocol State table exhaustion SYN flood, fragmented packets SYN cookies, rate limiting
Application L7 resource exhaustion HTTP flood, Slowloris Rate limiting, WAF, CAPTCHA

Linux-level rate limiting

# SYN flood protection: enable SYN cookies
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
# Persistent:
echo "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/99-security.conf
sysctl -p /etc/sysctl.d/99-security.conf

# Rate limit ICMP with nftables
nft add rule inet filter input \
    ip protocol icmp limit rate 10/second \
    counter accept

nft add rule inet filter input \
    ip protocol icmp \
    counter drop

# Rate limit new TCP connections per source IP with iptables
iptables -A INPUT -p tcp --syn \
    -m connlimit --connlimit-above 20 --connlimit-mask 32 \
    -j REJECT --reject-with tcp-reset

# Rate limit HTTP requests (new connections per IP)
iptables -A INPUT -p tcp --dport 443 \
    -m recent --set --name https-limit
iptables -A INPUT -p tcp --dport 443 \
    -m recent --update --seconds 60 --hitcount 120 --name https-limit \
    -j DROP

nginx rate limiting

# /etc/nginx/nginx.conf: define rate limit zone
http {
    # 10 MB zone (holds ~160,000 IPs); limit 30 requests/second per IP
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        location /api/ {
            # Allow bursts of 10 above the rate; delay (not reject) excess
            limit_req zone=api_limit burst=10 delay=5;

            # Limit simultaneous connections per IP
            limit_conn conn_limit 20;

            # Return 429 instead of default 503 on rate limit
            limit_req_status 429;
            limit_conn_status 429;
        }
    }
}
# Python: implement token bucket rate limiting for an API
import time
import threading
from collections import defaultdict

class TokenBucket:
    """Thread-safe token bucket rate limiter."""

    def __init__(self, rate: float, capacity: int):
        self.rate = rate          # tokens per second
        self.capacity = capacity  # maximum burst
        self._buckets = defaultdict(lambda: {'tokens': capacity, 'last': time.time()})
        self._lock = threading.Lock()

    def allow(self, key: str) -> bool:
        with self._lock:
            bucket = self._buckets[key]
            now = time.time()
            elapsed = now - bucket['last']
            bucket['tokens'] = min(
                self.capacity,
                bucket['tokens'] + elapsed * self.rate
            )
            bucket['last'] = now
            if bucket['tokens'] >= 1:
                bucket['tokens'] -= 1
                return True
            return False

# Usage in a Flask API
from flask import Flask, request, abort

app = Flask(__name__)
limiter = TokenBucket(rate=10, capacity=30)   # 10 req/s, burst of 30

@app.before_request
def check_rate_limit():
    client_ip = request.remote_addr
    if not limiter.allow(client_ip):
        abort(429, "Rate limit exceeded")

Secure Network Zoning in Practice

Worked example — three-tier web application:

# Assuming three Linux hosts in three zones:
# DMZ:  10.0.1.10 (nginx reverse proxy)
# App:  10.0.2.10 (Flask API)
# Data: 10.0.3.10 (PostgreSQL)

# --- DMZ host (nginx) ---
# Allow only 80/443 inbound from internet; only 8080 outbound to App tier
nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input tcp dport '{ 80, 443 }' accept
nft add rule inet filter input iif lo accept

nft add chain inet filter output '{ type filter hook output priority 0; policy drop; }'
nft add rule inet filter output ct state established,related accept
nft add rule inet filter output ip daddr 10.0.2.10 tcp dport 8080 accept
nft add rule inet filter output udp dport 53 accept  # DNS for certificate validation

# --- App host (Flask) ---
# Allow only 8080 from DMZ; only 5432 to Data tier
# (similar nft rules, scoped to 10.0.1.0/24 source and 10.0.3.10 dest)

# --- Data host (PostgreSQL) ---
# /etc/postgresql/16/main/pg_hba.conf: restrict to App tier source IP
# host  mydb  appuser  10.0.2.10/32  scram-sha-256
# All other connections: reject

Key takeaways

References


Related course pages: Introduction to Networking · Defensive Measures (firewalls, IDS/IPS) · VPNs and IPSec · DNS Security and Privacy · Email Security · Access Control and Authorization

🛠️ Maintenance note: the concepts are stable, but the tooling and cloud specifics drift — nftables syntax, Kubernetes NetworkPolicy/CNI behavior, and cloud security-group models all change. Re-verify the worked examples against current versions, and note this page leans toward a DevSecOps framing (k8s, cloud) even though it now lives in the introsec course.