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.
The Debian 12 and 13 cloud images already use https://deb.debian.org. A system installed with
the Debian installer uses http://deb.debian.org; the automated fix switches
deb.debian.org and security.debian.org to HTTPS. Other mirrors may not offer HTTPS and are
left to a human.
Rationale
APT verifies the signature of the Debian 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 |
| Default | https on cloud images, http after a Debian installer setup |
| Recommended | https (or a local method such as file:, cdrom:, copy:) for every URI, except those starting with: {{ http_allowed_uris }} |
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
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
- Install
ca-certificates. - In each source entry, and in the mirror lists named after
mirror+file:, replacehttp://withhttps://when the mirror serves HTTPS (deb.debian.organdsecurity.debian.orgdo). Pick another mirror otherwise. - Run
apt-get updateand check that it prints no error.
| Restart | none |
| Disruption | low |
Rollback
Restore the source files from the backup directory printed by remediate.sh, or replace
https:// with http:// again.
https-debian
#!/bin/sh
# CB-DEB-PKG-002 / https-debian - switch the Debian archive entries from HTTP to HTTPS.
# Only deb.debian.org and security.debian.org are changed: they serve HTTPS. Other mirrors 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 deb security; do
for a in ${CB_HTTP_ALLOWED_URIS:-}; do
case $a in *"://$host.debian.org"*) continue 2 ;; esac
done
for f in $files; do
grep -Eq "(^|[[:space:]+])http://$host\.debian\.org/" "$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\.debian\.org/#\1https://$host.debian.org/#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
- sources.list(5), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- apt-transport-https(1), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- Debian - deb.debian.org Debian, vendor-doc, accessed 2026-10-18
Mappings
| Framework | ID | Relation |
|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | SC-8 | related |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
Related controls
- related
CB-DEB-PKG-001Bind every APT repository to its signing key
People and lifecycle
| Authors |
|
| Verified | 2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Passes on stock cloud images (https mirror lists); http entries in sources.list, .sources and mirror+file lists detected; fix switches deb/security.debian.org to https, apt-get update works without warning; http_allowed_uris honoured by check and fix; kept after reboot. |
| Verified | 2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Passes on stock cloud images (https mirror lists); http entries in sources.list, .sources and mirror+file lists detected; fix switches deb/security.debian.org to https, apt-get update works without warning; http_allowed_uris honoured by check and fix; kept after reboot. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |