Uh oh!
There was an error while loading. Please reload this page.
feat: Enable Bound Token for Agentic Identities - #13873
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Agent Identity token binding support for Cloud Run. It adds AgentIdentityUtils to resolve, load, and verify certificates and private keys, and updates ComputeEngineCredentials to request bound tokens via POST requests when a valid certificate chain is present. The review feedback suggests a cohesive improvement to implement a single-read pattern for certificate files. By reading the certificate chain once, caching it in CertInfo, and passing it to parseCertificate and getBoundTokenPayload, the implementation can avoid redundant disk I/O and prevent potential race conditions during certificate rotation.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| // Environment variables | ||
| static final String GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG"; | ||
| static final String GOOGLE_API_PREVENT_TOKEN_SHARING_FOR_GCP_SERVICES = | ||
| "GOOGLE_API_PREVENT_TOKEN_SHARING_FOR_GCP_SERVICES"; |
There was a problem hiding this comment.
Note that based on googleapis/google-cloud-python#17698 (comment) this is not yet finalized
1. POST request to MDS with cert-chain 2. Cert-key matching 3. Included logic to consider the user's choice by looking at GOOGLE_API_USE_CLIENT_CERTIFICATE env variable 4. Bound ID tokens. # Conflicts: # google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java # google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/MockMetadataServerTransport.java
…etry logic. Nit fixes. # Conflicts: # google-auth-library-java/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java
# Conflicts: # google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java
…fill CI formatting rules
…n binding - Prevent 30-second polling delay and IOException on standard GCE/container environments when well-known credentials directory exists without certificate files unless mTLS is explicitly enabled. - Fail fast on malformed certificate config JSON without retrying. - Fix URI resource path decoding in AgentIdentityUtilsTest to prevent FileNotFoundException when workspace paths contain spaces. - Copy matching private key in well-known fallback test to verify full key pair loading. - Clean up static wellKnownDir state and temporary directories in ComputeEngineCredentialsTest. - Correct opt-out environment variable value in test setup from 'true' to 'false'.
| @InternalApi | ||
| public final class AgentIdentityUtils { | ||
| /** Javadoc. */ |
There was a problem hiding this comment.
Remove placeholder /** Javadoc. */ everywhere and replace with real docs where applicable
| static { | ||
| List<Long> intervals = new ArrayList<>(); | ||
| for (int i = 0; i < FAST_POLL_CYCLES; i++) { | ||
| intervals.add(FAST_POLL_INTERVAL_MS); | ||
| } | ||
| long remainingTime = TOTAL_TIMEOUT_MS - (FAST_POLL_CYCLES * FAST_POLL_INTERVAL_MS); | ||
| int slowPollCycles = (int) (remainingTime / SLOW_POLL_INTERVAL_MS); | ||
| for (int i = 0; i < slowPollCycles; i++) { | ||
| intervals.add(SLOW_POLL_INTERVAL_MS); | ||
| } | ||
| POLLING_INTERVALS = Collections.unmodifiableList(intervals); | ||
| } |
There was a problem hiding this comment.
Why not do this directly in the polling loop?
| static final String GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG"; | ||
| /** Javadoc. */ | ||
| static final String GOOGLE_API_PREVENT_TOKEN_SHARING_FOR_GCP_SERVICES = |
There was a problem hiding this comment.
GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES for the old one.
but I think they picked a new one.
| * @throws IOException If an I/O error occurs while reading the files, or if the key-pair | ||
| * verification fails after retries. | ||
| */ | ||
| static CertInfo getAgentIdentityCertInfo() throws IOException { |
There was a problem hiding this comment.
Should we first check if GOOGLE_API_USE_CLIENT_CERTIFICATE=false? AIP 4114
| private static final int CERT_KEY_MATCH_RETRIES = 3; | ||
| /** Javadoc. */ | ||
| private static final long CERT_KEY_MATCH_RETRY_INTERVAL_MS = 100; |
There was a problem hiding this comment.
AIP-4118 recommends ~5s between attempts. What does Python do?
| /** Javadoc. */ | ||
| private static final List<Pattern> AGENT_IDENTITY_SPIFFE_PATTERNS = | ||
| ImmutableList.of( | ||
| Pattern.compile("^agents\\.global\\.org-\\d+\\.system\\.id\\.goog$"), |
There was a problem hiding this comment.
I think in Python @nbayati went back and added non prod patterns. Let's add that + tests?
| } | ||
| if (!matched) { | ||
| throw new IOException( |
There was a problem hiding this comment.
should this be an IOException or something else? perhaps a retryable GoogleAuthException?
| transportFactory.transport.getRequest(); | ||
| assertEquals("POST", transportFactory.transport.getRequestMethod()); | ||
| String body = request.getContentAsString(); | ||
| assertTrue(body.contains("certificate_chain")); |
There was a problem hiding this comment.
Let's validate that it contains the full cert in these tests
| "spiffe://agents.global.org-INVALID.system.id.goog/path"; | ||
| private TestEnvironmentProvider envProvider; | ||
| private Path tempDir; |
| * <p>To handle transient race conditions during certificate rotation on disk, this method employs | ||
| * a retry mechanism with backoff when reading the configuration and certificate files. | ||
| * | ||
| * @return A {@link CertInfo} object containing the loaded certificate and its path, or {@code |
This PR introduces a feature which enables the auth library to acquire bound access-tokens and bound id-tokens in Agentic Environments.
We detect certs in default paths and check if they match the SPIFFE format for agents.
If 1. is a yes then we call the MDS endpoint in a POST request with the certificate in the body.
Note this PR was based on #13169