Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wrap secrets in custom types to prevent them from leaking #925

Merged
merged 8 commits into from
Oct 25, 2023
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.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,4 @@ utoipa = { version = "3.2.1", features = [ "uuid", "chrono" ] }
utoipa-swagger-ui = { version = "3.1.3", features = ["axum"] }
ulid = "1.0.0"
uuid = "1.2.2"
zeroize = "1.6.0"
11 changes: 5 additions & 6 deletions auth/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use axum::{
use serde::{Deserialize, Deserializer, Serialize};
use shuttle_common::{
claims::{Scope, ScopeBuilder},
secrets::Secret,
ApiKey,
};
use sqlx::{query, sqlite::SqliteRow, FromRow, Row, SqlitePool};
Expand Down Expand Up @@ -173,7 +174,7 @@ impl UserManagement for UserManager {
#[derive(Clone, Deserialize, PartialEq, Eq, Serialize, Debug)]
pub struct User {
pub name: AccountName,
pub key: ApiKey,
pub key: Secret<ApiKey>,
pub account_tier: AccountTier,
pub subscription_id: Option<SubscriptionId>,
}
Expand All @@ -191,7 +192,7 @@ impl User {
) -> Self {
Self {
name,
key,
key: Secret::new(key),
account_tier,
subscription_id,
}
Expand Down Expand Up @@ -237,11 +238,9 @@ impl User {

impl FromRow<'_, SqliteRow> for User {
fn from_row(row: &SqliteRow) -> Result<Self, sqlx::Error> {
let x: &str = row.try_get("subscription_id").unwrap();
println!("{:?}", x);
Ok(User {
name: row.try_get("account_name").unwrap(),
key: row.try_get("key").unwrap(),
key: Secret::new(row.try_get("key").unwrap()),
account_tier: row.try_get("account_tier").unwrap(),
subscription_id: row
.try_get("subscription_id")
Expand Down Expand Up @@ -281,7 +280,7 @@ impl From<User> for shuttle_common::models::user::Response {
fn from(user: User) -> Self {
Self {
name: user.name.to_string(),
key: user.key.as_ref().to_string(),
key: user.key.expose().as_ref().to_owned(),
account_tier: user.account_tier.to_string(),
subscription_id: user.subscription_id.map(|inner| inner.to_string()),
}
Expand Down
7 changes: 7 additions & 0 deletions cargo-shuttle/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ pub enum ResourceCommand {
#[arg(long, default_value_t = false)]
/// Output table in `raw` format
raw: bool,

#[arg(
long,
default_value_t = false,
help = "Show secrets from resources (e.g. a password in a connection string)"
)]
show_secrets: bool,
},
/// Delete a resource
Delete {
Expand Down
9 changes: 5 additions & 4 deletions cargo-shuttle/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};
use shuttle_common::models::deployment::DeploymentRequest;
use shuttle_common::models::{deployment, project, secret, service, ToJson};
use shuttle_common::project::ProjectName;
use shuttle_common::secrets::Secret;
use shuttle_common::{resource, ApiKey, ApiUrl, LogItem, VersionInfo};
use tokio::net::TcpStream;
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
Expand All @@ -19,7 +20,7 @@ use uuid::Uuid;
#[derive(Clone)]
pub struct Client {
api_url: ApiUrl,
api_key: Option<ApiKey>,
api_key: Option<Secret<ApiKey>>,
client: reqwest::Client,
retry_client: ClientWithMiddleware,
}
Expand All @@ -41,7 +42,7 @@ impl Client {
}

pub fn set_api_key(&mut self, api_key: ApiKey) {
self.api_key = Some(api_key);
self.api_key = Some(Secret::new(api_key));
}

pub async fn get_api_versions(&self) -> Result<VersionInfo> {
Expand Down Expand Up @@ -279,7 +280,7 @@ impl Client {
let mut request = url.into_client_request()?;

if let Some(ref api_key) = self.api_key {
let auth_header = Authorization::bearer(api_key.as_ref())?;
let auth_header = Authorization::bearer(api_key.expose().as_ref())?;
request.headers_mut().typed_insert(auth_header);
}

Expand Down Expand Up @@ -361,7 +362,7 @@ impl Client {

fn set_builder_auth(&self, builder: RequestBuilder) -> RequestBuilder {
if let Some(ref api_key) = self.api_key {
builder.bearer_auth(api_key.as_ref())
builder.bearer_auth(api_key.expose().as_ref())
} else {
builder
}
Expand Down
18 changes: 13 additions & 5 deletions cargo-shuttle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ impl Shuttle {
self.deployments_list(page, limit, raw).await
}
Command::Deployment(DeploymentCommand::Status { id }) => self.deployment_get(id).await,
Command::Resource(ResourceCommand::List { raw }) => self.resources_list(raw).await,
Command::Resource(ResourceCommand::List { raw, show_secrets }) => {
self.resources_list(raw, show_secrets).await
}
Command::Stop => self.stop().await,
Command::Clean => self.clean().await,
Command::Secrets { raw } => self.secrets(raw).await,
Expand Down Expand Up @@ -758,13 +760,18 @@ impl Shuttle {
Ok(CommandOutcome::Ok)
}

async fn resources_list(&self, raw: bool) -> Result<CommandOutcome> {
async fn resources_list(&self, raw: bool, show_secrets: bool) -> Result<CommandOutcome> {
let client = self.client.as_ref().unwrap();
let resources = client
.get_service_resources(self.ctx.project_name())
.await
.map_err(suggestions::resources::get_service_resources_failure)?;
let table = get_resources_table(&resources, self.ctx.project_name().as_str(), raw);
let table = get_resources_table(
&resources,
self.ctx.project_name().as_str(),
raw,
show_secrets,
);

println!("{table}");

Expand Down Expand Up @@ -989,7 +996,7 @@ impl Shuttle {

println!(
"{}",
get_resources_table(&resources, service_name.as_str(), false)
get_resources_table(&resources, service_name.as_str(), false, false)
);

let addr = SocketAddr::new(
Expand Down Expand Up @@ -1609,7 +1616,8 @@ impl Shuttle {
let resources = client
.get_service_resources(self.ctx.project_name())
.await?;
let resources = get_resources_table(&resources, self.ctx.project_name().as_str(), false);
let resources =
get_resources_table(&resources, self.ctx.project_name().as_str(), false, false);

println!("{resources}{service}");

Expand Down
19 changes: 11 additions & 8 deletions cargo-shuttle/src/provisioner_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ use crossterm::{
};
use futures::StreamExt;
use portpicker::pick_unused_port;
use shuttle_common::database::{AwsRdsEngine, SharedEngine};
use shuttle_common::{
database::{AwsRdsEngine, SharedEngine},
secrets::Secret,
};
use shuttle_proto::provisioner::{
provisioner_server::{Provisioner, ProvisionerServer},
DatabaseDeletionResponse, DatabaseRequest, DatabaseResponse, Ping, Pong,
Expand Down Expand Up @@ -166,7 +169,7 @@ impl LocalProvisioner {
let res = DatabaseResponse {
engine,
username,
password,
password: password.expose().to_owned(),
database_name,
port,
address_private: "localhost".to_string(),
Expand Down Expand Up @@ -333,7 +336,7 @@ struct EngineConfig {
image: String,
engine: String,
username: String,
password: String,
password: Secret<String>,
database_name: String,
port: String,
env: Option<Vec<String>>,
Expand All @@ -347,7 +350,7 @@ fn db_type_to_config(db_type: Type) -> EngineConfig {
image: "docker.io/library/postgres:14".to_string(),
engine: "postgres".to_string(),
username: "postgres".to_string(),
password: "postgres".to_string(),
password: "postgres".to_string().into(),
database_name: "postgres".to_string(),
port: "5432/tcp".to_string(),
env: Some(vec!["POSTGRES_PASSWORD=postgres".to_string()]),
Expand All @@ -362,7 +365,7 @@ fn db_type_to_config(db_type: Type) -> EngineConfig {
image: "docker.io/library/mongo:5.0.10".to_string(),
engine: "mongodb".to_string(),
username: "mongodb".to_string(),
password: "password".to_string(),
password: "password".to_string().into(),
database_name: "admin".to_string(),
port: "27017/tcp".to_string(),
env: Some(vec![
Expand All @@ -381,7 +384,7 @@ fn db_type_to_config(db_type: Type) -> EngineConfig {
image: "docker.io/library/postgres:13.4".to_string(),
engine: "postgres".to_string(),
username: "postgres".to_string(),
password: "postgres".to_string(),
password: "postgres".to_string().into(),
database_name: "postgres".to_string(),
port: "5432/tcp".to_string(),
env: Some(vec!["POSTGRES_PASSWORD=postgres".to_string()]),
Expand All @@ -396,7 +399,7 @@ fn db_type_to_config(db_type: Type) -> EngineConfig {
image: "docker.io/library/mariadb:10.6.7".to_string(),
engine: "mariadb".to_string(),
username: "root".to_string(),
password: "mariadb".to_string(),
password: "mariadb".to_string().into(),
database_name: "mysql".to_string(),
port: "3306/tcp".to_string(),
env: Some(vec!["MARIADB_ROOT_PASSWORD=mariadb".to_string()]),
Expand All @@ -413,7 +416,7 @@ fn db_type_to_config(db_type: Type) -> EngineConfig {
image: "docker.io/library/mysql:8.0.28".to_string(),
engine: "mysql".to_string(),
username: "root".to_string(),
password: "mysql".to_string(),
password: "mysql".to_string().into(),
database_name: "mysql".to_string(),
port: "3306/tcp".to_string(),
env: Some(vec!["MYSQL_ROOT_PASSWORD=mysql".to_string()]),
Expand Down
1 change: 1 addition & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ tracing-subscriber = { workspace = true, optional = true }
ttl_cache = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true }
uuid = { workspace = true, features = ["v4", "serde"], optional = true }
zeroize = { workspace = true }

[features]
backend = [
Expand Down
Loading