さくらVPS上のPostgreSQLにsshポートフォワーディングで接続する

なぜポートフォワーディングで接続するのか

先日MacからさくらVPSにあるPostgreSQLのデータベースに接続してみました。が、 外部接続の設定が誰でも接続できる設定にした(サブネットマスクが0)のがずっと気になっていました。

izumii19.hatenablog.com

ポートフォワーディングを使うことで、安全に接続できることを知ったのでまとめてみます。

ポートフォワーディングとは

http://e-words.jp/w/%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AF%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0.html にはこうあります。

ポートフォワーディングとは、IPネットワーク上のある機器が、自らのIPアドレスTCPUDPの特定のポート番号への通信を、別のアドレスの特定のポートへ自動的に転送すること。

これを使うことで、MacからVPSのPostgrSQLへ直接接続しているかのように操作できます。 現時点で自分が知っている接続方法を挙げてみます。

  1. VPSSSH接続し、PostgreSQLに接続する(Mac--->VPS--->PostgreSQL)

  2. PostgreSQLに外部からの通信を受け付ける設定をして、Macから直接PostgreSQLに接続する(Mac--->PostgreSQL)

  3. ポートフォワーディングの設定をし、MacからPostgreSQLに接続する (Mac--->(VPS)--->PostgreSQL)

今回試したのは3つ目のポートフォワーディングを使った接続です。 ポートフォワーディングはどちらかというと1のSSHに近いなとやってみて思いました。 この記事のリモートフォワードのイラストがとてもわかりやすかったです。 https://qiita.com/mechamogera/items/b1bb9130273deb9426f5

localhostとは

設定ファイルでよく見るlocalhostについて調べました。 loaclhostは自身を表すホスト名のことです。loaclhostを使うとTCP/IP ソケットの場合以下のように接続できます。 psql -h localhost postgres -p 5432 なんでTCP/IPを使うかというと、ユニックスドメインソケットによる接続はPostgreSQL が動いているマシンに直接ログイン(例えばSSH)しなければならないので、今回のような外部接続の場合、TCP/IPを使うことになります。 以下の記事の説明はとてもわかりやすかったです。 http://www.hizlab.net/app/pgsec.html

トンネルを掘る⛏

コマンド一本でポートフォワーディングの設定は終わります。 ポートフォワーディングを設定することを、「トンネルを掘る」と呼ぶそうです。 わかりやすい...! 実行したコマンドは以下の通りです。

ssh -N -L [ローカル側で転送に使用するPort)]:[PostgreSQLのHostName]:[PostgreSQLが解放しているPort(デフォルトだと5432)] -i [さくらVPSの秘密鍵])] -p [VPSが解放しているPort] [VPSのUser]@[VPSのIPアドレス]

PostgreSQLのHostNameはlocalhostを指定します。 VPSのポート、ユーザー名、ホスト名は普段SSHで使っているのでOKです。 これで接続の準備が整いました。-fオプションをつけるとバックグラウンドで実行できます。 接続するには以下のコマンドを実行します。

psql -h localhost -p [ローカル側で転送に使用するPort] -U [ユーザー名] -d [DB名]

接続完了!

不要な設定を削除する

接続はできたものの、不要な設定が残っていないか見直してみました。 外部接続に関するファイルは2つあります。

/etc/postgresql/12/main/postgresql.conf

listen_addresses = 'localhost'    

listen_address = '*'は「何でもOK!どうぞどうぞ」な設定なので変更。 listen_addresについて

クライアント認証 は誰がサーバにアクセス可能かをきめ細かく制御するのに対し、listen_addressesはどのインターフェイスが接続を試みるかを制御します。

listen_addressにどのインターフェースを受け付けるかのざっくりとした設定を書き、クラインアント認証(pg_hba.conf)でより詳細な設定を書くようです。

ちなみに、listen_addressで設定されていないことによって弾かれた時のメッセージと、listen_addressでは許可されているが、pg_hba.confでは設定されていない時に表示されるメッセージが違うことを確認しました。 - listen_addressで設定されていない時のメッセージ image.png

  • pg_hba.confで設定されていない時のメッセージ image.png

/etc/postgresql/12/main/pg_hba.conf

# Database administrative login by Unix domain socket
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
  local   all             all                                     peer
# IPv4 local connections:
  host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
 host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
 local   replication     all                                     peer
 host    replication     all             127.0.0.1/32            md5
 host    replication     all             ::1/128                 md5

hostの設定はlocalhost(自身のIPアドレス)のみとし、ポートフォワーディングで使用する接続以外は受け付けないようにしました。 psql -U ユーザー名 -d postgres -h さくらVPSのホスト名みたいな接続は受け付けません。 ユニックスドメインソケットの設定と、リプレーションの設定はそのままにしておきました。

参考記事(状況が全く同じだったためとても参考になりました) https://qiita.com/rjge/items/d9ec5eb463a0ce24cb8

感想

設定ファイルの意味などを調べつつ動作確認をしたので時間がかかりましたが、自分が何をしているのか&してきたのかをだいたい理解できました。 また何でpeer認証なんてあるんだ?なぜpostgresっていうユーザーがOSのユーザーとして作成されるんだという疑問も解決しました。 「UNIX ドメインソケット経由の場合、アクセス制御はOSレベルに任せることで、安全に運用できるから」となりました。 逆にOSレベルにユーザー管理任せないでどうするの?と考えたところ、そっか!OSに任せた方が都合いいかと納得しました。 読んでてSSHとあんまり変わらないじゃん!と思った方... ポートフォワーディングの方がかっこいいと僕は思います!

