use crate::aws::{
deployer_directory,
ec2::{self, *},
utils::{exact_cidr, get_public_ip, DEPLOYER_MAX_PORT, DEPLOYER_MIN_PORT, DEPLOYER_PROTOCOL},
Config, Error, CREATED_FILE_NAME, DESTROYED_FILE_NAME, MONITORING_REGION,
};
use std::{collections::HashSet, fs::File, path::PathBuf};
use tracing::info;
pub async fn authorize(config_path: &PathBuf, ip: Option<String>) -> Result<(), Error> {
let config: Config = {
let config_file = File::open(config_path)?;
serde_yaml::from_reader(config_file)?
};
let tag = &config.tag;
info!(tag = tag.as_str(), "loaded configuration");
let tag_directory = deployer_directory(Some(tag));
if !tag_directory.exists() {
return Err(Error::DeploymentDoesNotExist(tag.clone()));
}
let created_file = tag_directory.join(CREATED_FILE_NAME);
if !created_file.exists() {
return Err(Error::DeploymentNotComplete(tag.clone()));
}
let destroyed_file = tag_directory.join(DESTROYED_FILE_NAME);
if destroyed_file.exists() {
return Err(Error::DeploymentAlreadyDestroyed(tag.clone()));
}
let new_ip = if let Some(ip_str) = ip {
let ip_addr: std::net::IpAddr = ip_str.parse()?;
let std::net::IpAddr::V4(ipv4) = ip_addr else {
return Err(Error::IpAddrNotV4(ip_addr));
};
ipv4.to_string()
} else {
get_public_ip().await?
};
info!(
ip = new_ip.as_str(),
"adding IP to existing security groups"
);
let cidr = exact_cidr(&new_ip);
let mut all_regions = HashSet::new();
all_regions.insert(MONITORING_REGION.to_string());
for instance in &config.instances {
all_regions.insert(instance.region.clone());
}
let mut changes = Vec::new();
for region in all_regions {
let ec2_client = ec2::create_client(Region::new(region.clone())).await;
info!(region = region.as_str(), "created EC2 client");
let security_groups = find_security_groups_by_tag(&ec2_client, tag).await?;
for sg in security_groups {
let sg_id = sg.group_id().unwrap();
let mut already_allowed = false;
for perm in sg.ip_permissions() {
if perm.ip_protocol() == Some(DEPLOYER_PROTOCOL)
&& perm.from_port() == Some(DEPLOYER_MIN_PORT)
&& perm.to_port() == Some(DEPLOYER_MAX_PORT)
{
for ip_range in perm.ip_ranges() {
if ip_range.cidr_ip() == Some(&cidr) {
already_allowed = true;
break;
}
}
if already_allowed {
break;
}
}
}
if already_allowed {
info!(sg_id, "IP already allowed");
continue;
}
ec2_client
.authorize_security_group_ingress()
.group_id(sg_id)
.ip_permissions(
IpPermission::builder()
.ip_protocol(DEPLOYER_PROTOCOL)
.from_port(DEPLOYER_MIN_PORT)
.to_port(DEPLOYER_MAX_PORT)
.ip_ranges(IpRange::builder().cidr_ip(&cidr).build())
.build(),
)
.send()
.await
.map_err(|err| err.into_service_error())?;
info!(sg_id, "added ingress rule for IP");
changes.push(sg_id.to_string());
}
}
info!(?changes, "IP added to security groups");
Ok(())
}