Como Criar um NAT Instance na AWS
Introdução
Você pode ter diversas razões e motivos para querer configurar uma instância NAT na AWS, para meu caso de uso estava precisando criar um novo ambiente de QA, este ambiente consistia em ter uma função Lambda que fosse capaz de ter uma conexão com a internet e também com recursos da VPC.
Felizmente uma função Lambda pode ser configurada dentro de uma VPC e então conceder acesso aos recursos dela, mas infelizmente ao fazer isso, essa mesma função que agora pode se comunicar com RDS, Elasticache e outros recursos da VPC, perde a conexão com a internet, isso ocorre porque a AWS irá atribuir um ENI com endereço IP privado na função lambda quando adicionada na VPC, desta forma impedindo sua conexão com a internet.
Há uma solução sugerida pela AWS para este caso, que consiste em adicionar um NAT Gateway, isso é robusto, escalável e com certeza um caminho a seguir para um ambiente de produção. O problema que me encontrei aqui é que o NAT Gateway é um serviço caro, gerenciado pela AWS e que funciona o tempo todo, então parecia um grande exagero para um ambiente de QA que funciona em alguns horários pré-definidos.
Como citei, esse era o meu cenário, mas novamente você pode querer ter um NAT por qualquer outro motivo, por exemplo para fornecer conexões a internet de qualquer recurso criado em uma sub-rede privada, como instâncias, RDS, etc.
Como alternativa, optei por não usar o serviço gerenciado NAT Gateway, mas sim criei e configurei minha própria instância NAT, compartilharei com você uma breve descrição do que significa o NAT e passaremos por um tutorial de como atingi esse objetivo.
O que é NAT?
NAT significa Network Address Translation
, é uma técnica utilizada em redes de computadores para traduzir endereços IP de um formato para outro. Seu principal objetivo é permitir que vários dispositivos em uma rede compartilhem um único endereço IP externo, isso é especialmente útil em redes domésticas e empresariais, onde há escassez de endereços IP públicos disponíveis.
Por exemplo, quando você contrata um plano de internet para sua casa, sua operadora não entregará para você um endereço IP público para cada um de seus dispositivos, como laptops, computadores, TVs, impressoras, celulares, etc. Mas sim entregará um IP público dinâmico e todos os seus equipamentos conectados em seu roteador poderão usar o mesmo IP público dinâmico fornecido através do NAT, pense nisso conforme diagrama abaixo:
Onde cada endereço IP 192.168.0.X
de seus equipamentos conectados é entregue através do DHCP
configurado em seu roteador, e todos obtém um único endereço IP público, esse funcionamento se dá através do NAT configurado em seu roteador.
No contexto da AWS, o NAT desempenha um papel parecido e é fundamental na comunicação entre instâncias em redes privadas e recursos externos, como a internet. Ao criar instâncias em uma sub-rede privada, ela não terá um endereço IP público, mas sim um endereço privado da sua VPC, o que as impede de se comunicar diretamente com a internet. Então é neste momento que o NAT trabalha, pois atua por meio de tradução de endereços IP, conforme diagrama abaixo:
Cada recurso criado na sub-rede privada, possui uma rota para um NAT que está em uma sub-rede pública com acesso à internet, permitindo então assim que os recursos privados consigam acesso a internet.
Concluído essa introdução, espero que você esteja um pouco mais familiarizado com esse conceito, partiremos agora para o tutorial prático.
Tutorial
Antigamente, a AWS fornecia uma AMI pronta para instâncias NAT, porém seu suporte foi descontinuado em dezembro de 2023, conforme anúncio oficial:
Como alternativa, a AWS sugere criar sua própria instância NAT baseado no Amazon Linux mais recente, porém eu não gostaria de usar essa imagem e sim me basear no Ubuntu, bom e foi isso que eu fiz, abaixo estão os passos:
Pré-Requisitos
Este artigo é extenso, passaremos por muitos passos, para evitar que fique ainda maior, não cobriremos a configuração de uma VPC do zero, também não discutiremos em detalhes sobre a VPC ou rede, se você já possui uma VPC com sub-redes públicas e privadas, pode pular algumas etapas e passos que descrevo abaixo, senão for o caso, passaremos pelas principais configurações necessárias.
Criando Grupo de Segurança
1- Acesse o console AWS → VPC → Security Group → Create Security Group e defina de acordo com suas preferências:
Name: por exemplo
nat-instance-sg
Description: por exemplo
Security group with permissions required for NAT Instance
Inbound: suas regras de entrada, por exemplo
HTTP
,HTTPS
eSSH
SSH
estou definindo um Management Prefix List
, você pode querer consultar esse tutorial para saber mais:👉 Como melhorar a administração e organização dos Grupos de Segurança da AWS com o VPC Prefix List
- Outbound: suas regras de saída, você precisará adicionar
HTTP
eHTTPS
para0.0.0.0/0
para ter conexão com a internet
- Tags: se preferir, adicione
tags
Criando Nat Instance
1- Acesse o console AWS → EC2 → Instance → Launch Instance → preencha as informações:
Name: defina um nome de sua preferência, por exemplo
NAT Instance
AMI: escolha
Ubuntu
Architecture: escolha
64-bit (x86)
Instance Type: escolha o tamanho, lembre-se que todas as conexões irão passar por essa instância, então definir uma instância muito pequena pode aumentar o gargalo e a latência da conexão com a internet, porém para meu caso de uso que era um ambiente de QA, o tipo
micro
pareceu ser uma boa escolha.Keypair: escolha seu
keypair
, isso é sua chave de acesso que precisaremos dele depois para conseguir conectar através deSSH
VPC: escolha o
ID
da suaVPC
Subnet: defino como
sem preferências
, pois não me importa muito em qual sub-rede nossa instância NAT será criada, porém se preferir você pode definir uma subnet pública e não privada⚠O NAT Instance deve ser criada em uma sub-rede Pública!Auto-assign Public IP: escolho habilitado, pois desejo que essa instância NAT tenha um endereço IP público criado automaticamente
Security Group: escolho o grupo de segurança que criamos anteriormente
Storage: defino um tamanho e seleciono o tipo como
gp3
Por fim clico em Launch Instance
Configurando o NAT Instance
1- Com nossa instância criada, acesso através de SSH → e primeiro atualizo os pacotes do Ubuntu com o comando:
sudo apt update -y
2- Agora verifico qual o nome da interface de rede primária com o comando:
ip a
E descubro que ela se chama ens5
3- Agora ativo o encaminhamento de IP com os comandos:
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
4- Então agora criamos a regra para o NAT com o comando:
ens5
para o nome correto da sua interface de rede que descobrimos anteriormente se for o caso.# Making a catchall rule for routing and masking the private IP
sudo iptables -t nat -A POSTROUTING -o ens5 -s 0.0.0.0/0 -j MASQUERADE
5- Agora precisamos tornar essa regra persistente, pois quando acontecer um reboot ou stop nessa instância, a regra do iptables
para o NAT será perdida e você certamente não gostará de ter que acessar ela toda vez para repetir este comando, então para isso criamos uma pasta chamada iptables
no diretório etc
:
sudo mkdir -p /etc/iptables
6- E salvo nossa regra em um arquivo chamado rules.v4
com o comando:
sudo iptables-save | sudo tee /etc/iptables/rules.v4
7- Neste momento, nossa regra já está salva, mas ainda assim não está automático, seria necessário ainda acessar a instância para restaurar nossa regra, bom vamos corrigir isso agora instalando o iptables-persistent
no modo silencioso que fará esse trabalho automático para nós.
sudo debconf-set-selections <<EOF
iptables-persistent iptables-persistent/autosave_v4 boolean true
iptables-persistent iptables-persistent/autosave_v6 boolean true
EOF
sudo apt-get -y install iptables-persistent
Desativando Verificações de Origem/Destino
1- Ainda não acabamos tudo, precisamos agora parar a verificação de origem e destino dessa instância, então acesso o console AWS → EC2 → seleciono a instância → Actions → Networking → Change source/destination check
2- Clico em Stop
Criando Tabela de Roteamento na VPC
1- Acesse o console AWS → VPC → Route Tables → Create Route Table
2- Defina um nome de sua preferência como por exemplo Private Route → selecione sua VPC → defina Tags se desejar → clique em Create Route Table
3- Agora clique em Edit Routes
4- Adicione uma rota:
Destination:
0.0.0.0/0
Target:
escolha o ID da sua instância NAT
Criando Sub-Redes Privadas
Como estou na região de Ohio
, tenho três Availabilty Zones
disponíveis:
us-east-2a
us-east-2b
us-east-2c
Já tenho 3 sub-redes públicas criadas por padrão, então criarei outras três sub-redes privadas para cada Availability Zone
.
1- Acesse o console da AWS → VPC → Subnets → Create Subnet
172.31.32.0/20
para o Availabilty Zone us-east-2c
. Para você não precisar calcular esses blocos CIDR, clique na seta para direita (>
), isso irá preencher para você o próximo bloco CIDR corretamente.us-east-2a
|172.31.48.0/20
us-east-2b
|172.31.64.0/20
us-east-2c
|172.31.80.0/20
2- Volte para o menu Route Tables → selecione a Tabela de Roteamento Privado que criamos → selecione a aba Subnet Associations → Edit
3- Selecione agora as sub-redes privadas criadas → clique em Save Association
Testando
Fizemos muitas coisas até aqui, por favor não se sinta "perdido", recapitulando o que fizemos até aqui:
Criamos o Grupo de Segurança
Criamos o NAT Instance na sub-rede pública
Desativamos Verificações de Origem/Destino
Criamos a Tabela Privada de Roteamento
Criamos as Sub-redes Privadas
Neste momento, já temos tudo devidamente configurado, mas e agora como testar se tudo está funcionando? Bom para isso vou fazer 2 testes:
Criar uma função Lambda dentro da VPC (lembre-se que isso perde a conexão dela com a internet) e testar sua conexão com a internet através do NAT Instance
Criar uma instância EC2 na sub-rede privada e testar sua conexão com a internet
Lambda
Para adicionar a função Lambda que vamos criar, precisamos antes criar uma policy
e role
, vamos fazer isso agora:
Policy
1- Acesse o console AWS → IAM → Policies → Create Policy → crie a política com o código abaixo:
<your-region>
, <your-account-id>
e <your-lambda-function-name>
para seus valores reais{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:DescribeInstances",
"ec2:AttachNetworkInterface"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:<your-region>:<your-account-id>:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:<your-region>:<your-account-id>:log-group:/aws/lambda/<your-lambda-function-name>:*"
]
}
]
}
2- Defina de acordo com suas preferências:
Name: por exemplo
lambda-test-internet-connection-policy
Description: por exemplo
Policy with necessary permissions to be used in the lambda function named test-internet-connection
Role
1- Acesse o console AWS → IAM → Role → Create Role → crie a role escolhendo:
AWS Services
Lambda
2- Adicione a política que acabamos de criar
3- Defina de acordo com sua preferência:
Name: por exemplo
lambda-test-internet-connection-role
Description: por exemplo
Role with required permissions used in lambda function named test-internet-connection
Criando o Lambda Function
1- Acesse o console AWS → Lambda → Create Function → Author from Scratch
2- Defina de acordo com sua preferência:
- Name: por exemplo
test-lambda-internet-connection
3- Escolha as opções:
Runtime:
Node.js
Architecture:
x86_64
Use an existing role: escolha a role que criamos anteriormente
4- A função será criada → clique na aba Code → renomeie o arquivo de index.mjs
para index.js
(remova o m
do nome)
5- Apague o código criado automaticamente pela AWS → adicione este código:
const getContent = function (url) {
// return new pending promise
return new Promise((resolve, reject) => {
// select http or https module, depending on reqested url
const lib = url.startsWith('https') ? require('https') : require('http');
const request = lib.get(url, (response) => {
// handle http errors
if (response.statusCode < 200 || response.statusCode > 299) {
reject(new Error('Failed to load page, status code: ' + response.statusCode));
}
// temporary data holder
const body = [];
// on every content chunk, push it to the data array
response.on('data', (chunk) => body.push(chunk));
// we are done, resolve promise with those joined chunks
response.on('end', () => resolve(body.join('')));
});
// handle connection errors of the request
request.on('error', (err) => reject(err))
})
};
exports.handler = async (event) => {
console.log('Received event: %j', event);
const url = event.url || 'https://www.google.com';
try {
const content = await getContent(url);
console.log('Response content: %j', content);
console.log('You have access to the internet, congratulations!');
return {
statusCode: 200,
body: JSON.stringify({
message: 'You have access!',
content,
}),
};
} catch (e) {
console.error(e);
return {
statusCode: 500,
body: JSON.stringify({
message: e.toString(),
}),
};
}
}
6- Clique em Configure Test Event
7- Defina um nome de sua preferência, por exemplo TestInternetConnection
8- Clique em Deploy → Test
9- Vamos colocar nossa função lambda na VPC → clique na aba Configuration → VPC → Edit
10- Selecione as Sub-redes privadas → e o grupo de segurança (escolhi o default-vpc security group
)
11- Clico em Test novamente e concluo:
Criando um EC2 Privado
1- Acesse o console AWS → EC2 → Launch Instance → defina de acordo com sua preferência:
Name: por exemplo
ec2-private-test-internet-connection
AMI:
Amazon Linux
AMI Amazon Linux
por já ter o SSM Manager Agent
instalado por padrão para conseguirmos fazer ssh
através do Session Manager
, uma vez que esta instância de teste será criada na sub-rede privada e não terá um IP público.- Escolha uma sub-rede privada
- Verifique se a opção
Auto-assign public IP
é marcada comoDisable
Role
1- Com nossa instância criada, precisaremos criar uma role
para permitir conexões através do Session Manager
, então vou para o console AWS → IAM → AWS Service → seleciono EC2
2- Adiciono a permissão AmazonSSMManagedInstanceCore
3- Defina um nome e descrição de acordo com suas preferências:
Name: por exemplo
ec2-session-manager-role
Description: por exemplo
Role with required permissions to allow access to EC2 through Session Manager
4- Volto para o console AWS → EC2 → seleciono a instância privada de teste → Actions → Security → Modify IAM Role
5- Seleciono a role
que criamos anteriormente
6- Estamos quase lá, agora edito as regras de entrada do grupo de segurança do NAT Instance para permitir o ping
dos blocos CIDR
das sub-redes privadas
7- Edito agora nas regras de saída para permitir que o NAT Instance envie o ping
para a internet
8- Agora com tudo pronto, vamos tentar conectar em nossa instância privada que não tem um IP público, através do Session Manager
que possui permissões adequadas no IAM Role
que foi anexada em nossa instância. Para isso seleciono novamente a instância privada de teste → Connect
9- Altero para a aba Session Manager → Connect
10- Executo um ping
para o Google:
ping google.com
Conclusão
Fizemos uma breve explicação sobre o NAT Instance, também observamos o porque ele seria útil para meu caso de uso, além disso passamos por um tutorial de como configuramos um NAT Instance no Ubuntu com configurações persistentes para que funcione mesmo depois de reiniciar ou desligar.
Garantimos que mesmo após de um reboot, as regras do NAT estarão funcionando:
Garantimos também que mesmo que a instância esteja parada (stopped
), quando ligarmos ela, automaticamente a ENI da tabela de rota privada que criamos
mudará o status para ativo
sem qualquer ação manual, fazendo com que o NAT funcione.
Também criamos dois testes práticos e reais:
uma função lambda na VPC com conexão na internet
e outra instância EC2 privada com conexão na internet, para isso configuramos também permissões adequadas.
Ao final fomos capazes de atingir o objetivo.
Como mencionei no início, este meu cenário eu não precisava da instância ligada o tempo, então se você também estiver interessado em reduzir ainda mais seu custo, talvez ache interessante agendar o STAR/STOP
de uma instância EC2, veja mais detalhes sobre como fazer isso neste outro artigo:
Com isso consegui reduzir um custo inteiro mensal de um NAT Gateway para o preço sob demanda do EC2 em uma instância mínima e com horários agendados de funcionamento.
Subscribe to my newsletter
Read articles from SimplesCloud directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by