# Firewall

**Page:** foundation/networking/firewall

[Download Raw Markdown](./foundation/networking/firewall.md)

---

# Firewall

**Host-enforced network security for your containers.** Control exactly what can connect in (ingress) and out (egress) at the packet level.

---

## API Endpoints Summary

**Complete endpoint documentation:**

- **[GET /api/v1/containers/\{id\}/firewall/rules](/api/container-firewall/)** - List all firewall rules
- **[POST /api/v1/containers/\{id\}/firewall/ingress](/api/container-firewall/)** - Add ingress rule
- **[POST /api/v1/containers/\{id\}/firewall/egress](/api/container-firewall/)** - Add egress rule
- **[DELETE /api/v1/containers/\{id\}/firewall/ingress](/api/container-firewall/)** - Remove ingress rule(s)
- **[DELETE /api/v1/containers/\{id\}/firewall/egress](/api/container-firewall/)** - Remove egress rule(s)
- **[PATCH /api/v1/containers/\{id\}/firewall/ingress](/api/container-firewall/)** - Toggle ingress rule state
- **[PATCH /api/v1/containers/\{id\}/firewall/egress](/api/container-firewall/)** - Toggle egress rule state
- **[POST /api/v1/containers/\{id\}/firewall/reset](/api/container-firewall/)** - Reset firewall

---

## Key Concepts

**Host-level enforcement:** Firewall runs on YOUR server's host kernel—completely outside containers.

**What this means:**
- ✅ Containers **cannot bypass** firewall rules
- ✅ Containers **cannot modify** their own firewall
- ✅ Rules survive container restarts/snapshots

**Two directions:**
- **Ingress** - Traffic coming INTO container (who can connect)
- **Egress** - Traffic going OUT FROM container (what container can reach)

**Three actions:**
- **allow** - Permit matching traffic
- **reject** - Block and notify (ICMP/TCP unreachable)
- **drop** - Silently ignore (appears offline to scanners)

**Three protocols:** `tcp`, `udp`, `icmp4`

---

## Quick Examples

### Allow SSH from Specific IP


  
    ```bash
    # Allow SSH from office IP
    hoody firewall ingress create --container $CONTAINER_ID \
      --action allow --protocol tcp \
      --destination-port 22 --source "203.0.113.50/32" \
      --description "Allow SSH from office"
    ```
  
  
    ```typescript
    await client.api.firewall.addIngressRule(CONTAINER_ID, {
      action: 'allow',
      protocol: 'tcp',
      description: 'Allow SSH from office',
      destination_port: '22',
      source: '203.0.113.50/32',
    });
    ```
  
  
    ```bash
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/ingress" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "action": "allow",
        "protocol": "tcp",
        "description": "Allow SSH from office",
        "destination_port": "22",
        "source": "203.0.113.50/32"
      }'
    ```
  


### Block All Outbound Internet


  
    ```bash
    # Block all TCP egress traffic
    hoody firewall egress create --container $CONTAINER_ID \
      --action drop --protocol tcp \
      --destination "0.0.0.0/0" \
      --description "Block all TCP egress"
    ```
  
  
    ```typescript
    await client.api.firewall.addEgressRule(CONTAINER_ID, {
      action: 'drop',
      protocol: 'tcp',
      description: 'Block all TCP egress',
      destination: '0.0.0.0/0',
    });
    ```
  
  
    ```bash
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/egress" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "action": "drop",
        "protocol": "tcp",
        "description": "Block all TCP egress",
        "destination": "0.0.0.0/0"
      }'
    ```
  


**Use case:** Run untrusted code in complete isolation.

### Allow Specific Service

Allow only HTTPS to specific API:


  
    ```bash
    # Allow HTTPS to specific API range
    hoody firewall egress create --container $CONTAINER_ID \
      --action allow --protocol tcp \
      --destination-port 443 --destination "198.51.100.0/24" \
      --description "Allow HTTPS to API"
    ```
  
  
    ```typescript
    await client.api.firewall.addEgressRule(CONTAINER_ID, {
      action: 'allow',
      protocol: 'tcp',
      description: 'Allow HTTPS to API',
      destination_port: '443',
      destination: '198.51.100.0/24',
    });
    ```
  
  
    ```bash
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/egress" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "action": "allow",
        "protocol": "tcp",
        "description": "Allow HTTPS to API",
        "destination_port": "443",
        "destination": "198.51.100.0/24"
      }'
    ```
  


---

## Port Specification


  
    ```json
    {"destination_port": "80"}
    ```
  
  
    ```json
    {"destination_port": "8000-9000"}
    ```
  
  
    ```json
    {"destination_port": "80,443,8080"}
    ```
  
  
    ```json
    {"destination_port": "22,80-90,443,8000-9000"}
    ```
  


**CIDR notation:**
- `/32` - Single IP (`203.0.113.50/32`)
- `/24` - 256 IPs (`203.0.113.0/24`)
- `/0` - All IPs (`0.0.0.0/0`)

---

## Rule Evaluation Order

**First match wins.** Put specific rules before broad rules:

**✅ Correct: Specific first**





**❌ Wrong: Broad first (specific never evaluated)** - If you create the drop rule first, the allow rule will never be reached.

---

## Common Patterns

### Database Container (Restrict Access)

Allow PostgreSQL from backend only:



Drop all other attempts:



### Web Server (Public + Restricted SSH)

Allow HTTP/HTTPS from anywhere:



Allow SSH from office only:



Drop other SSH attempts:



### Isolated Sandbox (No Internet)

Block all egress:





**Perfect for AI-generated code execution.**

### Malware Defense (Allowlist Required Services)

**Security principle:** Malware ALWAYS needs to communicate (command-and-control, data exfiltration). Block everything except what your programs legitimately need.

