Firewalls — Controlling Access to Your Data

Quote

“Complexity is the worst enemy of security, and our systems are getting more complex all the time.”

Bruce Schneier, Schneier on Security blog (2007)

“You can’t trust code that you did not totally create yourself.”

Ken Thompson, Reflections on Trusting Trust, Turing Award lecture (1984)

Every production database should be accessible ONLY from authorized sources. A SQL Server port open to the internet is a security incident waiting to happen. Firewalls are your perimeter defense — and relying on only one layer is not enough.

The defense-in-depth model layers GCP VPC firewall rules (network level), OS-level firewall (ufw/Windows Firewall), application authentication, and IAP tunneling so that no single misconfiguration exposes a service.


flowchart TD
    Internet["Internet / External Client"]
    GCP["GCP VPC Firewall<br>(network perimeter)"]
    OS["OS Firewall<br>ufw / Windows Firewall"]
    App["Application Auth<br>SQL Server login / IAM"]
    IAP["IAP Tunnel<br>(no public IP)"]
    DB[("SQL Server / DB")]

    Internet -->|"blocked unless explicitly allowed"| GCP
    GCP -->|"allowed port 1433 from VPC subnet only"| OS
    OS -->|"allowed from authorized internal IPs"| App
    App -->|"strong credentials / service account"| DB
    Internet -.->|"SSH via IAP"| IAP
    IAP --> OS

Linux ufw tools

ufw (Uncomplicated Firewall) is a frontend to iptables designed to simplify firewall management on Linux. It maintains ordered rule chains: rules are evaluated top-to-bottom and the first match wins. On production VMs the correct baseline is default-deny inbound, default-allow outbound, with explicit allow rules for each required service and source range.

Linux | ufw | check firewall state

Use ufw status verbose to inspect current rules, default policies, and enabled state before making changes.

Check firewall status and active rules

sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
 
To                         Action      From
--                         ------      ----
1433/tcp                   ALLOW IN    10.132.0.0/24
22/tcp                     ALLOW IN    35.235.240.0/20

Linux | ufw | allow and deny rules

Inbound allow rules in ufw always filter by source IP, destination port, and protocol. Omitting the from clause opens the port to the entire internet — always specify a source range.

Allow SQL Server from a VPC subnet

sudo ufw allow from 10.132.0.0/24 to any port 1433 proto tcp

Allow SSH from the IAP IP range

sudo ufw allow from 35.235.240.0/20 to any port 22 proto tcp

Deny a specific source range

sudo ufw deny from 192.168.50.0/24 to any port 1433 proto tcp

Reject with an ICMP response (instead of silent drop)

reject sends a TCP RST or ICMP port-unreachable back to the sender, signaling a refused connection rather than a timeout. Use deny (silent drop) for internet-facing rules to avoid revealing open ports.

sudo ufw reject from 192.168.50.0/24 to any port 1433 proto tcp

Never allow a port without scoping the source

sudo ufw allow 1433 opens TCP port 1433 to every IP address on the internet. This is a critical misconfiguration for database ports.

Always scope allow rules to a source range

sudo ufw allow from 10.132.0.0/24 to any port 1433 proto tcp

Limit the source to the VPC subnet CIDR or the specific IAP range.

FlagSyntaxDescription
from <ip/cidr>ufw allow from 10.0.0.0/8 to any port 22Restrict rule to a source IP or CIDR range
to any port <n>ufw allow from 10.0.0.0/8 to any port 1433Target a specific destination port
proto tcp|udpufw allow from 10.0.0.0/8 to any port 53 proto udpRestrict rule to a protocol
allowufw allow from ... to any port 22Permit matching traffic
denyufw deny from 1.2.3.4 to anySilently drop matching traffic
rejectufw reject from 1.2.3.4 to anyDrop and reply with ICMP unreachable

Linux | ufw | default policies

Default policies define what happens to traffic that matches no explicit rule. Always set deny incoming before enabling ufw on a production server, then add explicit allow rules for each required service.

Set default deny incoming and allow outgoing

sudo ufw default deny incoming
sudo ufw default allow outgoing

Enable the firewall

Run this only after adding all required allow rules, especially SSH. Enabling ufw with no SSH allow rule in place will immediately terminate your remote session.

sudo ufw enable

ufw enable can lock you out of SSH

