From ecf1e2a7ab2970a0da134d9f551267f80e1356ce Mon Sep 17 00:00:00 2001 From: Lee Briggs Date: Wed, 21 Feb 2024 12:44:54 -0800 Subject: [PATCH] allow using public subnets --- examples/aws/py/__main__.py | 6 +-- provider/pkg/provider/aws/bastion.go | 41 ++++++++++++++++--- schema.yaml | 4 ++ sdk/dotnet/TailscaleBastion/Aws/Bastion.cs | 7 ++++ sdk/go/bastion/aws/bastion.go | 7 ++++ sdk/nodejs/aws/bastion.ts | 5 +++ .../aws/bastion.py | 26 +++++++++++- 7 files changed, 86 insertions(+), 10 deletions(-) diff --git a/examples/aws/py/__main__.py b/examples/aws/py/__main__.py index d5c1bab..67999da 100644 --- a/examples/aws/py/__main__.py +++ b/examples/aws/py/__main__.py @@ -7,20 +7,18 @@ vpc = awsx.ec2.Vpc( "example", cidr_block="172.20.0.0/22", -# nat_gateways=awsx.ec2.NatGatewayConfigurationArgs( -# strategy=awsx.ec2.NatGatewayStrategy.NONE -# ), ) bastion = tailscale.aws.Bastion( "example", vpc_id=vpc.vpc_id, - subnet_ids=vpc.private_subnet_ids, + subnet_ids=vpc.public_subnet_ids, route="172.20.0.0/22", tailscale_tags=["tag:bastion"], region="us-west-2", high_availability=True, enable_ssh=True, + public=True, ) diff --git a/provider/pkg/provider/aws/bastion.go b/provider/pkg/provider/aws/bastion.go index 9837d23..0849e80 100644 --- a/provider/pkg/provider/aws/bastion.go +++ b/provider/pkg/provider/aws/bastion.go @@ -33,6 +33,7 @@ type BastionArgs struct { InstanceType pulumi.StringInput `pulumi:"instanceType"` HighAvailability bool `pulumi:"highAvailability"` EnableSSH bool `pulumi:"enableSSH"` + Public bool `pulumi:"public"` } type UserDataArgs struct { @@ -179,9 +180,12 @@ func NewBastion(ctx *pulumi.Context, return nil, fmt.Errorf("error creating IAM instance profile: %v", err) } - sg, err := ec2.NewSecurityGroup(ctx, name, &ec2.SecurityGroupArgs{ - VpcId: args.VpcID, - Ingress: ec2.SecurityGroupIngressArray{ + var ingress ec2.SecurityGroupIngressArray + + // if we're using public subnets, we open the UDP port + // this ensure we don't use DERP relays + if args.Public { + ingress = ec2.SecurityGroupIngressArray{ ec2.SecurityGroupIngressArgs{ Protocol: pulumi.String("icmp"), FromPort: pulumi.Int(0), @@ -190,7 +194,32 @@ func NewBastion(ctx *pulumi.Context, pulumi.String("0.0.0.0/0"), }, }, - }, + // allow access to udp port 41641 + ec2.SecurityGroupIngressArgs{ + Protocol: pulumi.String("udp"), + FromPort: pulumi.Int(41641), + ToPort: pulumi.Int(41641), + CidrBlocks: pulumi.StringArray{ + pulumi.String("0.0.0.0/0"), + }, + }, + } + } else { + ingress = ec2.SecurityGroupIngressArray{ + ec2.SecurityGroupIngressArgs{ + Protocol: pulumi.String("icmp"), + FromPort: pulumi.Int(0), + ToPort: pulumi.Int(0), + CidrBlocks: pulumi.StringArray{ + pulumi.String("0.0.0.0/0"), + }, + }, + } + } + + sg, err := ec2.NewSecurityGroup(ctx, name, &ec2.SecurityGroupArgs{ + VpcId: args.VpcID, + Ingress: ingress, Egress: ec2.SecurityGroupEgressArray{ ec2.SecurityGroupEgressArgs{ Protocol: pulumi.String("-1"), @@ -280,9 +309,11 @@ func NewBastion(ctx *pulumi.Context, PublicKey: key.PublicKeyOpenssh, }, pulumi.Parent(component)) + + launchConfiguration, err := ec2.NewLaunchConfiguration(ctx, name, &ec2.LaunchConfigurationArgs{ InstanceType: instanceType, - AssociatePublicIpAddress: pulumi.Bool(false), + AssociatePublicIpAddress: pulumi.Bool(args.Public), ImageId: ami.Id(), SecurityGroups: pulumi.StringArray{ sg.ID(), diff --git a/schema.yaml b/schema.yaml index a970010..09077d7 100644 --- a/schema.yaml +++ b/schema.yaml @@ -58,6 +58,10 @@ resources: tailscale-bastion:aws:Bastion: isComponent: true inputProperties: + public: + type: boolean + description: "Whether the bastion is going in public subnets." + default: false enableSSH: type: boolean description: "Whether to enable SSH access to the bastion." diff --git a/sdk/dotnet/TailscaleBastion/Aws/Bastion.cs b/sdk/dotnet/TailscaleBastion/Aws/Bastion.cs index 60f2a2a..1382a29 100644 --- a/sdk/dotnet/TailscaleBastion/Aws/Bastion.cs +++ b/sdk/dotnet/TailscaleBastion/Aws/Bastion.cs @@ -72,6 +72,12 @@ public sealed class BastionArgs : global::Pulumi.ResourceArgs [Input("instanceType")] public Input? InstanceType { get; set; } + /// + /// Whether the bastion is going in public subnets. + /// + [Input("public")] + public Input? Public { get; set; } + /// /// The AWS region you're using. /// @@ -118,6 +124,7 @@ public BastionArgs() { EnableSSH = true; HighAvailability = false; + Public = false; } public static new BastionArgs Empty => new BastionArgs(); } diff --git a/sdk/go/bastion/aws/bastion.go b/sdk/go/bastion/aws/bastion.go index 22b2f8d..11c9e05 100644 --- a/sdk/go/bastion/aws/bastion.go +++ b/sdk/go/bastion/aws/bastion.go @@ -50,6 +50,9 @@ func NewBastion(ctx *pulumi.Context, if args.HighAvailability == nil { args.HighAvailability = pulumi.Bool(false) } + if args.Public == nil { + args.Public = pulumi.BoolPtr(false) + } opts = internal.PkgResourceDefaultOpts(opts) var resource Bastion err := ctx.RegisterRemoteComponentResource("tailscale-bastion:aws:Bastion", name, args, &resource, opts...) @@ -66,6 +69,8 @@ type bastionArgs struct { HighAvailability bool `pulumi:"highAvailability"` // The EC2 instance type to use for the bastion. InstanceType *string `pulumi:"instanceType"` + // Whether the bastion is going in public subnets. + Public *bool `pulumi:"public"` // The AWS region you're using. Region string `pulumi:"region"` // The route you'd like to advertise via tailscale. @@ -86,6 +91,8 @@ type BastionArgs struct { HighAvailability pulumi.BoolInput // The EC2 instance type to use for the bastion. InstanceType pulumi.StringPtrInput + // Whether the bastion is going in public subnets. + Public pulumi.BoolPtrInput // The AWS region you're using. Region pulumi.StringInput // The route you'd like to advertise via tailscale. diff --git a/sdk/nodejs/aws/bastion.ts b/sdk/nodejs/aws/bastion.ts index 3033f53..69e9a95 100644 --- a/sdk/nodejs/aws/bastion.ts +++ b/sdk/nodejs/aws/bastion.ts @@ -60,6 +60,7 @@ export class Bastion extends pulumi.ComponentResource { resourceInputs["enableSSH"] = (args ? args.enableSSH : undefined) ?? true; resourceInputs["highAvailability"] = (args ? args.highAvailability : undefined) ?? false; resourceInputs["instanceType"] = args ? args.instanceType : undefined; + resourceInputs["public"] = (args ? args.public : undefined) ?? false; resourceInputs["region"] = args ? args.region : undefined; resourceInputs["route"] = args ? args.route : undefined; resourceInputs["subnetIds"] = args ? args.subnetIds : undefined; @@ -92,6 +93,10 @@ export interface BastionArgs { * The EC2 instance type to use for the bastion. */ instanceType?: pulumi.Input; + /** + * Whether the bastion is going in public subnets. + */ + public?: pulumi.Input; /** * The AWS region you're using. */ diff --git a/sdk/python/lbrlabs_pulumi_tailscalebastion/aws/bastion.py b/sdk/python/lbrlabs_pulumi_tailscalebastion/aws/bastion.py index 48e1302..88a4218 100644 --- a/sdk/python/lbrlabs_pulumi_tailscalebastion/aws/bastion.py +++ b/sdk/python/lbrlabs_pulumi_tailscalebastion/aws/bastion.py @@ -21,7 +21,8 @@ def __init__(__self__, *, tailscale_tags: pulumi.Input[Sequence[pulumi.Input[str]]], vpc_id: pulumi.Input[str], enable_ssh: Optional[pulumi.Input[bool]] = None, - instance_type: Optional[pulumi.Input[str]] = None): + instance_type: Optional[pulumi.Input[str]] = None, + public: Optional[pulumi.Input[bool]] = None): """ The set of arguments for constructing a Bastion resource. :param pulumi.Input[bool] high_availability: Whether the bastion should be highly available. @@ -32,6 +33,7 @@ def __init__(__self__, *, :param pulumi.Input[str] vpc_id: The VPC the Bastion should be created in. :param pulumi.Input[bool] enable_ssh: Whether to enable SSH access to the bastion. :param pulumi.Input[str] instance_type: The EC2 instance type to use for the bastion. + :param pulumi.Input[bool] public: Whether the bastion is going in public subnets. """ if high_availability is None: high_availability = False @@ -47,6 +49,10 @@ def __init__(__self__, *, pulumi.set(__self__, "enable_ssh", enable_ssh) if instance_type is not None: pulumi.set(__self__, "instance_type", instance_type) + if public is None: + public = False + if public is not None: + pulumi.set(__self__, "public", public) @property @pulumi.getter(name="highAvailability") @@ -144,6 +150,18 @@ def instance_type(self) -> Optional[pulumi.Input[str]]: def instance_type(self, value: Optional[pulumi.Input[str]]): pulumi.set(self, "instance_type", value) + @property + @pulumi.getter + def public(self) -> Optional[pulumi.Input[bool]]: + """ + Whether the bastion is going in public subnets. + """ + return pulumi.get(self, "public") + + @public.setter + def public(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "public", value) + class Bastion(pulumi.ComponentResource): @overload @@ -153,6 +171,7 @@ def __init__(__self__, enable_ssh: Optional[pulumi.Input[bool]] = None, high_availability: Optional[pulumi.Input[bool]] = None, instance_type: Optional[pulumi.Input[str]] = None, + public: Optional[pulumi.Input[bool]] = None, region: Optional[pulumi.Input[str]] = None, route: Optional[pulumi.Input[str]] = None, subnet_ids: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, @@ -166,6 +185,7 @@ def __init__(__self__, :param pulumi.Input[bool] enable_ssh: Whether to enable SSH access to the bastion. :param pulumi.Input[bool] high_availability: Whether the bastion should be highly available. :param pulumi.Input[str] instance_type: The EC2 instance type to use for the bastion. + :param pulumi.Input[bool] public: Whether the bastion is going in public subnets. :param pulumi.Input[str] region: The AWS region you're using. :param pulumi.Input[str] route: The route you'd like to advertise via tailscale. :param pulumi.Input[Sequence[pulumi.Input[str]]] subnet_ids: The subnet Ids to launch instances in. @@ -198,6 +218,7 @@ def _internal_init(__self__, enable_ssh: Optional[pulumi.Input[bool]] = None, high_availability: Optional[pulumi.Input[bool]] = None, instance_type: Optional[pulumi.Input[str]] = None, + public: Optional[pulumi.Input[bool]] = None, region: Optional[pulumi.Input[str]] = None, route: Optional[pulumi.Input[str]] = None, subnet_ids: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None, @@ -223,6 +244,9 @@ def _internal_init(__self__, raise TypeError("Missing required property 'high_availability'") __props__.__dict__["high_availability"] = high_availability __props__.__dict__["instance_type"] = instance_type + if public is None: + public = False + __props__.__dict__["public"] = public if region is None and not opts.urn: raise TypeError("Missing required property 'region'") __props__.__dict__["region"] = region