-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from near/daniyar/gcp-auth
- Loading branch information
Showing
5 changed files
with
145 additions
and
180 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,69 @@ | ||
use std::fs::File; | ||
|
||
use hyper::client::HttpConnector; | ||
use hyper_rustls::HttpsConnector; | ||
use mpc_recovery_gcp::google::cloud::secretmanager::v1::{ | ||
secret_manager_service_client::SecretManagerServiceClient, AccessSecretVersionRequest, | ||
}; | ||
use tonic::{ | ||
transport::{Certificate, Channel, ClientTlsConfig}, | ||
Request, | ||
Request, Status, | ||
}; | ||
use yup_oauth2::{ | ||
authenticator::{ApplicationDefaultCredentialsTypes, Authenticator}, | ||
ApplicationDefaultCredentialsAuthenticator, ApplicationDefaultCredentialsFlowOpts, | ||
}; | ||
|
||
use self::auth::TokenManager; | ||
|
||
mod auth; | ||
|
||
const DOMAIN_NAME: &str = "secretmanager.googleapis.com"; | ||
const ENDPOINT: &str = "https://secretmanager.googleapis.com"; | ||
const SCOPES: [&str; 1] = ["https://www.googleapis.com/auth/cloud-platform"]; | ||
const TLS_CERTS: &[u8] = include_bytes!("../../roots.pem"); | ||
|
||
pub async fn load_secret_share(node_id: u64) -> anyhow::Result<Vec<u8>> { | ||
// GOOGLE_APPLICATION_CREDENTIALS points to the credentials file on GCP: | ||
// https://cloud.google.com/docs/authentication/application-default-credentials | ||
let path = std::env::var("GOOGLE_APPLICATION_CREDENTIALS")?; | ||
let file = File::open(path)?; | ||
let creds = serde_json::from_reader(file)?; | ||
let mut token_manager = TokenManager::new(creds, &SCOPES); | ||
let token = token_manager.token().await?; | ||
|
||
let tls_config = ClientTlsConfig::new() | ||
.ca_certificate(Certificate::from_pem(TLS_CERTS)) | ||
.domain_name(DOMAIN_NAME); | ||
|
||
let channel = Channel::from_static(ENDPOINT) | ||
.tls_config(tls_config)? | ||
.connect() | ||
.await?; | ||
let mut client = | ||
SecretManagerServiceClient::with_interceptor(channel, move |mut req: Request<()>| { | ||
req.metadata_mut() | ||
.insert("authorization", token.parse().unwrap()); | ||
Ok(req) | ||
}); | ||
let request = Request::new(AccessSecretVersionRequest { | ||
name: format!( | ||
"projects/pagoda-discovery-platform-dev/secrets/mpc-recovery-secret-share-{node_id}/versions/latest" | ||
), | ||
}); | ||
|
||
let response = client.access_secret_version(request).await?; | ||
let secret_payload = response | ||
.into_inner() | ||
.payload | ||
.ok_or_else(|| anyhow::anyhow!("failed to fetch secret share from GCP Secret Manager"))?; | ||
Ok(secret_payload.data) | ||
pub struct GcpService { | ||
authenticator: Authenticator<HttpsConnector<HttpConnector>>, | ||
} | ||
|
||
impl GcpService { | ||
pub async fn new() -> anyhow::Result<Self> { | ||
let opts = ApplicationDefaultCredentialsFlowOpts::default(); | ||
let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await { | ||
ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth.build().await?, | ||
ApplicationDefaultCredentialsTypes::ServiceAccount(auth) => auth.build().await?, | ||
}; | ||
|
||
Ok(Self { authenticator }) | ||
} | ||
|
||
pub async fn load_secret(&self, name: String) -> anyhow::Result<Vec<u8>> { | ||
let access_token = self | ||
.authenticator | ||
.token(&["https://www.googleapis.com/auth/cloud-platform"]) | ||
.await?; | ||
let token = access_token | ||
.token() | ||
.ok_or_else(|| anyhow::anyhow!("GCP token did not have access_token field in it"))?; | ||
|
||
let tls_config = ClientTlsConfig::new() | ||
.ca_certificate(Certificate::from_pem(TLS_CERTS)) | ||
.domain_name(DOMAIN_NAME); | ||
|
||
let channel = Channel::from_static(ENDPOINT) | ||
.tls_config(tls_config)? | ||
.connect() | ||
.await?; | ||
let mut client = | ||
SecretManagerServiceClient::with_interceptor(channel, move |mut req: Request<()>| { | ||
req.metadata_mut().insert( | ||
"authorization", | ||
format!("Bearer {}", token) | ||
.parse() | ||
.map_err(|_| Status::unauthenticated("failed to parse access token"))?, | ||
); | ||
Ok(req) | ||
}); | ||
|
||
let request = Request::new(AccessSecretVersionRequest { name }); | ||
let response = client.access_secret_version(request).await?; | ||
let secret_payload = response.into_inner().payload.ok_or_else(|| { | ||
anyhow::anyhow!("failed to fetch secret share from GCP Secret Manager") | ||
})?; | ||
Ok(secret_payload.data) | ||
} | ||
} |
Oops, something went wrong.