Skip to content

Commit 6ae464b

Browse files
authored
Use trust config everywhere (#1363)
* cli: Get OIDC url from trust config * trust: Provide methods to load TrustConfig from tuf We have previously done this for TrustedRoot but doing this for the whole TrustConfig makes sense. The only complication is that production instance does not have the SigningConfig component yet so we need to provide a fallback for that. * Use TrustConfig to initialize components This change makes almost all code paths now use TrustConfig to choose the sigstore instance (urls, keys, validity periods, etc). * Remove tuf methods from TrustedRoot * Update staging assets, refactor TUF asset lookup * Update the embedded data in sigstore/_store and the test assets in test/assets * refactor the embedded asset lookup: use the URL to build the asset dir. This means less code duplication and easier to make this work with non-Public Good Instance TUF repos * Make the tuf module work with non-PGI instances: if the local TUF metadata is initialized out of band, tuf module just works with it. If a root.json is provided in _store, it is still always used to initialize the client Signed-off-by: Jussi Kukkonen <[email protected]>
1 parent d4295dc commit 6ae464b

File tree

43 files changed

+692
-830
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+692
-830
lines changed

CHANGELOG.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ All versions prior to 0.9.0 are untracked.
2020
* TSA: Changed the Timestamp Authority requests to explicitly use sha256 for message digests.
2121
[#1373](https://github.com/sigstore/sigstore-python/pull/1373)
2222

23-
* Fixed the certificate calidity period check for Timestamp Authorities (TSA).
24-
Certificates need not have and end date, while still requiring a start date.
23+
* Fixed the certificate validity period check for Timestamp Authorities (TSA).
24+
Certificates need not have an end date, while still requiring a start date.
2525
[#1368](https://github.com/sigstore/sigstore-python/pull/1368)
2626

27-
* API: Make Rekor APIs compatible with Rekor v2 by removing trailing slashes
27+
* Made Rekor client more compatible with Rekor v2 by removing trailing slashes
2828
from endpoints ([#1366](https://github.com/sigstore/sigstore-python/pull/1366))
2929

3030
* Verify: verify that all established times (timestamps or the log integration time)
@@ -38,8 +38,25 @@ All versions prior to 0.9.0 are untracked.
3838

3939
### Changed
4040

41+
* API:
42+
* ClientTrustConfig now provides methods `production()`, `staging()`and `from_tuf()`
43+
to get access to current client configuration (trusted keys & certificates,
44+
URLs and their validity periods). [#1363](https://github.com/sigstore/sigstore-python/pull/1363)
4145
* `--trust-config` now requires a file with SigningConfig v0.2, and is able to fully
4246
configure the used Sigstore instance [#1358]/(https://github.com/sigstore/sigstore-python/pull/1358)
47+
* By default (when `--trust-config` is not used) the whole trust configuration now
48+
comes from the TUF repository [#1363](https://github.com/sigstore/sigstore-python/pull/1363)
49+
50+
### Removed
51+
* API:
52+
* `Issuer.production()` and `Issuer.staging()` have been removed: Use
53+
`Issuer()` instead with relevant URL. The current public good production and
54+
staging URLs are available via the `ClientTrustConfig` object.
55+
[#1363](https://github.com/sigstore/sigstore-python/pull/1363)
56+
* `SigningContext.production()` and `SigningContext.staging()` have been removed:
57+
Use `SigningContext.from_trust_config()` instead.
58+
[#1363](https://github.com/sigstore/sigstore-python/pull/1363)
59+
4360

4461
## [3.6.2]
4562

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,6 @@ update-embedded-root: $(VENV)/pyvenv.cfg
177177
. $(VENV_BIN)/activate && \
178178
python -m sigstore plumbing update-trust-root
179179
cp ~/.local/share/sigstore-python/tuf/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/root.json \
180-
sigstore/_store/prod/root.json
180+
sigstore/_store/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/root.json
181181
cp ~/.cache/sigstore-python/tuf/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/trusted_root.json \
182-
sigstore/_store/prod/trusted_root.json
182+
sigstore/_store/https%3A%2F%2Ftuf-repo-cdn.sigstore.dev/trusted_root.json

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ OpenID Connect options:
121121
--oidc-disable-ambient-providers
122122
Disable ambient OpenID Connect credential detection
123123
(e.g. on GitHub Actions) (default: False)
124-
--oidc-issuer URL The OpenID Connect issuer to use (conflicts with
125-
--staging) (default: https://oauth2.sigstore.dev/auth)
124+
--oidc-issuer URL The OpenID Connect issuer to use (default: None)
126125
--oauth-force-oob Force an out-of-band OAuth flow and do not
127126
automatically start the default web browser (default:
128127
False)
@@ -185,8 +184,7 @@ OpenID Connect options:
185184
--oidc-disable-ambient-providers
186185
Disable ambient OpenID Connect credential detection
187186
(e.g. on GitHub Actions) (default: False)
188-
--oidc-issuer URL The OpenID Connect issuer to use (conflicts with
189-
--staging) (default: https://oauth2.sigstore.dev/auth)
187+
--oidc-issuer URL The OpenID Connect issuer to use (default: None)
190188
--oauth-force-oob Force an out-of-band OAuth flow and do not
191189
automatically start the default web browser (default:
192190
False)

sigstore/_cli.py

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from sigstore._internal.fulcio.client import ExpiredCertificate
4040
from sigstore._internal.rekor import _hashedrekord_from_parts
4141
from sigstore._internal.rekor.client import RekorClient
42-
from sigstore._internal.trust import ClientTrustConfig, TrustedRoot
42+
from sigstore._internal.trust import ClientTrustConfig
4343
from sigstore._utils import sha256_digest
4444
from sigstore.dsse import StatementBuilder, Subject
4545
from sigstore.dsse._predicate import (
@@ -51,7 +51,6 @@
5151
from sigstore.hashes import Hashed
5252
from sigstore.models import Bundle, InvalidBundle
5353
from sigstore.oidc import (
54-
DEFAULT_OAUTH_ISSUER_URL,
5554
ExpiredIdentity,
5655
IdentityToken,
5756
Issuer,
@@ -229,8 +228,8 @@ def _add_shared_oidc_options(
229228
"--oidc-issuer",
230229
metavar="URL",
231230
type=str,
232-
default=os.getenv("SIGSTORE_OIDC_ISSUER", DEFAULT_OAUTH_ISSUER_URL),
233-
help="The OpenID Connect issuer to use (conflicts with --staging)",
231+
default=os.getenv("SIGSTORE_OIDC_ISSUER", None),
232+
help="The OpenID Connect issuer to use",
234233
)
235234
group.add_argument(
236235
"--oauth-force-oob",
@@ -614,11 +613,7 @@ def main(args: list[str] | None = None) -> None:
614613
elif args.verify_subcommand == "github":
615614
_verify_github(args)
616615
elif args.subcommand == "get-identity-token":
617-
identity = _get_identity(args)
618-
if identity:
619-
print(identity)
620-
else:
621-
_invalid_arguments(args, "No identity token supplied or detected!")
616+
_get_identity_token(args)
622617
elif args.subcommand == "plumbing":
623618
if args.plumbing_subcommand == "fix-bundle":
624619
_fix_bundle(args)
@@ -630,6 +625,17 @@ def main(args: list[str] | None = None) -> None:
630625
e.log_and_exit(_logger, args.verbose >= 1)
631626

632627

628+
def _get_identity_token(args: argparse.Namespace) -> None:
629+
"""
630+
Output the OIDC authentication token
631+
"""
632+
identity = _get_identity(args, _get_trust_config(args))
633+
if identity:
634+
print(identity)
635+
else:
636+
_invalid_arguments(args, "No identity token supplied or detected!")
637+
638+
633639
def _sign_common(
634640
args: argparse.Namespace, output_map: OutputMap, predicate: dict[str, Any] | None
635641
) -> None:
@@ -643,17 +649,8 @@ def _sign_common(
643649
not, it will use a hashedrekord.
644650
"""
645651
# Select the signing context to use.
646-
if args.staging:
647-
_logger.debug("sign: staging instances requested")
648-
signing_ctx = SigningContext.staging()
649-
elif args.trust_config:
650-
trust_config = ClientTrustConfig.from_json(args.trust_config.read_text())
651-
signing_ctx = SigningContext._from_trust_config(trust_config)
652-
else:
653-
# If the user didn't request the staging instance or pass in an
654-
# explicit client trust config, we're using the public good (i.e.
655-
# production) instance.
656-
signing_ctx = SigningContext.production()
652+
trust_config = _get_trust_config(args)
653+
signing_ctx = SigningContext.from_trust_config(trust_config)
657654

658655
# The order of precedence for identities is as follows:
659656
#
@@ -664,7 +661,7 @@ def _sign_common(
664661
if args.identity_token:
665662
identity = IdentityToken(args.identity_token)
666663
else:
667-
identity = _get_identity(args)
664+
identity = _get_identity(args, trust_config)
668665

669666
if not identity:
670667
_invalid_arguments(args, "No identity token supplied or detected!")
@@ -1009,14 +1006,8 @@ def _collect_verification_state(
10091006
f"Missing verification materials for {(hashed)}: {', '.join(missing)}",
10101007
)
10111008

1012-
if args.staging:
1013-
_logger.debug("verify: staging instances requested")
1014-
verifier = Verifier.staging(offline=args.offline)
1015-
elif args.trust_config:
1016-
trust_config = ClientTrustConfig.from_json(args.trust_config.read_text())
1017-
verifier = Verifier._from_trust_config(trust_config)
1018-
else:
1019-
verifier = Verifier.production(offline=args.offline)
1009+
trust_config = _get_trust_config(args)
1010+
verifier = Verifier(trusted_root=trust_config.trusted_root)
10201011

10211012
all_materials = []
10221013
for file_or_hashed, materials in input_map.items():
@@ -1167,7 +1158,27 @@ def _verify_common(
11671158
return None
11681159

11691160

1170-
def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
1161+
def _get_trust_config(args: argparse.Namespace) -> ClientTrustConfig:
1162+
"""
1163+
Return the client trust configuration (Sigstore service URLs, key material and lifetimes)
1164+
1165+
The configuration may come from explicit argument (--trust-config) or from the TUF
1166+
repository of the used Sigstore instance.
1167+
"""
1168+
# Not all commands provide --offline
1169+
offline = getattr(args, "offline", False)
1170+
1171+
if args.trust_config:
1172+
return ClientTrustConfig.from_json(args.trust_config.read_text())
1173+
elif args.staging:
1174+
return ClientTrustConfig.staging(offline=offline)
1175+
else:
1176+
return ClientTrustConfig.production(offline=offline)
1177+
1178+
1179+
def _get_identity(
1180+
args: argparse.Namespace, trust_config: ClientTrustConfig
1181+
) -> Optional[IdentityToken]:
11711182
token = None
11721183
if not args.oidc_disable_ambient_providers:
11731184
token = detect_credential()
@@ -1176,12 +1187,10 @@ def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
11761187
if token:
11771188
return IdentityToken(token)
11781189

1179-
if args.staging:
1180-
issuer = Issuer.staging()
1181-
elif args.oidc_issuer == DEFAULT_OAUTH_ISSUER_URL:
1182-
issuer = Issuer.production()
1183-
else:
1190+
if args.oidc_issuer is not None:
11841191
issuer = Issuer(args.oidc_issuer)
1192+
else:
1193+
issuer = Issuer(trust_config.signing_config.get_oidc_url())
11851194

11861195
if args.oidc_client_secret is None:
11871196
args.oidc_client_secret = "" # nosec: B105
@@ -1198,6 +1207,7 @@ def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]:
11981207
def _fix_bundle(args: argparse.Namespace) -> None:
11991208
# NOTE: We could support `--trusted-root` here in the future,
12001209
# for custom Rekor instances.
1210+
12011211
rekor = RekorClient.staging() if args.staging else RekorClient.production()
12021212

12031213
raw_bundle = RawBundle.from_dict(json.loads(args.bundle.read_bytes()))
@@ -1234,13 +1244,10 @@ def _fix_bundle(args: argparse.Namespace) -> None:
12341244

12351245

12361246
def _update_trust_root(args: argparse.Namespace) -> None:
1237-
# Simply creating the TrustedRoot in online mode is enough to perform
1247+
# Simply creating the TrustConfig in online mode is enough to perform
12381248
# a metadata update.
1239-
if args.staging:
1240-
trusted_root = TrustedRoot.staging(offline=False)
1241-
else:
1242-
trusted_root = TrustedRoot.production(offline=False)
12431249

1250+
config = _get_trust_config(args)
12441251
_console.print(
1245-
f"Trust root updated: {len(trusted_root.get_fulcio_certs())} Fulcio certificates"
1252+
f"Trust root & signing config updated: {len(config.trusted_root.get_fulcio_certs())} Fulcio certificates"
12461253
)

sigstore/_internal/fulcio/client.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939

4040
_logger = logging.getLogger(__name__)
4141

42-
DEFAULT_FULCIO_URL = "https://fulcio.sigstore.dev"
43-
STAGING_FULCIO_URL = "https://fulcio.sigstage.dev"
4442
SIGNING_CERT_ENDPOINT = "/api/v2/signingCert"
4543
TRUST_BUNDLE_ENDPOINT = "/api/v2/trustBundle"
4644

@@ -163,7 +161,7 @@ def get(self) -> FulcioTrustBundleResponse:
163161
class FulcioClient:
164162
"""The internal Fulcio client"""
165163

166-
def __init__(self, url: str = DEFAULT_FULCIO_URL) -> None:
164+
def __init__(self, url: str) -> None:
167165
"""Initialize the client"""
168166
_logger.debug(f"Fulcio client using URL: {url}")
169167
self.url = url
@@ -180,20 +178,6 @@ def __del__(self) -> None:
180178
"""
181179
self.session.close()
182180

183-
@classmethod
184-
def production(cls) -> FulcioClient:
185-
"""
186-
Returns a `FulcioClient` for the Sigstore production instance of Fulcio.
187-
"""
188-
return cls(DEFAULT_FULCIO_URL)
189-
190-
@classmethod
191-
def staging(cls) -> FulcioClient:
192-
"""
193-
Returns a `FulcioClient` for the Sigstore staging instance of Fulcio.
194-
"""
195-
return cls(STAGING_FULCIO_URL)
196-
197181
@property
198182
def signing_cert(self) -> FulcioSigningCert:
199183
"""

0 commit comments

Comments
 (0)