W poprzednim artykule dowiedzieliśmy się jak zbudowana jest sieć domyślnie dostarczana dla każdego nowego konta (default VPC) . Składa się ona z trzech publicznych podsieci, pozwalając na umieszczanie w niej zasobów wspierających publiczne adresowanie. Wyklucza to użycie choćby lambd, ponieważ nie jest możliwe przypisanie im publicznego adresu IP. Takie podejście nie wspiera również ograniczenia dostępu do zasobów, które nie muszą lub wręcz nie powinny być adresowane z publicznego Internetu.
Przyjrzymy się dwóm przypadkom. Pierwszym z nich będzie wystawienie API, drugim aplikacja do raportowania do naszego partnera biznesowego.
Przypadek pierwszy – API
Opis

Mamy aplikacje będącą backendem dla klientów mobilnych oraz przeglądarek. Jedną częścią aplikacji jest API, które jest publicznie dostępne. Drugą częścią jest baza danych dla której krytycznym wymaganiem jest by nie była dostępna przez Internet.
Rozwiązanie
Jednym ze sposobów na rozwiązanie tego problemu jest utworzenie dodatkowego, prywatnego subnetu w którym umieścimy naszą bazę danych. Takie rozwiązanie widoczne jest na poniższym diagramie.

Widzimy tutaj EC2 na którym umieścimy nasze API znajdujące się w publicznym subnecie. Security grupa przypisana do niego zezwala na ruch przychodzący z dowolnego miejsca. Cenna baza danych siedzi w prywatnym subnecie. Jest opakowana osobną security grupą, która pozwala tylko na ruch na jednym porcie pochodzący z grupy naszego API.
Prywatny subnet
Prywatny subnet różni się od publicznego tym, że nie posiada bezpośredniego połączenia sieciowego do Internet Gateway’a. Na przytoczonym przykładzie widać to dokładnie na powiązanej z nim tablicy routingu – gdzie trasujemy tylko pakiety wewnątrz naszego VPC (10.0.0.0/16).
Implementacja
Najpierw tworzymy vpc, internet gateway oraz wiążemy je ze sobą:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
InternetGateway:
Type: AWS::EC2::InternetGateway
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref Vpc
Następnie tworzymy tablicę routingu oraz określamy w niej trasę do internet gatewaya, efektywnie sprawiając, że każdy subnet do którego przypiszemy tą tablicę będzie publiczny.
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
InternetRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
RouteTableId: !Ref PublicRouteTable
GatewayId: !Ref InternetGateway
Tworzymy drugą tablicę routingu, która potrafi trasować pakiety tylko wewnątrz naszego VPC.
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1c
CidrBlock: 10.0.3.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref Vpc
Tworzymy subnet publiczny oraz prywatny i wiążemy je ze stworzonymi tablicami.
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref Vpc
PublicSubnetTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1c
CidrBlock: 10.0.3.0/24
VpcId: !Ref Vpc
PrivateSubnetTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet
Pozostało nam jeszcze stworzyć security grupy dla API oraz RDSa. Zgodnie z wcześniejszym opisem grupa dla API zezwala na ruch z dowolnego adresu na porcie 443. Grupa dla bazy pozwala tylko na ruch po porcie 3306 (MySql) i tylko pochodzący od zasobów powiązanych z security grupą API.
ApiSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Api security group
GroupName: api-security-group
SecurityGroupIngress:
- Description: Open https traffic
CidrIp: 0.0.0.0/0
IpProtocol: TCP
FromPort: 443
ToPort: 443
VpcId: !Ref Vpc
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Database secruity group
GroupName: db-security-group
SecurityGroupIngress:
- Description: Allow traffic from the api
IpProtocol: TCP
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref ApiSecurityGroup
VpcId: !Ref Vpc
Tak przygotowana sieć pozwala na umieszczenie w niej naszego API oraz RDSa.
Cały template gotowy na dodanie do niego EC2 i bazy dostępny jest tutaj.
Koszt
Dodanie drugiego subnetu oraz security grupy nie wpływa na zwiększenie kosztów. Mimo wszystko trzeba być świadomym tego ile kosztują EC2 oraz RDS które wykorzystujemy.
Przypadek drugi – raportowanie
Opis
Nasza aplikacja ma za zadanie wykonywać raportowanie do partnera biznesowego. By móc wysłać raport potrzebujemy połączyć się do bazy, wykonać jakieś operacje na danych i zarejestrować raport w określonym API, ot wszystko. Wymagamy by baza danych oraz nasza aplikacja nie były publicznie dostępne. Jednocześnie aplikacja musi być w stanie skontaktować się z API naszego partnera dostępnym po publicznym Internecie. Nasz partner oczekuje od nas podania zakresu IP z których będziemy się do niego łączyli.
Rozwiązanie

