Skip to content

Commit

Permalink
Merge pull request fermyon#1959 from fermyon/address-checks-mysql-pos…
Browse files Browse the repository at this point in the history
…tgres

Add address checks for mysql and postgres
  • Loading branch information
rylev authored Oct 27, 2023
2 parents ed4e13f + 2692d57 commit 47e40b9
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 124 deletions.
8 changes: 6 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/manifest/src/schema/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub struct Component {
/// `allowed_http_hosts = ["example.com"]`
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub allowed_http_hosts: Vec<String>,
/// `allowed_outbound_hosts = ["myredishost.com"]`
/// `allowed_outbound_hosts = ["myredishost.com:6379"]`
#[serde(default, skip_serializing_if = "is_none_or_empty")]
pub allowed_outbound_hosts: Option<Vec<String>>,
/// `key_value_stores = ["default"]`
Expand Down
2 changes: 2 additions & 0 deletions crates/outbound-mysql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ mysql_async = { version = "0.32.2", default-features = false, features = [
] }
# Removing default features for mysql_common to remove flate2/zlib feature
mysql_common = { version = "0.30.6", default-features = false }
spin-app = { path = "../app" }
spin-core = { path = "../core" }
spin-outbound-networking = { path = "../outbound-networking" }
spin-world = { path = "../world" }
table = { path = "../table" }
tokio = { version = "1", features = ["rt-multi-thread"] }
Expand Down
35 changes: 34 additions & 1 deletion crates/outbound-mysql/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::Result;
use anyhow::{Context, Result};
use mysql_async::{consts::ColumnType, from_value_opt, prelude::*, Opts, OptsBuilder, SslOpts};
use spin_app::DynamicHostComponent;
use spin_core::wasmtime::component::Resource;
use spin_core::{async_trait, HostComponent};
use spin_world::v1::mysql as v1;
Expand All @@ -12,6 +13,7 @@ use url::Url;
/// A simple implementation to support outbound mysql connection
#[derive(Default)]
pub struct OutboundMysql {
allowed_hosts: Option<spin_outbound_networking::AllowedHosts>,
pub connections: table::Table<mysql_async::Conn>,
}

Expand All @@ -24,6 +26,10 @@ impl OutboundMysql {
.get_mut(connection.rep())
.ok_or_else(|| v2::Error::ConnectionFailed("no connection found".into()))
}

fn is_address_allowed(&self, address: &str, default: bool) -> bool {
spin_outbound_networking::check_address(address, "mysql", &self.allowed_hosts, default)
}
}

impl HostComponent for OutboundMysql {
Expand All @@ -42,11 +48,33 @@ impl HostComponent for OutboundMysql {
}
}

impl DynamicHostComponent for OutboundMysql {
fn update_data(
&self,
data: &mut Self::Data,
component: &spin_app::AppComponent,
) -> anyhow::Result<()> {
let hosts = component
.get_metadata(spin_outbound_networking::ALLOWED_HOSTS_KEY)?
.unwrap_or_default();
data.allowed_hosts = hosts
.map(|h| spin_outbound_networking::AllowedHosts::parse(&h[..]))
.transpose()
.context("`allowed_outbound_hosts` contained an invalid url")?;
Ok(())
}
}

impl v2::Host for OutboundMysql {}

#[async_trait]
impl v2::HostConnection for OutboundMysql {
async fn open(&mut self, address: String) -> Result<Result<Resource<Connection>, v2::Error>> {
if !self.is_address_allowed(&address, false) {
return Ok(Err(v2::Error::ConnectionFailed(format!(
"address {address} is not permitted"
))));
}
Ok(async {
self.connections
.push(
Expand Down Expand Up @@ -125,6 +153,11 @@ impl v2::HostConnection for OutboundMysql {
/// Delegate a function call to the v2::HostConnection implementation
macro_rules! delegate {
($self:ident.$name:ident($address:expr, $($arg:expr),*)) => {{
if !$self.is_address_allowed(&$address, true) {
return Ok(Err(v1::MysqlError::ConnectionFailed(format!(
"address {} is not permitted", $address
))));
}
let connection = match <Self as v2::HostConnection>::open($self, $address).await? {
Ok(c) => c,
Err(e) => return Ok(Err(e.into())),
Expand Down
2 changes: 2 additions & 0 deletions crates/outbound-networking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ edition.workspace = true

[dependencies]
anyhow = "1.0"
spin-locked-app = { path = "../locked-app" }
terminal = { path = "../terminal" }
url = "2.4.1"
Loading

0 comments on commit 47e40b9

Please sign in to comment.