Skip to content

Security

Anagnostakis Ioannis edited this page Jul 1, 2026 · 7 revisions

Security

slacker is fail-closed: a bad signature or a failed integrity check stops the operation, it never warns-and-continues. Verification, key pinning, and a quarantine model work together.


Verification at a glance

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#161b22','primaryBorderColor':'#1f6feb','primaryTextColor':'#e6edf3','lineColor':'#6e7781','fontFamily':'monospace'}}}%%
flowchart TB
  U["slacker update"]:::blue --> G{"CHECKSUMS.md5<br/>vs its .asc signature"}
  G -->|good| OK["metadata accepted"]:::green
  G -->|bad / key changed| DROP["metadata discarded,<br/>repo warned, others continue"]:::grey
  I["install a package"]:::blue --> V{"per-package check<br/>(policy = VERIFY)"}
  V -->|".txz.asc GPG"| VG["verified: gpg"]:::green
  V -->|"md5 / sha"| VM["integrity verified"]:::green
  V -->|"nothing available"| STOP["install stops"]:::grey
  classDef grey fill:#0d1117,stroke:#6e7781,color:#8b949e;
  classDef blue fill:#0d1117,stroke:#1f6feb,color:#e6edf3;
  classDef green fill:#0d1117,stroke:#2ea043,color:#e6edf3;
Loading

On update

Once a repo's key is imported, slacker verifies its CHECKSUMS.md5 against the detached signature on every update. If that signature is bad or the key is missing, the repo is not aborted wholesale: slacker warns, discards that repo's freshly-fetched metadata so it is never used unverified, and continues with the others - then tells you how to proceed if you trust the repo anyway.

On install

Each package is checked according to the repo's policy. Slackware ships a per-package .txz.asc, so under the default all policy slacker also GPG-verifies the package itself when a signature is present (falling back to the md5 from the signature-verified CHECKSUMS otherwise), and prints which checks passed (e.g. verified: gpg (signer) + md5). At least one of gpg/md5/sha must pass; if none is available, the install stops.


The VERIFY policy

Set globally in slacker.conf and overridable per repo with a verify= flag in repos.

Policy Behaviour
all (default) GPG verified when present (a bad signature always fails); at least one integrity checksum (md5 or sha) must match. If neither is available, install stops.
gpg,md5,sha (a required list) Requires every listed method; if one is absent slacker stops and explains how to relax it.
none Disables verification - slacker prints a warning after update and in check-updates naming that repo. Applies to file:// repos too.

The official repo gets no exemption - official only affects install-new scope and ChangeLog tracking, never verification.


Key pinning (trust on first use)

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#161b22','primaryBorderColor':'#1f6feb','primaryTextColor':'#e6edf3','lineColor':'#6e7781','fontFamily':'monospace'}}}%%
flowchart LR
  F["first GPG-KEY import"]:::green --> PIN["fingerprint pinned"]:::blue
  PIN --> L{"later import:<br/>same fingerprint?"}
  L -->|yes| OK["accepted"]:::green
  L -->|no| REJ["refused -<br/>possible key substitution"]:::grey
  classDef grey fill:#0d1117,stroke:#6e7781,color:#8b949e;
  classDef blue fill:#0d1117,stroke:#1f6feb,color:#e6edf3;
  classDef green fill:#0d1117,stroke:#2ea043,color:#e6edf3;
Loading

The first time a repo's GPG-KEY is imported, slacker pins its fingerprint. On every later import the fetched key must match the pinned one; a changed or substituted key is refused as a possible attack rather than trusted silently.

For a subtree repo the key is fetched from the parent/root URL, where Slackware keeps the one key that signs the whole tree - so extra/testing/ patches pin the same fingerprint as the official repo.

Run slacker update gpg once before trusting a mirror.


Release matching