Allow only what your app needs (e.g., your API):



Allow DNS:



Block everything else:



**Even if container is compromised, malware cannot phone home.**

---

## Firewall vs Hoody Proxy

**Two separate security layers:**

| Layer | Controls | Use For |
|-------|----------|---------|
| **Firewall** | Network packets (TCP/UDP/ICMP) | Port restrictions, IP filtering |
| **Proxy Permissions** | HTTP service access | User auth, service-level control |

**Firewall** controls network traffic. **Proxy Permissions** controls HTTP service access.

**Layer both for defense-in-depth.**

See: [Proxy Permissions →](/foundation/proxy/permissions/)

---

## Managing Rules

### List Rules


  
    ```bash
    # List all firewall rules for a container
    hoody firewall list --container $CONTAINER_ID
    ```
  
  
    ```typescript
    const rules = await client.api.firewall.list(CONTAINER_ID);
    console.log(rules.data);
    ```
  
  
    ```bash
    curl "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/rules" \
      -H "Authorization: Bearer $TOKEN"
    ```
  


### Disable Rule (Keep Configuration)


  
    ```bash
    # Disable an ingress rule by description
    hoody firewall ingress toggle --container $CONTAINER_ID \
      --state disabled --description "Allow SSH from office"
    ```
  
  
    ```typescript
    await client.api.firewall.toggleIngressRule(CONTAINER_ID, {
      state: 'disabled',
      description: 'Allow SSH from office',
    });
    ```
  
  
    ```bash
    curl -X PATCH "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/ingress" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"state": "disabled", "description": "Allow SSH from office"}'
    ```
  


### Remove Rules

Remove specific rule:


  
    ```bash
    # Remove a specific ingress rule
    hoody firewall ingress delete --container $CONTAINER_ID \
      --description "Allow SSH from office"
    ```
  
  
    ```typescript
    await client.api.firewall.removeIngressRule(CONTAINER_ID, {
      description: 'Allow SSH from office',
    });
    ```
  
  
    ```bash
    curl -X DELETE "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/ingress" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"description": "Allow SSH from office"}'
    ```
  


Reset completely (remove ALL rules):


  
    ```bash
    # Reset firewall to default (removes ALL rules)
    hoody firewall reset --container $CONTAINER_ID
    ```
  
  
    ```typescript
    await client.api.firewall.reset(CONTAINER_ID);
    ```
  
  
    ```bash
    curl -X POST "https://api.hoody.icu/api/v1/containers/$CONTAINER_ID/firewall/reset" \
      -H "Authorization: Bearer $TOKEN"
    ```
  


---

## Best Practices

### 1. Specific Rules Before Broad Rules

Create specific allow rule first:



Then create the broad drop rule:



### 2. Use Drop for Public Services

- ✅ `"action": "drop"` = stealth (appears offline to scanners)
- ⚠️ `"action": "reject"` = reveals existence (sends ICMP unreachable)

### 3. Allow DNS for Egress Restrictions

When blocking egress, remember to allow DNS:



### 4. Snapshot Before Major Changes



See: [Snapshots →](/foundation/containers/snapshots/)

---

## Useful Questions

### Does the firewall apply to traffic through the Hoody Proxy?

No. Hoody Proxy traffic (service URLs like `https://{project}-{container}-terminal-1.{server}.containers.hoody.icu`) bypasses the firewall. Use [Proxy Permissions](/foundation/proxy/permissions/) for HTTP service access control.

### Can containers modify their own firewall rules?

No. Firewall rules are host-enforced and only modifiable via the Hoody API. Containers cannot see or change them.

### What's the difference between firewall and Network Configuration's "block" mode?

Network Config `block` mode prevents ALL outbound traffic. Firewall egress rules provide **granular control**—allow some, block others.

### Do firewall rules persist through restarts?

Yes. Rules are stored at host level and survive container restarts, pauses, and snapshots.

### What if I lock myself out with firewall rules?

Access via [hoody-terminal](/kit/terminals/) URL (HTTP bypasses firewall) or use API to add an allow rule or reset: `POST /firewall/reset`

### Can firewall rules use domain names?

No. Firewall operates at IP layer—use CIDR notation only.

---

## Troubleshooting

### Cannot Connect After Adding Rules

**Solutions:**

1. List rules: `GET /firewall/rules`
2. Disable blocking rule: `PATCH /firewall/ingress {"state": "disabled", "description": "..."}`
3. Add allow rule for your IP: `POST /firewall/ingress {"action": "allow", "source": "YOUR_IP/32"}`
4. Reset firewall: `POST /firewall/reset` (removes ALL rules)

### Rules Not Working

**Check:**
- Rule state: Must be `"state": "enabled"`
- Rule order: Specific before broad
- Protocol: TCP rule won't block UDP traffic

### Egress Blocks Package Installation

**Allow package repositories:**





---

## What's Next

**Complete networking setup:**
- **[Network Configuration →](./network/)** - Route traffic through proxies/VPNs
- **[SSH Access →](./ssh/)** - Secure shell and SFTP
- **[IPv4 Management →](./ipv4/)** - Dedicated IPs (coming soon)

**Related security:**
- **[Proxy Permissions →](/foundation/proxy/permissions/)** - Application-level access control
- **[Security Principles →](/vision/security/)** - Hoody's security philosophy

**Understanding gained:**
- ✅ Firewall enforced at host level (tamper-proof)
- ✅ Ingress controls inbound, egress controls outbound
- ✅ Rules evaluated in order (first match wins)
- ✅ Three actions: allow, reject, drop
- ✅ Independent from Hoody Proxy Permissions

---

> **Host-level enforcement. Container-level control.**  
> **Tamper-proof security that containers cannot bypass.**

**Network security starts at the packet level.**