bitbank techblog

SELinux を踏み台サーバに使ってみた話

これは ビットバンク株式会社 Advent Calendar 2020 の 17 日目の記事です。

はじめに

皆さん setenforce 1してますか? AWS エンジニアの koarakko です。
普段は DevOps や 統制周りの業務を担当しています。
今回は踏み台サーバでの SELinux 活用事例を交えながら実際にポリシー調査から実装までの方法を紹介したいと思います。
SELinux を本番利用している環境は少なく、貴重な経験ができたと自負しています。
この記事を読むことで SELinux の本番利用の一助になれば幸いです。

当社の踏み台の活用背景

踏み台サーバは本番アプリサーバにログインする場合に経由サーバとして利用しています。
OS は RHEL 8 で2台構成です。

当社では踏み台サーバを2年程度使っていますが、この間に踏み台サーバの置き換えもしています。
以前は amazon Linux 上で構築していましたが、EOLが予定されていた事から RHEL 8 に移行をしました。
RHEL 8 に決めた理由は OS サポート期間が 10 年と長期間サポートの恩恵を受けたかったからです。
旧 OS から 新 OS に移行するには、パッケージ対応の差異などが出てしまい検証が大変になりがちです。
こうした EOL 起因による OS 置き換えをなるべく行わず安定稼働を期待したことも選定理由の一つです。

ssh 秘密鍵を安全に管理する仕組み

アプリサーバーにログインする認証手段に ssh 秘密鍵を用いており、鍵があれば簡単にログインできます。
秘密鍵があれば簡単にログインできてしまうため、この鍵を誰に割当するか管理する仕組みが必要になってきます。
その仕組みには DynamoDB のテーブル上に割当する鍵をユーザごとに定義することで実現しています。
以上の仕組みを基にユーザが踏み台サーバにログインするとLambda を実行します。
この Lambda が Dynamo DB の権限情報を参照を行い権限が割当たっている ssh 秘密鍵を ssm parameter store から取得して./ssh 配下に配布します。
ユーザはこの鍵を用いて本番アプリサーバにアクセスをします。
そしてユーザが踏み台サーバからログアウトすると ssh 秘密鍵を削除する仕組みとすることで、鍵を安全に管理するようにしています。
ここまでが踏み台サーバーログインからアプリサーバにログインするまでの一連の流れとなります。

もっと ssh 秘密鍵を安全に管理したい

ssh 秘密鍵の管理方法を説明しましたが、鍵を安全に管理する観点において課題が残っています。
それは 「ユーザが秘密鍵の中身を見れてしまう」 事です。
つまり cat コマンド等を通してユーザが中身を見れるということで、これをコピーして流出させてしまうことも可能です。
秘密鍵の対して cat できる理由は、従来の Unix パーミッションの権限構造がユーザー紐づく仕組みであるためです。この仕組みが悪いと否定するつもりはありません。
この仕組みがゆえに秘密鍵を用いて ssh コマンドにて サーバにアクセスするユーザーに対して秘密鍵の read 権限(chmod 400)が必要となってきます。

ここで考えたいことは、「本当にユーザーに対して ssh 秘密鍵の read 権限は必要なのか」という事です。
挙動で見てみると ssh コマンドが秘密鍵を読み取り、アプリサーバーに認証してログインに至るということです。
この事からssh コマンドが ssh 秘密鍵を必要としていて、ユーザーは不要であると考えることができます。

アプリサーバーのセキュリティグループ定義で踏み台サーバからのみ接続できる設定としています。
そのため鍵を流出してもすぐに危険な状態になることはありませんが安全であるとは言えない状況ですし、流出の事実を知ることすらできない可能性もあります。

SELinux という救世主?

この課題を根本から解決しようとすると、詳細な権限制御できる機能性を備えたソリューションが必要でした。
そこで登場したのが SELinux です。

SELinux はファイル、プロセスを制限するセキュリティポリシーを定義します。
アクセス可否はセキュリティポリシーの定義に従ってアクセス可否が判断され、このポリシーでアクセス許可されていない動作はすべて拒否されます。つまり許可する動作をホワイトリストで記述可能であり、一般的なアクセス制御方式のパーミッションより厳密な権限制御が可能です。

SELinux は i node の拡張属性にラベルを割当し、ソースラベルがターゲットラベルに対するアクセス制御していく仕組みであることから「どのラベル」が「どこのラベル」に対してアクセス許可するのかという事をポリシーとして表現しておく必要があります。

