OCI LoadBalancerを使ってNATとSSL/TLS終端を行う

## 概要
外部ネットワークから内部セグメントのサーバーにアクセスする場合はNATと通信暗号化が必須条件となります。
一番メジャーな方法としてはクライアントのブラウザ等からhttpsでWebサーバやAPIサーバにアクセスしますが、今回はhttp以外のアプリケーションについて外部から安全にアクセスできるようサーバー側にLoadbalancerを設置してNATとSSL/TLS終端(SSL/TLSオフロード)を行いました。

## 構成
今回は検証サーバーとしてRedisを使用しました。
RedisクライアントとLoad Balancer間はSSL/TLSで通信を行います。
宛先IPアドレス/宛先Portは変換され、Load BalancerとRedis server間は別セッションで通信を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/25efa0d8-0b81-a3e7-da50-ba5f026b9cf3.png)
											
## クライアント側準備
* インターネット環境
* Redis client
　今回はPython3を使ってRedis serverにアクセスできるよう準備しました。
  インタプリタにて手順を確認しながらすすめます。

## サーバー側準備
OCI公式ガイド等を参照しながら各リソースを準備します。
* VCN
* Load balancerを設置するPublic subnet
* Redis serverを設置するPrivate subnet
* Public subnetにInternet Gatewayを追加
* Public subnetのRoute tableにデフォルトルートをInternet Gatewayに向くよう設定
* Public subnetのSecurity listに送信元IP 0.0.0.0/0, 宛先IP 0.0.0.0/0のOutbound通信を許可
* Public subnetのSecurity listまたはNetwork Security Group(NSG)に送信元IP Redis clientのIP, 宛先Port 6378のInbound通信を許可
* Private subnetにNAT Gatewayを追加
* Private subnetのRoute tableにデフォルトルートをNAT Gatewayに向くよう設定
* Private subnetのSecurity listに送信元IP 0.0.0.0/0, 宛先IP 0.0.0.0/0のOutbound通信を許可
* Private subnetのSecurity listに送信元IP Public subnet, 宛先Port 6379のInbound通信を許可
* Private subnet内にRedis serverを構築して、Public subnetからport 6379で通信を受け付けるようredis.confやfirewallを設定
* SSL証明書の準備

## Loadbalancer設定手順
* Loadbalancer作成
-OCIコンソールのナビゲーションメニューよりネットワーキング>>ロード・バランサを選択して、「ロード・バランサの作成」を押下
-ロード・バランサ名：任意の名前
-可視性タイプの選択：パブリック
-パブリックIPアドレスの割当て：エフェメラル
　パブリックIPを予約しておくとIPアドレス再利用ができます。
-シェイプ：検証利用であれば10Mbps
-ネットワーキングの選択：作成したVCNとPublic subnetを指定
-ネットワーク・セキュリティ・グループを使用してトラフィックを制御：NSGで通信許可する場合はチェックを入れて作成したNSGを選択
-「次」を押下
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/6ad359d5-422f-1963-330d-995dd4abcb8a.png">

-ロード・バランシング・ポリシーの指定：IPハッシュ
-バックエンドの追加：作成したRedis serverを選択してポートを6379に変更
-ヘルス・チェック・ポリシーの指定：TCP 6379を指定
-「次」を押下
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/6dafc822-900e-f2f3-17eb-e47aa3763204.png">
-リスナー名：任意の名前
-リスナーで処理するトラフィックのタイプ：TCP
-モニターするポート：6378
-SSLの使用：後で設定
-「次」を押下
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/4a6d6283-6c0a-2f0e-0fd3-755898e297cf.png">

-ロギングの管理は任意。後で設定も可能です
-「送信」を押下
-アクティブになったらリソースから証明書を選択して、証明書リソースをロードバランサ管理対象証明書を選択して「証明書の追加」を押下
-証明書名：任意の名前
-SSL証明書：認証局から発行された情報を指定
-「証明書の追加」を押下
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/26d32c66-6221-7591-a709-30be2448277c.png">
-リソースからリスナーを選択して、作成済みリスナーの右端の【︙】メニューから編集を選択
-SSLの使用：選択
-証明書リソース：ロードバランサ管理対象証明書
-証明書名：作成した証明書を選択
-「変更の保存」を押下
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/3e9489cd-d7bd-aa23-3364-8757718e3b93.png">

* DNSサーバーにホスト登録
-ロード・バランサ情報にIPアドレスが表示されるのでメモ
-DNSサーバにAレコードとして追加

## 実行結果
Redis clientからキーバリューの登録と取得を行ってみます。
```
[user@linux ~]$ python3
>>> import redis
>>> r=redis.Redis(host="【ホスト名】",port=6378,ssl=True)

## 登録 HSET name1 key1 value1 ##
>>> r.hset('name1','key1', 'value1')
1

## 取得 HGETALL name1 ##
>>> name1 = r.hgetall('name1')
>>> print(name1['key1'.encode()].decode())
value1
>>>
```
-通信内容をRedis client側とRedis server側のパケットキャプチャを取得しました。
 Redis clientについてはLoad balanccerとSSL/TLSセッションを確立したうえで暗号化通信を行っていますので通信内容は確認できません。
 Redis serverについてはLoad balancerにてSSL/TLS終端されているため、Load balancerとRedis server間の通信内容は暗号化していないことが判ります。
【Redis client】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/68a72580-a276-8c98-49d6-4b0f942c2ef0.png)

【Redis server】
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/96e98fd3-9115-c044-8e60-0a5f095efd8c.png)

## 注意点
NLBのセッションタイムアウトについて、TCPのアイドルタイムアウトは6分、UDPのアイドルタイムアウトは2分です。
セッションを維持する場合はOSやアプリケーションのKeep Alive機能を使用する必要があります。
[What is the Idle Timeout for a Session on a NLB](https://support.oracle.com/knowledge/Oracle%20Cloud/2920671_1.html)