yoshidan / google-cloud-rust

Google Cloud Client Libraries for Rust.
MIT License
233 stars 85 forks source link

Auth without a service account? #99

Open johnrichardrinehart opened 1 year ago

johnrichardrinehart commented 1 year ago

It seems that it's required to be using a service account for interacting with Cloud Storage. Is my understanding wrong?

Google's official SDKs allow authenticating without providing service account credentials so are you open to changing the behavior of (at least) google-cloud-storage to allow for creating a Client without using a service account?

yoshidan commented 1 year ago

As you indicated, it does not need to be required, so I will modify it to optional. Thanks.

johnrichardrinehart commented 1 year ago

Thank you! We regularly use Service Account impersonation for our development flows so this would be a great feature/improvement for us.

yoshidan commented 1 year ago

I removed the dependency for google-cloud-auth and published the new version.

johnrichardrinehart commented 1 year ago

@yoshidan I'm sorry for my late response. I ran a small test whose source I've included below for future readers' convenience.

It seems to work really well! Great job and thank you!

I did encounter one behavior that concerned me, though. GCP allows you to change the application default credentials (ADC) in a few ways. One of them is using a flow like

gcloud config set auth/impersonate_service_account $SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com

followed by

gcloud auth application-default login

and, assuming that the original user executing these commands has the Service Account Token Creator role (I think) then the ~/.config/gcloud/application_default_credentials.json file will then be replaced by

{
  "delegates": [],
  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com:generateAccessToken",
  "source_credentials": {
    "client_id": "$IMACLIENTID",
    "client_secret": "$IMASECRET",
    "refresh_token": "$IMAREFRESHTOKEN",
    "type": "authorized_user"
  },
  "type": "impersonated_service_account"
}

However, I'm finding that this authorization flow is not supported by google-cloud-auth. Using the source code at the bottom of this comment:

$ ./target/debug/google_cloud_storage
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "failed to get cloud storage client: unsupported account impersonated_service_account"', src/main.rs:15:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The error message is clear: impersonate_service_account is an unsupported type. But, is that intentional? Would you consider adding support for configuring and using ADC in this way?

$ cat src/main.rs     
use google_cloud_default::WithAuthExt;
use google_cloud_storage::client::ClientConfig;

fn main() {
    let rt = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap();

    let client = rt.block_on(async {
        let cfg = ClientConfig::default()
            .with_auth()
            .await
            .map_err(|e| format!("failed to get cloud storage client: {}", e))
            .unwrap();

        return google_cloud_storage::client::Client::new(cfg);
    });

    let req = google_cloud_storage::http::objects::list::ListObjectsRequest {
        bucket: "$MY_BUCKET".to_string(),
        max_results: Some(10),
        prefix: Some("a".to_string()),
        ..Default::default()
    };

    let res = rt.block_on(client.list_objects(&req, None)).unwrap();

    for item in res.items.unwrap() {
        println!("item {}: md5: {}", item.name, item.md5_hash.unwrap())
    }
}

and a Cargo.toml like

$ cat Cargo.toml                                            
[package]
name = "google_cloud_storage"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
google-cloud-default = { version = "0.1.0", features = ["storage"] }
google-cloud-storage = { version = "0.9.0", features = ["default"] }
tokio = { version = "1.26.0", features = ["rt", "macros", "rt-multi-thread", "time", "sync"] }
johnrichardrinehart commented 1 year ago

I may be slow to respond since I'm no longer rely\ing on google-cloud-rust as I've switched our bucket storage to R2 and am using an S3-compatible storage rust library, now (rusoto_s3, I think?).