AzureでNATを構築する

こんにちは。Azureで構成を組む際、NATを構築するのにはまってしまいました。
結果的に解決できたのですが、同様のポイントでハマる人が一定数いるだろうということでメモを残しておきます。

# AzureのvirtualMachineについて
実はAzureではNATを立てなくてもインターネットにアクセスすることができます。
virtualMachineにpublicIPが付与されていなくても動的に割り当てて外部と通信をはかることできます。
しかし、往々にして外部に通信する際はIPを固定にしたいし、きちんと管理しておきたいもの。
実運用に耐えるレベルのNATを構成するやり方をterraformで記述したので以下説明です。

# terraform
## resource group / virtual network

```json:resource_group.tf
resource "azurerm_resource_group" "nat-test" {
  name     = "natTest"
  location = "Japan East"
}

resource "azurerm_virtual_network" "default-network" {
  name = "defaultNetwork"
  address_space       = ["10.0.0.0/16"]
  location = "Japan East"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
}
```

ここは特に注意することはありません。普通にresource groupとvirtual networkを作成します。

## security group <- これがはまりポイントだった
```json:nat_security_group.tf
resource "azurerm_network_security_group" "nat-test" {
  name = "natTestSecurityGroup"
  location = "Japan East"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"

  security_rule {
    name = "AllowAnyOutBoundInnerSubnet"
    priority = 500
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "VirtualNetwork"
    destination_address_prefix = "Internet"
  }
}
```
ここがはまりポイントです。
サービス運用する上でセキュリティグループの設定は必須ですが、上記のInboundRuleがないと、プライベートネットワーク内から送信されたパケットがNATに到達することができません。
何を許可しているかというと、
- `source_address_prefix = "virtualNetwork"`: virtualNetworkから
- `direction = Inbound`: NATインスタンスに入ってくる
- `destination_address_prefix = "Internet"`: インターネットに向けた

パケットを許可しているわけです。NATなんだから当然なのですが。
なぜこれを許可リストに加えないといけないかというと、
![Kobito.M8wsj8.png](https://qiita-image-store.s3.amazonaws.com/0/125051/366140a5-7340-d703-fb12-e44dba212773.png "Kobito.M8wsj8.png")

既定の規則で設定される、`DenyAllInBound`によって上記のパケットは拒否されてしまうからです。既定の規則はデフォルト付与され、ポータルではボタンを押さないと表示されないので気づくのに随分時間を要してしまいました。

あとはNATインスタンス専用のサブネットを切っておきます。

```json:nat_subnet.tf
resource "azurerm_subnet" "nat-test" {
  name = "natTest"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
  virtual_network_name = "${azurerm_virtual_network.default-network.name}"
  address_prefix = "10.0.0.0/24"
  network_security_group_id = "${azurerm_network_security_group.nat-test.id}"
}
```
## NATのNetworkInterface
NATインスタンスにアタッチするNetworkInterfaceも注意が必要です。

```json:nat_networkinterface.tf
resource "azurerm_public_ip" "nat-test" {
  name = "natTest"
  location = "Japan East"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
  public_ip_address_allocation = "static"
}

resource "azurerm_network_interface" "nat-test" {
  name = "natTest"
  location = "Japan East"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
  enable_ip_forwarding = "true"

  ip_configuration {
    name = "nat-default-ip"
    subnet_id = "${azurerm_subnet.nat-test.id}"
    private_ip_address_allocation = "static"
    private_ip_address = "10.0.0.10"
    public_ip_address_id = "${azurerm_public_ip.nat-test.id}"
  }
}
```
注意点としては、

- ipフォワーデイングを有効にすること
- NetworkInterfaceの`ip_configuration`で、アロケーションをstaticにすること

です。public ipのアロケーションも当然ながらstaticにしておくべきでしょう。

## route table
VirtualNetwork内から外部に向けた通信を吸収するのはroute tableを使うのがよいです。

```json:nat_routetable.tf
resource "azurerm_route_table" "nat-test" {
  name = "natTestRouteTable"
  location = "Japan East"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
}

resource "azurerm_route" "nat" {
  name = "nat"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
  route_table_name    = "${azurerm_route_table.nat-test.name}"
  address_prefix = "0.0.0.0/0"
  next_hop_type = "VirtualAppliance"
  next_hop_in_ip_address = "10.0.0.10"
}
```

- ルートのアドレスプレフィックスを0.0.0.0/0にしておけば外部に向かう全ての通信を吸収することができます
- `next_hop_type = "VirtualAppliance", `next_hop_in_ip_address = "10.0.0.10"`これはNATインスタンスのprivate IPです。アドレスプレフィックスに該当したパケットはそのIPに飛ばすよ、ということですね。

## NAT内のインスタンスの設定

```json:application.tf
resource "azurerm_subnet" "application" {
  name = "application"
  resource_group_name = "${azurerm_resource_group.nat-test.name}"
  virtual_network_name = "${azurerm_virtual_network.default-network.name}"
  address_prefix = "10.0.1.0/24"
  network_security_group_id = "${azurerm_network_security_group.application.id}"
  route_table_id = "${azurerm_route_table.nat-test.id}"
}
```

NAT内の設定で注意する点は一点です。
subnetを切る際に、`route_table_id`で先ほど作成したルートテーブルを参照するようにするだけです。

## NATインスタンス上の操作
ここまででAzure特有の設定は終わりです。
自分のマシンはubuntu16.04だったのでおまけとしてubuntuのインスタンスでの操作を記載しておきます。

```bash:IPフォワードを有効にする手順
[root@nat ~]# vim /etc/sysctl.conf
# net.ipv4.ip_forward = 1　←この一行を追記

[root@nat ~]# /etc/init.d/networking restart
[root@nat ~]# apt-get install firewalld
[root@nat ~]# systemctl enable firewalld
[root@nat ~]# systemctl start firewalld
[root@nat ~]# firewall-cmd --state
running <- これは確認

[root@nat ~]# firewall-cmd --get-default-zone
public <- これをexternalにする必要がある

[root@nat ~]# firewall-cmd --set-default-zone=external
[root@nat ~]# firewall-cmd --get-default-zone
external <- これでOK
[root@nat ~]# firewall-cmd --reload
success
```

# まとめ
以上、Azure上でNATインスタンスを立てる手順を解説しました。
まとめると、

- セキュリティグループではVirtualNetworkないからInternetに向けたInboundを許可しなければならない
- ルートテーブルを設定する
- ネットワークインターフェースのIPフォワーディングを有効にする
- NAT内のサブネットを定義する際に、ルートテーブルを紐づける

こんなところでしょうか。
なにかと注意しなければならない点が多いですが、正しく設定できればポータルがみやすかったりといい点も多いです。それではみなさん快適なAzureライフを！