Debian GNU/Linux · Packages & updates

Restrict write access to APT keys and repository lists

When it fails: Non-root users can change the APT keys or repository lists. Whoever can write these files chooses which repositories and signing keys APT trusts, and so which code runs as root.

CB-DEB-PKG-004mediumactiveautomatedautomated fixprofile core

As released in v0.1.1 beta · pre-release. Current state.

Description

APT trusts the keyrings in /etc/apt/trusted.gpg.d/, /etc/apt/keyrings/, /usr/share/keyrings/ (and the legacy /etc/apt/trusted.gpg), and downloads from the repositories listed in /etc/apt/sources.list and /etc/apt/sources.list.d/. These directories should be root:root mode 0755 and their files root:root mode 0644 or stricter.

On Debian 13, the files in /usr/share/keyrings/ named *.gpg are symbolic links to *.pgp files. The check follows links and checks their target.

Rationale

A user able to add a key or a source entry can make APT install a package of their choice, with maintainer scripts running as root, at the next update. Only root may change them. Reading them is harmless: keys are public and APT's unprivileged _apt user needs to read them.

Impact

None known. Debian ships these files with these permissions.

Setting

Location/etc/apt/trusted.gpg.d, /etc/apt/keyrings, /usr/share/keyrings, /etc/apt/sources.list, /etc/apt/sources.list.d
Defaultroot:root, 0755 for directories and 0644 for files
Recommendedroot:root, 0755 or stricter for directories, 0644 or stricter for files

Audit

  1. stat -c '%U:%G %a %n' /etc/apt/trusted.gpg.d /etc/apt/keyrings /usr/share/keyrings /etc/apt/sources.list.d shows root:root and 755 or stricter.
  2. stat -L -c '%U:%G %a %n' /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d/* /etc/apt/keyrings/* /usr/share/keyrings/* /etc/apt/sources.list /etc/apt/sources.list.d/* shows root:root and 644 or stricter for every existing file.

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

trusted-gpg-d

The /etc/apt/trusted.gpg.d directory.

path: /etc/apt/trusted.gpg.d
owner: root
group: root
mode: '0755'

etc-keyrings

The /etc/apt/keyrings directory.

path: /etc/apt/keyrings
owner: root
group: root
mode: '0755'

usr-share-keyrings

The /usr/share/keyrings directory.

path: /usr/share/keyrings
owner: root
group: root
mode: '0755'

key-files

Every keyring file APT can use, following symbolic links.

script: |
  d="/etc/apt/trusted.gpg /etc/apt/trusted.gpg.d /etc/apt/keyrings /usr/share/keyrings"
  # shellcheck disable=SC2086
  bad=$(find -L $d -maxdepth 1 -type f \( ! -user root -o ! -group root -o -perm /7133 \) \
        -exec stat -L -c '%n %U:%G %a' {} + 2>/dev/null)
  if [ -n "$bad" ]; then
    printf '%s\n' "$bad"
    exit 1
  fi
  # shellcheck disable=SC2086
  echo "$(find -L $d -maxdepth 1 -type f 2>/dev/null | wc -l) key files root:root 644 or stricter"

sources-list

The /etc/apt/sources.list file.

path: /etc/apt/sources.list
owner: root
group: root
mode: '0644'

sources-list-d

The /etc/apt/sources.list.d directory.

path: /etc/apt/sources.list.d
owner: root
group: root
mode: '0755'

sources-list-d-files

Files in /etc/apt/sources.list.d.

path: /etc/apt/sources.list.d/*
owner: root
group: root
mode: '0644'

Remediation

  1. chown root:root /etc/apt/trusted.gpg.d /etc/apt/keyrings /usr/share/keyrings /etc/apt/sources.list.d and chmod go-w on them.
  2. For each key file and source file: chown root:root <file> and chmod 644 <file> (or stricter).
Restartnone
Disruptionnone

Rollback

Restore the previous owner and mode with chown and chmod. Nothing else depends on them.

trusted-gpg-d

Generated from check trusted-gpg-d: the engine writes the fix for every output, it sets the expected value and the check runs again after.

etc-keyrings

Generated from check etc-keyrings: the engine writes the fix for every output, it sets the expected value and the check runs again after.

usr-share-keyrings

Generated from check usr-share-keyrings: the engine writes the fix for every output, it sets the expected value and the check runs again after.

key-files

find -L /etc/apt/trusted.gpg /etc/apt/trusted.gpg.d /etc/apt/keyrings /usr/share/keyrings \
  -maxdepth 1 -type f \( ! -user root -o ! -group root -o -perm /7133 \) 2>/dev/null |
  while IFS= read -r f; do
    chown root:root "$f" && chmod u-xs,go-wxs,o-t "$f" || exit 1
  done

sources-list

Generated from check sources-list: the engine writes the fix for every output, it sets the expected value and the check runs again after.

sources-list-d

Generated from check sources-list-d: the engine writes the fix for every output, it sets the expected value and the check runs again after.

sources-list-d-files

Generated from check sources-list-d-files: the engine writes the fix for every output, it sets the expected value and the check runs again after.

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.1CM-5related
NIST SP 800-53 Rev. 55.1.1AC-3related

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

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Passes on stock (Debian 13 keyring symlinks followed); writable keyring, foreign owner and group-writable sources.list.d detected and fixed; kept after reboot.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Passes on stock (Debian 13 keyring symlinks followed); writable keyring, foreign owner and group-writable sources.list.d detected and fixed; kept after reboot.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr