# yaml-language-server: $schema=../../../../schema/control.schema.json 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 the connection to the mirror; `http` and `ftp` do not. Entries using `mirror+file:` point to a local list of mirrors, whose URIs are checked too. rationale: | APT verifies the signature of the Ubuntu archive whatever the transport, so HTTP does not let an attacker change packages. It still exposes the list of installed packages and their versions, which tells an attacker which vulnerabilities are left, and lets a network attacker serve stale but validly signed metadata until it expires, or block updates silently. HTTPS removes these exposures and is required for repositories that are not signed by their provider. impact: | The system needs the `ca-certificates` package and outbound HTTPS. Caching proxies such as apt-cacher-ng cannot cache HTTPS traffic they do not terminate: list their HTTP URIs in the `http_allowed_uris` parameter rather than weaken the other controls. notes: | Every Ubuntu release, cloud images included, ships `http://archive.ubuntu.com` and `http://security.ubuntu.com` (or `http://.archive.ubuntu.com` after a desktop installation): the control fails by default. `archive.ubuntu.com`, `security.ubuntu.com` and `ports.ubuntu.com` serve HTTPS; the automated fix switches them. Country mirrors (`.archive.ubuntu.com`) are run by third parties and may not offer HTTPS with a valid certificate: they are left to a human. The Ubuntu Pro repositories (`esm.ubuntu.com`) already use HTTPS. 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 the files named after `mirror+file:` if any. Every URI must use `https` or a local method, or start with one of the allowed prefixes. checks: - id: uris engine: shell description: Transport of every URI of the enabled APT source entries, including mirror lists. spec: script: | files= for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do [ -f "$f" ] && files="$files $f" done [ -n "$files" ] || { echo "no APT source file"; exit 2; } # One line per entry: file:line, types, URIs, suites, ;option=value list (lowercase). # shellcheck disable=SC2086 entries=$(awk ' function flush() { if (st && en != "no") print src "\t" ty "\t" ur "\t" su "\t" op; st = 0; ty = ur = su = op = en = "" } FNR == 1 { flush(); d = FILENAME ~ /\.sources$/ } !d { sub(/#.*/, ""); if ($1 !~ /^deb(-src)?$/) next l = $0; o = ""; i = index(l, "["); j = index(l, "]") if (i && j > i) { o = substr(l, i + 1, j - i - 1); l = substr(l, 1, i - 1) " " substr(l, j + 1) } split(l, w, " "); gsub(/[ \t]+/, ";", o) print FILENAME ":" FNR "\t" w[1] "\t" w[2] "\t" w[3] "\t;" tolower(o); next } /^#/ { next } /^[ \t]*$/ { flush(); next } /^[ \t]/ { next } { if (!st) { st = 1; src = FILENAME ":" FNR } k = $0; sub(/:.*/, "", k); k = tolower(k); v = $0; sub(/^[^:]*:[ \t]*/, "", v) if (k == "types") ty = v; else if (k == "uris") ur = v; else if (k == "suites") su = v else if (k == "enabled") en = tolower(v) else op = op ";" k "=" tolower(v == "" ? "inline" : v) } END { flush() }' $files) bad=$(printf '%s\n' "$entries" | while IFS="$(printf '\t')" read -r src _ uris _ _; do for u in $uris; do case $u in mirror+file:*) p=$(printf '%s' "$u" | sed 's#^mirror+file:/*#/#') [ -f "$p" ] || { echo "$src: $u (missing mirror list)"; continue; } list=$(grep -v '^[[:space:]]*#' "$p" 2>/dev/null | awk '{ print $1 }') ;; *) list=$u ;; esac for m in $list; do case $m in https://* | tor+https://* | mirror+https://* | file:* | cdrom:* | copy:*) continue ;; esac for a in ${CB_HTTP_ALLOWED_URIS:-}; do case $m in "$a"*) continue 2 ;; esac done echo "$src: $m" done done done) n=$(printf '%s\n' "$entries" | grep -c .) if [ -n "$bad" ]; then printf '%s\n' "$bad" exit 1 fi echo "$n entries, all over https or a local method" remediation: manual: | 1. Install `ca-certificates`. 2. In each source entry, and in the mirror lists named after `mirror+file:`, replace `http://` with `https://` when the mirror serves HTTPS (`archive.ubuntu.com`, `security.ubuntu.com` and `ports.ubuntu.com` do). Pick another mirror otherwise. 3. Run `apt-get update` and check that it prints no error. effects: restart: none disruption: low lockout_risk: false rollback: | Restore the source files from the backup directory printed by `remediate.sh`, or replace `https://` with `http://` again. 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 - framework: cis-ubuntu-2604 framework_version: "1.0.0" id: "1.2.1.10" relation: equivalent - framework: cis-ubuntu-2604 framework_version: "1.0.0" id: "1.2.1.11" relation: equivalent 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