A GPG or checksum signature authenticates who built a package, not which Slackware release it targets. So a repo left pointing at the wrong release - a classic example is an alienbob -current repo still active on a 15.0 system - can verify perfectly yet install packages that break the system. Before any install or upgrade, slacker refuses a package whose source repo targets a different release than the running system (read from /etc/os-release), naming the offenders; status reports the same mismatch on its Repo release row. Pass --yes to override when you are sure. A distribution upgrade (upgrade-dist) changes the release on purpose and is exempt - it is the supported way to cross that boundary. See Distribution Upgrade.


Quarantine and trust

A repository that fails vetting - unreachable, or serving metadata that looks malformed or hostile - is put into quarantine and provides no packages until you act.

Command What it does
vet-repo NAME Re-run the safety vetting on demand (quarantine on fail, clear on pass).
trust-repo NAME Lift a quarantine you judge a false positive (overriding the verdict, at your responsibility).
distrust-repo NAME Manually quarantine (freeze) a repo yourself.

A freshly added or as-yet-untrusted repo is light-vetted on the next update; add-repo and vet-repo run a thorough vet. Quarantine state is shown by list-repos and status.


Repository credentials

Public Slackware mirrors need no login, and slacker fetches them anonymously. For a repository that does require one - a private Forgejo/Gitea repo, an authenticated mirror - slacker sends HTTP Basic credentials, modelled on zypper: the secret lives in a file you control, never in the repos file.

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#161b22','primaryBorderColor':'#1f6feb','primaryTextColor':'#e6edf3','lineColor':'#6e7781','fontFamily':'monospace'}}}%%
flowchart LR
  R["repo needs a login"]:::blue --> F{"credentials= NAME<br/>flag on the repo?"}
  F -->|yes| N["credentials.d/NAME"]:::green
  F -->|no| C{"longest matching prefix<br/>in credentials.cat?"}
  C -->|match| CAT["that section's login"]:::green
  C -->|none| A["anonymous"]:::grey
  classDef grey fill:#0d1117,stroke:#6e7781,color:#8b949e;
  classDef blue fill:#0d1117,stroke:#1f6feb,color:#e6edf3;
  classDef green fill:#0d1117,stroke:#2ea043,color:#e6edf3;
Loading

