use crate::region::Region;
pub(super) fn require_egress_connector_arn(arn: &str, region: &Region) -> Result<(), crate::Error> {
let parts: Vec<&str> = arn.splitn(6, ':').collect();
let valid = 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.as_str()
&& 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()
});
if !valid {
return Err(crate::Error::invalid_arg(format!(
"egressNetworkConnectors requires a customer-managed Lambda network connector ARN \
in {} (arn:aws:lambda:{}:<12-digit-account>:network-connector:<id>[:<version>]); \
create the VPC egress connector through lambda-core first",
region.as_str(),
region.as_str(),
)));
}
Ok(())
}
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::UsEast1).expect("at the request limit");
require_egress_connector_arn(&format!("{arn}x"), &Region::UsEast1)
.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"
);
}
#[test]
fn the_ingress_arn_is_the_measured_literal() {
assert_eq!(
ConnectorIntent::AllIngress.arn(&Region::UsEast1),
"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::UsEast1),
"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 region in crate::region::MICROVM_REGIONS {
let arn = ConnectorIntent::AllIngress.arn(®ion);
assert!(
arn.contains(&format!(":lambda:{}:aws:", region.as_str())),
"{arn}"
);
}
let unlisted = Region::unlisted("me-south-1");
assert!(
ConnectorIntent::Egress
.arn(&unlisted)
.contains(":lambda:me-south-1:aws:"),
"the escape hatch 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::UsEast1),
"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::UsEast1),
"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::EuWest1))
.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 region in crate::region::MICROVM_REGIONS {
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}");
}
}
}
}