Ponownie chcemy schować naszą bazę danych w prywatnym subnecie, tutaj nie powinno być wątpliwości.
Aby spełnić wymagania dotyczące aplikacji trzeba umieścić ją w prywatnym, ale lekko zmodyfikowanym subnecie. Konkretnie, ruch do adresów z poza naszego VPC (10.0.0.0/16) przekierować na NAT Gateway. Zajmie się on translacją adresu z prywatnego na publiczny. Wykorzystanie NAT Gateway’a w trasowaniu ruchu da nam dodatkowy plus w postaci stałego adresu IP z którego wychodzi na nasz ruch. Używany jest wtedy adresu IP gatewaya.
Elastic IP
Zasób Elastic IP to jeden publiczny adres IPv4. Taki adres możemy przypisać do innego zasobu, np EC2, NAT Gatewaya, itd. Samo pozyskanie adresu nic nie kosztuje, ale tylko jeżeli jest on powiązany z innym zasobem. W innym przypadku zostaje za niego naliczona opłata. Drugi i każdy kolejny adres przypisany do tego samego zasobu również obciąży nasz rachunek. W obu przypadkach AWS policzy $0,005 za każdą godzinę istnienia adresu.
Implementacja
Przy opisie tego rozwiązania pominiemy tworzenie VPC oraz publicznej podsieci ponieważ poruszyliśmy je chwilę temu.
Publiczny subnet musimy jednak rozszerzyć o NAT Gateway, robimy to dodając następujące zasoby do naszego CloudFormation.
ElasticIpForNat:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt ElasticIpForNat.AllocationId
SubnetId: !Ref PublicSubnet
NAT wymaga tego by przypisany był do niego publiczny adres IP.
Zgodnie z opisem potrzebujemy stworzyć prywatny subnet dla aplikacji.
ApplicationSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.3.0/24
VpcId: !Ref Vpc
ApplicationSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
ApplicationSubnetTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref ApplicationSubnetRouteTable
SubnetId: !Ref ApplicationSubnet
W przypisanej do niego tablicy routingu musimy umieścić regułę, która ruch nie pasujący do naszego VPC skieruje na NAT Gateway.
InternetRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
RouteTableId: !Ref ApplicationSubnetRouteTable
NatGatewayId: !Ref NatGateway
Subnet dla bazy danych nie różni się od podanego w poprzednim przykładzie i wygląda następująco:
DatabaseSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.4.0/24
VpcId: !Ref Vpc
DatabaseRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
DatabaseSubnetTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref DatabaseRouteTable
SubnetId: !Ref DatabaseSubnet
Gotowy template dostępny jest tutaj.
Koszt
NAT Gateway wprowadza dodatkowy koszt. W przypadku rejonu eu-west-1 (Irlandia) jest to $0,048 za każdą godzinę działania oraz $0,048 za każdy przetworzony GB. Jeśli rozpatrzymy to w skali miesiąca to mamy 35,715 dolara za samo tylko istnienie gateway’a, nie uwzględniając transferu. Poza tym na poziomie sieciowym nie generujemy dodatkowych kosztów.