Skip to content

[SS-188] Check the token_uri in Google Cloud Service Account keys#36997

Merged
ublubu merged 5 commits into
MaterializeInc:mainfrom
ublubu:ss-188
Jun 11, 2026
Merged

[SS-188] Check the token_uri in Google Cloud Service Account keys#36997
ublubu merged 5 commits into
MaterializeInc:mainfrom
ublubu:ss-188

Conversation

@ublubu

@ublubu ublubu commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

This PR adds a simple framework for checking the Secrets used by a Connection when either the Secret or the Connection changes.

So far, it's just a hook to check GCP secrets for a bad token_uri field.

This is a followup to #36694.

Just for fun: Opus vs Fable code comparison

@ublubu ublubu requested a review from a team as a code owner June 11, 2026 18:36
Comment thread src/sql/src/plan.rs
// want to send requests to the actual Google OAuth2 token API.
ConnectionDetails::Gcp(gcp) => vec![(
gcp.credentials_json,
GcpServiceAccountKeyTokenUri::validate_json,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is tuple of item ID (which I assume points to json credentials) and a validation function for that json.

Then more broadly this is structured so we can add validation functions from varied ConnectionDetails.

Commentary: Returning a mapping of catalog item and function from each connection detail to later call feels similar to me to having an abstract method of a parent class implemented by a handful of subclasses.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: Commentary

The short answer is yes, it's definitely similar. And enum variants are kinda like subclasses.

Slightly longer answer: #36997 (comment)

secret_id: CatalogItemId,
contents: &str,
) -> Result<(), AdapterError> {
for user_id in self.catalog().get_entry(&secret_id).used_by() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user_id is a bit of a confusing name for the dependent catalog item ids.

Also, just to confirm:

  1. used_by is only direct dependencies
  2. connection always directly references the the secret -- as opposed to some possibility of transitive relationships/constraints
  3. reads from catalog are fast in-memory reads so sequential reads or relatively high counts of used_by entries aren't a concern for latency of processing the query

@ublubu ublubu Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yes
  2. For GCP Connections, yes. (I actually don't know if it holds for all the other connection types 😮)
  3. Good question. Time to ask Marty. EDIT: Yes, we have a cheap in-memory view of the catalog, including used_by.

.caching_secrets_reader
.read_string(gcp.credentials_json)
.await
.and_then(|json| GcpServiceAccountKeyTokenUri::validate_json(&json))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, at a high level we're doing two things in this PR overall:

  1. Porting this logic to more cases -- i.e. validating connection details on alter secret ad not just creating the connection
  2. Updating the code to make it more abstract so we can set up constraints on the contents of a secret depended on by a connection

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a poke at making it less abstract (I mean, there's only one branch, "Gcp". Come on.) but I couldn't do it.
So I added a comment to secret_content_guards to explain why it's like that.

    /// We rely on the caller to actually execute these checks because we don't know:
    /// - which secrets the caller cares about
    /// - which secrets require an async operation to fetch
    /// 
    /// For example, the ALTER SECRET caller should only perform checks on its own secret,
    /// while the ALTER CONNECTION caller fetches and checks every secret from its connection.
    pub fn secret_content_guards(
        &self,
    ) -> Vec<(CatalogItemId, fn(&str) -> Result<(), anyhow::Error>)>

> ALTER SECRET gcp_good AS '{"type":"service_account","project_id":"y","client_email":"a@b.com","private_key":"${gcp-ssrf-key}","token_uri":"https://oauth2.googleapis.com/token"}'

> CREATE SECRET gcp_good_2 AS '{"type":"service_account","project_id":"x","client_email":"a@b.com","private_key":"${gcp-ssrf-key}","token_uri":"https://oauth2.googleapis.com/token"}'
> ALTER CONNECTION gcp_alter_conn SET (SERVICE ACCOUNT KEY = SECRET gcp_good_2) WITH (VALIDATE = false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This testing looks good to me.

let caching_secrets_reader = self.caching_secrets_reader.clone();
let secrets_controller = Arc::clone(&self.secrets_controller);
let payload = self.extract_secret(session, &mut secret_as)?;
let contents = std::str::from_utf8(&payload).expect("validated as UTF-8 by extract_secret");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validated that "validated as UTF-8 by extract_secret" is true

@ublubu ublubu enabled auto-merge (squash) June 11, 2026 20:55
@ublubu ublubu merged commit 8861155 into MaterializeInc:main Jun 11, 2026
116 checks passed
@ublubu ublubu deleted the ss-188 branch June 11, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants