Amazon Lightsailインスタンス内のMySQLに外部から接続する

AWSのセキュリティグループに頼り切りの人生なので、自分で設定するとなると怖かったです(感想)

# 前提
Lightsailでbitnami社のblueprintを使って構築したワードプレス環境に含まれているMySQLサーバに対して、インターネッツからアクセスしたい。

例えばDataStudioとかRedashとかでクエりたい時がきっとある。

もしくは個人のマシンから接続したいなど。

ちなみに今回はインスタンスは構築済みとします。

# 解決すべき問題と対処法
1. bitnamiイメージで構築されたワードプレス環境のMySQLは、デフォルトではlocalhostからのアクセスしか想定していない
    - ローカルホスト以外から接続するためのユーザーを追加する
2. デフォルト状態では、Lightsailインスタンスは3306ポートへの通信を許可していない
    - LightsailのGUIでファイアウォール設定を変える
3. LightsailインスタンスのファイアウォールはAWSのVPCセキュリティグループみたいに通信元・通信先を設定できない
    - インスタンス内でiptablesを使って通信可能なホストを絞る

# 解決する順番
気にし過ぎなのかもしれないですが、上記問題を解決するにあたって順番が結構重要な気がしている。

3つめの問題点が割とクセモノで、1, 2を先に解決してしまうと、3306に対して誰でも通信できちゃうことになる。
まあ1で作成するユーザーにpass付いてるので大丈夫っちゃ大丈夫な気もするが、念の為。

なので、解決の順番は 1 -> 3 -> 2で進めることにする。

# MySQLにユーザーを追加する
まずはLightsaileインスタンスにSSHログインしましょう。

Lightsailにログインするとインスタンス一覧があるので、対象のインスタンスを選択します
<img width="1291" alt="スクリーンショット 2020-04-08 16.51.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207329/bc6d0b0c-d9d7-f3ed-a97e-fe4464805a78.png">

するとでっかい「Connect using SSH」ボタンがあると思うので、それをクリックしましょう。
<img width="1297" alt="スクリーンショット 2020-04-08 16.54.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207329/1f9873f1-fb84-f243-0d99-073293acfc7b.png">

したらブラウザでターミナルチックなやつが開いて、SSHログインした状態になります。

ここから後の作業にはroot権限が必要になるため、予め

```bash
$ sudo su -
```

でrootユーザーになっておきましょう。

## MySQLへの接続元設定を解除

デフォルトではMySQLへの接続がローカルホストにバインドされているとのことなので、下記で設定を変更します。

```bash
$ vim /opt/bitnami/mysql/my.cnf
...
# bind-address=127.0.0.1 <- コメントアウトする
...
```

上記保存してからMySQLを再起動。

注: 再起動中はWordpressが表示できなくなるので、ダウンタイムが気になる方は深夜などに作業しましょ

```bash
$ /opt/bitnami/ctlscript.sh restart mysql
```

## MySQLログイン

下記コマンドでパスワードが表示されるので、それを使ってMySQLにログインします。

```bash
$ cat /home/bitnami/bitnami_application_password
YOURPASSHERE
$ mysql -u root -p
Enter password: (パス入力してEnter)
```

## MySQLにユーザーを追加

最近のbitnamiイメージで環境を作った場合、MySQLのバージョンが8系になっているはず。
なので真面目に、ユーザーを追加して、grantして、flushします。

```
mysql> CREATE USER 'root'@'%' identified by 'YOUR_PASSWORD';

mysql> GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION;

mysql> FLUSH PRIVILEGES;
```

ここまででMySQL自体の設定が完了。
引き続きSSH環境で作業します。

# Lightsailインスタンスのiptablesを設定する
`iptables`とは、一般的なLinuxに搭載されているパケットフィルタのこと。。らしい。

AWSに慣れている人であれば、セキュリティグループでできることはこいつでできると思ってれば良い。

ただし、単純にiptablesにルールを追加しただけだと、インスタンス自体が再起動した際にルールが保持されないとのこと。

そこで、`iptables-persistent`を同時に利用していく。

## iptables-persistentのインストール
```bash
$ apt-get install iptables-persistent
```

上記コマンドを進めていくと、ターミナルが普段あまり見慣れない赤紫？色になり、
`Save current IPv4 rules?`と聞かれるので`YES`と答えておく。
次に
`Save current IPv6 rules?`と出るので`NO`にしておく。 (IPv6のルールも作りたければYESで。)