秘密鍵の現状ポリシーを理解する

ここでは ssh 秘密鍵をユーザからの読み取りを制限するポリシーを実装する上で、実装方法を紹介します。
まず ssh 秘密鍵に対して割当たっているラベルは ssh_home_tです。

$ ls -trlZ ~/.ssh/
total 4
-rw-------. 1 ec2-user ec2-user user_u:object_r:ssh_home_t:s0 398 Aug 18 10:57 authorized_keys
-r--------. 1 ec2-user ec2-user user_u:object_r:ssh_home_t:s0   0 Dec  8 11:49 hoge.pem

次に、ユーザのラベルはuser_tなのでuser_tssh_home_tに対する読み取り可否を確認します。

$ sesearch --allow -s user_t -t ssh_home_t -c file -ds -dt
allow user_t ssh_home_t:file { append create getattr ioctl link lock open read rename setattr unlink write };

読み取り権限に必要なread,openが許可されていることが確認できます。
したがってuser_tssh_home_t 読み取りが可能であることから「ユーザは ssh 秘密鍵を読み取り可能」という事になります。

秘密鍵に他のラベルを割当てる

ユーザによる秘密鍵の読み取りを防ぐために、秘密鍵のラベルの変更を検討します。
ラベルを新規定義することもできましたが、ssh には key を扱うラベルが存在していたのでこのラベルを使うことにしました。

$ man ssh_selinux
...
       sshd_key_t

       - Set files with the sshd_key_t type, if you want to treat the files as sshd key data.

       Paths:
            /etc/ssh/ssh_host.*_key, /etc/ssh/ssh_host.*_key.pub, /etc/ssh/primes
...

そして、ポリシーを見てみると user_tsshd_key_tに対する read 権限が無いことが確認できます。
したがってsshd_key_tラベルを使えば、user_tが読み取りできなくなり、「ユーザが秘密鍵を読み取りできない」という事が実現できます。

$ sesearch --allow -s user_t -t sshd_key_t -c file -ds -dt
(該当無し)

ラベルを一時的に付け替えて cat で中身を見ようとすると、読み取り権限が割当たっている状態だが Permission denied される事を確認できます。

$ chcon -t sshd_key_t hoge.pem
$ ls -trlZ
ls: cannot access 'hoge.pem': Permission denied
total 4
-?????????? ? ?                            ?                                                      0   ?            ? hoge.pem
-rw-r--r--. 1 ec2-user ec2-user user_u:object_r:ssh_home_t:s0 174 Dec  9 14:51 known_hosts
$ cat hoge.pem
cat: hoge.pem: Permission denied

SELinux により拒否された動作は /var/log/audit/audit.log に監査ログとして格納されるので、これを ausearch で検索してみます。
その結果、user_tからsshd_key_tが紐づくファイルhoge.pemに対するreadが denied されている事を監査ログから確認できます。

$ ausearch -m avc -su user_t -o sshd_key_t
...
type=AVC msg=audit(1607451672.866:149336): avc:  denied  { read } for  pid=1737982 comm="cat" name="hoge.pem" dev="xvda2" ino=88087995 scontext=user_u:user_r:user_t:s0 tcontext=user_u:object_r:sshd_key_t:s0 tclass=file permissive=0

read できない秘密鍵で接続確認

read できない鍵を用いて ssh 接続してみると ssh コマンドが ssh 秘密鍵を読み取り、サーバにアクセスできました。
このようにして SELinux を用いることで妥協しない厳密な権限制御ができます。

$ ssh -i ~/.ssh/hoge.pem ec2-user@10.249.0.188
This system is not registered to Red Hat Insights. See https://cloud.redhat.com/
To register this system, run: insights-client --register

Last login: xxx xxx  x xx:xx:xx 2020 from 10.249.0.94
[ec2-user@ip-10-249-0-188 ~]$

最後に

踏み台サーバで ssh 秘密鍵を厳密に権限制御した例を紹介しました。
当社での踏み台サーバのおける SELinux は安全性の要であり、これを最大限に享受するために MLS モードで運用しています。
複雑な権限制御が可能であるため運用は難しいですが、それ以上の恩恵が得られていますし、その結果で得られた運用知見は有用なものです。
今後も厳密な権限制御における技術的な調査を行い、より安全な踏み台サーバを追求していきたいと思います。

Author image
About koarakko
expand_less