If no SSH allow rule exists before running ufw enable, remote access is lost immediately. The VM must be recovered via console or IAP.

Add the SSH allow rule before enabling

sudo ufw allow from 35.235.240.0/20 to any port 22 proto tcp
sudo ufw enable

Verify rule is present with sudo ufw status numbered first.

FlagSyntaxDescription
default deny incomingufw default deny incomingBlock all inbound traffic not matched by a rule
default allow outgoingufw default allow outgoingAllow all outbound traffic not matched by a rule
default deny outgoingufw default deny outgoingBlock all outbound (strict egress control)
default allow incomingufw default allow incomingAllow all inbound (insecure — avoid in production)

Linux | ufw | delete and reset rules

Rules are managed by number in ufw. Always list numbered rules before deleting to avoid removing the wrong entry.

List rules with numbers

sudo ufw status numbered
Status: active
 
     To                         Action      From
     --                         ------      ----
[ 1] 1433/tcp                   ALLOW IN    10.132.0.0/24
[ 2] 22/tcp                     ALLOW IN    35.235.240.0/20

Delete a rule by number

sudo ufw delete 3

Reset all rules to factory defaults

sudo ufw reset

ufw reset removes all rules including SSH

Running ufw reset on a remote VM without console access will lock you out.

Disable ufw temporarily instead of resetting

sudo ufw disable

This stops enforcement without deleting rules, preserving the configuration for re-enable.

FlagSyntaxDescription
status numberedufw status numberedList all rules with index numbers for deletion
delete <n>ufw delete 3Remove rule at position n
delete allow <port>ufw delete allow 22Remove allow rule matching port (if unique)
resetufw resetRemove all rules and disable ufw
disableufw disableStop ufw enforcement without removing rules

Linux | ufw | logging

ufw writes to /var/log/ufw.log. Logging levels control verbosity: low logs blocked packets, medium adds allowed packets, high adds rate-limited packets.

Set logging level

sudo ufw logging medium

Tail firewall log for live traffic inspection

sudo tail -f /var/log/ufw.log
Apr  3 14:22:01 vm-prod kernel: [UFW BLOCK] IN=eth0 OUT= MAC=... SRC=185.220.101.45 DST=10.132.0.5 LEN=44 TOS=0x00 PREC=0x00 TTL=53 ID=0 DF PROTO=TCP DPT=1433 WINDOW=65535 RES=0x00 SYN URGP=0
FlagSyntaxDescription
logging offufw logging offDisable firewall logging
logging lowufw logging lowLog only blocked packets (default)
logging mediumufw logging mediumLog blocked and allowed packets
logging highufw logging highLog all packets including rate-limited
logging fullufw logging fullMaximum verbosity — use for short debugging sessions only

PowerShell Windows Firewall tools

Windows Firewall (Windows Defender Firewall) is managed through the NetSecurity PowerShell module via cmdlets like New-NetFirewallRule, Get-NetFirewallRule, and Set-NetFirewallProfile. It operates on three profiles — Domain, Private, and Public — applied based on the detected network type.

PowerShell | Windows Firewall | inspect rules

Get-NetFirewallRule retrieves all firewall rules from the Windows Firewall policy store. Pipe with Where-Object to filter by state, direction, or action.

List all enabled inbound rules

Get-NetFirewallRule | Where-Object Enabled -eq True |
    Select-Object DisplayName, Direction, Action | Sort-Object DisplayName
DisplayName                         Direction Action
-----------                         --------- ------
SQL Server - Pipeline               Inbound   Allow
Remote Desktop - User Mode (TCP-In) Inbound   Allow

Show port filters for a specific rule

Get-NetFirewallRule -DisplayName "SQL Server - Pipeline" |
    Get-NetFirewallPortFilter
Protocol      : TCP
LocalPort     : 1433
RemotePort    : Any
IcmpType      : Any
DynamicTarget : Any

Show address filters for a specific rule

Get-NetFirewallRule -DisplayName "SQL Server - Pipeline" |
    Get-NetFirewallAddressFilter
