{
  "id": "CB-UBU-NET-007",
  "standard": "ubuntu",
  "version": "0.1.1",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.1/CB-UBU-NET-007",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.1/CB-UBU-NET-007.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.1/CB-UBU-NET-007.yml",
  "profiles_effective": [
    "server-hardened",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-NET-007",
    "title": "Drop outgoing traffic by default",
    "finding": {
      "title": "The host firewall lets the system open any outgoing connection",
      "description": "Malware and intruders on the host can reach any server on the Internet to download tools or send data out."
    },
    "status": "active",
    "profiles": [
      "server-hardened",
      "desktop-hardened"
    ],
    "risk": {
      "severity": "medium"
    },
    "assessment": "automated",
    "tags": [
      "firewall",
      "ufw",
      "nftables",
      "network",
      "egress"
    ],
    "description": "Most host firewalls only filter incoming traffic. This control requires the default policy for\noutgoing packets to drop or reject them, so that the system can only reach the destinations\nand ports allowed by explicit rules (DNS, NTP, package mirrors, proxies, log collectors...):\n\n- with ufw: `deny (outgoing)` or `reject (outgoing)` in `ufw status verbose`;\n- with nftables (when `nftables.service` is enabled and ufw is not active): a base chain on the\n  `output` hook with `policy drop`;\n- with iptables-persistent (when `netfilter-persistent.service` is enabled and neither ufw nor\n  nftables is): `OUTPUT` policy `DROP` for iptables and, with IPv6, ip6tables.\n",
    "rationale": "An outgoing allow list stops a compromised service from downloading a second stage, reaching a\ncommand and control server or sending data out directly. Blocked attempts also show up in the\nfirewall logs.\n",
    "impact": "Every outgoing flow the system needs must be listed beforehand, including DNS, NTP, package\nupdates and replies handled outside connection tracking. A missing rule breaks updates, time\nsynchronization or name resolution. There is no automatic fix for this reason.\n",
    "audit": {
      "manual": "- ufw: `ufw status verbose` shows `deny (outgoing)` or `reject (outgoing)`.\n- nftables: `nft list ruleset` has a chain with `hook output` and `policy drop`.\n- iptables-persistent: `iptables -S` and `ip6tables -S` show `-P OUTPUT DROP`.\n",
      "checks": [
        {
          "id": "ufw-outgoing",
          "engine": "shell",
          "description": "ufw default policy for outgoing traffic.",
          "facts": [
            "ufw"
          ],
          "spec": {
            "script": "ufw status 2>/dev/null | grep -q '^Status: active' || { echo 'ufw is not active'; exit 2; }\nd=$(ufw status verbose 2>/dev/null | sed -n 's/^Default: //p')\necho \"$d\"\nprintf '%s' \"$d\" | grep -Eq '(deny|reject) \\(outgoing\\)'\n"
          }
        },
        {
          "id": "nft-output",
          "engine": "shell",
          "description": "nftables output base chain with policy drop.",
          "facts": [
            "nftables"
          ],
          "spec": {
            "script": "[ \"$(systemctl is-enabled nftables.service 2>/dev/null)\" = enabled ] || { echo 'nftables.service is not enabled'; exit 2; }\nif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then\n  echo 'ufw manages the firewall'; exit 2\nfi\nr=$(nft list ruleset 2>/dev/null) || exit 3\nif printf '%s\\n' \"$r\" | grep -Eq 'hook output .*policy drop'; then echo 'output: policy drop'; else echo 'output: no chain with policy drop'; exit 1; fi\n"
          }
        },
        {
          "id": "iptables-output",
          "engine": "shell",
          "description": "iptables and ip6tables OUTPUT policy with iptables-persistent.",
          "spec": {
            "script": "[ \"$(systemctl is-enabled netfilter-persistent.service 2>/dev/null)\" = enabled ] || { echo 'netfilter-persistent.service is not enabled'; exit 2; }\nif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then\n  echo 'ufw manages the firewall'; exit 2\nfi\n[ \"$(systemctl is-enabled nftables.service 2>/dev/null)\" = enabled ] && { echo 'nftables manages the firewall'; exit 2; }\nPATH=\"$PATH:/usr/sbin\"\nrc=0\nfor t in iptables ip6tables; do\n  [ \"$t\" = ip6tables ] && [ ! -d /proc/sys/net/ipv6 ] && continue\n  r=$($t -S OUTPUT 2>/dev/null) || exit 3\n  if printf '%s\\n' \"$r\" | grep -Eq '^-P OUTPUT (DROP|REJECT)$'; then echo \"$t OUTPUT: DROP\"; else echo \"$t OUTPUT: not DROP\"; rc=1; fi\ndone\nexit \"$rc\"\n"
          }
        }
      ]
    },
    "remediation": {
      "manual": "1. List the outgoing flows the system needs (`ss -tunp` over a typical day, the configured DNS,\n   NTP and package mirrors, proxies, log and backup servers).\n2. Allow them, then change the default:\n   - ufw: `ufw allow out 53`, `ufw allow out 123/udp`, `ufw allow out to <mirror> port 443 proto tcp`...\n     then `ufw default deny outgoing`;\n   - nftables: add an `output` base chain with `policy drop;`, a rule\n     `ct state established,related accept`, `oif lo accept` and one rule per flow;\n   - iptables-persistent: add the `OUTPUT` rules for each flow, then `iptables -P OUTPUT DROP`\n     (and ip6tables), and `netfilter-persistent save`.\n3. Check name resolution, time synchronization and `apt update` right after the change.\n",
      "rollback": "ufw: `ufw default allow outgoing`. nftables: set the output chain policy back to `accept`\nand reload. iptables: `iptables -P OUTPUT ACCEPT`, `ip6tables -P OUTPUT ACCEPT`, then\n`netfilter-persistent save`.\n"
    },
    "references": [
      {
        "title": "ufw(8), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man8/ufw.8.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "nft(8), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man8/nft.8.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "NIST SP 800-41 Rev. 1 - Guidelines on Firewalls and Firewall Policy",
        "url": "https://csrc.nist.gov/pubs/sp/800/41/r1/final",
        "publisher": "NIST",
        "kind": "guide",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "SC-7(5)",
        "relation": "partial"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1105"
      },
      {
        "framework": "mitre-attack",
        "id": "T1041"
      },
      {
        "framework": "cwe",
        "id": "CWE-284"
      }
    ],
    "related_controls": [
      {
        "id": "CB-UBU-NET-005",
        "type": "requires"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic, BIOS boot",
        "notes": "Profile server-hardened; ufw allow outgoing reported, deny outgoing passes with SSH still accepted; nftables output drop and iptables-persistent output policy checks tested."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic, BIOS boot",
        "notes": "Profile server-hardened; ufw allow outgoing reported, deny outgoing passes with SSH still accepted."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic, BIOS boot",
        "notes": "Profile server-hardened; ufw allow outgoing reported, deny outgoing passes with SSH still accepted."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic, BIOS boot",
        "notes": "Profile server-hardened; ufw allow outgoing reported, deny outgoing passes with SSH still accepted; nftables output drop and iptables-persistent output policy checks tested."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}