Skip to content

treewide: Replace ambient credential detection with id #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ All versions prior to 0.9.0 are untracked.

## [1.1.1]

### Changed

* Replaced ambient credential detection logic with the `id` package
([#535](https://github.com/sigstore/sigstore-python/pull/535))

### Fixed

* Fixed a bug in TUF target handling revealed by changes to the production
Expand Down
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,15 +338,8 @@ provided below.
### Signing with ambient credentials

For environments that support OpenID Connect, natively `sigstore` supports ambient credential
detection. This includes many popular CI platforms and cloud providers.

| Service | Status | Notes |
|-----------------------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| GitHub Actions | Supported | Requires the `id-token` permission; see [the docs](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) and [this example](https://github.com/sigstore/sigstore-python/blob/main/.github/workflows/release.yml) |
| Google Compute Engine (GCE) | Supported | Automatic |
| Google Cloud Build (GCB) | Supported | Requires setting `GOOGLE_SERVICE_ACCOUNT_NAME` to an appropriately configured service account name; see [the docs](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-direct) and [this example](https://github.com/sigstore/sigstore-python/blob/main/cloudbuild.yaml) |
| GitLab CI | Planned | See [#31](https://github.com/sigstore/sigstore-python/issues/31) |
| CircleCI | Planned | See [#31](https://github.com/sigstore/sigstore-python/issues/31) |
detection. This includes many popular CI platforms and cloud providers. See the full list of
supported environments [here](https://github.com/di/id#supported-environments).

Sign a single file (`foo.txt`) using an ambient OpenID Connect credential,
saving the signature and certificate to `foo.txt.sig` and `foo.txt.crt`:
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ classifiers = [
dependencies = [
"appdirs ~= 1.4",
"cryptography >= 39",
"id >= 1.0.0",
"importlib_resources ~= 5.7; python_version < '3.11'",
"pydantic ~= 1.10",
"pyjwt >= 2.1",
Expand Down
6 changes: 3 additions & 3 deletions sigstore/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
from typing import Optional, TextIO, Union, cast

from cryptography.x509 import load_pem_x509_certificates
from id import GitHubOidcPermissionCredentialError, detect_credential
from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle

from sigstore import __version__
from sigstore._internal.ctfe import CTKeyring
from sigstore._internal.fulcio.client import DEFAULT_FULCIO_URL, FulcioClient
from sigstore._internal.keyring import Keyring
from sigstore._internal.oidc import DEFAULT_AUDIENCE
from sigstore._internal.rekor.client import (
DEFAULT_REKOR_URL,
RekorClient,
Expand All @@ -40,9 +42,7 @@
from sigstore.oidc import (
DEFAULT_OAUTH_ISSUER_URL,
STAGING_OAUTH_ISSUER_URL,
GitHubOidcPermissionCredentialError,
Issuer,
detect_credential,
)
from sigstore.sign import Signer
from sigstore.transparency import LogEntry
Expand Down Expand Up @@ -1012,7 +1012,7 @@ def _get_identity_token(args: argparse.Namespace) -> Optional[str]:
token = None
if not args.oidc_disable_ambient_providers:
try:
token = detect_credential()
token = detect_credential(DEFAULT_AUDIENCE)
except GitHubOidcPermissionCredentialError as exception:
# Provide some common reasons for why we hit permission errors in
# GitHub Actions.
Expand Down
3 changes: 1 addition & 2 deletions sigstore/_internal/oidc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
"""

import jwt

from sigstore.oidc import IdentityError
from id import IdentityError

# See: https://github.com/sigstore/fulcio/blob/b2186c0/pkg/config/config.go#L182-L201
_KNOWN_OIDC_ISSUERS = {
Expand Down
193 changes: 0 additions & 193 deletions sigstore/_internal/oidc/ambient.py

This file was deleted.

4 changes: 3 additions & 1 deletion sigstore/_internal/oidc/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
import uuid
from typing import Any, Dict, List, Optional, cast

from id import IdentityError

from sigstore._utils import B64Str
from sigstore.oidc import IdentityError, Issuer
from sigstore.oidc import Issuer

logger = logging.getLogger(__name__)

Expand Down
46 changes: 1 addition & 45 deletions sigstore/oidc.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
import time
import urllib.parse
import webbrowser
from typing import Callable, List, Optional

import requests
from id import IdentityError
from pydantic import BaseModel, StrictStr

DEFAULT_OAUTH_ISSUER_URL = "https://oauth2.sigstore.dev/auth"
Expand Down Expand Up @@ -169,47 +169,3 @@ def identity_token( # nosec: B107
raise IdentityError(f"Error response from token endpoint: {token_error}")

return str(token_json["access_token"])


class IdentityError(Exception):
"""
Raised on any OIDC token format or claim error.
"""

pass


class AmbientCredentialError(IdentityError):
"""
Raised when an ambient credential should be present, but
can't be retrieved (e.g. network failure).
"""

pass


class GitHubOidcPermissionCredentialError(AmbientCredentialError):
"""
Raised when the current GitHub Actions environment doesn't have permission
to retrieve an OIDC token.
"""

pass


def detect_credential() -> Optional[str]:
"""
Try each ambient credential detector, returning the first one to succeed
or `None` if all fail.

Raises `AmbientCredentialError` if any detector fails internally (i.e.
detects a credential, but cannot retrieve it).
"""
from sigstore._internal.oidc.ambient import detect_gcp, detect_github

detectors: List[Callable[..., Optional[str]]] = [detect_github, detect_gcp]
for detector in detectors:
credential = detector()
if credential is not None:
return credential
return None
13 changes: 7 additions & 6 deletions test/unit/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@
from typing import Iterator

import pytest
from id import (
AmbientCredentialError,
GitHubOidcPermissionCredentialError,
detect_credential,
)
from sigstore_protobuf_specs.dev.sigstore.bundle.v1 import Bundle
from tuf.api.exceptions import DownloadHTTPError
from tuf.ngclient import FetcherInterface

from sigstore._internal import tuf
from sigstore.oidc import (
AmbientCredentialError,
GitHubOidcPermissionCredentialError,
detect_credential,
)
from sigstore._internal.oidc import DEFAULT_AUDIENCE
from sigstore.verify import VerificationMaterials
from sigstore.verify.policy import VerificationSuccess

Expand All @@ -42,7 +43,7 @@

def _is_ambient_env():
try:
token = detect_credential()
token = detect_credential(DEFAULT_AUDIENCE)
if token is None:
return False
except GitHubOidcPermissionCredentialError:
Expand Down
Loading