diff --git a/scripts/alpine-ec2-eni-hook b/scripts/alpine-ec2-eni-hook new file mode 100755 index 0000000..a7e2992 --- /dev/null +++ b/scripts/alpine-ec2-eni-hook @@ -0,0 +1,112 @@ +#!/usr/bin/env sh +# vim: set ft=sh noet ts=4: + +# This script should be installed as symlinks in +# /etc/udhcpc//alpine-ec2-eni-hook +# :- +# post-bound - after udhcpc binds an IP to the interface +# post-renew - after udhcpc renews the lease for the IP +# +# udhcpc provides via ENV... +# IFACE - eth0, etc. +# mask - bits in IPv4 subnet mask + +set -e + +HOOK="$(basename "$(dirname "$0")")" + +log() { + LEV="$1" + shift + logger -s -p "daemon.$LEV" -t "udhcpc/${HOOK}[$$]" "$@" +} + +IMDS="X-aws-ec2-metadata-token" + +meta_token() { + echo -ne "PUT /latest/api/token HTTP/1.0\r\n$IMDS-ttl-seconds: 5\r\n\r\n" | + nc 169.254.169.254 80 | tail -n 1 +} + +meta() { + wget -qO - --header "$IMDS: $(meta_token)" \ + "http://169.254.169.254/latest/meta-data/$1" 2>/dev/null \ + || true # when no ipv6s attached, wget will 404 error +} + +iface_mac() { + cat "/sys/class/net/$IFACE/address" +} + +iface_ip6s() { + # only inet6 lines, except fe80:: + ip -6 addr show "$IFACE" | + sed -E -e '/inet6/!d' \ + -e '/inet6 fe80::/d' \ + -e 's/.*inet6 ([0-9a-f:]+).*/\1/' +} + +iface_sec_ip4s() { + # only inet secondary lines + ip -4 addr show "$IFACE" | + sed -E -e '/inet .* secondary/!d' \ + -e 's/.*inet ([0-9.]+).*/\1/' +} + +ec2_ip6s() { + meta "$MAC_PATH/ipv6s" +} + +ec2_sec_ip4s() { + # first one listed is primary + meta "$MAC_PATH/local-ipv4s" | tail +2 +} + +in_list() { + echo "$2" | grep -q "^$1$" +} + +ip_addr() { + [ "$1" -eq 4 ] && MASK="$mask" || MASK=128 + MSG="$IFACE $2 $3" + if ip -"$1" addr "$2" "$3/$MASK" dev "$IFACE"; then + log notice "$MSG - success" + else + log err "$MSG - FAILED" + fi +} + +case "$HOOK" in + #pre-deconfig) ACT=del ;; # issues with 'service networking restart' + post-bound) ACT=add ;; + post-renew) ACT=sync ;; + *) log err "Unhandled udhcpc hook: '$HOOK'"; exit 1 ;; +esac + +MAC_PATH="network/interfaces/macs/$(iface_mac)" +EC2_IP4S="$(ec2_sec_ip4s)" +EC2_IP6S="$(ec2_ip6s)" +IFACE_IP4S="$(iface_sec_ip4s)" +IFACE_IP6S="$(iface_ip6s)" + +# add or sync IPs +if [ "$ACT" != "del" ]; then + for ip4 in $EC2_IP4S; do + in_list "$ip4" "$IFACE_IP4S" || ip_addr 4 add "$ip4" + done + for ip6 in $EC2_IP6S; do + in_list "$ip6" "$IFACE_IP6S" || ip_addr 6 add "$ip6" + done +fi + +# del or sync IPs +if [ "$ACT" != "add" ]; then + for ip4 in $IFACE_IP4S; do + [ "$ACT" = "sync" ] && in_list "$ip4" "$EC2_IP4S" && continue + ip_addr 4 del "$ip4" + done + for ip6 in $IFACE_IP6S; do + [ "$ACT" = "sync" ] && in_list "$ip6" "$EC2_IP6S" && continue + ip_addr 6 del "$ip6" + done +fi diff --git a/scripts/builder.py b/scripts/builder.py index 2107474..e1f0f92 100755 --- a/scripts/builder.py +++ b/scripts/builder.py @@ -576,8 +576,9 @@ class ConfigBuilder: shutil.rmtree(build_dir, ignore_errors=True) os.makedirs(setup_dir) - # symlink nvme script + # symlink nvme and ENI scripts self.rel_symlink("scripts/nvme-ebs-links", setup_dir, "nvme-ebs-links") + self.rel_symlink("scripts/alpine-ec2-eni-hook", setup_dir, "alpine-ec2-eni-hook") # symlink additional setup_script if "setup_script" in cfg.keys() and cfg["setup_script"] is not None: diff --git a/scripts/setup-ami b/scripts/setup-ami index 9688b3b..84b0d46 100755 --- a/scripts/setup-ami +++ b/scripts/setup-ami @@ -242,6 +242,7 @@ EOF } setup_networking() { + # configure standard interfaces cat > "$TARGET/etc/network/interfaces" <