Ubuntu · Packages & updates

Download packages over HTTPS

When it fails: APT downloads from repositories over an unencrypted protocol. Anyone on the network path sees which packages and versions the system installs, and can delay or block its updates.

CB-UBU-PKG-002lowactiveautomatedautomated fixprofile core

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.

Every Ubuntu release, cloud images included, ships http://archive.ubuntu.com and http://security.ubuntu.com (or http://<country>.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 (<country>.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.

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.

Setting

Location/etc/apt/sources.list, /etc/apt/sources.list.d/*.list, /etc/apt/sources.list.d/*.sources
Defaulthttp (archive.ubuntu.com, security.ubuntu.com)
Recommendedhttps (or a local method such as file:, cdrom:, copy:) for every URI, except those starting with: {{ http_allowed_uris }}

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
http_allowed_urislist. URI prefixes allowed over plain HTTP, for example a local apt-cacher-ng proxy (http://apt-cache.example.org:3142/). Empty means none.

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

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.

Automated: every check must pass. Scanners report each check by its id.

uris

Transport of every URI of the enabled APT source entries, including mirror lists.

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

  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.
Restartnone
Disruptionlow

Rollback

Restore the source files from the backup directory printed by remediate.sh, or replace https:// with http:// again.

https-ubuntu

#!/bin/sh
# CB-UBU-PKG-002 / https-ubuntu - switch the Ubuntu archive entries from HTTP to HTTPS.
# Only archive.ubuntu.com, security.ubuntu.com and ports.ubuntu.com are changed: Canonical serves
# them over HTTPS. Country mirrors and other hosts need a human. Hosts listed in
# CB_HTTP_ALLOWED_URIS are left alone. Idempotent, no-op when compliant.
# SPDX-License-Identifier: Apache-2.0
set -eu

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" ] || exit 0
# Mirror lists referenced as mirror+file: hold the real URIs.
# shellcheck disable=SC2086
for p in $(grep -hoE 'mirror\+file:/*[^[:space:]]+' $files | sed -E 's#^mirror\+file:/*#/#' | sort -u); do
  [ -f "$p" ] && files="$files $p"
done

for host in archive security ports; do
  for a in ${CB_HTTP_ALLOWED_URIS:-}; do
    case $a in *"://$host.ubuntu.com"*) continue 2 ;; esac
  done
  for f in $files; do
    grep -Eq "(^|[[:space:]+])http://$host\.ubuntu\.com/" "$f" || continue
    if [ ! -s /etc/ssl/certs/ca-certificates.crt ]; then
      echo "ca-certificates is not installed, APT could not check the HTTPS servers" >&2
      exit 1
    fi
    cb_backup "$f"
    sed -E -i "s#(^|[[:space:]+])http://$host\.ubuntu\.com/#\1https://$host.ubuntu.com/#g" "$f"
  done
done

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelation
NIST SP 800-53 Rev. 55.1.1SC-8related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. 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.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. 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.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. 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.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. 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 inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr