ava-labs/avalanche-ops

Expose arbitrary port

gyuho opened this issue · 3 comments

gyuho commented

Should allow arbitrary port from user and expose that to a certain IP or public.

gyuho commented

Some example workflow...

-        let parameters = Vec::from([
+        let mut parameters = Vec::from([
             build_param("Id", &spec.id),
             build_param("UserId", &aws_resources.identity.user_id),
             build_param("VpcCidr", "10.0.0.0/16"),
             build_param("PublicSubnetCidr1", "10.0.64.0/19"),
             build_param("PublicSubnetCidr2", "10.0.128.0/19"),
             build_param("PublicSubnetCidr3", "10.0.192.0/19"),
-            build_param("SshPortIngressIpv4Range", "0.0.0.0/0"),
-            build_param("HttpPortIngressIpv4Range", "0.0.0.0/0"),
+            build_param(
+                "SshPortIngressIpv4Range",
+                if !spec.aws_resources.ssh_ingress_ipv4_cidr.is_empty() {
+                    spec.aws_resources.ssh_ingress_ipv4_cidr.as_str()
+                } else {
+                    "0.0.0.0/0"
+                },
+            ),
+            build_param(
+                "UserDefinedTcpIngressPortsIngressIpv4Range",
+                if !spec
+                    .aws_resources
+                    .user_defined_tcp_ingress_ports_ipv4_cidr
+                    .is_empty()
+                {
+                    spec.aws_resources
+                        .user_defined_tcp_ingress_ports_ipv4_cidr
+                        .as_str()
+                } else {
+                    "0.0.0.0/0"
+                },
+            ),
         ]);
+        if !spec.aws_resources.user_defined_tcp_ingress_ports.is_empty() {
+            let mut ports_in_str: Vec<String> = Vec::new();
+            for p in spec.aws_resources.user_defined_tcp_ingress_ports.iter() {
+                ports_in_str.push(format!("{}", p));
+            }
+            parameters.push(build_param(
+                "UserDefinedTcpIngressPorts",
+                ports_in_str.join(",").as_str(),
+            ));
+            parameters.push(build_param(
+                "UserDefinedTcpIngressPortsCount",
+                format!("{}", ports_in_str.len()).as_str(),
+            ));
+        } else {
+            parameters.push(build_param("UserDefinedTcpIngressPortsCount", "0"));
+        }
 
+  UserDefinedTcpIngressPorts:
+    Type: CommaDelimitedList
+    Default: 0
+    Description: User-defined ports to TCP open for ingress traffic
+
+  UserDefinedTcpIngressPortsCount:
+    Type: Number
+    Default: 0
+    MinValue: 0
+    MaxValue: 10
+    Description: The number of instance types (must be >0 to enable)
+
   # in case, we deploy some sample app
   # limit this to your IP
-  HttpPortIngressIpv4Range:
+  UserDefinedTcpIngressPortsIngressIpv4Range:
     Type: String
     Default: 0.0.0.0/0
     AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
-    Description: IP range for HTTP inbound traffic
-
-  HttpPort:
-    Type: Number
-    Default: 3000
-    Description: HTTP port
+    Description: IP range for user-defined TCP inbound traffic
 
 Conditions:
   Has2Azs:
@@ -84,6 +91,173 @@ Conditions:
     Fn::Not:
       - Condition: Has2Azs
 
+  Has1UserDefinedTcpIngressPorts:
+    Fn::Not:
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
+          - 0
+
+  Has2UserDefinedTcpIngressPorts:
+    Fn::Or:
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
+          - 2
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
+          - 3
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
+          - 4
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
+          - 5
+      - Fn::Equals:
+          - Ref: UserDefinedTcpIngressPortsCount
       CidrIp: !Ref SshPortIngressIpv4Range
 
-  HttpIngress:
+  UserDefinedTcpIngress1:
+    Type: AWS::EC2::SecurityGroupIngress
+    Condition: Has1UserDefinedTcpIngressPorts
+    Properties:
+      GroupId: !Ref SecurityGroup
+      IpProtocol: tcp
+      FromPort: !Select [0, !Ref UserDefinedTcpIngressPorts]
+      ToPort: !Select [0, !Ref UserDefinedTcpIngressPorts]
+      CidrIp: !Ref UserDefinedTcpIngressPortsIngressIpv4Range
+
+  UserDefinedTcpIngress2:
+    Type: AWS::EC2::SecurityGroupIngress
+    Condition: Has2UserDefinedTcpIngressPorts
+    Properties:
+      GroupId: !Ref SecurityGroup
+      IpProtocol: tcp
+      FromPort: !Select [1, !Ref UserDefinedTcpIngressPorts]
+      ToPort: !Select [1, !Ref UserDefinedTcpIngressPorts]
+      CidrIp: !Ref UserDefinedTcpIngressPortsIngressIpv4Range
+
+  UserDefinedTcpIngress3:
+    Type: AWS::EC2::SecurityGroupIngress
+    Condition: Has3UserDefinedTcpIngressPorts
+    Properties:
+      GroupId: !Ref SecurityGroup
+      IpProtocol: tcp
+      FromPort: !Select [2, !Ref UserDefinedTcpIngressPorts]
+      ToPort: !Select [2, !Ref UserDefinedTcpIngressPorts]
+      CidrIp: !Ref UserDefinedTcpIngressPortsIngressIpv4Range
         .arg(
-            Arg::new("INGRESS_IPV4_CIDR")
-                .long("ingress-ipv4-cidr")
-                .help("Sets the IPv4 CIDR range for ingress traffic HTTP/SSH (leave empty to default to public IP on the local)")
+            Arg::new("SSH_INGRESS_IPV4_CIDR")
+                .long("ssh-ingress-ipv4-cidr")
+                .help("Sets the IPv4 CIDR range for ingress SSH traffic (leave empty to default to public IP on the local)")
+                .required(false)
+                .num_args(1),
+        )
+        .arg(
+            Arg::new("USER_DEFINED_TCP_INGRESS_PORTS_IPV4_CIDR")
+                .long("user-defined-tcp-ingress-ports-ipv4-cidr")
+                .help("Sets the IPv4 CIDR range for ingress SSH traffic (leave empty to default to public IP on the local)")
+                .required(false)
+                .num_args(1),
+        )
+        .arg(
+            Arg::new("USER_DEFINED_TCP_INGRESS_PORTS")
+                .long("user-defined-tcp-ingress-ports")
+                .help("Sets the comma-separated TCP ingress ports (up to 10)")
                 .required(false)
                 .num_args(1),
@@ -89,7 +89,11 @@ pub struct Resources {
     pub region: String,
 
     #[serde(default)]
-    pub ingress_ipv4_cidr: String,
+    pub ssh_ingress_ipv4_cidr: String,
+    #[serde(default)]
+    pub user_defined_tcp_ingress_ports_ipv4_cidr: String,
+    #[serde(default)]
+    pub user_defined_tcp_ingress_ports: Vec<u32>,
 
     #[serde(default)]
     pub s3_bucket: String,
@@ -146,7 +150,9 @@ impl Resources {
 
             region: String::from("us-west-2"),
 
-            ingress_ipv4_cidr: String::from("0.0.0.0/0"),
+            ssh_ingress_ipv4_cidr: String::from("0.0.0.0/0"),
+            user_defined_tcp_ingress_ports_ipv4_cidr: String::from("0.0.0.0/0"),
+            user_defined_tcp_ingress_ports: Vec::new(),
 
             s3_bucket: String::from(""),
 
@@ -209,7 +215,9 @@ pub struct DefaultSpecOption {
     pub unsafe_temporary_aws_secret_key_id: String,
     pub unsafe_temporary_aws_secret_access_key: String,
 
-    pub ingress_ipv4_cidr: String,
+    pub ssh_ingress_ipv4_cidr: String,
+    pub user_defined_tcp_ingress_ports_ipv4_cidr: String,
+    pub user_defined_tcp_ingress_ports: Vec<u32>,
-        let ingress_ipv4_cidr = if !opts.ingress_ipv4_cidr.is_empty() {
-            opts.ingress_ipv4_cidr.clone()
+        let ssh_ingress_ipv4_cidr = if !opts.ssh_ingress_ipv4_cidr.is_empty() {
+            opts.ssh_ingress_ipv4_cidr.clone()
         } else {
-            log::info!("empty ingress_ipv4_cidr, so default to public IP on the local host");
+            log::info!("empty ssh_ingress_ipv4_cidr, so default to public IP on the local host");
+            if let Some(ip) = public_ip::addr().await {
+                log::info!("found public ip address {:?}", ip);
+                format!("{}/32", ip.to_string())
+            } else {
+                log::warn!("failed to get a public IP address -- default to 0.0.0.0/0");
+                "0.0.0.0/0".to_string()
+            }
+        };
+
+        let user_defined_tcp_ingress_ports_ipv4_cidr = if !opts
+            .user_defined_tcp_ingress_ports_ipv4_cidr
+            .is_empty()
+        {
+            opts.user_defined_tcp_ingress_ports_ipv4_cidr.clone()
+        } else {
+            log::info!("empty user_defined_tcp_ingress_ports_ipv4_cidr, so default to public IP on the local host");
@@ -44,7 +44,29 @@ async fn main() {
             let plugins: Vec<String> = if s.is_empty() {
                 Vec::new()
             } else {
-                s.split(',').map(|x| x.to_string()).collect()
+                s.split(',').map(|x| x.trim().to_string()).collect()
+            };
+
+            let s = sub_matches
+                .get_one::<String>("USER_DEFINED_TCP_INGRESS_PORTS")
+                .unwrap_or(&String::new())
+                .clone();
+            let user_defined_tcp_ingress_ports: Vec<u32> = if s.is_empty() {
+                Vec::new()
+            } else {
+                s.split(',')
+                    .map(|x| x.trim().to_string().parse::<u32>().unwrap())
+                    .collect()
+            };
+
+            let s = sub_matches
+                .get_one::<String>("INSTANCE_TYPES")
+                .unwrap_or(&String::new())
+                .clone();
+            let instance_types: Vec<String> = if s.is_empty() {
+                Vec::new()
+            } else {
+                s.split(',').map(|x| x.trim().to_string()).collect()
             };
 
-                ingress_ipv4_cidr: sub_matches
-                    .get_one::<String>("INGRESS_IPV4_CIDR")
+                ssh_ingress_ipv4_cidr: sub_matches
+                    .get_one::<String>("SSH_INGRESS_IPV4_CIDR")
+                    .unwrap_or(&String::new())
+                    .clone(),
+
+                user_defined_tcp_ingress_ports_ipv4_cidr: sub_matches
+                    .get_one::<String>("USER_DEFINED_TCP_INGRESS_PORTS_IPV4_CIDR")
                     .unwrap_or(&String::new())
                     .clone(),
+                user_defined_tcp_ingress_ports,
exdx commented

Use case:
Run grafana on the dev box and be able to open 9090 without going into the AWS console

exdx commented

To start we will just allow a user to set one arbitrary port (default 9090).