175 lines
8.4 KiB
Bash
Executable File
175 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# vault1984 POP Deploy Script — canonical, idempotent
|
|
# Usage: ./deploy-pop.sh <node-id> <region>
|
|
# Example: ./deploy-pop.sh virginia us-east-1
|
|
set -euo pipefail
|
|
|
|
NODE_ID="${1:?usage: deploy-pop.sh <node-id> <region>}"
|
|
REGION="${2:?usage: deploy-pop.sh <node-id> <region>}"
|
|
HQ_URL="http://185.218.204.47:8080"
|
|
IAM_PROFILE="vault1984-ssm-profile"
|
|
INSTANCE_TYPE="t4g.micro"
|
|
|
|
echo "=== vault1984 POP Deploy: $NODE_ID in $REGION ==="
|
|
|
|
# 1. Find latest AL2 arm64 AMI
|
|
echo "[1/6] Finding AMI..."
|
|
AMI_ID=$(aws --region "$REGION" ec2 describe-images \
|
|
--owners amazon \
|
|
--filters "Name=name,Values=amzn2-ami-kernel-5.10-hvm-*-arm64-gp2" "Name=state,Values=available" \
|
|
--query "sort_by(Images, &CreationDate)[-1].ImageId" --output text)
|
|
echo " AMI: $AMI_ID"
|
|
|
|
# 2. Get/create vault1984-pop security group (outbound only, NO inbound)
|
|
echo "[2/6] Security group..."
|
|
VPC_ID=$(aws --region "$REGION" ec2 describe-vpcs \
|
|
--filters "Name=isDefault,Values=true" --query "Vpcs[0].VpcId" --output text)
|
|
SUBNET_ID=$(aws --region "$REGION" ec2 describe-subnets \
|
|
--filters "Name=defaultForAz,Values=true" --query "Subnets[0].SubnetId" --output text)
|
|
|
|
# Look up existing SG — treat empty or literal "None" as missing
|
|
SG_ID=$(aws --region "$REGION" ec2 describe-security-groups \
|
|
--filters "Name=group-name,Values=vault1984-pop" "Name=vpc-id,Values=$VPC_ID" \
|
|
--query "SecurityGroups[0].GroupId" --output text 2>/dev/null || true)
|
|
|
|
if [[ -z "$SG_ID" || "$SG_ID" == "None" ]]; then
|
|
echo " Creating vault1984-pop SG..."
|
|
SG_ID=$(aws --region "$REGION" ec2 create-security-group \
|
|
--group-name vault1984-pop \
|
|
--description "Vault1984 POP - outbound only, no inbound" \
|
|
--vpc-id "$VPC_ID" --query "GroupId" --output text)
|
|
# Remove the default allow-all inbound rule AWS adds to new SGs
|
|
aws --region "$REGION" ec2 revoke-security-group-ingress \
|
|
--group-id "$SG_ID" --protocol -1 --cidr 0.0.0.0/0 2>/dev/null || true
|
|
aws --region "$REGION" ec2 revoke-security-group-ingress \
|
|
--group-id "$SG_ID" --protocol -1 --source-group "$SG_ID" 2>/dev/null || true
|
|
echo " Created SG: $SG_ID"
|
|
else
|
|
echo " Existing SG: $SG_ID"
|
|
fi
|
|
|
|
# Verify SG has no inbound rules (safety check)
|
|
INBOUND=$(aws --region "$REGION" ec2 describe-security-groups \
|
|
--group-ids "$SG_ID" --query "SecurityGroups[0].IpPermissions" --output text)
|
|
if [[ -n "$INBOUND" ]]; then
|
|
echo " WARNING: vault1984-pop SG has inbound rules — check manually:"
|
|
echo " $INBOUND"
|
|
fi
|
|
|
|
# 3. Launch instance
|
|
echo "[3/6] Launching $INSTANCE_TYPE..."
|
|
INSTANCE_ID=$(aws --region "$REGION" ec2 run-instances \
|
|
--image-id "$AMI_ID" \
|
|
--instance-type "$INSTANCE_TYPE" \
|
|
--subnet-id "$SUBNET_ID" \
|
|
--security-group-ids "$SG_ID" \
|
|
--iam-instance-profile Name="$IAM_PROFILE" \
|
|
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=vault1984-$NODE_ID},{Key=vault1984-node,Value=$NODE_ID}]" \
|
|
--query "Instances[0].InstanceId" --output text)
|
|
echo " Instance: $INSTANCE_ID"
|
|
|
|
# Verify SG assignment immediately after launch
|
|
ASSIGNED_SG=$(aws --region "$REGION" ec2 describe-instances \
|
|
--instance-ids "$INSTANCE_ID" \
|
|
--query "Reservations[0].Instances[0].SecurityGroups[0].GroupName" --output text)
|
|
if [[ "$ASSIGNED_SG" != "vault1984-pop" ]]; then
|
|
echo "ERROR: Instance launched with wrong SG: $ASSIGNED_SG (expected vault1984-pop)"
|
|
echo " Terminating instance to avoid misconfigured POP."
|
|
aws --region "$REGION" ec2 terminate-instances --instance-ids "$INSTANCE_ID" >/dev/null
|
|
exit 1
|
|
fi
|
|
echo " SG verified: $ASSIGNED_SG ✓"
|
|
|
|
# 4. Wait for SSM
|
|
echo "[4/6] Waiting for SSM (~2-3 min)..."
|
|
PUBLIC_IP=""
|
|
for i in $(seq 1 30); do
|
|
STATUS=$(aws --region "$REGION" ssm describe-instance-information \
|
|
--filters "Key=InstanceIds,Values=$INSTANCE_ID" \
|
|
--query "InstanceInformationList[0].PingStatus" --output text 2>/dev/null || echo "None")
|
|
if [[ "$STATUS" == "Online" ]]; then
|
|
PUBLIC_IP=$(aws --region "$REGION" ec2 describe-instances \
|
|
--instance-ids "$INSTANCE_ID" \
|
|
--query "Reservations[0].Instances[0].PublicIpAddress" --output text)
|
|
echo " SSM online after ~${i}0s — IP: $PUBLIC_IP"
|
|
break
|
|
fi
|
|
echo " ($i/30) $STATUS..."
|
|
sleep 10
|
|
done
|
|
[[ "$STATUS" == "Online" ]] || { echo "ERROR: SSM never came online"; exit 1; }
|
|
|
|
# 5. Install vault1984 + apply hardening inline (same approach as update-pop.sh)
|
|
echo "[5/6] Deploying vault1984 and hardening..."
|
|
|
|
CMD_ID=$(aws --region "$REGION" ssm send-command \
|
|
--instance-ids "$INSTANCE_ID" \
|
|
--document-name AWS-RunShellScript \
|
|
--parameters commands="[
|
|
\"set -e\",
|
|
\"hostnamectl set-hostname $NODE_ID\",
|
|
\"curl -sfo /usr/local/bin/vault1984 $HQ_URL/download/vault1984-arm64\",
|
|
\"chmod +x /usr/local/bin/vault1984\",
|
|
\"mkdir -p /var/lib/vault1984\",
|
|
\"printf '[Unit]\\nDescription=Vault1984\\nAfter=network.target\\n\\n[Service]\\nEnvironment=NODE_ID=$NODE_ID\\nExecStart=/usr/local/bin/vault1984 --telemetry-freq=60 --telemetry-host=$HQ_URL/telemetry\\nRestart=always\\nRestartSec=10\\nWorkingDirectory=/var/lib/vault1984\\n\\n[Install]\\nWantedBy=multi-user.target\\n' > /etc/systemd/system/vault1984.service\",
|
|
\"systemctl daemon-reload && systemctl enable vault1984 && systemctl start vault1984\",
|
|
\"systemctl is-active vault1984 && echo 'vault1984: OK' || echo 'vault1984: FAILED'\",
|
|
\"amazon-linux-extras install epel -y -q > /dev/null 2>&1 || true\",
|
|
\"yum install -y -q fail2ban > /dev/null 2>&1 || true\",
|
|
\"printf '[DEFAULT]\\nbantime = 86400\\nfindtime = 600\\nmaxretry = 3\\nignoreip = 127.0.0.1/8 ::1\\n\\n[sshd]\\nenabled = true\\nport = ssh\\nfilter = sshd\\nlogpath = /var/log/secure\\nmaxretry = 3\\nbantime = 86400\\n' > /etc/fail2ban/jail.local\",
|
|
\"systemctl enable fail2ban && systemctl restart fail2ban\",
|
|
\"sleep 5 && fail2ban-client status sshd\",
|
|
\"for svc in postfix rpcbind sshd; do systemctl stop \$svc 2>/dev/null; systemctl disable \$svc 2>/dev/null; done\",
|
|
\"yum install -y -q chrony > /dev/null 2>&1 && systemctl enable chronyd && systemctl start chronyd || true\",
|
|
\"printf 'net.ipv4.tcp_syncookies=1\\nnet.ipv4.icmp_echo_ignore_broadcasts=1\\nnet.ipv4.conf.all.rp_filter=1\\n' > /etc/sysctl.d/99-vault1984.conf && sysctl --system -q\",
|
|
\"systemctl enable firewalld && systemctl start firewalld\",
|
|
\"firewall-cmd --permanent --remove-service=ssh 2>/dev/null || true\",
|
|
\"firewall-cmd --permanent --add-port=1984/tcp && firewall-cmd --reload\",
|
|
\"echo 'DEPLOY_COMPLETE'\"
|
|
]" \
|
|
--query "Command.CommandId" --output text)
|
|
|
|
for i in $(seq 1 30); do
|
|
R=$(aws --region "$REGION" ssm get-command-invocation \
|
|
--command-id "$CMD_ID" --instance-id "$INSTANCE_ID" \
|
|
--query "{S:Status,O:StandardOutputContent,E:StandardErrorContent}" --output json 2>/dev/null || echo '{"S":"Pending"}')
|
|
S=$(echo "$R" | python3 -c "import json,sys; print(json.load(sys.stdin).get('S','?'))")
|
|
if [[ "$S" == "Success" || "$S" == "Failed" || "$S" == "TimedOut" ]]; then
|
|
echo "$R" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('O','')); e=d.get('E',''); e and print('STDERR:',e)"
|
|
[[ "$S" == "Success" ]] || { echo "ERROR: deploy command $S"; exit 1; }
|
|
break
|
|
fi
|
|
echo " ($i/30) $S..."
|
|
sleep 10
|
|
done
|
|
|
|
# 6. Final verification
|
|
echo "[6/6] Verification..."
|
|
VERIFY_ID=$(aws --region "$REGION" ssm send-command \
|
|
--instance-ids "$INSTANCE_ID" \
|
|
--document-name AWS-RunShellScript \
|
|
--parameters commands="[
|
|
\"echo 'hostname:' $(hostname)\",
|
|
\"systemctl is-active vault1984 && echo 'vault1984: active' || echo 'vault1984: FAILED'\",
|
|
\"systemctl is-active fail2ban && echo 'fail2ban: active' || echo 'fail2ban: FAILED'\",
|
|
\"fail2ban-client status sshd 2>/dev/null && echo 'fail2ban-sshd: OK' || echo 'fail2ban-sshd: FAILED'\",
|
|
\"firewall-cmd --list-ports 2>/dev/null | grep -q 1984 && echo 'firewall: OK' || echo 'firewall: FAILED'\",
|
|
\"systemctl is-active rpcbind 2>/dev/null && echo 'rpcbind: WARNING still active' || echo 'rpcbind: disabled OK'\"
|
|
]" \
|
|
--query "Command.CommandId" --output text)
|
|
sleep 10
|
|
aws --region "$REGION" ssm get-command-invocation \
|
|
--command-id "$VERIFY_ID" --instance-id "$INSTANCE_ID" \
|
|
--query "StandardOutputContent" --output text
|
|
|
|
echo ""
|
|
echo "=== Deploy complete ==="
|
|
echo " Node: $NODE_ID"
|
|
echo " Region: $REGION"
|
|
echo " Instance: $INSTANCE_ID"
|
|
echo " IP: $PUBLIC_IP"
|
|
echo " SG: vault1984-pop ($SG_ID)"
|
|
echo ""
|
|
echo "Add to POPS in update-pop.sh:"
|
|
echo " [\"$NODE_ID\"]=\"$REGION:$INSTANCE_ID\""
|