LocalAddress  : Any
RemoteAddress : 10.132.0.0/24
FlagSyntaxDescription
-DisplayNameGet-NetFirewallRule -DisplayName "name"Filter by display name
-NameGet-NetFirewallRule -Name "name"Filter by internal rule name
-DirectionGet-NetFirewallRule -Direction InboundFilter by traffic direction
-ActionGet-NetFirewallRule -Action AllowFilter by action (Allow / Block)
-EnabledGet-NetFirewallRule -Enabled TrueFilter by enabled state
-ProfileGet-NetFirewallRule -Profile DomainFilter by profile (Domain/Private/Public)

PowerShell | Windows Firewall | create rules

New-NetFirewallRule adds inbound or outbound rules to the Windows Firewall policy. Always specify -RemoteAddress to scope the rule to a source subnet; omitting it allows any IP.

Allow SQL Server inbound from a VPC subnet

New-NetFirewallRule -DisplayName "SQL Server - Pipeline" `
    -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow `
    -RemoteAddress "10.132.0.0/24"

Allow RDP from a management subnet only

New-NetFirewallRule -DisplayName "RDP - Management Only" `
    -Direction Inbound -LocalPort 3389 -Protocol TCP -Action Allow `
    -RemoteAddress "10.200.0.0/24"

Block outbound to a known malicious range

New-NetFirewallRule -DisplayName "Block Malicious Range" `
    -Direction Outbound -RemoteAddress "185.220.101.0/24" `
    -Action Block

Omitting -RemoteAddress opens the port to all IPs

New-NetFirewallRule -Direction Inbound -LocalPort 1433 -Action Allow allows TCP 1433 from any source address on the internet. Do not omit -RemoteAddress for database or admin ports.

Always scope with -RemoteAddress

New-NetFirewallRule -DisplayName "SQL Server - Pipeline" `
    -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow `
    -RemoteAddress "10.132.0.0/24"
FlagSyntaxDescription
-DisplayName-DisplayName "name"Human-readable name shown in Windows Firewall UI
-Direction-Direction Inbound|OutboundTraffic direction the rule applies to
-LocalPort-LocalPort 1433Destination port on the local machine
-RemotePort-RemotePort 443Source port on the remote machine
-Protocol-Protocol TCP|UDPProtocol filter
-Action-Action Allow|BlockPermit or drop matching traffic
-RemoteAddress-RemoteAddress "10.0.0.0/8"Restrict rule to a source IP or CIDR
-Profile-Profile Domain,PrivateApply rule only when connected to matching network profiles
-Enabled-Enabled True|FalseCreate rule in enabled or disabled state

PowerShell | Windows Firewall | default policies

Set-NetFirewallProfile configures the default action for each of the three Windows Firewall profiles. In production, block all inbound by default and add explicit allow rules for each service.

Block all inbound by default across all profiles

Set-NetFirewallProfile -Profile Domain,Public,Private `
    -DefaultInboundAction Block

Allow all outbound (default, but explicit is better)

Set-NetFirewallProfile -Profile Domain,Public,Private `
    -DefaultOutboundAction Allow
Set-NetFirewallProfile -Profile Public -Enabled False

Disabling a Windows Firewall profile removes all protection on that network type

Setting -Enabled False for the Public profile removes all inbound blocking when the machine connects to any public network.

Keep all profiles enabled and block inbound by default