Two ways to supply a login, both under /etc/slacker:

  • Named set - a file credentials.d/NAME holding one username/password pair, referenced from a repo line with the credentials=NAME flag (zypper's ?credentials=). The flag carries only the name, never the secret:

    80  myforge  https://forge.slackware.nl/rizitis/myrepo  credentials=forge
    
  • Global catalogue - credentials.cat with one [url-prefix] section per login; any repo whose URL begins with a prefix uses that section, no flag needed.

Lookup per repo: the credentials=NAME flag first, else the longest matching catalogue prefix, else anonymous.

Handling of the secret

Guarantee How
Never in the repo list the repos line holds only the name (or nothing - the catalogue matches by URL)
Never in a URL the user:pass@host form is unsupported on purpose - it leaks into logs and the process list
Never logged or printed on a failed fetch the URL is shown without credentials
In transit only sent solely as an Authorization: Basic header, to the matching repo

Permissions - enforced, not hoped

A credential file is honoured only when it is a regular file owned by root with no group or other permission bits (chmod 600, chown root:root). Anything looser is refused with a warning and the repo is fetched anonymously - a mis-permissioned secret never silently leaks, exactly as ssh refuses a world-readable private key. Keep the directory itself 0700 so the file names stay invisible to non-root users.

This is the same model apt (/etc/apt/auth.conf.d) and zypper (/etc/zypp/credentials.d) use: plain files under /etc, protected by permissions, not encrypted in place. Encryption at rest would only re-lock what root can already read; offline protection is the job of full-disk encryption (LUKS), not the package manager.

Transport - https only

Credentials are sent over https only, so the login is never exposed on the wire. A repository whose URL is plaintext http does not receive them, and add-repo refuses to add a credentials= repo over http. This matches apt, which by default matches credentials to https only.

The escape hatch is the per-repo insecure flag - an explicit, at-your-own-risk override (like trust-repo for a quarantine, or verify=none for verification):

55  local  http://10.0.0.9/repo  credentials=lab  insecure

With insecure, credentials are sent over http for that repo (with a warning), and vetting no longer freezes it for its plaintext transport - the user opted into that risk. GPG/checksum verification still applies. Without insecure, a hand-written http credentials= line simply has its credentials withheld. Either state is surfaced by status (the Credentials row) and by list-repos (which shows the (credentials=NAME) and (insecure) flags).


Confirm it all works: status

Verification, key pinning, and quarantine are only worth their current state on the box. slacker status is how you check that the machinery on this page is actually intact and active, not merely configured. The security-relevant rows it prints (see Commands for the full report):

Row What it proves
Verify every repo verifies packages, or it names the ones where verify=none has switched it off
Transport every repo uses https/file, or it names a plaintext-http repo a MITM could tamper with
Credentials flags any repo carrying credentials= over plaintext http - red when they are withheld (no insecure), yellow when insecure forces them onto the wire at your own risk
Repo release every repo targets the running system's Slackware release, or it names a repo built for a different one - a mismatch normal installs refuse (a signature proves who built a package, not which release it targets)
Repo trust which repos are frozen - hard (failed vetting, needs trust-repo) or soft (unreachable, auto-retried) - each with its reason
GPG keys the keyring is actually exercised: every repo's cached CHECKSUMS.md5 signature is re-verified, reporting a BAD signature (tampering), no imported key verifies, or how many repos verify (+N via md5). Root only.

A repo verifies under any pinned key, so extra/testing/patches show as verified off the single official key they share - exactly as pinning intends.

The integrity audit - guarding the trust state itself

The pinned fingerprints and the trusted/quarantine markers are files under STATE_DIR (default /var/lib/slacker), kept separate from the disposable CACHE_DIR so that clearing the cache can never destroy the trust anchors. Anyone who could rewrite them would defeat key pinning and the quarantine model before a single signature was checked. So status audits slacker's own config + cache + state, not just the repos:

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#161b22','primaryBorderColor':'#1f6feb','primaryTextColor':'#e6edf3','lineColor':'#6e7781','fontFamily':'monospace'}}}%%
flowchart TB
  C["STATE_DIR holds the trust state:<br/>pinned fingerprints + trusted/quarantine markers"]:::blue --> T{"tampered?<br/>world-writable / stray symlink"}
  T -->|"yes, undetected"| BAD["pinning &amp; quarantine defeated<br/>before any check runs"]:::grey
  T -->|"status audit"| DET["lstat walk, never follows a link -<br/>flagged with the exact chown/chmod fix"]:::green
  classDef grey fill:#0d1117,stroke:#6e7781,color:#8b949e;
  classDef blue fill:#0d1117,stroke:#1f6feb,color:#e6edf3;
  classDef green fill:#0d1117,stroke:#2ea043,color:#e6edf3;
Loading
✗ Symlinks    1 unexpected symlink(s) - slacker plants none here; investigate
              before trusting the state: /var/lib/slacker/gpg/slackware.fpr -> /tmp/x.fpr
✗ Writable    1 world-writable path(s) - anyone can tamper (e.g. /var/lib/slacker/gpg);
              fix: sudo chmod -R go-w /etc/slacker /var/cache/slacker /var/lib/slacker

It flags world-writable, group-writable, and not-root-owned paths, and any unexpected symlink (slacker creates none under these paths). The walk uses lstat and never follows a symlink, so a planted link cannot lure the audit outside the tree - only the roots themselves are followed. Every finding carries the exact chown/chmod to fix it.

Why it matters

status turns "configured to be secure" into "verified secure, right now." It is the one command that proves the fail-closed checks are live, surfaces a bad signature as an explicit advisory ("do NOT install from it until resolved"), and protects the integrity of the very state - pinned keys, trust markers - that everything above depends on.

Run slacker status after every update, and sudo slacker status for the full GPG verification and integrity audit (the keyring and markers are root-only to read).

Clone this wiki locally