Skip to content

Commit

Permalink
Add Sts Assume Role for security-watchtower
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaspustina committed Oct 1, 2020
1 parent 6e428c7 commit f105b6e
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 14 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 26 additions & 4 deletions aws/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ use rusoto_core::{
};
use rusoto_sts::{StsAssumeRoleSessionCredentialsProvider, StsClient};
use std::{path::PathBuf, time::Duration};
use rusoto_core::credential::StaticProvider;

pub fn create_provider() -> Result<AutoRefreshingProvider<CeresAwsCredentialProvider>, Error> {
let ceres_credential_provider = CeresAwsCredentialProvider::new(None)?;
let ceres_credential_provider = CeresAwsCredentialProvider::sts(None)?;
let credentials_provider = AutoRefreshingProvider::new(ceres_credential_provider)?;

Ok(credentials_provider)
Expand All @@ -19,7 +20,16 @@ pub fn create_provider() -> Result<AutoRefreshingProvider<CeresAwsCredentialProv
pub fn create_provider_with_assuem_role(
sts_config: StsAssumeRoleSessionCredentialsProviderConfig,
) -> Result<AutoRefreshingProvider<CeresAwsCredentialProvider>, Error> {
let ceres_credential_provider = CeresAwsCredentialProvider::new(sts_config)?;
let ceres_credential_provider = CeresAwsCredentialProvider::sts(sts_config)?;
let credentials_provider = AutoRefreshingProvider::new(ceres_credential_provider)?;

Ok(credentials_provider)
}

pub fn create_provider_with_static_provider(
static_provider: StaticProvider,
) -> Result<AutoRefreshingProvider<CeresAwsCredentialProvider>, Error> {
let ceres_credential_provider = CeresAwsCredentialProvider::static_provider(static_provider)?;
let credentials_provider = AutoRefreshingProvider::new(ceres_credential_provider)?;

Ok(credentials_provider)
Expand Down Expand Up @@ -50,16 +60,23 @@ impl StsAssumeRoleSessionCredentialsProviderConfig {

pub struct CeresAwsCredentialProvider {
sts: Option<StsAssumeRoleSessionCredentialsProvider>,
static_provider: Option<StaticProvider>,
chain: ChainProvider,
}

impl CeresAwsCredentialProvider {
pub fn new<T: Into<Option<StsAssumeRoleSessionCredentialsProviderConfig>>>(sts_config: T) -> Result<Self, Error> {
pub fn sts<T: Into<Option<StsAssumeRoleSessionCredentialsProviderConfig>>>(sts_config: T) -> Result<Self, Error> {
let sts_config = sts_config.into();
let sts = sts_config.and_then(|x| sts_provider(x.credentials_path, x.profile_name, x.role_arn, x.region).ok());
let chain = chain_provider()?;

Ok(CeresAwsCredentialProvider { sts, chain })
Ok(CeresAwsCredentialProvider { sts, static_provider: None, chain })
}

pub fn static_provider(static_provider: StaticProvider) -> Result<Self, Error> {
let chain = chain_provider()?;

Ok(CeresAwsCredentialProvider { sts: None, static_provider: Some(static_provider), chain })
}
}

Expand Down Expand Up @@ -101,6 +118,11 @@ impl ProvideAwsCredentials for CeresAwsCredentialProvider {
let chain_f = self.chain.credentials();
let f = sts_f.or_else(|_| chain_f);
Box::new(f)
} else if let Some(ref static_provider) = self.static_provider {
let static_provider_f = static_provider.credentials();
let chain_f = self.chain.credentials();
let f = static_provider_f.or_else(|_| chain_f);
Box::new(f)
} else {
let f = self.chain.credentials();
Box::new(f)
Expand Down
11 changes: 9 additions & 2 deletions aws/src/iam.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::AwsClientConfig;
use chrono::{DateTime, Utc};
use failure::{err_msg, Error};
use log::{debug, warn};
use rusoto_iam::{GetAccessKeyLastUsedRequest, Iam, IamClient, ListAccessKeysRequest, ListUsersRequest, UpdateAccessKeyRequest, DeleteAccessKeyRequest, DeleteLoginProfileRequest, DeleteUserRequest};
use log::{debug, error, warn};
use rusoto_iam::{GetAccessKeyLastUsedRequest, Iam, IamClient, ListAccessKeysRequest, ListUsersRequest, UpdateAccessKeyRequest, DeleteAccessKeyRequest, DeleteLoginProfileRequest, DeleteUserRequest, ListUsersError};
use std::str::FromStr;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -43,6 +43,13 @@ pub fn list_users(aws_client_config: &AwsClientConfig) -> Result<Vec<User>, Erro
};
let res = iam.list_users(request).sync();
debug!("Finished list user request; success={}.", res.is_ok());
match res {
Err(ListUsersError::Unknown(ref buf)) => {
let str = String::from_utf8_lossy(&buf.body);
error!("Error: {}", str);
}
_ => {}
}
let res = res.expect("failed to list users");

if log::max_level() >= log::Level::Warn && res.is_truncated.is_some() && res.is_truncated.unwrap() {
Expand Down
1 change: 1 addition & 0 deletions security-watchtower/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ log = "0.4"
prettytable-rs = "0.8"
reqwest = "0.9"
rusoto_core = "0.36"
rusoto_sts = "0.36"
serde = "1"
serde_derive = "1"
serde_json = "1"
Expand Down
53 changes: 45 additions & 8 deletions security-watchtower/src/events/cron.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use chrono::Utc;
use failure::Error;
use failure::{Error, Fail};
use lambda_runtime::Context;
use log::{debug, info, trace};
use log::{debug, error, info, trace};
use rusoto_sts::{StsClient, Sts, AssumeRoleRequest, AssumeRoleError};
use serde_derive::{Deserialize, Serialize};

use aws::{AwsClientConfig, Filter};
use aws::AwsClientConfig;
use bosun::{Bosun, Datum, Tags};
use duo::DuoClient;

Expand All @@ -15,6 +16,10 @@ use crate::check_credentials::{
use crate::config::{CredentialsConfig, FunctionConfig};
use crate::events::HandleResult;
use crate::metrics;
use aws::auth::{create_provider_with_static_provider};
use rusoto_core::Region;
use rusoto_core::credential::StaticProvider;
use lambda::error::LambdaError;

// cf. https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents.html
// {
Expand All @@ -41,7 +46,7 @@ pub struct ScheduledEvent {
}

pub fn handle<T: Bosun>(
aws_client_config: &AwsClientConfig,
_: &AwsClientConfig,
_: &Context,
config: &FunctionConfig,
bosun: &T,
Expand All @@ -54,13 +59,45 @@ pub fn handle<T: Bosun>(
&config.duo.secret_key,
)?;

let credentials = credentials(aws_client_config, &duo_client, &config.credentials, bosun)?;
let iam_role_arn = std::env::var("CD_IAM_ROLE_ARN").map_err(|e| e.context(LambdaError::FailedEnvVar("CD_IAM_ROLE_ARN")))?;
let iam_aws_client_config = assume_iam_role(iam_role_arn)?;

let credentials = get_credentials(&iam_aws_client_config, &duo_client, &config.credentials, bosun)?;

let handle_result = HandleResult::Cron { credentials };

Ok(handle_result)
}

fn assume_iam_role(iam_role_arn: String) -> Result<AwsClientConfig, Error> {
let sts = StsClient::new(Region::UsEast1);
let credentials = sts.assume_role(AssumeRoleRequest {
role_arn: iam_role_arn,
role_session_name: "Lambda2Iam".to_string(),
..Default::default()
}).sync();
match credentials {
Err(AssumeRoleError::Unknown(ref buf)) => {
let str = String::from_utf8_lossy(&buf.body);
error!("Error: {}", str);
}
_ => {}
}
let credentials = credentials?.credentials.unwrap(); // Safe unwrap, because the call was successfull
let static_provider = StaticProvider::new(
credentials.access_key_id,
credentials.secret_access_key,
Some(credentials.session_token),
None,
);

let credential_provider = create_provider_with_static_provider(static_provider)?;
let iam_aws_client_config =
AwsClientConfig::with_credentials_provider_and_region(credential_provider, Region::UsEast1)?;

Ok(iam_aws_client_config)
}

#[derive(Debug, Serialize)]
pub struct CredentialStats {
pub total: usize,
Expand All @@ -70,7 +107,7 @@ pub struct CredentialStats {
pub failed: usize,
}

pub fn credentials<T: Bosun>(
pub fn get_credentials<T: Bosun>(
aws_client_config: &AwsClientConfig,
duo_client: &DuoClient,
config: &CredentialsConfig,
Expand All @@ -79,11 +116,11 @@ pub fn credentials<T: Bosun>(
debug!("Config: {:?}", config);
let mut credentials = check_duo_credentials(&duo_client).expect("Failed to get Duo credentials");
debug!("Retrieved DUO credentials: {}", credentials.len());
/*

let aws_credentials = check_aws_credentials(&aws_client_config).expect("failed to load credentials");
debug!("Retrieved AWS credentials: {}", aws_credentials.len());
credentials.extend(aws_credentials);
*/

bosun_emit_credential_last_used(bosun, &credentials)?;

debug!("Checking for inactive credentials");
Expand Down

0 comments on commit f105b6e

Please sign in to comment.