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
|
# Newer apt verifies with sqv (Sequoia), which rejects the signature Forgejo
|
||||||
# currently produces for its Debian registry (malformed Ed25519 MPI encoding
|
# currently produces for its Debian registry (malformed Ed25519 MPI encoding
|
||||||
# in the upstream signing library). Try the properly signed source first so
|
# in the upstream signing library). Try the properly signed source first so
|
||||||
# this heals automatically once the forge is fixed; otherwise fall back to
|
# this heals automatically once the forge is fixed. Only that signature-error
|
||||||
# [trusted=yes] — package integrity then relies on HTTPS to our own forge.
|
# class permits the compatibility fallback; auth, network, and other failures
|
||||||
if ! update_only_source "$LIST"; then
|
# 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
|
||||||
echo "WARNING: signature verification failed (known Forgejo registry issue" >&2
|
echo "WARNING: signature verification failed (known Forgejo registry issue" >&2
|
||||||
echo "with sqv-based apt). Falling back to [trusted=yes]; transport" >&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`
|
// candAfterUpdate Candidate after any `apt-get update`
|
||||||
// candAfterNodesource Candidate after an update once nodesource.list exists
|
// candAfterNodesource Candidate after an update once nodesource.list exists
|
||||||
// releaseStatus HTTP status curl reports for the registry Release file
|
// 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,
|
// 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.
|
// so every scenario doubles as a regression test for locale-safe parsing.
|
||||||
const cleanups = [];
|
const cleanups = [];
|
||||||
process.on('exit', () => { for (const dir of cleanups) fs.rmSync(dir, { recursive: true, force: true }); });
|
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-'));
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-apt-test-'));
|
||||||
cleanups.push(root);
|
cleanups.push(root);
|
||||||
const bin = path.join(root, 'bin');
|
const bin = path.join(root, 'bin');
|
||||||
|
|
@ -58,8 +59,16 @@ function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexi
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
stub('apt-get', [
|
stub('apt-get', [
|
||||||
'echo "apt-get $*" >> "$STATE_DIR/apt-get.log"',
|
'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',
|
'for a in "$@"; do',
|
||||||
' if [ "$a" = update ]; then',
|
' 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',
|
' if [ -e "$STOKE_APT_ETC/sources.list.d/nodesource.list" ] && [ -n "${CAND_AFTER_NODESOURCE:-}" ]; then',
|
||||||
' echo "$CAND_AFTER_NODESOURCE" > "$STATE_DIR/candidate"',
|
' echo "$CAND_AFTER_NODESOURCE" > "$STATE_DIR/candidate"',
|
||||||
' elif [ -n "${CAND_AFTER_UPDATE:-}" ]; then',
|
' elif [ -n "${CAND_AFTER_UPDATE:-}" ]; then',
|
||||||
|
|
@ -82,6 +91,7 @@ function runScenario({ candInitial, candAfterUpdate, candAfterNodesource, preexi
|
||||||
CAND_AFTER_UPDATE: candAfterUpdate || '',
|
CAND_AFTER_UPDATE: candAfterUpdate || '',
|
||||||
CAND_AFTER_NODESOURCE: candAfterNodesource || '',
|
CAND_AFTER_NODESOURCE: candAfterNodesource || '',
|
||||||
RELEASE_STATUS: releaseStatus || '',
|
RELEASE_STATUS: releaseStatus || '',
|
||||||
|
SOURCE_UPDATE_ERROR: sourceUpdateError || '',
|
||||||
LC_ALL: 'es_ES.UTF-8', // localized environment; the script must force C
|
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')),
|
nodesourceKey: read(path.join(aptEtc, 'keyrings', 'nodesource.asc')),
|
||||||
nodesourceKeyMode: mode(path.join(aptEtc, 'keyrings', 'nodesource.asc')),
|
nodesourceKeyMode: mode(path.join(aptEtc, 'keyrings', 'nodesource.asc')),
|
||||||
forgeKeyMode: mode(path.join(aptEtc, 'keyrings', 'forgejo-heavy-duty.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')) || '',
|
aptGetLog: read(path.join(state, 'apt-get.log')) || '',
|
||||||
};
|
};
|
||||||
// Drop the throwaway tree after we have read everything we need.
|
// 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.equal(s.res.status, 0, s.res.stderr);
|
||||||
assert.match(s.aptGetLog, /install -y stoke/);
|
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