## iptablesにルールを追加する
まずは現状のiptablesの中身を覗いてみよう。
おそらく同様に出力されるはず。

```bash
$ iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
```

INPUT, FORWARD, OUTPUTはそれぞれ、インスタンスに入ってきたデータの経路(チェイン)を表す。
`(policy ACCEPT)`となっているのはデフォルト設定で、それぞれの経路についてルールが指定されていなければすべて`ACCEPT` or `DROP`する、という挙動になる。

今回利用するのはINPUTのみ。
いきなりだが、ルールを追加していく。

```bash
$ iptables -A INPUT -p tcp --dport 3306 -j ACCEPT -s 127.0.0.1 -d 127.0.0.1
$ iptables -A INPUT -p tcp --dport 3306 -j ACCEPT -s <ipアドレス>/<サブネット> # ここに必要なipを入れる
$ iptables -A INPUT -p tcp --dport 3306 -j REJECT
```

コマンドを分解すると、

```
-A <CHAIN>     : INPUTチェインにルールを追加(Add)
-p <PROTOCOL>  : tcpプロトコルを指定
--dport <port> : データ受信側のポート指定
-j <TARGET>    : パケットを許可(ACCEPT)/拒否(DROP|REJECT)する
-s <ip>/<sub>  : データ送信元ip
-d <ip>/<sub>  : データ送信先ip
```

となる。

つまり、それぞれ

```bash
$ iptables -A INPUT -p tcp --dport 3306 -j ACCEPT -s 127.0.0.1 -d 127.0.0.1
 -> 3306番ポートに対して、ローカルから受けてローカルに返す通信は許可する
$ iptables -A INPUT -p tcp --dport 3306 -j ACCEPT -s <ipアドレス>/<サブネット>
 -> 3306番ポートに対して、指定のipから受ける通信は許可する (送信先は指定していないため何でもOK)
$ iptables -A INPUT -p tcp --dport 3306 -j REJECT
 -> 3306番ポートに対しての通信を拒否する
```

という設定となる。
ローカル通信については、WordpressからこのDBを参照するために必要となる。

この時、iptablesの設定は上から順に適用されるため、最後のREJECTにたどり着くまでの間に適合するルールがあれば、その通信はREJECTされずに通過できる。

逆に言えば、上記でACCEPTされた以外のソースからの通信はすべてREJECTされる。

## iptablesのルールを永続化する
```bash
$ iptables-save >/etc/iptables/rules.v4 # 保存
$ cat /etc/iptables/rules.v4 # 確認。上記で追加したルールが表示されればOK
```

これで通信可能なホストを絞ることができた。
これ以上インスタンスを直接いじる必要はないので、タブを閉じて大丈夫です。

最後にLightsailのファイアウォール設定を開いて完了。

# Lightsailのファイアウォール設定を変える
LightsailのGUIに戻り、作業対象のインスタンスの`Networking`タブを選択する。

するとFirewallの設定があるので、`Add another`して下記のように設定しましょう。

<img width="1174" alt="スクリーンショット 2020-04-08 18.47.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207329/7bcc3c83-8f36-d688-e524-e27d413fd56b.png">

これで設定完了！

# 接続をテストする
Hostはご自身のLightsailインスタンスのパブリックip
Userは、このマニュアル通りなら`root`
Passwordは[MySQLにユーザーを追加](https://qiita.com/Ishidall/items/21b7167bd17bb5fc7707#mysql%E3%81%AB%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0)部分で入力したパスワード
データベースは`bitnami_wordpress` (2020/04/08現在)

![スクリーンショット 2020-04-08 18.51.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207329/dedc560d-195e-7200-e1e2-b924f06b31a4.png)

接続できました！

めでたしめでたし。

# 参考にさせていただいた記事
ありがとうございましたm(_ _)m

https://dev.classmethod.jp/articles/access-lightsail-wordpress-outside-instance/
https://qiita.com/na0AaooQ/items/781b3823b79fd3b747a3
https://stackoverflow.com/questions/11001368/restricting-mysql-3306-port-to-localhost-with-iptables
https://eng-entrance.com/linux-firewall
https://unix.stackexchange.com/questions/125833/why-isnt-the-iptables-persistent-service-saving-my-changes