Set-NetFirewallProfile -Profile Domain,Public,Private `
    -DefaultInboundAction Block -DefaultOutboundAction Allow
FlagSyntaxDescription
-Profile-Profile Domain,Public,PrivateTarget one or more profiles
-DefaultInboundAction-DefaultInboundAction Block|AllowDefault action for inbound traffic with no matching rule
-DefaultOutboundAction-DefaultOutboundAction Block|AllowDefault action for outbound traffic with no matching rule
-Enabled-Enabled True|FalseEnable or disable the firewall profile entirely
-NotifyOnListen-NotifyOnListen True|FalseShow a notification when a new app registers a listener

PowerShell | Windows Firewall | remove rules

Remove a rule by display name

Remove-NetFirewallRule -DisplayName "SQL Server - Pipeline"

Remove all disabled rules

Get-NetFirewallRule -Enabled False | Remove-NetFirewallRule

Remove-NetFirewallRule is permanent and does not prompt by default

There is no undo. If you remove the wrong rule, you must recreate it manually.

Disable a rule first instead of removing it

Set-NetFirewallRule -DisplayName "SQL Server - Pipeline" -Enabled False

Disabling preserves the rule definition and allows re-enabling without reconfiguration.

FlagSyntaxDescription
-DisplayNameRemove-NetFirewallRule -DisplayName "name"Remove by display name
-NameRemove-NetFirewallRule -Name "name"Remove by internal name
-DirectionRemove-NetFirewallRule -Direction InboundRemove all rules matching a direction

PowerShell / Linux | gcloud | firewall rules

GCP VPC firewall rules operate at the network level, before traffic reaches any VM’s OS firewall. They are evaluated independently from ufw or Windows Firewall — both layers must allow traffic for a connection to succeed. gcloud compute firewall-rules manages these rules from either Linux or PowerShell.

GCP VPC firewall and OS firewall are independent layers

A GCP firewall rule allowing port 1433 does not override an OS-level ufw deny rule, and vice versa. Both must permit the traffic. A common debugging pattern: ufw allows port 1433 but the GCP firewall blocks it at the network level, so nc -zv times out even from inside the VPC.

PowerShell / Linux | gcloud | list firewall rules

List all VPC firewall rules in the project

gcloud compute firewall-rules list \
    --format="table(name,direction,allowed,sourceRanges)"
NAME                    DIRECTION  ALLOW                 SRC_RANGES
allow-iap-ssh           INGRESS    tcp:22                35.235.240.0/20
allow-sql-pipeline      INGRESS    tcp:1433              10.132.0.0/24
default-allow-internal  INGRESS    tcp,udp,icmp          10.128.0.0/9

Show details of a specific rule

gcloud compute firewall-rules describe allow-iap-ssh
allowed:
- IPProtocol: tcp
  ports:
  - '22'
direction: INGRESS
name: allow-iap-ssh
network: https://www.googleapis.com/compute/v1/projects/my-project/global/networks/default
sourceRanges:
- 35.235.240.0/20
FlagSyntaxDescription
--format--format="table(name,direction,allowed,sourceRanges)"Control output columns and format
--filter--filter="direction=INGRESS"Filter results server-side
--project--project=my-project-idTarget a specific GCP project
--network--network=my-vpcList rules for a specific VPC network

PowerShell / Linux | gcloud | create firewall rules

Allow SSH from the IAP IP range

For iap-tunneling to work, a GCP firewall rule must allow TCP 22 from 35.235.240.0/20. Without this rule, gcloud compute ssh times out even if the VM is running and ufw allows SSH.

gcloud compute firewall-rules create allow-iap-ssh \
    --allow tcp:22 \
    --source-ranges 35.235.240.0/20 \
    --description "Allow SSH via IAP tunnel"

Allow SQL Server from a VPC subnet

gcloud compute firewall-rules create allow-sql-pipeline \
    --allow tcp:1433 \
    --source-ranges 10.132.0.0/24 \
    --target-tags sql-server \
    --description "Allow SQL Server from pipeline VPC subnet"

Allow ICMP (ping) from internal range for diagnostics

gcloud compute firewall-rules create allow-internal-icmp \
    --allow icmp \
    --source-ranges 10.128.0.0/9 \
    --description "Allow ICMP from internal VPC range for diagnostics"
FlagSyntaxDescription
--allow--allow tcp:1433Protocol and port(s) to permit
--source-ranges--source-ranges 10.0.0.0/8CIDR source ranges for ingress rules
--destination-ranges--destination-ranges 10.0.0.0/8CIDR destination ranges for egress rules
--direction--direction INGRESS|EGRESSTraffic direction (default: INGRESS)
--target-tags--target-tags sql-serverApply rule only to VMs with this network tag
--target-service-accounts--target-service-accounts sa@project.iam.gserviceaccount.comApply rule only to VMs running this service account
--priority--priority 1000Rule priority (0–65535, lower = higher priority)
--network--network my-vpcVPC network the rule applies to
--description--description "text"Human-readable description stored with the rule

PowerShell / Linux | gcloud | update and delete rules

Update the source range of an existing rule

gcloud compute firewall-rules update allow-sql-pipeline \
    --source-ranges 10.132.0.0/24,10.140.0.0/24

Delete a firewall rule

gcloud compute firewall-rules delete allow-sql-pipeline

Deleting a GCP firewall rule takes effect immediately

Unlike OS-level firewall changes that require a service restart, GCP firewall rule deletions propagate within seconds and immediately block matching traffic.

Disable a rule instead of deleting during troubleshooting

gcloud compute firewall-rules update allow-sql-pipeline --disabled

This preserves the rule definition and allows re-enabling with --no-disabled.

FlagSyntaxDescription
--source-rangesupdate --source-ranges 10.0.0.0/8Replace source CIDR ranges
--allowupdate --allow tcp:1433,tcp:1434Replace allowed protocol/port list
--disabledupdate --disabledDisable the rule without deleting it
--no-disabledupdate --no-disabledRe-enable a disabled rule
--priorityupdate --priority 500Change rule priority

Defense in depth

The most effective firewall strategy for production databases is layered: no single control point is trusted to be the sole barrier. Each layer operates independently, so a misconfiguration in one does not expose the database.

Never rely on a single firewall layer

If only the GCP VPC firewall blocks port 1433 and that rule is accidentally deleted or modified, the port becomes immediately accessible from the internal network. Each layer must function as if the others do not exist.

Apply all five layers simultaneously

  1. GCP VPC firewall — block port 1433 from external IPs at the network level (manage declaratively with networking)
  2. OS firewall (ufw / Windows Firewall) — block port 1433 from unauthorized internal IPs at the OS level
  3. SQL Server login — require strong passwords and specific login names (see service-accounts-and-iam for IAM-based access)
  4. No public IP — remove the VM’s external IP entirely; use IAP tunneling for SSH (see iap-tunneling)
  5. VPC Service Controls — prevent data exfiltration from the project (see vpc-service-controls; GCP Enterprise tier required)

Warnings

Firewall changes can lock you out

Three operational mistakes regularly turn a routine firewall change into an outage:

  • Default-deny before SSH allow. ufw enable with no prior SSH allow rule blocks remote access immediately.
  • Treating cloud and host firewalls as one layer. Allowing port 1433 in ufw but not in the GCP VPC firewall, or the reverse, still blocks the connection.
  • Using raw iptables rules without persistence. Manual rules disappear on reboot unless they are explicitly saved and restored.

If you are working on a remote host, assume every firewall change is a potential self-lockout until both the access path and the rollback path are verified.

Stage firewall changes safely

Apply firewall changes in a sequence that preserves recovery:

  • Protect remote access first. Run ufw allow ssh before enabling a default-deny policy, and keep serial console or GCP IAP access available as a fallback.
  • Validate both enforcement layers. Confirm that the cloud firewall and the OS firewall both permit the protocol and source range you intend to use.
  • Persist host rules deliberately. Save raw iptables rules with iptables-save plus iptables-persistent, or use ufw so the persistence behavior is handled for you.

The safe pattern is to treat firewall policy as layered state that must survive both the immediate change and the next reboot.

Recommendations

ScenarioRecommendation
Secure a new VMufw default deny incoming && ufw default allow outgoing && ufw allow ssh && ufw enable. Then add application ports.
Allow a specific portufw allow 1433/tcp (Linux) or New-NetFirewallRule -DisplayName "SQL" -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow (PowerShell).
Restrict by source IPufw allow from 10.132.0.0/24 to any port 1433 — allows only the application subnet.
Audit rulesufw status numbered (Linux) or Get-NetFirewallRule | Where Enabled -eq True | Format-Table (PowerShell).
GCP VPC + OS alignmentConfigure VPC firewall rules in Terraform/gcloud first, then mirror with ufw on the VM. Document both layers.

Troubleshooting

SymptomLikely causeFix
”Connection timed out” after deploying a serviceFirewall blocking the port (OS-level, VPC-level, or both).Check ufw status, VPC firewall rules, and ss -tlnp to verify the service is listening.
ufw allow has no effectufw is not enabled (ufw status shows “inactive”).Run ufw enable after adding rules.
Locked out of VM after enabling firewallSSH was not allowed before enabling the firewall.Access via GCP serial console or IAP tunnel. Run ufw allow ssh then ufw enable.
Rule added but traffic still blockedVPC firewall does not have a matching allow rule.Add the corresponding rule in GCP VPC: gcloud compute firewall-rules create ....
Windows Firewall rule not workingRule is in the wrong profile (Domain/Private/Public) or wrong direction (Inbound/Outbound).Verify profile and direction: Get-NetFirewallRule -DisplayName "name" | Format-List.

Cross-references