プロセスとジョブの違い

コマンドを実行する時、内部の動きは以下のようになる。

  1. シェルからコマンドを実行

  2. カーネルはディスクから実行ファイルを読み出してメモリに入れる

  3. メモリ内容に従ってCPUがプログラムを実行する

プロセス
カーネルから見た処理の単位。 メモリ上で実行状態にあるプログラムのことを「プロセス」と呼ぶ。 「プロセス」にはLinuxのシステム全体で一意のIDがつけられる。
ジョブ
シェルから見た処理の単位(使う人間側から見た時の単位)。 シェルのコマンドラインに入力している1つの行が、1つのジョブに該当する。 ジョブはシェルごとにジョブ番号を持っている。

プロセスIDはlinuxのシステム全体で一意のIDだが、ジョブ番号は「シェル」の中で一意のIDなので、複数ターミナルを立ち上げている場合、同じジョブ番号を他のシェルから確認できる。(重複する)

connections on Unix domain socket "/tmp/.s.PGSQL.5432"? を解決する

PostgreSQLMacで使おうとした時に以下のエラーが出た時の対処法

psqlコマンドを打つと以下のようなエラーが出ました。

$ psql -l
psql: error: could not connect to server: could not connect to server: No such file or directory
    Is the server running locally and accepting
    connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

ググったところ、servicesを使ってPostgreSQLを再起動すると解決できる記事を見つけ、再起動を試して見ましたが、変わらず同じエラーが出ています。

ログにヒントがないか確認したところ、データベースのファイルがサーバーと互換性がないと書かれていました。

$ postgres -D /usr/local/var/postgres/
2020-06-02 11:26:49.560 JST [33160] FATAL:  database files are incompatible with server

以下のコマンドでアップグレードしたところ動くようになりました。

$ brew postgresql-upgrade-database
$ psql -l
                                          データベース一覧
   名前    |  所有者   | エンコーディング |  照合順序   | Ctype(変換演算子) |     アクセス権限      
-----------+-----------+------------------+-------------+-------------------+-----------------------
 postgres  | postgres  | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
 template0 | postgres  | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |           |                  |             |                   | postgres=CTc/postgres
 template1 | postgres  | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | =c/postgres          +
           |           |                  |             |                   | postgres=CTc/postgres
 家計簿    | user1 l | UTF8             | ja_JP.UTF-8 | ja_JP.UTF-8       | 
(4 行)

参考

make-from-scratch.com

PostgreSQL DB のアップグレードは brew postgresql-upgrade-database が便利 - Qiita

実行権限のないファイルをrootユーザーでも実行できない理由

試した環境

以下のような権限が何もないシェルスクリプトを作成して、rootで実行してみた所、このような結果になりました。

# hoge.shの中身
echo 'hogeeeeee!!'
root@test~# ls -l 
---------- 1 root root 15 4月 12 00:07 hoge.sh

root@test~# ./hoge.sh
-bash: ./hoge.sh: 許可がありません

rootユーザーってなんでもできる最強のユーザーじゃなかったのかよ?と思いながら挙動について調べてみました。

以下の記事の一番目の回答を訳しつつ、できるだけわかりやすく単語の説明などを加えてまとめました。 unix.stackexchange.com

rootユーザーがなんでもできるとは?ケーパビリティについて

rootユーザってなんでもできるイメージがありました。 そのなんでもできるって具体的にどういった状態なのでしょうか。 Linuxにはrootが持っている絶対的な権限を細かく分け,必要な権限だけを与える仕組みが存在します。 その仕組みをケーパビリティと呼びます。rootは全てのケーパビリティを持っている状態、つまりなんできる状態です。

Man page of CAPABILITIES

ファイルアクセスのケーパビリティ CAP_DAC_OVERRIDE

今回の状況に関係するケーパビリティは CAP_DAC_OVERRIDEです。 CAP_DAC_OVERRIDEはファイルの読みこみ、書き込み、実行の権限チェックをバイパスするケーパビリティです。 ちなみにバイパスとは迂回という意味で、上記の場合だとファイルへのアクセス(読み込み、書き込み、実行)の権限チェックを回避するという意味になります。 権限チェックを回避することでファイルへのアクセスの権限が上書きされ、なんでもできる状態になるのですが、例外もあります。 実行権限のないファイルを実行する際は例外です。このことはファイルに対するアクセスをチェックするgeneric_permission (fs/namei.c)のコメントに書かれています。

読み書きのDAC(訳注:DAC = "discretionary access control"の略で、任意アクセス制御と呼ばれる)は常に上書き可能です。実行のDACは少なくとも1つの実行ビットがセットされているときに上書き可能です。

つまりls -l した時にxが一文字もない場合、rootユーザーであったとしても実行できないのです。 これは通常実行しないような、テキストファイル、画像などを誤って実行しエラーを出さないための仕様だと考えられます。

まとめ

rootユーザーでも実行権限のないファイルは実行できないので、適切な権限を付けて実行しましょう!

ケーパビリティの単語の説明で使わせていただきました。 第42回 Linuxカーネルのケーパビリティ[1]:LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術|gihyo.jp … 技術評論社