Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to use NAT instance with existing EIP #44

Merged
merged 1 commit into from
Nov 9, 2023

Conversation

RaJiska
Copy link
Contributor

@RaJiska RaJiska commented Nov 9, 2023

This PR gives support for static EIP by allowing instance to allocate itself an EIP passed as parameter to its public interface (eth0). This ensures network always flows through the same IP even in case of instance recreation / failure.

A few points to note in this PR:

  • Once assigned, a sleep 3 is issued to ensure the instance network networking is functional as I encountered cases where a direct egress interaction following EIP change would result in failure
  • I added IAM permissions on the documentation Cloudformation, without adding a way to actually pass an EIP allocation as I am fairly unfamiliar with Cloudformation
  • A few user data API calls were shifted from the eni_id scope to the global scope so that those variables may be reused in other blocks (in this case eip_id)

This PR only adds support for the service to have an EIP. This change is compatible with current implementations of CDK and Cloudformation, but they will need to be updated to pass the eip_id parameter to the configuration in order to have static EIP support.

@AndrewGuenther
Copy link
Owner

lgtm

@AndrewGuenther AndrewGuenther merged commit 9662561 into AndrewGuenther:main Nov 9, 2023
@rcoundon
Copy link

rcoundon commented Nov 9, 2023

Is the difference here that an existing EIP can be associated with the fck-nat gateway?

We're currently doing this:

const elasticIp = new ec2.CfnEIP(ctx.stack, 'elasticIp', {
    domain: 'vpc',
  });

  const natGatewayProvider = new ec2.NatInstanceProvider({
    instanceType: ec2.InstanceType.of(ec2.InstanceClass.T4G, ec2.InstanceSize.NANO),
    machineImage: new ec2.LookupMachineImage({
      name: 'fck-nat-amzn2-*-arm64-ebs',
      owners: ['568608671756'],
    }),
  });

  vpc = new ec2.Vpc(ctx.stack, 'vpc', {
    maxAzs: 1,
    subnetConfiguration: [
      {
        name: 'privateLambda',
        cidrMask: 24,
        subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      },
      {
        name: 'public',
        cidrMask: 24,
        subnetType: ec2.SubnetType.PUBLIC,
      },
    ],
    natGateways: 1,
    natGatewayProvider,
  });

  const eipAllocationIds = [elasticIp.attrAllocationId];
  natGatewayProvider.configuredGateways.forEach((gateway, idx) => {
    new ec2.CfnEIPAssociation(ctx.stack, `EIPAssociation${idx}`, {
      allocationId: eipAllocationIds[idx],
      instanceId: gateway.gatewayId,
    });
  });

  vpcPrivateSubnets = vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS });
  vpcPublicSubnets = vpc.selectSubnets({ subnetType: ec2.SubnetType.PUBLIC });

I'd just like to understand if we need to do something different as a result of this change? Or if we don't need to, maybe we should, so I'd like to grasp the difference. Cheers!

@AndrewGuenther
Copy link
Owner

This change effectively enables an EIP attachment in fck-nat's HA mode. What you have there works if the instances are never replaced. But if you are using an ASG and a new instance is brought online, this change will automatically move the assigned EIP to the new instance.

@rcoundon
Copy link

rcoundon commented Nov 9, 2023

This change effectively enables an EIP attachment in fck-nat's HA mode. What you have there works if the instances are never replaced. But if you are using an ASG and a new instance is brought online, this change will automatically move the assigned EIP to the new instance.

I see - thank you for explaining, much appreciated

@renanwilliam
Copy link

renanwilliam commented Dec 20, 2023

if anyone struggling to use it in CloudFormation, this example works fine just adding the EIP Association ID parameter:

Parameters:
  vpc:
    Type: String
    Default: "vpc-xxxxxx"
  subnet:
    Type: String
    Default: "subnet-xxxxxx"
  CIDR:
    Type: String
    Default: "10.0.0.0/16"
  KeyName:
    Type: String
    Default: "my-key-pair"
  EipAssociationID:
    Type: String
    Default: 'eipassoc-xxxxxxxxx'

Resources:
  FckNatInterface:
    Type: AWS::EC2::NetworkInterface
    Properties:
      SubnetId: !Sub "${subnet}"
      GroupSet:
        - Fn::GetAtt:
            - NatSecurityGroup
            - GroupId
      SourceDestCheck: false

  FckNatAsgInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - Ref: NatRole

  FckNatAsgLaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: ami-05b6d5a2e26f13c93
      InstanceType: t4g.nano
      KeyName: !Sub "${KeyName}"
      IamInstanceProfile:
        Ref: FckNatAsgInstanceProfile
      SecurityGroups:
        - Fn::GetAtt:
            - NatSecurityGroup
            - GroupId
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          echo "eni_id=${FckNatInterface}" >> /etc/fck-nat.conf
          echo "eip_id=${EipAssociationID}" >> /etc/fck-nat.conf  
          service fck-nat restart
    DependsOn:
      - NatRole

  FckNatAsg:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MaxSize: "1"
      MinSize: "1"
      DesiredCapacity: "1"
      LaunchConfigurationName:
        Ref: FckNatAsgLaunchConfig
      VPCZoneIdentifier:
        - !Sub "${subnet}"
      Tags:
        - Key: Name
          Value: Fck-NAT
          PropagateAtLaunch: TRUE
    UpdatePolicy:
      AutoScalingScheduledAction:
        IgnoreUnmodifiedGroupSizeProperties: true

  NatSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group for NAT
      SecurityGroupIngress:
        - CidrIp: !Sub "${CIDR}"
          IpProtocol: "-1"
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      VpcId: !Sub "${vpc}"

  NatRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
        Version: "2012-10-17"
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - ec2:AttachNetworkInterface
                  - ec2:ModifyNetworkInterfaceAttribute
                Effect: Allow
                Resource: "*"
            Version: "2012-10-17"
          PolicyName: attachNatEniPolicy
        - PolicyDocument:
            Statement:
              - Action:
                  - ec2:AssociateAddress
                  - ec2:DisassociateAddress
                Effect: Allow
                Resource: "*"
            Version: "2012-10-17"
          PolicyName: associateNatAddressPolicy

@RaJiska
Copy link
Contributor Author

RaJiska commented Dec 21, 2023

If you are down for that, the documentation might need to be updated with the newest features for the various ways of deployment. A PR adding in static EIP support for the CloudFormation deployment would definitely be a good thing :)

@renanwilliam
Copy link

I think it's more related to Cloudformation syntax because the docs used Fn::Join, and Fn::Sub is not working inside Fn::Join.

Maybe changing the Cloudformation docs to use Fn::Sub syntax is more clean and easy for not Cloudformation experts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants