use crate::region::Region;
pub(super) fn require_egress_connector_arn(arn: &str, region: &Region) -> Result<(), crate::Error> {
if !is_connector_arn_in(arn, region.as_str()) {
return Err(connector_arn_error(region.as_str()));
}
Ok(())
}
fn connector_arn_error(region: &str) -> crate::Error {
crate::Error::invalid_arg(format!(
"egressNetworkConnectors requires a customer-managed Lambda network connector ARN \
in {region} (arn:aws:lambda:{region}:<12-digit-account>:network-connector:<id>[:<version>]); \
create the VPC egress connector through lambda-core first",
))
}
fn is_connector_arn_in(arn: &str, region: &str) -> bool {
let parts: Vec<&str> = arn.splitn(6, ':').collect();
parts.len() == 6
&& (1..=crate::constants::MAX_NETWORK_CONNECTOR_LEN).contains(&arn.len())
&& parts[0] == "arn"
&& parts[1] == "aws"
&& parts[2] == "lambda"
&& parts[3] == region
&& parts[4].len() == 12
&& parts[4].bytes().all(|byte| byte.is_ascii_digit())
&& parts[5]
.strip_prefix("network-connector:")
.is_some_and(|resource| {
let mut parts = resource.split(':');
let name = parts.next().unwrap_or_default();
let version = parts.next();
!name.is_empty()
&& name
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_'))
&& version.is_none_or(|version| {
!version.is_empty()
&& !version.starts_with('0')
&& version.bytes().all(|byte| byte.is_ascii_digit())
})
&& parts.next().is_none()
})
}
pub(crate) fn egress_with_connectors() -> crate::Error {
crate::Error::invalid_arg(
"INTERNET_EGRESS cannot be combined with customer-managed egress connectors: \
choose managed internet egress or VPC routing",
)
}
pub(crate) fn egress_with_deny() -> crate::Error {
crate::Error::invalid_arg(
"egress and deny_egress ask for opposite things: egress puts the \
INTERNET_EGRESS connector on the launch, and deny_egress sets the guest's \
proxy variables to a black hole so a well-behaved client refuses to leave \
the VM. Pick one. Neither seals the VM — omitting the connector is measured \
not to (docs/PLATFORM.md), and the deny is advisory.",
)
}
pub(crate) fn over_connector_ceiling(member: &str, count: usize) -> Option<crate::Error> {
(count > crate::constants::MAX_NETWORK_CONNECTORS).then(|| {
crate::Error::invalid_arg(format!(
"{member} has {count} network connectors, over the NetworkConnectorList \
ceiling of {} (service model {}).",
crate::constants::MAX_NETWORK_CONNECTORS,
crate::constants::MODEL_API_VERSION,
))
})
}
pub fn egress_posture_for(
egress: bool,
egress_network_connectors: &[String],
deny_egress: bool,
region: Option<&Region>,
) -> Result<EgressPosture, crate::Error> {
if egress && deny_egress {
return Err(egress_with_deny());
}
if egress && !egress_network_connectors.is_empty() {
return Err(egress_with_connectors());
}
for arn in egress_network_connectors {
match region {
Some(region) => require_egress_connector_arn(arn, region)?,
None => {
let named = arn.split(':').nth(3).unwrap_or_default();
let plausible = !named.is_empty()
&& named.bytes().all(|byte| {
byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-'
});
if !(plausible && is_connector_arn_in(arn, named)) {
return Err(connector_arn_error(if plausible {
named
} else {
"<region>"
}));
}
}
}
}
if let Some(refusal) =
over_connector_ceiling("egressNetworkConnectors", egress_network_connectors.len())
{
return Err(refusal);
}
Ok(EgressPosture::for_launch(egress, deny_egress))
}
pub const PLATFORM_HONOURS_OMITTED_EGRESS: bool = false;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EgressPosture {
Open,
Unsealed,
BestEffort,
Sealed,
}
impl EgressPosture {
pub fn for_launch(egress: bool, deny_egress: bool) -> Self {
match (egress, deny_egress) {
(true, _) => EgressPosture::Open,
(false, true) => EgressPosture::BestEffort,
(false, false) => EgressPosture::Unsealed,
}
}
pub fn as_str(self) -> &'static str {
match self {
EgressPosture::Open => "open",
EgressPosture::Unsealed => "unsealed",
EgressPosture::BestEffort => "best-effort",
EgressPosture::Sealed => "sealed",
}
}
pub fn is_sealed(self) -> bool {
matches!(self, EgressPosture::Sealed)
}
pub fn from_label(label: &str) -> Option<Self> {
[
EgressPosture::Open,
EgressPosture::Unsealed,
EgressPosture::BestEffort,
EgressPosture::Sealed,
]
.into_iter()
.find(|posture| posture.as_str() == label)
}
pub fn describe(self) -> &'static str {
match self {
EgressPosture::Open => {
"the INTERNET_EGRESS connector is on the request; the VM reaches the \
internet by design"
}
EgressPosture::Unsealed => {
"internet isolation is unverified. Use a VPC egress connector with a VPC \
without an internet gateway or NAT gateway; connector omission does not \
block internet access. Not a seal"
}
EgressPosture::BestEffort => {
"advisory proxy-deny environment variables affect clients that honor them; \
workloads can bypass them. Not a seal"
}
EgressPosture::Sealed => {
"internet isolation requires separately verified VPC routing without an \
internet gateway or NAT gateway; launch flags alone do not establish it"
}
}
}
}
impl Default for EgressPosture {
fn default() -> Self {
EgressPosture::for_launch(false, false)
}
}
impl std::fmt::Display for EgressPosture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ConnectorIntent {
AllIngress,
HttpIngress,
ShellIngress,
Egress,
}
impl ConnectorIntent {
pub const ALL: [ConnectorIntent; 4] = [
ConnectorIntent::AllIngress,
ConnectorIntent::HttpIngress,
ConnectorIntent::ShellIngress,
ConnectorIntent::Egress,
];
pub fn wire_name(self) -> &'static str {
match self {
ConnectorIntent::AllIngress => "ALL_INGRESS",
ConnectorIntent::HttpIngress => "HTTP_INGRESS",
ConnectorIntent::ShellIngress => "SHELL_INGRESS",
ConnectorIntent::Egress => "INTERNET_EGRESS",
}
}
pub fn arn(self, region: &Region) -> String {
format!(
"arn:aws:lambda:{}:aws:network-connector:aws-network-connector:{}",
region.as_str(),
self.wire_name()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn customer_connector_arn_length_uses_the_run_microvm_limit() {
let prefix = "arn:aws:lambda:us-east-1:123456789012:network-connector:";
let maximum = crate::constants::MAX_NETWORK_CONNECTOR_LEN;
let arn = format!("{prefix}{}", "x".repeat(maximum - prefix.len()));
require_egress_connector_arn(&arn, &Region::US_EAST_1).expect("at the request limit");
require_egress_connector_arn(&format!("{arn}x"), &Region::US_EAST_1)
.expect_err("one over the request limit");
}
#[test]
fn a_launch_with_no_connector_is_unsealed_and_not_sealed() {
let posture = EgressPosture::for_launch(false, false);
assert_eq!(posture, EgressPosture::Unsealed);
assert_eq!(posture.as_str(), "unsealed");
assert!(
!posture.is_sealed(),
"omitting the connector does not seal the VM; it is measured not to"
);
}
#[test]
fn the_posture_table_is_the_whole_domain() {
assert_eq!(EgressPosture::for_launch(true, false), EgressPosture::Open);
assert_eq!(
EgressPosture::for_launch(true, true),
EgressPosture::Open,
"asking for egress is asking for egress; the advisory deny cannot dress it as \
anything narrower"
);
assert_eq!(
EgressPosture::for_launch(false, true),
EgressPosture::BestEffort
);
assert_eq!(EgressPosture::BestEffort.as_str(), "best-effort");
assert!(
!EgressPosture::BestEffort.is_sealed(),
"best effort is not a seal, and this predicate is the only place that decides it"
);
assert!(EgressPosture::Sealed.is_sealed());
}
#[test]
fn every_posture_has_a_distinct_label_and_a_description() {
let postures = [
EgressPosture::Open,
EgressPosture::Unsealed,
EgressPosture::BestEffort,
EgressPosture::Sealed,
];
let labels: Vec<&str> = postures.iter().map(|posture| posture.as_str()).collect();
for (i, a) in labels.iter().enumerate() {
assert!(!a.is_empty());
for b in &labels[i + 1..] {
assert_ne!(a, b);
}
}
for posture in postures {
assert!(posture.describe().len() > 40, "{posture}");
}
for posture in [EgressPosture::Unsealed, EgressPosture::BestEffort] {
assert!(
posture.describe().contains("Not a seal"),
"{posture} must state that it is not a seal: {}",
posture.describe()
);
}
}
#[test]
fn the_default_posture_is_the_derived_one() {
assert_eq!(
EgressPosture::default(),
EgressPosture::for_launch(false, false),
"a defaulted posture must be the posture of a launch that asks for nothing, \
derived from the same launch options"
);
}
#[test]
fn every_label_round_trips_and_an_unknown_one_is_none() {
for posture in [
EgressPosture::Open,
EgressPosture::Unsealed,
EgressPosture::BestEffort,
EgressPosture::Sealed,
] {
assert_eq!(EgressPosture::from_label(posture.as_str()), Some(posture));
}
assert_eq!(EgressPosture::from_label("sealed-ish"), None);
assert_eq!(EgressPosture::from_label(""), None);
assert_eq!(
EgressPosture::from_label("Open"),
None,
"the labels are the wire spelling, and case is part of it"
);
}
fn vpc(region: &str, index: usize) -> String {
format!("arn:aws:lambda:{region}:123456789012:network-connector:vpc-{index}")
}
#[test]
fn the_posture_of_launch_options_is_the_models_table() {
use EgressPosture::{BestEffort, Open, Unsealed};
type Row = (bool, usize, bool, bool, Result<EgressPosture, &'static str>);
let region = Region::US_EAST_1;
let malformed = "network-connector:vpc-0".to_string();
let table: [Row; 14] = [
(false, 0, false, false, Ok(Unsealed)),
(false, 0, false, true, Ok(BestEffort)),
(false, 1, false, false, Ok(Unsealed)),
(false, 1, false, true, Ok(BestEffort)),
(false, 10, false, false, Ok(Unsealed)),
(false, 1, true, false, Err("network connector ARN")),
(false, 11, false, false, Err("NetworkConnectorList ceiling")),
(false, 11, false, true, Err("NetworkConnectorList ceiling")),
(false, 11, true, false, Err("network connector ARN")),
(true, 0, false, false, Ok(Open)),
(true, 0, false, true, Err("opposite things")),
(
true,
1,
false,
false,
Err("INTERNET_EGRESS cannot be combined"),
),
(true, 1, false, true, Err("opposite things")),
(
true,
1,
true,
false,
Err("INTERNET_EGRESS cannot be combined"),
),
];
for (egress, count, bad, deny, expected) in table {
let mut connectors: Vec<String> = (0..count).map(|i| vpc("us-east-1", i)).collect();
if bad {
connectors[0] = malformed.clone();
}
let answer = egress_posture_for(egress, &connectors, deny, Some(®ion));
let row = format!("egress={egress} connectors={count} malformed={bad} deny={deny}");
match (answer, expected) {
(Ok(posture), Ok(want)) => assert_eq!(posture, want, "{row}"),
(Err(error), Err(needle)) => {
assert_eq!(error.kind(), crate::ErrorKind::InvalidArg, "{row}: {error}");
assert!(error.to_string().contains(needle), "{row}: {error}");
}
(answer, expected) => panic!("{row}: {answer:?}, expected {expected:?}"),
}
}
}
#[test]
fn no_launch_options_answer_sealed() {
for egress in [false, true] {
for deny in [false, true] {
for count in 0..=crate::constants::MAX_NETWORK_CONNECTORS + 1 {
let connectors: Vec<String> = (0..count).map(|i| vpc("us-east-1", i)).collect();
if let Ok(posture) =
egress_posture_for(egress, &connectors, deny, Some(&Region::US_EAST_1))
{
assert!(!posture.is_sealed(), "{egress} {count} {deny}");
}
}
}
}
}
#[test]
fn a_connector_is_checked_against_the_launch_region_when_there_is_one() {
let elsewhere = [vpc("eu-west-1", 0)];
assert_eq!(
egress_posture_for(false, &elsewhere, false, None).expect("a well-formed ARN"),
EgressPosture::Unsealed
);
let error = egress_posture_for(false, &elsewhere, false, Some(&Region::US_EAST_1))
.expect_err("an ARN from another region cannot attach to a us-east-1 launch");
assert!(error.to_string().contains("in us-east-1"), "{error}");
let error = egress_posture_for(false, &["not-an-arn".into()], false, None)
.expect_err("a string that is no ARN names no region either");
assert_eq!(error.kind(), crate::ErrorKind::InvalidArg);
}
#[test]
fn the_ingress_arn_is_the_measured_literal() {
assert_eq!(
ConnectorIntent::AllIngress.arn(&Region::US_EAST_1),
"arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:ALL_INGRESS"
);
}
#[test]
fn the_egress_arn_uses_the_internet_egress_wire_name() {
assert_eq!(
ConnectorIntent::Egress.arn(&Region::US_EAST_1),
"arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:INTERNET_EGRESS"
);
}
#[test]
fn the_arn_carries_the_request_region_for_every_region() {
for name in crate::region::MEASURED_REGIONS {
let region: Region = name.parse().expect("a measured name parses");
let arn = ConnectorIntent::AllIngress.arn(®ion);
assert!(
arn.contains(&format!(":lambda:{}:aws:", region.as_str())),
"{arn}"
);
}
let unlisted: Region = "me-south-1".parse().expect("a region name");
assert!(
ConnectorIntent::Egress
.arn(&unlisted)
.contains(":lambda:me-south-1:aws:"),
"an unlisted region still derives an ARN for its own region"
);
}
#[test]
fn shell_ingress_is_one_deliberate_intent() {
assert_eq!(
ConnectorIntent::ALL.len(),
4,
"four intents, and shell is deliberately one of them"
);
let shells: Vec<ConnectorIntent> = ConnectorIntent::ALL
.into_iter()
.filter(|intent| intent.wire_name().contains("SHELL"))
.collect();
assert_eq!(
shells,
vec![ConnectorIntent::ShellIngress],
"exactly one intent names the shell, and it is the one that says so"
);
assert_eq!(
ConnectorIntent::ShellIngress.arn(&Region::US_EAST_1),
"arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:SHELL_INGRESS"
);
}
#[test]
fn the_http_ingress_arn_is_the_measured_literal() {
assert_eq!(
ConnectorIntent::HttpIngress.arn(&Region::US_EAST_1),
"arn:aws:lambda:us-east-1:aws:network-connector:aws-network-connector:HTTP_INGRESS"
);
}
#[test]
fn no_two_intents_render_the_same_arn() {
let arns: Vec<String> = ConnectorIntent::ALL
.iter()
.map(|intent| intent.arn(&Region::EU_WEST_1))
.collect();
for (i, a) in arns.iter().enumerate() {
for b in &arns[i + 1..] {
assert_ne!(a, b);
}
}
assert!(!arns[0].contains("INTERNET"), "{}", arns[0]);
}
#[test]
fn every_derived_arn_fits_the_models_connector_bounds() {
for name in crate::region::MEASURED_REGIONS {
let region: Region = name.parse().expect("a measured name parses");
for intent in ConnectorIntent::ALL {
let arn = intent.arn(®ion);
assert!((1..=2048).contains(&arn.len()), "{arn}");
assert!(arn.starts_with("arn:aws:lambda:"), "{arn}");
}
}
}
}