When working with AWS EC2 instances, many developers encounter the frustrating scenario where their ISP-assigned public IP changes periodically. This becomes particularly problematic when:
- Trying to SSH into EC2 instances
- Maintaining whitelisted IPs in security groups
- Running automated deployment scripts
Most residential ISPs provide dynamic IP addresses that typically change every 24 hours or after router reboots. The traditional approach of manually updating security groups becomes unsustainable for developers who need:
# Typical security group ingress rule that needs constant updating
{
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22,
"IpRanges": [{"CidrIp": "123.45.67.89/32"}]
}
For production environments, AWS provides Elastic IP addresses that remain static. However, these come with limitations:
- Limited to 5 Elastic IPs per region by default
- Charges apply when not associated with running instances
- Still requires security group updates if your origin IP changes
For development environments, consider automating security group updates using the AWS CLI:
#!/bin/bash
# Script to update security group with current public IP
MY_IP=$(curl -s https://checkip.amazonaws.com)
AWS_SG_ID="sg-1234567890abcdef0"
aws ec2 authorize-security-group-ingress \
--group-id $AWS_SG_ID \
--protocol tcp \
--port 22 \
--cidr $MY_IP/32
# Optional: Remove old rules
aws ec2 revoke-security-group-ingress \
--group-id $AWS_SG_ID \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
For teams or long-term solutions, consider implementing:
- A dedicated bastion host with static IP
- VPN access to your VPC
- AWS Client VPN endpoint
Example CloudFormation snippet for bastion setup:
Resources:
BastionHost:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-12345678
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: 0
GroupSet:
- !Ref BastionSecurityGroup
SubnetId: !Ref PublicSubnet
For advanced users, create a serverless solution that:
const AWS = require('aws-sdk');
const ec2 = new AWS.EC2();
exports.handler = async (event) => {
const currentIp = event.requestContext.identity.sourceIp;
await ec2.authorizeSecurityGroupIngress({
GroupId: process.env.SECURITY_GROUP_ID,
IpPermissions: [{
IpProtocol: 'tcp',
FromPort: 22,
ToPort: 22,
IpRanges: [{ CidrIp: ${currentIp}/32 }]
}]
}).promise();
return { statusCode: 200 };
};
Combine these approaches with SSH config management:
Host my-ec2-instance
HostName ec2-12-34-56-78.compute-1.amazonaws.com
User ec2-user
IdentityFile ~/.ssh/my-key.pem
ProxyCommand bash -c "aws ec2-instance-connect send-ssh-public-key --instance-id i-1234567890abcdef0 --instance-os-user %r --ssh-public-key file://%d/%i.pub && nc %h %p"
For budget-conscious developers:
- Use AWS Session Manager (no inbound ports needed)
- Configure EC2 Instance Connect
- Leverage temporary credentials
When your ISP assigns dynamic public IPs that rotate every 24 hours, managing AWS EC2 access becomes tedious. Traditional security group configurations require constant updates to whitelist your new IP address. This creates operational overhead and potential service interruptions.
You have three primary approaches to solve this:
- Elastic IP assignment
- Security Group CIDR range expansion
- SSH bastion/jump host setup
Allocate an Elastic IP in your AWS account and associate it with your instance. This gives you a persistent public IP that won't change:
# AWS CLI commands aws ec2 allocate-address --domain vpc aws ec2 associate-address --instance-id i-1234567890abcdef0 --public-ip 203.0.113.10
Remember that AWS charges for unassociated Elastic IPs, so only use this when you need permanent persistence.
If your ISP provides IPs within a predictable range, you can whitelist an entire CIDR block:
# Terraform example resource "aws_security_group" "allow_ssh" { ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["123.45.67.0/24"] # Your ISP's IP range } }
Use whois
or contact your ISP to determine your provider's allocation patterns.
For enterprise environments, implement SSH certificate-based authentication:
# On your CA server ssh-keygen -t rsa -f host_ca -C host_ca ssh-keygen -s host_ca -h -I "ec2-host" -n ec2.example.com ec2_host_key.pub # In sshd_config TrustedUserCAKeys /etc/ssh/host_ca.pub HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
Consider setting up a site-to-site VPN or AWS Client VPN endpoint. This provides secure access without exposing SSH ports:
# Sample OpenVPN client config client dev tun proto udp remote vpn.example.com 1194 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server
Implement a Lambda function to update security groups when your IP changes:
import boto3 import requests def lambda_handler(event, context): current_ip = requests.get('https://checkip.amazonaws.com').text.strip() ec2 = boto3.client('ec2') ec2.authorize_security_group_ingress( GroupId='sg-12345678', IpPermissions=[{ 'IpProtocol': 'tcp', 'FromPort': 22, 'ToPort': 22, 'IpRanges': [{'CidrIp': f'{current_ip}/32'}]}])