10000 cli: --offline means fully offline (#1143) · sigstore/sigstore-python@af8c575 · GitHub
[go: up one dir, main page]

Skip to content

Commit af8c575

Browse files
authored
cli: --offline means fully offline (#1143)
1 parent 32919e1 commit af8c575

File tree

6 files changed

+77
-10
lines changed

6 files changed

+77
-10
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ All versions prior to 0.9.0 are untracked.
88

99
## [Unreleased]
1010

11+
### Changed
12+
13+
* CLI: When verifying, the `--offline` flag now fully disables all online
14+
operations, including routine local TUF repository refreshes
15+
([#1143](https://github.com/sigstore/sigstore-python/pull/1143))
16+
1117
## [3.3.0]
1218

1319
### Added

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ else!
2929
* [Signing with ambient credentials](#signing-with-ambient-credentials)
3030
* [Signing with an email identity](#signing-with-an-email-identity)
3131
* [Signing with an explicit identity token](#signing-with-an-explicit-identity-token)
32-
* [Verifying against a signature and certificate](#verifying-against-a-signature-and-certificate)
32+
* [Verifying against a bundle](#verifying-against-a-bundle)
33+
* [Offline verification](#offline-verification)
34+
* [Verifying a digest instead of a file](#verifying-a-digest-instead-of-a-file)
3335
* [Verifying signatures from GitHub Actions](#verifying-signatures-from-github-actions)
3436
* [Licensing](#licensing)
3537
* [Community](#community)
@@ -402,7 +404,7 @@ $ python -m sigstore sign --identity-token YOUR-LONG-JWT-HERE foo.txt
402404
Note that passing a custom identity token does not circumvent Fulcio's requirements,
403405
namely the Fulcio's supported identity providers and the claims expected within the token.
404406

405-
### Verifying against a signature and certificate
407+
### Verifying against a bundle
406408

407409
By default, `sigstore verify identity` will attempt to find a `<filename>.sigstore.json`
408410
or `<filename>.sigstore` in the same directory as the file being verified:
@@ -423,6 +425,50 @@ $ python -m sigstore verify identity foo.txt bar.txt \
423425
--cert-oidc-issuer 'https://github.com/login/oauth'
424426
```
425427

428+
### Offline verification
429+
430+
> [!IMPORTANT]
431+
> Because `--offline` disables trust root updates, `sigstore-python` falls back
432+
> to the latest cached trust root or, if none exists, the trust root baked
433+
> into `sigstore-python` itself. Like with any other offline verification,
434+
> this means that users may miss trust root changes (such as new root keys,
435+
> or revocations) unless they separately keep the trust root up-to-date.
436+
>
437+
> Users who need to operationalize offline verification may wish to do this
438+
> by distributing their own trust configuration; see
439+
> [Configuring a custom root of trust](#configuring-a-custom-root-of-trust-byo-pki).
440+
441+
During verification, there are two kinds of network access that `sigstore-python`
442+
*can* perform:
443+
444+
1. When verifying against "detached" materials (e.g. separate `.crt` and `.sig`
445+
files), `sigstore-python` can perform an online transparency log lookup.
446+
2. By default, during all verifications, `sigstore-python` will attempt to
447+
refresh the locally cached root of trust via a TUF update.
448+
449+
When performing bundle verification (i.e. `.sigstore` or `.sigstore.json`),
450+
(1) does not apply. However, (2) can still result in online accesses.
451+
452+
To perform **fully** offline verification, pass `--offline` to your
453+
`sigstore verify` subcommand:
454+
455+
```bash
456+
$ python -m sigstore verify identity foo.txt \
457+
--offline \
458+
--cert-identity 'hamilcar@example.com' \
459+
--cert-oidc-issuer 'https://github.com/login/oauth'
460+
```
461+
462+
Alternatively, users may choose to bypass TUF entirely by passing
463+
an entire trust configuration to `sigstore-python` via `--trust-config`:
464+
465+
```bash
466+
$ python -m sigstore --trust-config public.trustconfig.json verify identity ...
467+
```
468+
469+
This will similarly result in fully offline operation, as the trust
470+
configuration contains a full trust root.
471+
426472
### Verifying a digest instead of a file
427473

428474
`sigstore-python` supports verifying digests directly, without requiring the artifact to be

sigstore/_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,12 +996,12 @@ def _collect_verification_state(
996996

997997
if args.staging:
998998
_logger.debug("verify: staging instances requested")
999-
verifier = Verifier.staging()
999+
verifier = Verifier.staging(offline=args.offline)
10001000
elif args.trust_config:
10011001
trust_config = ClientTrustConfig.from_json(args.trust_config.read_text())
10021002
verifier = Verifier._from_trust_config(trust_config)
10031003
else:
1004-
verifier = Verifier.production()
1004+
verifier = Verifier.production(offline=args.offline)
10051005

10061006
all_materials = []
10071007
for file_or_hashed, materials in input_map.items():

sigstore/_internal/tuf.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ def __init__(self, url: str, offline: bool = False) -> None:
115115
_logger.debug(f"TUF targets cache: {self._targets_dir}")
116116

117117
self._updater: None | Updater = None
118-
if not offline:
118+
if offline:
119+
_logger.warning(
120+
"TUF repository is loaded in offline mode; updates will not be performed"
121+
)
122+
else:
119123
# Initialize and update the toplevel TUF metadata
120124
self._updater = Updater(
121125
metadata_dir=str(self._metadata_dir),

sigstore/verify/verifier.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,23 @@ def __init__(self, *, rekor: RekorClient, trusted_root: TrustedRoot):
7777
self._trusted_root = trusted_root
7878

7979
@classmethod
80-
def production(cls) -> Verifier:
80+
def production(cls, *, offline: bool = False) -> Verifier:
8181
"""
8282
Return a `Verifier` instance configured against Sigstore's production-level services.
8383
"""
8484
return cls(
8585
rekor=RekorClient.production(),
86-
trusted_root=TrustedRoot.production(),
86+
trusted_root=TrustedRoot.production(offline=offline),
8787
)
8888

8989
@classmethod
90-
def staging(cls) -> Verifier:
90+
def staging(cls, *, offline: bool = False) -> Verifier:
9191
"""
9292
Return a `Verifier` instance configured against Sigstore's staging-level services.
9393
"""
9494
return cls(
9595
rekor=RekorClient.staging(),
96-
trusted_root=TrustedRoot.staging(),
96+
trusted_root=TrustedRoot.staging(offline=offline),
9797
)
9898

9999
@classmethod

test/unit/verify/test_verifier.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,27 @@ def test_verifier_multiple_verifications(signing_materials, null_policy):
6969
verifier.verify_artifact(file.read_bytes(), bundle, null_policy)
7070

7171

72+
@pytest.mark.online
7273
@pytest.mark.parametrize(
7374
"filename", ("bundle.txt", "bundle_v3.txt", "bundle_v3_alt.txt")
7475
)
75-
def test_verifier_bundle(signing_bundle, null_policy, mock_staging_tuf, filename):
76+
def test_verifier_bundle(signing_bundle, null_policy, filename):
7677
(file, bundle) = signing_bundle(filename)
7778

7879
verifier = Verifier.staging()
7980
verifier.verify_artifact(file.read_bytes(), bundle, null_policy)
8081

8182

83+
@pytest.mark.parametrize(
84+
"filename", ("bundle.txt", "bundle_v3.txt", "bundle_v3_alt.txt")
85+
)
86+
def test_verifier_bundle_offline(signing_bundle, null_policy, filename):
87+
(file, bundle) = signing_bundle(filename)
88+
89+
verifier = Verifier.staging(offline=True)
90+
verifier.verify_artifact(file.read_bytes(), bundle, null_policy)
91+
92+
8293
@pytest.mark.staging
8394
def test_verifier_email_identity(signing_materials):
8495
verifier = Verifier.staging()

0 commit comments

Comments
 (0)
0