{
  "id": "CB-UBU-PKG-002",
  "standard": "ubuntu",
  "version": "0.1.2",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-PKG-002",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-PKG-002.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.2/CB-UBU-PKG-002.yml",
  "profiles_effective": [
    "core",
    "server",
    "server-hardened",
    "desktop",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-PKG-002",
    "title": "Download packages over HTTPS",
    "finding": {
      "title": "APT downloads from repositories over an unencrypted protocol",
      "description": "Anyone on the network path sees which packages and versions the system installs, and can delay or block its updates."
    },
    "status": "active",
    "profiles": [
      "core"
    ],
    "risk": {
      "severity": "low"
    },
    "assessment": "automated",
    "tags": [
      "apt",
      "repositories",
      "https"
    ],
    "description": "Each APT source entry names one or more URIs. The `https` method encrypts and authenticates\nthe connection to the mirror; `http` and `ftp` do not. Entries using `mirror+file:` point to a\nlocal list of mirrors, whose URIs are checked too.\n",
    "rationale": "APT verifies the signature of the Ubuntu archive whatever the transport, so HTTP does not let an\nattacker change packages. It still exposes the list of installed packages and their versions,\nwhich tells an attacker which vulnerabilities are left, and lets a network attacker serve stale\nbut validly signed metadata until it expires, or block updates silently. HTTPS removes these\nexposures and is required for repositories that are not signed by their provider.\n",
    "impact": "The system needs the `ca-certificates` package and outbound HTTPS. Caching proxies such as\napt-cacher-ng cannot cache HTTPS traffic they do not terminate: list their HTTP URIs in the\n`http_allowed_uris` parameter rather than weaken the other controls.\n",
    "notes": "Every Ubuntu release, cloud images included, ships `http://archive.ubuntu.com` and\n`http://security.ubuntu.com` (or `http://<country>.archive.ubuntu.com` after a desktop\ninstallation): the control fails by default. `archive.ubuntu.com`, `security.ubuntu.com` and\n`ports.ubuntu.com` serve HTTPS; the automated fix switches them. Country mirrors\n(`<country>.archive.ubuntu.com`) are run by third parties and may not offer HTTPS with a valid\ncertificate: they are left to a human. The Ubuntu Pro repositories (`esm.ubuntu.com`) already\nuse HTTPS.\n",
    "setting": {
      "location": "/etc/apt/sources.list, /etc/apt/sources.list.d/*.list, /etc/apt/sources.list.d/*.sources",
      "default_value": "http (archive.ubuntu.com, security.ubuntu.com)",
      "recommended_value": "https (or a local method such as file:, cdrom:, copy:) for every URI, except those starting with: {{ http_allowed_uris }}"
    },
    "parameters": [
      {
        "name": "http_allowed_uris",
        "type": "list",
        "description": "URI prefixes allowed over plain HTTP, for example a local apt-cacher-ng proxy (http://apt-cache.example.org:3142/). Empty means none.",
        "default": []
      }
    ],
    "audit": {
      "manual": "Run `grep -rhiE '^[^#]*(http|ftp)://' /etc/apt/sources.list /etc/apt/sources.list.d/`, and read\nthe files named after `mirror+file:` if any. Every URI must use `https` or a local method, or\nstart with one of the allowed prefixes.\n",
      "checks": [
        {
          "id": "uris",
          "engine": "shell",
          "description": "Transport of every URI of the enabled APT source entries, including mirror lists.",
          "spec": {
            "script": "files=\nfor f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do\n  [ -f \"$f\" ] && files=\"$files $f\"\ndone\n[ -n \"$files\" ] || { echo \"no APT source file\"; exit 2; }\n# One line per entry: file:line, types, URIs, suites, ;option=value list (lowercase).\n# shellcheck disable=SC2086\nentries=$(awk '\n  function flush() { if (st && en != \"no\") print src \"\\t\" ty \"\\t\" ur \"\\t\" su \"\\t\" op; st = 0; ty = ur = su = op = en = \"\" }\n  FNR == 1 { flush(); d = FILENAME ~ /\\.sources$/ }\n  !d {\n    sub(/#.*/, \"\"); if ($1 !~ /^deb(-src)?$/) next\n    l = $0; o = \"\"; i = index(l, \"[\"); j = index(l, \"]\")\n    if (i && j > i) { o = substr(l, i + 1, j - i - 1); l = substr(l, 1, i - 1) \" \" substr(l, j + 1) }\n    split(l, w, \" \"); gsub(/[ \\t]+/, \";\", o)\n    print FILENAME \":\" FNR \"\\t\" w[1] \"\\t\" w[2] \"\\t\" w[3] \"\\t;\" tolower(o); next\n  }\n  /^#/ { next }\n  /^[ \\t]*$/ { flush(); next }\n  /^[ \\t]/ { next }\n  {\n    if (!st) { st = 1; src = FILENAME \":\" FNR }\n    k = $0; sub(/:.*/, \"\", k); k = tolower(k); v = $0; sub(/^[^:]*:[ \\t]*/, \"\", v)\n    if (k == \"types\") ty = v; else if (k == \"uris\") ur = v; else if (k == \"suites\") su = v\n    else if (k == \"enabled\") en = tolower(v)\n    else op = op \";\" k \"=\" tolower(v == \"\" ? \"inline\" : v)\n  }\n  END { flush() }' $files)\nbad=$(printf '%s\\n' \"$entries\" | while IFS=\"$(printf '\\t')\" read -r src _ uris _ _; do\n  for u in $uris; do\n    case $u in\n      mirror+file:*)\n        p=$(printf '%s' \"$u\" | sed 's#^mirror+file:/*#/#')\n        [ -f \"$p\" ] || { echo \"$src: $u (missing mirror list)\"; continue; }\n        list=$(grep -v '^[[:space:]]*#' \"$p\" 2>/dev/null | awk '{ print $1 }') ;;\n      *) list=$u ;;\n    esac\n    for m in $list; do\n      case $m in https://* | tor+https://* | mirror+https://* | file:* | cdrom:* | copy:*) continue ;; esac\n      for a in ${CB_HTTP_ALLOWED_URIS:-}; do\n        case $m in \"$a\"*) continue 2 ;; esac\n      done\n      echo \"$src: $m\"\n    done\n  done\ndone)\nn=$(printf '%s\\n' \"$entries\" | grep -c .)\nif [ -n \"$bad\" ]; then\n  printf '%s\\n' \"$bad\"\n  exit 1\nfi\necho \"$n entries, all over https or a local method\"\n"
          }
        }
      ]
    },
    "remediation": {
      "manual": "1. Install `ca-certificates`.\n2. In each source entry, and in the mirror lists named after `mirror+file:`, replace\n   `http://` with `https://` when the mirror serves HTTPS (`archive.ubuntu.com`,\n   `security.ubuntu.com` and `ports.ubuntu.com` do). Pick another mirror otherwise.\n3. Run `apt-get update` and check that it prints no error.\n",
      "effects": {
        "restart": "none",
        "disruption": "low",
        "lockout_risk": false
      },
      "rollback": "Restore the source files from the backup directory printed by `remediate.sh`, or replace\n`https://` with `http://` again.\n",
      "scripts": [
        {
          "id": "https-ubuntu",
          "engine": "shell",
          "file": "fix.sh"
        }
      ]
    },
    "references": [
      {
        "title": "sources.list(5), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man5/sources.list.5.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "apt-transport-https(1), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man1/apt-transport-https.1.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "Ubuntu Server documentation - Package management",
        "url": "https://documentation.ubuntu.com/server/how-to/software/package-management/",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "SC-8",
        "relation": "related"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1557"
      },
      {
        "framework": "cwe",
        "id": "CWE-319"
      }
    ],
    "related_controls": [
      {
        "id": "CB-UBU-PKG-001",
        "type": "related"
      }
    ],
    "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",
        "notes": "Fails on stock (http archive and security); fix switches archive/security.ubuntu.com to https (and a test .list), apt-get update works without warning; idempotent, kept after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic",
        "notes": "Fails on stock (http archive and security); fix switches archive/security.ubuntu.com to https (and a test .list), apt-get update works without warning; idempotent, kept after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic",
        "notes": "Fails on stock (http archive and security); fix switches archive/security.ubuntu.com to https (and a test .list), apt-get update works without warning; idempotent, kept after reboot."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic",
        "notes": "Fails on stock (http archive and security); fix switches archive/security.ubuntu.com to https (and a test .list), apt-get update works without warning; idempotent, kept after reboot."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}