Atualizar crialab.sh
This commit is contained in:
+59
-48
@@ -5,38 +5,39 @@ set -Eeuo pipefail
|
|||||||
export AWS_PAGER="" AWS_CLI_AUTO_PROMPT=off
|
export AWS_PAGER="" AWS_CLI_AUTO_PROMPT=off
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local mode="${1:---plan}"
|
local mode="${1:---apply}"
|
||||||
case "$mode" in --plan|--apply) ;; *) echo 'Use --plan ou --apply' >&2; return 2;; esac
|
case "$mode" in --plan|--apply) ;; *) echo 'Use --plan ou --apply' >&2; return 2;; esac
|
||||||
for tool in aws python3; do
|
for tool in aws python3 curl; do
|
||||||
command -v "$tool" >/dev/null || { echo "Falta instalar: $tool" >&2; return 2; }
|
command -v "$tool" >/dev/null || { echo "Falta instalar: $tool" >&2; return 2; }
|
||||||
done
|
done
|
||||||
REGION="${REGION:-us-east-1}"
|
REGION="${REGION:-us-east-1}"
|
||||||
LAB_NAME="${LAB_NAME:-modulo12-vpn}"
|
LAB_NAME="${LAB_NAME:-modulo12-vpn}"
|
||||||
INSTANCE_TYPE="${INSTANCE_TYPE:-t3.micro}"
|
INSTANCE_TYPE="${INSTANCE_TYPE:-t3.micro}"
|
||||||
LOCAL_CIDR="${LOCAL_CIDR:-192.168.10.0/24}"
|
local create_key=false key_file='' run_id
|
||||||
: "${KEY_NAME:?Defina KEY_NAME com um par de chaves EC2 existente na regiao.}"
|
run_id="$(date -u +%Y%m%dT%H%M%SZ)-$$"
|
||||||
: "${ADMIN_CIDR:?Defina ADMIN_CIDR com o IPv4 publico de administracao seguido de /32.}"
|
if [[ -z "${KEY_NAME:-}" ]]; then
|
||||||
: "${VPN_PEER_CIDR:?Defina VPN_PEER_CIDR com o IPv4 publico de saida do laboratorio seguido de /32.}"
|
KEY_NAME="$LAB_NAME-$run_id"
|
||||||
|
create_key=true
|
||||||
|
fi
|
||||||
|
if [[ -z "${ADMIN_CIDR:-}" ]]; then
|
||||||
|
ADMIN_CIDR="$(curl -4 --noproxy '*' -fsSL --connect-timeout 10 --max-time 20 https://checkip.amazonaws.com)/32"
|
||||||
|
echo 'SSH autorizado para o IP de saida deste terminal. Use SSH a partir dele.'
|
||||||
|
fi
|
||||||
[[ "$LAB_NAME" =~ ^[a-zA-Z][a-zA-Z0-9-]{0,39}$ ]] || { echo 'LAB_NAME invalido' >&2; return 2; }
|
[[ "$LAB_NAME" =~ ^[a-zA-Z][a-zA-Z0-9-]{0,39}$ ]] || { echo 'LAB_NAME invalido' >&2; return 2; }
|
||||||
|
|
||||||
# CIDRs canonicos evitam erros e sobreposicao com VPC/tunel planejados.
|
# Validar somente a origem do acesso administrativo por SSH.
|
||||||
python3 - "$ADMIN_CIDR" "$VPN_PEER_CIDR" "$LOCAL_CIDR" <<'PY'
|
python3 - "$ADMIN_CIDR" <<'PY'
|
||||||
import ipaddress, sys
|
import ipaddress, sys
|
||||||
admin, peer, local = [ipaddress.IPv4Network(x, strict=True) for x in sys.argv[1:]]
|
admin = ipaddress.IPv4Network(sys.argv[1], strict=True)
|
||||||
for label, net in [('ADMIN_CIDR', admin), ('VPN_PEER_CIDR', peer)]:
|
if admin.prefixlen != 32 or not admin.network_address.is_global:
|
||||||
if net.prefixlen != 32 or not net.network_address.is_global:
|
raise SystemExit('ADMIN_CIDR deve ser um IPv4 publico /32 real.')
|
||||||
raise SystemExit(f'{label} deve ser um IPv4 publico /32 real.')
|
|
||||||
private = [ipaddress.ip_network(x) for x in ['10.0.0.0/8','172.16.0.0/12','192.168.0.0/16']]
|
|
||||||
if not any(local.subnet_of(x) for x in private):
|
|
||||||
raise SystemExit('LOCAL_CIDR deve ser uma rede privada RFC1918.')
|
|
||||||
for cidr in ['10.20.0.0/16', '10.8.0.0/24']:
|
|
||||||
if local.overlaps(ipaddress.ip_network(cidr)):
|
|
||||||
raise SystemExit(f'LOCAL_CIDR sobrepoe {cidr}.')
|
|
||||||
PY
|
PY
|
||||||
aw() { aws --region "$REGION" "$@"; }
|
aw() { aws --region "$REGION" "$@"; }
|
||||||
local account existing ami root_device az
|
local account existing ami root_device az
|
||||||
account=$(aw sts get-caller-identity --query Account --output text)
|
account=$(aw sts get-caller-identity --query Account --output text)
|
||||||
aw ec2 describe-key-pairs --key-names "$KEY_NAME" >/dev/null
|
if [[ "$create_key" == false ]]; then
|
||||||
|
aw ec2 describe-key-pairs --key-names "$KEY_NAME" >/dev/null
|
||||||
|
fi
|
||||||
existing=$(aw ec2 describe-vpcs --filters "Name=tag:Lab,Values=$LAB_NAME" --query 'Vpcs[].VpcId' --output text)
|
existing=$(aw ec2 describe-vpcs --filters "Name=tag:Lab,Values=$LAB_NAME" --query 'Vpcs[].VpcId' --output text)
|
||||||
if [[ -n "$existing" && "$existing" != None ]]; then
|
if [[ -n "$existing" && "$existing" != None ]]; then
|
||||||
echo "Ja existe uma VPC deste laboratorio: $existing. Revise-a antes de executar novamente." >&2
|
echo "Ja existe uma VPC deste laboratorio: $existing. Revise-a antes de executar novamente." >&2
|
||||||
@@ -59,9 +60,9 @@ Conta: $account | Regiao: $REGION | Zona: $az | Lab: $LAB_NAME
|
|||||||
Ubuntu 24.04: $ami | Duas EC2: $INSTANCE_TYPE | Chave: $KEY_NAME
|
Ubuntu 24.04: $ami | Duas EC2: $INSTANCE_TYPE | Chave: $KEY_NAME
|
||||||
VPC: 10.20.0.0/16
|
VPC: 10.20.0.0/16
|
||||||
Gateway publico: 10.20.1.10 | Servidor privado: 10.20.2.10
|
Gateway publico: 10.20.1.10 | Servidor privado: 10.20.2.10
|
||||||
Rede local: $LOCAL_CIDR | Tunel planejado: 10.8.0.0/24
|
|
||||||
SSH publico permitido de: $ADMIN_CIDR
|
SSH publico permitido de: $ADMIN_CIDR
|
||||||
OpenVPN UDP/1194 permitido de: $VPN_PEER_CIDR
|
Criar chave SSH automaticamente: $create_key
|
||||||
|
VPN: configuracao, encaminhamento, rotas entre sites e porta UDP ficam para a aula.
|
||||||
Serao criados recursos que consomem o saldo do laboratorio: EC2, EBS e IPv4 publico.
|
Serao criados recursos que consomem o saldo do laboratorio: EC2, EBS e IPv4 publico.
|
||||||
Aloca um Elastic IP fixo para o gateway. Nao cria NAT Gateway, IAM ou certificados VPN.
|
Aloca um Elastic IP fixo para o gateway. Nao cria NAT Gateway, IAM ou certificados VPN.
|
||||||
EOF
|
EOF
|
||||||
@@ -74,11 +75,24 @@ EOF
|
|||||||
umask 077
|
umask 077
|
||||||
local state_dir="${LAB_STATE_DIR:-$PWD/aws-lab-state}"
|
local state_dir="${LAB_STATE_DIR:-$PWD/aws-lab-state}"
|
||||||
mkdir -p "$state_dir"
|
mkdir -p "$state_dir"
|
||||||
STATE_FILE="$state_dir/${LAB_NAME}-$(date -u +%Y%m%dT%H%M%SZ)-$$.txt"
|
state_dir=$(cd "$state_dir" && pwd -P)
|
||||||
|
STATE_FILE="$state_dir/${LAB_NAME}-$run_id.txt"
|
||||||
printf 'Account=%s\nRegion=%s\nLab=%s\n' "$account" "$REGION" "$LAB_NAME" > "$STATE_FILE"
|
printf 'Account=%s\nRegion=%s\nLab=%s\n' "$account" "$REGION" "$LAB_NAME" > "$STATE_FILE"
|
||||||
trap 'echo "Falha na linha $LINENO. Recursos parciais podem existir; consulte $STATE_FILE e a tag Lab=$LAB_NAME. Nao houve rollback automatico." >&2' ERR
|
trap 'echo "Falha na linha $LINENO. Recursos parciais podem existir; consulte $STATE_FILE e a tag Lab=$LAB_NAME. Nao houve rollback automatico." >&2' ERR
|
||||||
record() { printf '%s=%s\n' "$1" "$2" | tee -a "$STATE_FILE"; }
|
record() { printf '%s=%s\n' "$1" "$2" | tee -a "$STATE_FILE"; }
|
||||||
tags() { printf 'ResourceType=%s,Tags=[{Key=Lab,Value=%s},{Key=Name,Value=%s-%s}]' "$1" "$LAB_NAME" "$LAB_NAME" "$2"; }
|
tags() { printf 'ResourceType=%s,Tags=[{Key=Lab,Value=%s},{Key=Name,Value=%s-%s}]' "$1" "$LAB_NAME" "$LAB_NAME" "$2"; }
|
||||||
|
record AdminCidr "$ADMIN_CIDR"
|
||||||
|
record KeyName "$KEY_NAME"
|
||||||
|
record KeyCreatedByScript "$create_key"
|
||||||
|
if [[ "$create_key" == true ]]; then
|
||||||
|
key_file="$state_dir/$KEY_NAME.pem"
|
||||||
|
# noclobber impede sobrescrever uma chave preexistente; conteudo nunca vai para stdout.
|
||||||
|
(set -o noclobber; aw ec2 create-key-pair --key-name "$KEY_NAME" --key-type rsa --key-format pem \
|
||||||
|
--tag-specifications "$(tags key-pair ssh)" --query KeyMaterial --output text > "$key_file")
|
||||||
|
chmod 600 "$key_file"
|
||||||
|
[[ -s "$key_file" ]] || { echo 'Arquivo da chave vazio.' >&2; return 1; }
|
||||||
|
record PrivateKeyFile "$key_file"
|
||||||
|
fi
|
||||||
local vpc igw public_subnet private_subnet public_rt private_rt gateway_sg services_sg gateway server
|
local vpc igw public_subnet private_subnet public_rt private_rt gateway_sg services_sg gateway server
|
||||||
vpc=$(aw ec2 create-vpc --cidr-block 10.20.0.0/16 --tag-specifications "$(tags vpc vpc)" --query Vpc.VpcId --output text)
|
vpc=$(aw ec2 create-vpc --cidr-block 10.20.0.0/16 --tag-specifications "$(tags vpc vpc)" --query Vpc.VpcId --output text)
|
||||||
record VpcId "$vpc"
|
record VpcId "$vpc"
|
||||||
@@ -105,32 +119,14 @@ EOF
|
|||||||
record ServicesSecurityGroupId "$services_sg"
|
record ServicesSecurityGroupId "$services_sg"
|
||||||
allow() { aw ec2 authorize-security-group-ingress --group-id "$1" --protocol "$2" --port "$3" --cidr "$4" >/dev/null; }
|
allow() { aw ec2 authorize-security-group-ingress --group-id "$1" --protocol "$2" --port "$3" --cidr "$4" >/dev/null; }
|
||||||
allow "$gateway_sg" tcp 22 "$ADMIN_CIDR"
|
allow "$gateway_sg" tcp 22 "$ADMIN_CIDR"
|
||||||
allow "$gateway_sg" udp 1194 "$VPN_PEER_CIDR"
|
# Administracao do servidor privado via salto SSH, sem depender de VPN.
|
||||||
# Pacotes dos servidores atravessam o gateway para chegar a rede local.
|
allow "$services_sg" tcp 22 10.20.1.10/32
|
||||||
aw ec2 authorize-security-group-ingress --group-id "$gateway_sg" --ip-permissions \
|
|
||||||
'[{"IpProtocol":"-1","IpRanges":[{"CidrIp":"10.20.2.0/24","Description":"Encaminhamento da sub-rede privada para VPN"}]}]' >/dev/null
|
|
||||||
local cidr
|
|
||||||
for cidr in "$LOCAL_CIDR" 10.8.0.0/24 10.20.1.10/32; do
|
|
||||||
allow "$services_sg" tcp 22 "$cidr"
|
|
||||||
allow "$services_sg" tcp 80 "$cidr"
|
|
||||||
allow "$services_sg" icmp -1 "$cidr"
|
|
||||||
done
|
|
||||||
# Sem pacotes externos: bootstrap funciona inclusive na sub-rede privada isolada.
|
|
||||||
local gateway_user_data='#cloud-config
|
|
||||||
write_files:
|
|
||||||
- path: /etc/sysctl.d/90-vpn-lab.conf
|
|
||||||
permissions: "0644"
|
|
||||||
content: |
|
|
||||||
net.ipv4.ip_forward=1
|
|
||||||
runcmd:
|
|
||||||
- [sysctl, --system]
|
|
||||||
'
|
|
||||||
local disk="[{\"DeviceName\":\"$root_device\",\"Ebs\":{\"VolumeSize\":8,\"VolumeType\":\"gp3\",\"DeleteOnTermination\":true,\"Encrypted\":true}}]"
|
local disk="[{\"DeviceName\":\"$root_device\",\"Ebs\":{\"VolumeSize\":8,\"VolumeType\":\"gp3\",\"DeleteOnTermination\":true,\"Encrypted\":true}}]"
|
||||||
gateway=$(aw ec2 run-instances --image-id "$ami" --instance-type "$INSTANCE_TYPE" --count 1 \
|
gateway=$(aw ec2 run-instances --image-id "$ami" --instance-type "$INSTANCE_TYPE" --count 1 \
|
||||||
--key-name "$KEY_NAME" --subnet-id "$public_subnet" --private-ip-address 10.20.1.10 \
|
--key-name "$KEY_NAME" --subnet-id "$public_subnet" --private-ip-address 10.20.1.10 \
|
||||||
--security-group-ids "$gateway_sg" --no-associate-public-ip-address \
|
--security-group-ids "$gateway_sg" --no-associate-public-ip-address \
|
||||||
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
|
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
|
||||||
--block-device-mappings "$disk" --user-data "$gateway_user_data" \
|
--block-device-mappings "$disk" \
|
||||||
--tag-specifications "$(tags instance gateway)" "$(tags volume gateway)" \
|
--tag-specifications "$(tags instance gateway)" "$(tags volume gateway)" \
|
||||||
--client-token "$vpc-gateway" --query 'Instances[0].InstanceId' --output text)
|
--client-token "$vpc-gateway" --query 'Instances[0].InstanceId' --output text)
|
||||||
record GatewayInstanceId "$gateway"
|
record GatewayInstanceId "$gateway"
|
||||||
@@ -147,9 +143,6 @@ runcmd:
|
|||||||
record ElasticIpAssociationId "$eip_association"
|
record ElasticIpAssociationId "$eip_association"
|
||||||
eip_target=$(aw ec2 describe-addresses --allocation-ids "$eip_allocation" --query 'Addresses[0].InstanceId' --output text)
|
eip_target=$(aw ec2 describe-addresses --allocation-ids "$eip_allocation" --query 'Addresses[0].InstanceId' --output text)
|
||||||
[[ "$eip_target" == "$gateway" ]] || { echo 'Elastic IP nao associado ao gateway esperado.' >&2; return 1; }
|
[[ "$eip_target" == "$gateway" ]] || { echo 'Elastic IP nao associado ao gateway esperado.' >&2; return 1; }
|
||||||
aw ec2 modify-instance-attribute --instance-id "$gateway" --no-source-dest-check
|
|
||||||
aw ec2 create-route --route-table-id "$private_rt" --destination-cidr-block "$LOCAL_CIDR" --instance-id "$gateway" >/dev/null
|
|
||||||
aw ec2 create-route --route-table-id "$private_rt" --destination-cidr-block 10.8.0.0/24 --instance-id "$gateway" >/dev/null
|
|
||||||
server=$(aw ec2 run-instances --image-id "$ami" --instance-type "$INSTANCE_TYPE" --count 1 \
|
server=$(aw ec2 run-instances --image-id "$ami" --instance-type "$INSTANCE_TYPE" --count 1 \
|
||||||
--key-name "$KEY_NAME" --subnet-id "$private_subnet" --private-ip-address 10.20.2.10 \
|
--key-name "$KEY_NAME" --subnet-id "$private_subnet" --private-ip-address 10.20.2.10 \
|
||||||
--security-group-ids "$services_sg" --no-associate-public-ip-address \
|
--security-group-ids "$services_sg" --no-associate-public-ip-address \
|
||||||
@@ -161,12 +154,30 @@ runcmd:
|
|||||||
local private_public_ip
|
local private_public_ip
|
||||||
private_public_ip=$(aw ec2 describe-instances --instance-ids "$server" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
|
private_public_ip=$(aw ec2 describe-instances --instance-ids "$server" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
|
||||||
[[ "$private_public_ip" == None || -z "$private_public_ip" ]] || { echo 'Servidor privado recebeu IP publico inesperado.' >&2; return 1; }
|
[[ "$private_public_ip" == None || -z "$private_public_ip" ]] || { echo 'Servidor privado recebeu IP publico inesperado.' >&2; return 1; }
|
||||||
|
if [[ "$create_key" == true ]]; then
|
||||||
|
local ssh_config="$state_dir/${LAB_NAME}-$run_id.ssh-config"
|
||||||
|
cat > "$ssh_config" <<EOF
|
||||||
|
Host lab-gateway
|
||||||
|
HostName $public_ip
|
||||||
|
User ubuntu
|
||||||
|
IdentityFile "$key_file"
|
||||||
|
IdentitiesOnly yes
|
||||||
|
Host lab-servicos
|
||||||
|
HostName 10.20.2.10
|
||||||
|
User ubuntu
|
||||||
|
IdentityFile "$key_file"
|
||||||
|
IdentitiesOnly yes
|
||||||
|
ProxyCommand ssh -F "$ssh_config" -W %h:%p lab-gateway
|
||||||
|
EOF
|
||||||
|
record SshConfigFile "$ssh_config"
|
||||||
|
printf 'Acesso pelo terminal atual:\n ssh -F %q lab-gateway\n ssh -F %q lab-servicos\n' "$ssh_config" "$ssh_config"
|
||||||
|
echo 'Guarde uma copia privada do arquivo .pem antes de perder acesso a este terminal.'
|
||||||
|
fi
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Infraestrutura criada. Diario: $STATE_FILE
|
Infraestrutura criada. Diario: $STATE_FILE
|
||||||
Gateway: ubuntu@$public_ip | Servidor privado: ubuntu@10.20.2.10
|
Gateway: ubuntu@$public_ip | Servidor privado: ubuntu@10.20.2.10
|
||||||
As EC2 estao running; cloud-init e SSH podem levar mais alguns minutos.
|
As EC2 estao running; cloud-init e SSH podem levar mais alguns minutos.
|
||||||
Acesso privado via salto SSH: ssh -i CAMINHO_CHAVE -J ubuntu@$public_ip ubuntu@10.20.2.10
|
Se usou KEY_NAME preexistente, configure a IdentityFile para ambos os hosts (veja README).
|
||||||
Configure a IdentityFile para ambos os hosts em ~/.ssh/config (veja README).
|
|
||||||
OpenVPN, certificados, intranet e coleta de logs ainda precisam ser configurados.
|
OpenVPN, certificados, intranet e coleta de logs ainda precisam ser configurados.
|
||||||
A sub-rede privada nao possui saida para Internet. Veja README para instalar pacotes.
|
A sub-rede privada nao possui saida para Internet. Veja README para instalar pacotes.
|
||||||
Elastic IP do gateway: $public_ip (alocacao: $eip_allocation).
|
Elastic IP do gateway: $public_ip (alocacao: $eip_allocation).
|
||||||
|
|||||||
Reference in New Issue
Block a user