fix: keep apt signature verification on transient failures
This commit is contained in:
parent
c09943ea32
commit
acb46d0707
2 changed files with 48 additions and 4 deletions
|
|
@ -104,9 +104,19 @@ fi
|
|||
# Newer apt verifies with sqv (Sequoia), which rejects the signature Forgejo
|
||||
# currently produces for its Debian registry (malformed Ed25519 MPI encoding
|
||||
# in the upstream signing library). Try the properly signed source first so
|
||||
# this heals automatically once the forge is fixed; otherwise fall back to
|
||||
# [trusted=yes] — package integrity then relies on HTTPS to our own forge.
|
||||
if ! update_only_source "$LIST"; then
|
||||
# this heals automatically once the forge is fixed. Only that signature-error
|
||||
# class permits the compatibility fallback; auth, network, and other failures
|
||||
# must leave verification enabled and retain apt's original diagnostic.
|
||||
if update_output="$(update_only_source "$LIST" 2>&1)"; then
|
||||
printf '%s\n' "$update_output"
|
||||
else
|
||||
update_status=$?
|
||||
if ! grep -Eiq \
|
||||
'NO_PUBKEY|EXPKEYSIG|BADSIG|signatures? (could not|couldn.t) be verified|signature (verification )?(failed|failure|error|invalid)|repository .*not signed|is not signed' \
|
||||
<<<"$update_output"; then
|
||||
printf '%s\n' "$update_output" >&2
|
||||
exit "$update_status"
|
||||
fi
|
||||
echo
|
||||
echo "WARNING: signature verification failed (known Forgejo registry issue" >&2
|
||||
echo "with sqv-based apt). Falling back to [trusted=yes]; transport" >&2
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ const SCRIPT = path.join(__dirname, '..', 'scripts', 'install-apt.sh');
|
|||
// candAfterUpdate Candidate after any `apt-get update`
|
||||
// candAfterNodesource Candidate after an update once nodesource.list exists
|
||||
// releaseStatus HTTP status curl reports for the registry Release file
|
||||
// sourceUpdateError stderr and exit 100 for the first signed stoke update
|
||||
// The apt-cache stub localizes the "Candidate:" label unless LC_ALL=C is set,
|
||||
// so every scenario doubles as a regression test for locale-safe parsing.
|
||||
const cleanups = [];
|
||||
process.on('exit', () => { for (const dir of cleanups) fs.rmSync(dir, { recursive: true, force: true }); });
|
||||
|
||||
function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexistingNodesourceList, releaseStatus }) {
|
||||
function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexistingNodesourceList, releaseStatus, sourceUpdateError }) {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-apt-test-'));
|
||||
cleanups.push(root);
|
||||
const bin = path.join(root, 'bin');
|
||||
|
|
@ -58,8 +59,16 @@ function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexi
|
|||
].join('\n'));
|
||||
stub('apt-get', [
|
||||
'echo "apt-get $*" >> "$STATE_DIR/apt-get.log"',
|
||||
'source_list=""',
|
||||
'for a in "$@"; do',
|
||||
' case "$a" in Dir::Etc::sourcelist=*) source_list="${a#*=}";; esac',
|
||||
'done',
|
||||
'for a in "$@"; do',
|
||||
' if [ "$a" = update ]; then',
|
||||
' if [ -n "$source_list" ] && grep -q "signed-by=" "$source_list" && [ -n "${SOURCE_UPDATE_ERROR:-}" ]; then',
|
||||
' printf "%s\\n" "$SOURCE_UPDATE_ERROR" >&2',
|
||||
' exit 100',
|
||||
' fi',
|
||||
' if [ -e "$STOKE_APT_ETC/sources.list.d/nodesource.list" ] && [ -n "${CAND_AFTER_NODESOURCE:-}" ]; then',
|
||||
' echo "$CAND_AFTER_NODESOURCE" > "$STATE_DIR/candidate"',
|
||||
' elif [ -n "${CAND_AFTER_UPDATE:-}" ]; then',
|
||||
|
|
@ -82,6 +91,7 @@ function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexi
|
|||
CAND_AFTER_UPDATE: candAfterUpdate || '',
|
||||
CAND_AFTER_NODESOURCE: candAfterNodesource || '',
|
||||
RELEASE_STATUS: releaseStatus || '',
|
||||
SOURCE_UPDATE_ERROR: sourceUpdateError || '',
|
||||
LC_ALL: 'es_ES.UTF-8', // localized environment; the script must force C
|
||||
},
|
||||
});
|
||||
|
|
@ -96,6 +106,7 @@ function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexi
|
|||
nodesourceKey: read(path.join(aptEtc, 'keyrings', 'nodesource.asc')),
|
||||
nodesourceKeyMode: mode(path.join(aptEtc, 'keyrings', 'nodesource.asc')),
|
||||
forgeKeyMode: mode(path.join(aptEtc, 'keyrings', 'forgejo-heavy-duty.asc')),
|
||||
forgeList: read(path.join(aptEtc, 'sources.list.d', 'forgejo-heavy-duty.list')),
|
||||
aptGetLog: read(path.join(state, 'apt-get.log')) || '',
|
||||
};
|
||||
// Drop the throwaway tree after we have read everything we need.
|
||||
|
|
@ -171,3 +182,26 @@ test('registry Release file present: proceeds with the install', () => {
|
|||
assert.equal(s.res.status, 0, s.res.stderr);
|
||||
assert.match(s.aptGetLog, /install -y stoke/);
|
||||
});
|
||||
|
||||
test('signature verification failure alone may use the trusted compatibility fallback', () => {
|
||||
const s = runScenario({
|
||||
candInitial: '22.23.1-1nodesource1',
|
||||
sourceUpdateError: 'W: GPG error: signatures could not be verified: NO_PUBKEY DEADBEEF\nE: The repository is not signed.',
|
||||
});
|
||||
assert.equal(s.res.status, 0, s.res.stderr);
|
||||
assert.match(s.forgeList, /\[trusted=yes\]/);
|
||||
assert.match(s.aptGetLog, /install -y stoke/);
|
||||
});
|
||||
|
||||
test('network update failure stays fatal and never disables signature verification', () => {
|
||||
const failure = 'Temporary failure resolving forgejo.heavyduty.builders';
|
||||
const s = runScenario({
|
||||
candInitial: '22.23.1-1nodesource1',
|
||||
sourceUpdateError: failure,
|
||||
});
|
||||
assert.notEqual(s.res.status, 0);
|
||||
assert.match(s.res.stderr, new RegExp(failure));
|
||||
assert.match(s.forgeList, /\[signed-by=/);
|
||||
assert.doesNotMatch(s.forgeList, /trusted=yes/);
|
||||
assert.doesNotMatch(s.aptGetLog, /install -y stoke/);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue