Last active
April 6, 2024 03:39
-
-
Save davidedg/c29c478ee9c15a804a99cbd1de364647 to your computer and use it in GitHub Desktop.
AWS NAT Instance HA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
#https://gist.github.com/davidedg/c29c478ee9c15a804a99cbd1de364647#file-userdata | |
# Intended to run together with AMI amzn-ami-vpc-nat-hvm-*, with ASG min=max=desired=1 | |
# Tested with amzn-ami-vpc-nat-hvm-2018.03.0.20180811-x86_64-ebs (ami-0ea87e2bfa81ca08a) | |
# Expected TAGS: | |
# - Backend Subnets: Name=InternetNAT Values=AvailabilityZone ( eg: eu-west-1 ) where Frontend NAT Instance is (this allows for multiple NAT instances to serve differenze AZ subnets | |
# - Backend Subnets + ASG: Name=Environment, Values=EnvironmentLabel ( eg: "production", "staging" ... ) (this allows for multiple environments in same VPC, served by different NAT instances) | |
# - ASG: Name=EIP Values=EIP-allocation-id ( eg eipalloc-abcdef12 ) | |
PATH="/usr/sbin:/sbin:/usr/bin:/bin" | |
function log { logger -t "NAT-instance" -- $1; } | |
function die { | |
[[ -n "$1" ]] && log "$1" | |
log "NAT Instance Configuration failed" | |
exit 1 | |
} | |
stop_and_disable_services() { | |
## Clean up image | |
log "Stopping unneeded services" | |
initctl stop amazon-ssm-agent >/dev/null 2>/dev/null & | |
service atd stop & | |
service nfs stop & | |
service lvm2-lvmetad stop & | |
service lvm2-lvmpolld stop & | |
service lvm2-monitor stop & | |
service mdmonitor stop & | |
service nfslock stop & | |
service rpcbind stop & | |
service rpcgssd stop & | |
service sendmail stop & | |
wait | |
log "Uninstalling/Disabling unneeded services" | |
yum -y erase amazon-ssm-agent & | |
chkconfig atd off >/dev/null & | |
chkconfig nfs off >/dev/null & | |
chkconfig lvm2-lvmetad off >/dev/null & | |
chkconfig lvm2-lvmpolld off >/dev/null & | |
chkconfig lvm2-monitor off >/dev/null & | |
chkconfig mdmonitor off >/dev/null & | |
chkconfig nfslock off >/dev/null & | |
chkconfig rpcbind off >/dev/null & | |
chkconfig rpcgssd off >/dev/null & | |
chkconfig sendmail off >/dev/null & | |
wait | |
} | |
get_aws_data() { | |
## Get AWS details | |
export AWS_DEFAULT_OUTPUT="text" | |
export INSTANCE_ID=$(curl --retry 3 --silent --fail http://169.254.169.254/latest/meta-data/instance-id) | |
export MAC=$(curl --retry 3 --silent --fail http://169.254.169.254/latest/meta-data/mac) | |
export ENI=$(curl --retry 3 --silent --fail http://169.254.169.254/latest/meta-data/network/interfaces/macs/$MAC/interface-id) | |
AWS_IIDOC=$(curl --retry 3 --silent --fail http://169.254.169.254/latest/dynamic/instance-identity/document) | |
export AWS_DEFAULT_REGION=$(echo $AWS_IIDOC | python -c "import json,sys; print json.loads(sys.stdin.read())['region']") #" | |
} | |
apply_updates() { | |
log "Applying Security Updates" | |
yum -y update --security | |
} | |
##iptables_active_ftp() { | |
##log "Adding support for Active FTP" | |
##cat << EOF | sudo tee /etc/sysconfig/modules/nat_ftp.modules | |
##!/bin/sh | |
## Support for Active FTP initiated from INSIDE | |
##/sbin/modprobe nf_nat_ftp >/dev/null 2>&1 | |
##iptables -A PREROUTING -t raw -m rpfilter --invert -j DROP >/dev/null 2>&1 | |
##iptables -A PREROUTING -t raw -p tcp --sport 1024: --dport 21 -j CT --helper ftp >/dev/null 2>&1 | |
##EOF | |
##chmod +x /etc/sysconfig/modules/nat_ftp.modules | |
##/etc/sysconfig/modules/nat_ftp.modules | |
##} | |
## | |
attach_eip() { | |
EIPTAG=$(aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCE_ID" --query 'Reservations[].Instances[].Tags[?Key==`EIP`].Value') | |
log "Attaching EIP $EIPTAG to $INSTANCE_ID" | |
aws ec2 associate-address --allow-reassociation --instance-id $INSTANCE_ID --allocation-id "$EIPTAG" | |
} | |
update_routes() { | |
log "Updating Routing Tables" | |
VPC_ID=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].VpcId') || die "Unable to determine VPC ID for instance." | |
ENVTAG=$(aws ec2 describe-instances --filters "Name=instance-id,Values=$INSTANCE_ID" --query 'Reservations[].Instances[].Tags[?Key==`Environment`].Value') | |
PRIVATE_SUBNETS="$(aws ec2 describe-subnets --query 'Subnets[*].SubnetId' --filters Name=vpc-id,Values=$VPC_ID Name=tag:InternetNAT,Values=1 Name=tag:Environment,Values=$ENVTAG Name=state,Values=available)" | |
[[ -z "$PRIVATE_SUBNETS" ]] && die "No private subnets found" | |
aws ec2 modify-instance-attribute --instance-id $INSTANCE_ID --no-source-dest-check | |
for subnet in $PRIVATE_SUBNETS; do | |
ROUTE_TABLE_ID=$(aws ec2 describe-route-tables --query 'RouteTables[*].RouteTableId' --filters Name=association.subnet-id,Values=$subnet) | |
if [[ -z "$ROUTE_TABLE_ID" ]]; then | |
log "$subnet is not associated with a Route Table. Skipping this subnet." | |
else # create or replace route | |
aws ec2 create-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --network-interface-id $ENI & | |
aws ec2 replace-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --network-interface-id $ENI & | |
wait # backgrounding everything is too heavy for t2.nano - you might skip this wait with larger instances | |
fi | |
done | |
} | |
function script_full_path { | |
prg=$0 | |
if [ ! -e "$prg" ]; then | |
case $prg in | |
(*/*) exit 1;; | |
(*) prg=$(command -v -- "$prg") || exit;; | |
esac | |
fi | |
dir=$( | |
cd -P -- "$(dirname -- "$prg")" && pwd -P | |
) || exit | |
prg=$dir/$(basename -- "$prg") || exit | |
printf '%s' "$prg" | |
} | |
update_rc_local() { | |
p=$(script_full_path) | |
printf '\n%s on-reboot\n' "$p" >> /etc/rc.local | |
} | |
case "$1" in | |
attach-eip) | |
get_aws_data | |
attach_eip | |
;; | |
update-routes) | |
get_aws_data | |
update_routes | |
;; | |
on-reboot) | |
get_aws_data | |
attach_eip | |
update_routes | |
;; | |
*) # very first boot | |
stop_and_disable_services | |
get_aws_data | |
apply_updates | |
update_rc_local | |
needs-restarting -r | |
if [[ $? -ne 0 ]]; then | |
log "A Reboot is required" | |
reboot | |
else | |
bash $(script_full_path) on-reboot | |
fi | |
;; | |
esac | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment