鳩小屋

落書き帳

Tracee:コンテナトレーサ

今回のテーマはAqua社が主導するコンテナトレーサ:traceeです。
システムコールフックのeBPFを利用していることからsysdig社Falcoが類似ツールだと思います。
aquasecurity.github.io

インストール

#eBPFモジュールのビルド
$ git clone --recursive https://github.com/aquasecurity/tracee.git
$ cd tracee
$ make bpf
$ ls
tracee.bpf.5_11_0-25-generic.v0_6_0-7-g26a9eb2.o  tracee.bpf.core.o

eBPFモジュールをマウントしてtraceeコンテナを起動します。

# docker run --privileged -it -v /path/in/host/tracee.bpf.5_11_0-25-generic.v0_6_0-7-g26a9eb2.o:/path/in/container/tracee.bpf.o -e TRACEE_BPF_FILE=/path/in/container/tracee.bpf.o aquasec/tracee
Loaded signature(s):  [TRC-1 TRC-2 TRC-3 TRC-4 TRC-5 TRC-6 TRC-7]

7つのシグネチャがロードされたみたいですが、特に何も出力されません。

動作確認

シグネチャ機能は鋭意作成中とのことです。
現状のシグネチャにはアンチデバッグやコードインジェクションなど、マルウェアチックな挙動が並んでいます。

シグネチャ機能

We are currently working on creating a library of behavioral signature detections. Currently, the following are available:

Name Description
Standard Input/Output Over Socket Redirection of process's standard input/output to socket
Anti-Debugging Process uses anti-debugging technique to block debugger
Code injection Possible code injection into another process
Dynamic Code Loading Writing to executable allocated memory region
Fileless Execution Executing a process from memory, without a file in the disk
kernel module loading Attempt to load a kernel module detection
LD_PRELOAD Usage of LD_PRELOAD to allow hooks on process

今回はアンチデバッグ検出機能を確認してみます。

#適当なコンテナを起動します。
$ sudo docker run --rm -it ubuntu
root@d23f0851e601:/#

#デバッグ検出プログラムの用意
$ nano antidebug.c
#include <stdio.h>
#include <sys/ptrace.h>

int main()
{
        if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
                printf("Debugging Dedected , Fuck You !\n");
                return 1;
        }
        printf("Normal Execution\n");
        return 0;
}

$gcc antidebug.c

# デバッグ検出プログラムをコンテナに配置
$docker cp a.out d23f0851e601:/a.out

#コンテナでデバッグ検出プログラムを実行
root@d23f0851e601:/# ./a.out 
Normal Execution

コンテナでデバッグ検出プログラムを実行すると、traceeコンテナのコンソールでAnti-Debuggingのアラートが出力されました。
おー動いた。

# docker run --privileged -it -v /path/in/host/tracee.bpf.5_11_0-25-generic.v0_6_0-7-g26a9eb2.o:/path/in/container/tracee.bpf.o -e TRACEE_BPF_FILE=/path/in/container/tracee.bpf.o aquasec/tracee
Loaded signature(s):  [TRC-1 TRC-2 TRC-3 TRC-4 TRC-5 TRC-6 TRC-7]

Detection
Time: 2021-08-08T12:52:30Z
Signature ID: TRC-2
Signature: Anti-Debugging
Data: map[]
Command: a.out
Hostname: d23f0851e601

シグネチャはRego言語で書かれている?ようです。
MITRE ATT&CKの記載もあるので、実装方針はfalcoと似ているのでしょうか。

package tracee.TRC_2

__rego_metadoc__ := {
    "id": "TRC-2",
    "version": "0.1.0",
    "name": "Anti-Debugging",
    "description": "Process uses anti-debugging technique to block debugger",
    "tags": ["linux", "container"],
    "properties": {
        "Severity": 3,
        "MITRE ATT&CK": "Defense Evasion: Execution Guardrails",
    }
}

tracee_selected_events[eventSelector] {
	eventSelector := {
		"source": "tracee",
		"name": "ptrace"
	}
}

tracee_match {
    input.eventName == "ptrace"
    arg := input.args[_]
    arg.name == "request"
    arg.value == "PTRACE_TRACEME"
}

トレース機能

traceサブコマンド実行するシステムコールトレースっぽいログが出力されます。
フォレンジックあたりで使えそうです。
Traceeという名前からトレース機能がメインと思われますが、今後シグネチャが充実すればEDRツールとしても期待できそうです。

# docker run --privileged -it -v /path/in/host/tracee.bpf.123.o:/path/in/container/tracee.bpf.o -e TRACEE_BPF_FILE=/path/in/container/tracee.bpf.o aquasec/tracee trace
TIME             UID    COMM             PID     TID     RET              EVENT                ARGS
...
13:36:50:056945  1000   dbus-daemon      1835    1835    0                close                fd: 9
13:36:50:059291  1000   snap-store       2279    2279    0                security_file_open   pathname: /usr/share/zoneinfo/Japan, flags: O_RDONLY|O_LARGEFILE, dev: 7340033, inode: 9322
13:36:50:059272  1000   snap-store       2279    2279    16               openat               dirfd: -100, pathname: /etc/localtime, flags: O_RDONLY, mode: 0
13:36:50:059416  1000   snap-store       2279    2279    0                fstat                fd: 16, statbuf: 0x7FFFBE8FF1D0
13:36:50:059466  1000   snap-store       2279    2279    0                close                fd: 16
13:36:50:059629  1000   snap-store       2279    2279    0                security_file_open   pathname: /usr/share/zoneinfo/Japan, flags: O_RDONLY|O_LARGEFILE, dev: 7340033, inode: 9322
13:36:50:059617  1000   snap-store       2279    2279    16               openat               dirfd: -100, pathname: /etc/localtime, flags: O_RDONLY, mode: 0
13:36:50:059712  1000   snap-store       2279    2279    0                fstat                fd: 16, statbuf: 0x7FFFBE8FF1D0
13:36:50:059746  1000   snap-store       2279    2279    0                close                fd: 16
13:36:50:106080  118    pool-whoopsie    838     8281    0                security_file_open   pathname: /etc/services, flags: O_RDONLY|O_LARGEFILE, dev: 8388611, inode: 1704147
13:36:50:106070  118    pool-whoopsie    838     8281    11               openat               dirfd: -100, pathname: /etc/services, flags: O_RDONLY|O_CLOEXEC, mode: 0
13:36:50:106162  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:106192  118    pool-whoopsie    838     8281    0                security_file_open   pathname: /etc/services, flags: O_RDONLY|O_LARGEFILE, dev: 8388611, inode: 1704147
13:36:50:106187  118    pool-whoopsie    838     8281    11               openat               dirfd: -100, pathname: /etc/services, flags: O_RDONLY|O_CLOEXEC, mode: 0
13:36:50:106238  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:106402  118    pool-whoopsie    838     8281    0                security_socket_create family: AF_NETLINK, type: SOCK_RAW, protocol: 0, kern: 0
13:36:50:106398  118    pool-whoopsie    838     8281    11               socket               domain: AF_NETLINK, type: SOCK_RAW|SOCK_CLOEXEC, protocol: 0
13:36:50:106429  118    pool-whoopsie    838     8281    0                bind                 sockfd: 11, addr: {'sa_family': 'AF_NETLINK'}, addrlen: 12
13:36:50:106465  118    pool-whoopsie    838     8281    0                getsockname          sockfd: 11, addr: {'sa_family': 'AF_NETLINK'}, addrlen: 0x7F9770D5A3D4
13:36:50:106538  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:106577  118    pool-whoopsie    838     8281    0                security_file_open   pathname: /etc/hosts, flags: O_RDONLY|O_LARGEFILE, dev: 8388611, inode: 1704099
13:36:50:106572  118    pool-whoopsie    838     8281    11               openat               dirfd: -100, pathname: /etc/hosts, flags: O_RDONLY|O_CLOEXEC, mode: 0
13:36:50:106632  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:106668  118    pool-whoopsie    838     8281    0                security_socket_create family: AF_INET, type: SOCK_DGRAM, protocol: 0, kern: 0
13:36:50:106665  118    pool-whoopsie    838     8281    11               socket               domain: AF_INET, type: SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, protocol: 0
13:36:50:106707  118    pool-whoopsie    838     8281    0                security_socket_connect sockfd: 11, remote_addr: {'sa_family': 'AF_INET','sin_port': '53','sin_addr': '127.0.0.53'}
13:36:50:106702  118    pool-whoopsie    838     8281    0                connect              sockfd: 11, addr: {'sa_family': 'AF_INET','sin_port': '53','sin_addr': '127.0.0.53'}, addrlen: 16
13:36:50:107084  101    systemd-resolve  592     592     0                security_socket_create family: AF_INET, type: SOCK_DGRAM, protocol: 0, kern: 0
13:36:50:107081  101    systemd-resolve  592     592     16               socket               domain: AF_INET, type: SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, protocol: 0
13:36:50:107142  101    systemd-resolve  592     592     0                security_socket_connect sockfd: 16, remote_addr: {'sin_addr': '192.168.11.1','sa_family': 'AF_INET','sin_port': '53'}
13:36:50:107138  101    systemd-resolve  592     592     0                connect              sockfd: 16, addr: {'sa_family': 'AF_INET','sin_port': '53','sin_addr': '192.168.11.1'}, addrlen: 16
13:36:50:138031  101    systemd-resolve  592     592     0                close                fd: 16
13:36:50:138165  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:138236  118    pool-whoopsie    838     8281    0                security_socket_create family: AF_INET, type: SOCK_DGRAM, protocol: 0, kern: 0
13:36:50:138200  118    pool-whoopsie    838     8281    11               socket               domain: AF_INET, type: SOCK_DGRAM|SOCK_CLOEXEC, protocol: 0
13:36:50:138273  118    pool-whoopsie    838     8281    0                security_socket_connect sockfd: 11, remote_addr: {'sin_addr': '162.213.33.108','sa_family': 'AF_INET','sin_port': '0'}
13:36:50:138268  118    pool-whoopsie    838     8281    0                connect              sockfd: 11, addr: {'sa_family': 'AF_INET','sin_port': '0','sin_addr': '162.213.33.108'}, addrlen: 16
13:36:50:138301  118    pool-whoopsie    838     8281    0                getsockname          sockfd: 11, addr: {'sa_family': 'AF_INET','sin_port': '35285','sin_addr': '10.0.2.15'}, addrlen: 0x7F9770D5A540
13:36:50:138317  118    pool-whoopsie    838     8281    0                connect              sockfd: 11, addr: {'sa_family': 'AF_UNSPEC'}, addrlen: 16
13:36:50:138334  118    pool-whoopsie    838     8281    0                security_socket_connect sockfd: 11, remote_addr: {'sa_family': 'AF_INET','sin_port': '0','sin_addr': '162.213.33.132'}
13:36:50:138330  118    pool-whoopsie    838     8281    0                connect              sockfd: 11, addr: {'sin_addr': '162.213.33.132','sa_family': 'AF_INET','sin_port': '0'}, addrlen: 16
13:36:50:138349  118    pool-whoopsie    838     8281    0                getsockname          sockfd: 11, addr: {'sa_family': 'AF_INET','sin_port': '57882','sin_addr': '10.0.2.15'}, addrlen: 0x7F9770D5A540
13:36:50:138371  118    pool-whoopsie    838     8281    0                close                fd: 11
13:36:50:181517  1000   gnome-shell      1982    1982    0                cap_capable          cap: CAP_SYS_ADMIN
13:36:50:181618  1000   gnome-shell      1982    1982    0                cap_capable          cap: CAP_SYS_ADMIN
13:36:50:181730  1000   gnome-shell      1982    1982    0                cap_capable          cap: CAP_SYS_ADMIN
13:36:50:181908  1000   gnome-shell      1982    1982    0                cap_capable          cap: CAP_SYS_ADMIN

Falco:コンテナの動的脅威アラート

Falcoを少し触ってみたのでメモ。
Falcoはシステムコールベースの脅威アラート機能を提供するOSSです。
MITRE ATT&CKの内容も取り込まれているようです。

falco.org
Matrix - Enterprise | MITRE ATT&CK®

インストール

ドキュメントに従ってインストールします。
Install | Falco

#Trust the falcosecurity GPG key, configure the apt repository, and update the package list
curl -s https://falco.org/repo/falcosecurity-3672BA8F.asc | apt-key add -
echo "deb https://download.falco.org/packages/deb stable main" | tee -a /etc/apt/sources.list.d/falcosecurity.list
apt-get update -y

#Install kernel headers
apt-get -y install linux-headers-$(uname -r)

#Install Falco
apt-get install -y falco

#Run Falco as a service
systemctl start falco

設定ファイル

$ ls /etc/falco/
falco.yaml  falco_rules.local.yaml  falco_rules.yaml  k8s_audit_rules.yaml  rules.available  rules.d

デフォルトルールはfalco_rules.yamlに定義されていて、falco_rules.local.yamlに追記することでユーザ定義のルールを追加できるみたいです。

以下の定義は/ と /rootへの書込みを検出するルールです。
condition:のところが条件式になっていて対象のファイルパスや対象外の条件が記載されています。
output:のところがログ出力になっていて、commandやコンテナIDが含まれています。
内部的にはシステムコールベースみたいですが、ユーザルールでは抽象化されているので、システムコールが分からなくても何とかなりそうです。

$ less /etc/falco/falco_rules.yaml
...
  • rule: Write below root
desc: an attempt to write to any file directly below / or /root condition: > root_dir and evt.dir = < and open_write and proc_name_exists and not fd.name in (known_root_files) and not fd.directory pmatch (known_root_directories) and not exe_running_docker_save and not gugent_writing_guestagent_log and not dse_writing_tmp and not zap_writing_state and not airflow_writing_state and not rpm_writing_root_rpmdb and not maven_writing_groovy and not chef_writing_conf and not kubectl_writing_state and not cassandra_writing_state and not galley_writing_state and not calico_writing_state and not rancher_writing_root and not runc_writing_exec_fifo and not mysqlsh_writing_state and not known_root_conditions and not user_known_write_root_conditions and not user_known_write_below_root_activities output: "File below / or /root opened for writing (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)"

動作確認

適当にコンテナを動かしてコマンドを実行してみます。

$ sudo docker run --rm -it ubuntu
# touch unchi
# apt-get update
# apt-get install curl

falcoの出力を確認します。

# sudo journalctl -f -u falco.service
 8月 07 15:28:23 user-VirtualBox falco[6718]: 15:28:23.754459162: Error File below / or /root opened for writing (user=root user_loginuid=1000 command=nano /etc/falco/falco.yaml parent=bash file=/root/.local/share/nano/search_history program=nano container_id=host image=)
...
 8月 07 15:36:27 user-VirtualBox falco[6718]: 15:36:27.977718775: Error File below / or /root opened for writing (user=root user_loginuid=-1 command=touch unchi parent=bash file=/unchi program=touch container_id=e5e80beb3428 image=ubuntu)
...
 8月 07 15:55:59 user-VirtualBox falco[6718]: 15:55:59.199264022: Error Package management process launched in container (user=root user_loginuid=-1 command=apt-get install curl container_id=e5e80beb3428 container_name=magical_kepler image=ubuntu:latest)

/rootへの書込みやパッケージマネージャの起動が検出されています。
また、container_id=hostとcontainer_id=コンテナIDでコンテナとホストOSの挙動も区別できています。

適当にインストールするだけでもある程度機能しそうですが、アラート機能しかないので、悪性挙動のブロック処理は独自実装が必要です。有償製品のsysdigを利用すれば、ブロック機能も使えるみたいですが。。

Falco単体では実運用は厳しいかもしれません。テスト工程のサンドボックステストあたりなら使えそうです。

NSA,CISA:Kubernetes Hardening Guidance

National Security Agency(NSA)とCyber​​security and Infrastructure Security Agency(CISA)がKubernetes Hardening Guidanceなるものを公開していた模様。
目新しい情報はなさそうですが、抽象対策と実装対策がコンパクトに整理されていて読みやすいと思いました。
www.nsa.gov

今回のKubernetes Hardening Guidanceや国防総省のDevSecOpsから、米国がコンテナ技術を有望視していることがうかがえます。
クラウド、コンテナ、オーケストレータ、DevOps、マイクロサービス...
最近はインフラ、開発手法、アーキテクチャが絡み合ってわけわかめ状態ですが、本格的に普及するのであればセキュリティ側も本腰を入れなければなりません。

米国防省、KubernetesをF-16ジェット戦闘機に載せてみた - Publickey

Kubernetes Hardening Guidanceは下記で公開されています。
技術寄りだと5GやZero Trustなど、上流周りだとRussianやChineseといった地政学関連レポートも並んでいます。
情報技術分野の頂点に君臨する米国のサイバーセキュリティ戦略が反映されているので、レポートのタイトルを眺めるだけでも面白いと思います。

www.nsa.gov

以降はKubernetes Hardening Guidanceの機械翻訳版です。
さらっと内容を流し読みしたい方はどうぞ。

Executive summary

Kubernetes®は、コンテナで実行されるアプリケーションのデプロイ、スケーリング、管理を自動化するオープンソースシステムであり、多くの場合、クラウド環境でホストされます。このタイプの仮想化インフラストラクチャを使用すると、従来のモノリシックソフトウェアプラットフォームと比較して、いくつかの柔軟性とセキュリティ上の利点が得られます。ただし、マイクロサービスから基盤となるインフラストラクチャまですべてを安全に管理すると、他の複雑さが生じます。このレポートで詳しく説明されている強化ガイダンスは、組織が関連するリスクを処理し、このテクノロジーを使用するメリットを享受できるように設計されています。 Kubernetesでの3つの一般的な侵害の原因は、サプライチェーンのリスク、悪意のある脅威アクター、および内部脅威です。サプライチェーンのリスクは、軽減するのが難しいことが多く、コンテナの構築サイクルやインフラストラクチャの取得で発生する可能性があります。悪意のある脅威アクターは、コントロールプレーン、ワーカーノード、コンテナ化されたアプリケーションなど、Kubernetesアーキテクチャコンポーネント脆弱性と設定ミスを悪用する可能性があります。インサイダーの脅威は、管理者、ユーザー、またはクラウドサービスプロバイダーである可能性があります。組織のKubernetesインフラストラクチャに特別にアクセスできる内部関係者は、これらの特権を悪用できる可能性があります。このガイダンスでは、Kubernetesクラスターのセットアップとセキュリティ保護に関連するセキュリティの課題について説明します。これには、一般的な構成ミスを回避するための強化戦略が含まれており、システム管理者とNational Security Systemsの開発者に、推奨される強化策と緩和策の構成例を使用してKubernetesをデプロイする方法をガイドします。このガイダンスでは、次の緩和策について詳しく説明します。
コンテナとポッドをスキャンして、脆弱性や設定ミスがないか確認します。
•可能な限り最小限の特権でコンテナーとポッドを実行します。
•ネットワーク分離を使用して、侵害によって引き起こされる可能性のある損害の量を制御します。
ファイアウォールを使用して不要なネットワーク接続と暗号化を制限し、機密性を保護します。
•強力な認証と承認を使用して、ユーザーと管理者のアクセスを制限し、攻撃対象領域を制限します。
•ログ監査を使用して、管理者がアクティビティを監視し、潜在的な悪意のあるアクティビティについて警告できるようにします。
•すべてのKubernetes設定を定期的に確認し、脆弱性スキャンを使用して、リスクが適切に考慮され、セキュリティパッチが適用されていることを確認します。

追加のセキュリティ強化ガイダンスについては、Center for Internet Security Kubernetesベンチマーク、DockerおよびKubernetesセキュリティ技術実装ガイド、Cybersecurity and Infrastructure Security Agency(CISA)分析レポート、およびKubernetesドキュメントを参照してください。

Introduction

Kubernetesは、しばしば「K8s」と略され、コンテナ化されたアプリケーションのデプロイ、スケーリング、管理を自動化するために使用されるオープンソースのコンテナオーケストレーションシステムです。アプリケーション内の各マイクロサービスからクラスター全体まで、クラスターを構成するすべての要素を管理します。コンテナ化されたアプリケーションをマイクロサービスとして使用すると、モノリシックソフトウェアプラットフォームと比較して柔軟性とセキュリティ上のメリットが得られますが、他の複雑さも生じる可能性があります。
このガイダンスは、セキュリティの課題に焦点を当て、国家安全保障システムと重要なインフラストラクチャの管理者に適用できる可能な場合の強化戦略を提案します。このガイダンスは、国家安全保障システムおよび重要なインフラストラクチャ組織に合わせて調整されていますが、連邦および州、地方、部族、および領土(SLTT)の政府ネットワークの管理者も、提供された推奨事項を実装することをお勧めします。 Kubernetesクラスターは保護が複雑になる可能性があり、構成の誤りを悪用する侵害で悪用されることがよくあります。次のガイダンスは、より安全なKubernetesクラスターの構築に役立つ特定のセキュリティ構成を提供します

Threat model

Kubernetesは、データや計算能力の盗難の貴重な標的になる可能性があります。従来、データの盗難が主な動機ですが、計算能力(多くの場合暗号通貨マイニング)を求めるサイバーアクターも、基盤となるインフラストラクチャを利用するためにKubernetesに引き付けられます。リソースの盗難に加えて、サイバー攻撃者はKubernetesを標的にしてサービス拒否を引き起こす可能性もあります。次の脅威は、Kubernetesクラスターの最も可能性の高い侵害の原因のいくつかを表しています。

Supply Chain Risk

サプライチェーンへの攻撃ベクトルは多様であり、軽減するのが困難です。サプライチェーンリスクとは、製品コンポーネント、サービス、または最終製品の供給を支援する人員など、システムを構成する要素を攻撃者が破壊する可能性があるリスクです。これには、Kubernetesクラスターの作成と管理に使用されるサードパーティソフトウェアとベンダーが含まれる場合があります。サプライチェーンの侵害は、次のような複数のレベルでKubernetesに影響を与える可能性があります。

Container/Application level

Kubernetesで実行されているアプリケーションのセキュリティとそのサードパーティの依存関係は、開発者の信頼性と開発インフラストラクチャの防御に依存しています。サードパーティからの悪意のあるコンテナまたはアプリケーションは、サイバーアクターにクラスター内の足がかりを提供する可能性があります。

Infrastructure

Kubernetesをホストする基盤となるシステムには、独自のソフトウェアとハードウェアの依存関係があります。ワーカーノードまたはコントロールプレーンの一部として使用されるシステムの侵害は、サイバーアクターにクラスター内の足がかりを提供する可能性があります。

Malicious Threat Actor

悪意のある攻撃者は、脆弱性を悪用して遠隔地からアクセスすることがよくあります。 Kubernetesアーキテクチャは、サイバーアクターがリモートエクスプロイトに利用できる可能性のあるいくつかのAPIを公開しています。

Control plane

Kubernetesコントロールプレーンには、クラスターを追跡および管理するために通信するさまざまなコンポーネントがあります。サイバー攻撃者は、適切なアクセス制御がない公開されたコントロールプレーンコンポーネントを頻繁に利用します。

Worker nodes

コンテナーエンジンの実行に加えて、ワーカーノードはkubeletおよびkube-proxyサービスをホストします。これらは、サイバーアクターによって悪用される可能性があります。さらに、ワーカーノードはロックダウンされたコントロールプレーンの外側に存在し、サイバー攻撃者にとってよりアクセスしやすい可能性があります。

Containerized applications

クラスター内で実行されているアプリケーションが一般的なターゲットです。アプリケーションはクラスターの外部から頻繁にアクセスできるため、リモートのサイバー攻撃者がアプリケーションにアクセスできるようになります。アクターは、公開されたアプリケーションの内部的にアクセス可能なリソースを使用して、すでに侵害されたポッドからピボットしたり、クラスター内の特権を昇格したりできます。

Insider Threat

脅威アクターは、組織内での作業中に脆弱性を悪用したり、個人に与えられた特権を使用したりする可能性があります。組織内の個人には、Kubernetesクラスターに対して使用できる特別な知識と特権が与えられます。

Administrator

Kubernetes管理者は、コンテナ化された環境内で任意のコマンドを実行する機能など、実行中のコンテナを制御できます。 Kubernetesが適用するRBAC認証は、機密性の高い機能へのアクセスを制限することでリスクを軽減するのに役立ちます。ただし、Kubernetesには2人の整合性制御がないため、クラスターの制御を取得できる管理アカウントが少なくとも1つ必要です。管理者は多くの場合、システムまたはハイパーバイザーに物理的にアクセスできます。これらは、Kubernetes環境を危険にさらすためにも使用される可能性があります。

User

コンテナ化されたアプリケーションのユーザーは、Kubernetesクラスター内のコンテナ化されたサービスにアクセスするための知識と資格を持っている場合があります。このレベルのアクセスは、アプリケーション自体または他のクラスタコンポーネントのいずれかを悪用するための十分な手段を提供する可能性があります。

Cloud Service or Infrastructure Provider

Kubernetesノードを管理する物理システムまたはハイパーバイザーへのアクセスを使用して、Kubernetes環境を危険にさらす可能性があります。クラウドサービスプロバイダーは、多くの場合、特権管理者からシステムを保護するための技術的および管理的制御のレイヤーを持っています。

Kubernetes Pod security

“Non-root” containers and “rootless” container engines

デフォルトでは、多くのコンテナサービスは特権rootユーザーとして実行され、アプリケーションは特権実行を必要としないにもかかわらず、コンテナ内でrootとして実行されます。非ルートコンテナまたはルートレスコンテナエンジンを使用してルート実行を防止すると、コンテナの侵害による影響が制限されます。これらの方法はどちらもランタイム環境に大きな影響を与えるため、互換性を確認するためにアプリケーションを徹底的にテストする必要があります。

Non-root containers

コンテナーエンジンを使用すると、コンテナーは、非rootグループメンバーシップを持つ非rootユーザーとしてアプリケーションを実行できます。通常、このデフォルト以外の設定は、コンテナイメージの構築時に構成されます。付録A:ルート以外のアプリケーションのDockerfileの例は、アプリケーションをとして実行するDockerfileの例を示しています。
非rootユーザー。または、Kubernetesは、ゼロ以外のユーザーを指定するSecurityContext:runAsUserを使用してコンテナをポッドにロードできます。 runAsUserディレクティブは、展開時に非root実行を効果的に強制しますが、NSAおよびCISAは、開発者が非rootユーザーとして実行するコンテナーアプリケーションを構築することを推奨します。ビルド時にroot以外の実行を統合すると、root権限がなくてもアプリケーションが正しく機能することが保証されます。

Rootless container engines

一部のコンテナエンジンは、ルートとして実行されているデーモンを使用するのではなく、非特権コンテキストで実行できます。このシナリオでは、実行はコンテナ化されたアプリケーションの観点からはrootユーザーを使用しているように見えますが、実行はホスト上のエンジンのユーザーコンテキストに再マッピングされます。ルートレスコンテナエンジンは効果的なセキュリティレイヤーを追加しますが、多くは現在実験的なものとしてリリースされており、実稼働環境では使用しないでください。管理者は、ベンダーがKubernetesと互換性のある安定バージョンをリリースするときに、この新しいテクノロジーを認識し、ルートレスコンテナエンジンの採用を模索する必要があります。

Immutable container file systems

デフォルトでは、コンテナは独自のコンテキスト内でほとんど無制限に実行できます。コンテナ内で実行されたサイバーアクターは、ファイルの作成、スクリプトのダウンロード、およびコンテナ内のアプリケーションの変更を行うことができます。 Kubernetesはコンテナのファイルシステムをロックダウンできるため、悪用後の多くのアクティビティを防ぐことができます。ただし、これらの制限は正当なコンテナアプリケーションにも影響を及ぼし、クラッシュや異常な動作を引き起こす可能性があります。正当なアプリケーションの損傷を防ぐために、Kubernetes管理者は、アプリケーションが書き込みアクセスを必要とする特定のディレクトリにセカンダリ読み取り/書き込みファイルシステムをマウントできます。付録B:読み取り専用ファイルシステムのデプロイメントテンプレートの例は、書き込み可能なディレクトリを持つ不変のコンテナの例を示しています。

Building secure container images

コンテナイメージは通常、コンテナを最初から構築するか、リポジトリから取得した既存のイメージの上に構築することによって作成されます。信頼できるリポジトリを使用してコンテナを構築することに加えて、イメージスキャンは、デプロイされたコンテナの安全性を確保するための鍵となります。コンテナー構築ワークフロー全体で、イメージをスキャンして、古いライブラリ、既知の脆弱性、または安全でないポートやアクセス許可などの構成の誤りを特定する必要があります。

画像スキャンを実装するための1つのアプローチは、アドミッションコントローラを使用することです。アドミッションコントローラーはKubernetesネイティブの機能であり、オブジェクトを永続化する前に、リクエストが認証および承認された後に、KubernetesAPIへのリクエストをインターセプトして処理できます。カスタムまたは独自のWebhookを実装して、クラスターにデプロイする前に任意のイメージをスキャンできます。イメージがWebhook構成で定義された組織のセキュリティポリシーに準拠していない場合、このアドミッションコントローラーはデプロイメントをブロックする可能性があります[4]。

Pod Security Policies

ポッドセキュリティポリシーPSP)は、クラスター内で実行するポッドのセキュリティ要件/デフォルトを指定するクラスター全体のポリシーです。多くの場合、セキュリティメカニズムはポッド/展開構成内で指定されますが、PSPは、すべてのポッドが準拠する必要のある最小セキュリティしきい値を確立します。一部のPSPフィールドは、ポッドの構成でフィールドが省略されている場合に使用されるデフォルト値を提供します。他のPSPフィールドは、不適合ポッドの作成を拒否するために使用されます。 PSPKubernetesアドミッションコントローラーを介して適用されるため、PSPはポッドの作成中にのみ要件を適用できます。 PSPは、クラスターで既に実行されているポッドには影響しません。
PSPは、クラスター内のセキュリティ対策を実施するための便利な技術的制御です。 PSPは、階層化された役割を持つ管理者によって管理されるクラスターに特に効果的です。このような場合、トップレベルの管理者はデフォルトを課して、下位レベルの管理者に要件を適用できます。 NSACISAは、組織が付録C:ポッドセキュリティポリシーの例にあるKubernetesで強化されたPSPテンプレートをニーズに適合させることを推奨しています。次の表は、広く適用可能ないくつかのPSPコンポーネントについて説明しています。

Protecting Pod service account tokens

デフォルトでは、Kubernetesはポッドの作成時にサービスアカウントを自動的にプロビジョニングし、実行時にアカウントのシークレットトークンをポッド内にマウントします。 Kubernetesオーケストレーションはバックグラウンドで透過的に行われるため、多くのコンテナ化されたアプリケーションはサービスアカウントへの直接アクセスを必要としません。アプリケーションが侵害された場合、ポッド内のアカウントトークンはサイバーアクターによって収集され、クラスターをさらに侵害するために使用される可能性があります。アプリケーションがサービスアカウントに直接アクセスする必要がない場合、Kubernetes管理者は、ポッドの仕様でマウントされているシークレットトークンが無効になっていることを確認する必要があります。これは、ポッドのYAML仕様の「automountServiceAccountToken:false」ディレクティブを使用して実現できます。

Hardening container engines

一部のプラットフォームとコンテナエンジンは、コンテナ化された環境を強化するための追加オプションを提供します。強力な例は、ハイパーバイザーを使用してコンテナーを分離することです。ハイパーバイザーは、オペレーティングシステムではなく、ハードウェアに依存して仮想化の境界を適用します。ハイパーバイザーの分離は、従来のコンテナーの分離よりも安全です。 Windows®オペレーティングシステムで実行されているコンテナエンジンは、組み込みのWindowsハイパーバイザーであるHyper-V®を使用してセキュリティを強化するように構成できます。さらに、一部のセキュリティ重視のコンテナエンジンは、縦深防御のために軽量ハイパーバイザー内に各コンテナをネイティブにデプロイします。ハイパーバイザーに裏打ちされたコンテナーは、コンテナーのブレークアウトを軽減します。

Network separation and hardening

Kubernetesの中心的な概念です。コンテナ、ポッド、サービス、および外部サービス間の通信を考慮する必要があります。デフォルトでは、リソースを分離し、クラスターが危険にさらされた場合に横方向の移動やエスカレーションを防ぐためのネットワークポリシーはほとんどありません。リソースの分離と暗号化は、クラスター内でのサイバー攻撃者の移動とエスカレーションを制限する効果的な方法です。
・ネットワークポリシーとファイアウォールを使用して、リソースを分離および分離します。
・コントロールプレーンを固定します。
・保存中のトラフィックと機密データ(シークレットなど)を暗号化します。

Namespaces

Kubernetes名前空間は、同じクラスター内の複数の個人、チーム、またはアプリケーション間でクラスターリソースを分割する1つの方法です。デフォルトでは、ネームスペースは自動的に分離されません。ただし、名前空間はスコープにラベルを割り当てます。これを使用して、RBACおよびネットワークポリシーを介して承認ルールを指定できます。ネットワークの分離に加えて、ポリシーはストレージとコンピューティングリソースを制限して、名前空間レベルでポッドをより適切に制御できるようにします。

デフォルトでは3つの名前空間があり、それらを削除することはできません。
• kube-system (for Kubernetes components)
• kube-public (for public resources)
• default (for user resources)

ユーザーポッドは、クラスターサービス用に予約されているため、kube-systemまたはkube-publicに配置しないでください。付録D:名前空間の例に示されているYAMLファイルを使用して、新しい名前空間を作成できます。異なる名前空間のポッドとサービスは、ネットワークポリシーなどの追加の分離が強制されない限り、引き続き相互に通信できます。

Network policies

ネットワークポリシーは、ポッド、名前空間、および外部IPアドレス間のトラフィックフローを制御します。デフォルトでは、ポッドまたは名前空間にネットワークポリシーは適用されないため、ポッドネットワーク内で無制限の入力および出力トラフィックが発生します。ポッドは、ポッドまたはポッドの名前空間に適用されるネットワークポリシーによって分離されます。ネットワークポリシーでポッドが選択されると、該当するポリシーオブジェクトで特に許可されていない接続はすべて拒否されます。

ネットワークポリシーを作成するには、NetworkPolicyAPIをサポートするネットワークプラグインが必要です。ポッドは、podSelectorおよび/またはnamespaceSelectorオプションを使用して選択されます。ネットワークポリシーの例を付録E:ネットワークポリシーの例に示します。ネットワークポリシーのフォーマットは、クラスターに使用されるコンテナーネットワークインターフェイス(CNI)プラグインによって異なる場合があります。管理者は、すべてのポッドを選択するデフォルトのポリシーを使用して、すべての入力および出力トラフィックを拒否し、選択されていないポッドが分離されていることを確認する必要があります。その後、追加のポリシーにより、許可される接続に対するこれらの制限を緩和できます。
NetworkPolicyAPIをサポートするCNIプラグインを使用します
podSelectorおよび/またはnamespaceSelectorを使用してポッドを選択するポリシーを作成します
デフォルトのポリシーを使用して、すべての入力および出力トラフィックを拒否します。選択されていないポッドがkube-systemを除くすべての名前空間に分離されていることを確認します
LimitRangeおよびResourceQuotaポリシーを使用して、名前空間またはポッドレベルでリソースを制限します

Resource policies

ネットワークポリシーに加えて、LimitRangeとResourceQuotaは、名前空間またはノードのリソース使用を制限できる2つのポリシーです。 LimitRangeポリシーは、特定の名前空間内のポッドまたはコンテナーごとに個々のリソースを制限します。たとえば、最大のコンピューティングリソースとストレージリソースを適用します。付録F:LimitRangeの例のYAMLファイルの例に示されているように、名前空間ごとに作成できるLimitRange制約は1つだけです。 Kubernetes 1.10以降では、デフォルトでLimitRangeがサポートされています。各ポッドまたはコンテナに個別に適用されるLimitRangeポリシーとは異なり、
ResourceQuotasは、CPUとメモリの合計使用量に課せられる制限など、名前空間全体の総リソース使用量に課せられる制限です。ユーザーがLimitRangeまたはResourceQuotaポリシーに違反するポッドを作成しようとすると、ポッドの作成は失敗します。 ResourceQuotaポリシーの例は、付録G:ResourceQuotaの例に示されています。

Control plane hardening

コントロールプレーンはKubernetesのコアであり、ユーザーはコンテナーを表示したり、新しいポッドをスケジュールしたり、シークレットを読み取ったり、クラスター内でコマンドを実行したりできます。これらの機密性の高い機能のため、コントロールプレーンは高度に保護する必要があります。ネットワークの分離は、TLS暗号化、RBAC、強力な認証方法などの安全な構成に加えて、権限のないユーザーがコントロールプレーンにアクセスするのを防ぐのに役立ちます。 Kubernetes APIサーバーはポート6443と8080で実行されます。これらは、予想されるトラフィックのみを受け入れるようにファイアウォールで保護する必要があります。デフォルトでは、ポート8080は、ローカルマシンからTLS暗号化なしでアクセス可能であり、要求は認証および承認モジュールをバイパスします。安全でないポートは、APIサーバーフラグ--insecure-port = 0を使用して無効にできます。 Kubernetes APIサーバーは、インターネットや信頼できないネットワークに公開しないでください。ネットワークポリシーをkube-system名前空間に適用して、kube-systemへのインターネットアクセスを制限できます。デフォルトの拒否ポリシーがすべての名前空間に実装されている場合でも、kube-system名前空間は他のコントロールプレーンセグメントおよびワーカーノードと通信できる必要があります。

コントロールプレーンを保護する手順
1.TLS暗号化を設定します
2.強力な認証方法を設定します
3.インターネットおよび不要なネットワークまたは信頼できないネットワークへのアクセスを無効にします
4.RBACポリシーを使用してアクセスを制限します
5.認証およびRBACポリシーでetcdデータストアを保護します
6.kubeconfigファイルを不正な変更から保護します

Etcd

etcdバックエンドデータベースには、状態情報とクラスターシークレットが格納されます。これは重要なコントロールプレーンコンポーネントであり、etcdへの書き込みアクセスを取得すると、サイバーアクターにクラスター全体へのルートアクセスを与える可能性があります。 Etcdには、クラスターの認証方法とRBACポリシーがユーザーを制限できるAPIサーバーを介してのみアクセスする必要があります。 etcdデータストアは別のコントロールプレーンノードで実行できるため、ファイアウォールAPIサーバーのみへのアクセスを制限できます。管理者は、etcdサーバーとAPIサーバー間のHTTPS通信を実施するためにTLS証明書を設定する必要があります。 etcdサーバーは、APIサーバーに割り当てられた証明書のみを信頼するように構成する必要があります。

Kubeconfig Files

kubeconfigファイルには、クラスター、ユーザー、名前空間、および認証メカニズムに関する機密情報が含まれています。 Kubectlは、ワーカーノードとコントロールプレーンのローカルマシンの$ HOME /.kubeディレクトリに保存されている構成ファイルを使用します。サイバーアクターは、この構成ディレクトリへのアクセスを悪用して、構成またはクレデンシャルにアクセスして変更し、クラスターをさらに侵害する可能性があります。構成ファイルは意図しない変更から保護する必要があり、認証されていない非rootユーザーはファイルへのアクセスをブロックする必要があります。

Worker node segmentation

ワーカーノードは、クラスターの実装に応じて、仮想マシンまたは物理マシンにすることができます。ノードはマイクロサービスを実行し、クラスターのWebアプリケーションをホストするため、多くの場合、ノードはエクスプロイトのターゲットになります。ノードが危険にさらされた場合、管理者は、ワーカーノードまたはKubernetesサービスと通信する必要のない他のネットワークセグメントからワーカーノードを分離することにより、攻撃対象領域を事前に制限する必要があります。ファイアウォールを使用して、ネットワークに応じて、内部ネットワークセグメントを外部に面したワーカーノードまたはKubernetesサービス全体から分離できます。ワーカーノードの攻撃対象領域から分離する必要がある可能性のあるサービスの例は、インターネットにアクセス可能である必要のない機密データベースまたは内部サービスです。

Encryption

管理者は、TLS 1.2または1.3暗号化を使用するように、コンポーネント、ノード、コントロールプレーン間を含むKubernetesクラスター内のすべてのトラフィックを構成する必要があります。暗号化は、インストール中またはインストール後に、Kubernetesドキュメントで詳しく説明されているTLSブートストラップを使用して設定し、証明書を作成してノードに配布できます。すべての方法で、安全に通信するために証明書をノード間で配布する必要があります。

Secrets

Kubernetesシークレットは、パスワード、OAuthトークン、SSHキーなどの機密情報を保持します。シークレットに機密情報を保存すると、YAMLファイル、コンテナーイメージ、または環境変数にパスワードやトークンを保存するよりも優れたアクセス制御が提供されます。デフォルトでは、Kubernetesはシークレットを暗号化されていないbase64エンコードされた文字列として保存します。この文字列は、APIアクセス権を持つすべてのユーザーが取得できます。シークレットリソースにRBACポリシーを適用することにより、アクセスを制限できます。シークレットは、APIサーバーで保存データの暗号化を構成するか、クラウドプロバイダーを通じて利用できる外部のキー管理サービス(KMS)を使用して暗号化できます。 APIサーバーを使用してシークレットの保存データの暗号化を有効にするには、管理者はkube-apiserverマニフェストファイルを変更して、-encryption-provider-config引数を使用して実行する必要があります。暗号化プロバイダー構成ファイルの例を付録H:暗号化の例に示します。 KMSプロバイダーを使用すると、生の暗号化キーがローカルディスクに保存されなくなります。シークレットをKMSプロバイダーで暗号化するには、encryption-provider-configファイルで、付録I:KMS構成の例に示すようにKMSプロバイダーを指定する必要があります。

Protecting sensitive cloud infrastructure

Kubernetesは、多くの場合、クラウド環境の仮想マシンにデプロイされます。そのため、管理者は、Kubernetesワーカーノードが実行されている仮想マシンの攻撃対象領域を慎重に検討する必要があります。多くの場合、これらの仮想マシンで実行されているポッドは、ルーティング不可能なアドレスで機密性の高いクラウドメタデータサービスにアクセスできます。これらのメタデータサービスは、サイバーアクターにクラウドインフラストラクチャに関する情報を提供し、場合によってはクラウドリソースの短期間のクレデンシャルさえも提供します。サイバー攻撃者は、特権昇格のためにこれらのメタデータサービスを悪用します[5]。 Kubernetes管理者は、ネットワークポリシーを使用するか、クラウド構成ポリシーを使用して、ポッドがクラウドメタデータサービスにアクセスできないようにする必要があります。これらのサービスはクラウドプロバイダーによって異なるため、管理者はベンダーのガイダンスに従ってこれらのアクセスベクトルを強化する必要があります。

Authentication and authorization

認証と承認は、クラスターリソースへのアクセスを制限するための主要なメカニズムです。サイバーアクターは、よく知られているKubernetesポートをスキャンしてクラスターのデータベースにアクセスしたり、クラスターが正しく構成されていない場合に認証されずにAPI呼び出しを行ったりすることができます。ユーザー認証はKubernetesの組み込み機能ではありません。ただし、管理者がクラスターに認証を追加するには、いくつかの方法があります。

Authentication

Kubernetesクラスタには、サービスアカウントと通常のユーザーアカウントの2種類のユーザーがあります。サービスアカウントは、ポッドに代わってAPIリクエストを処理します。認証は通常、ベアラートークンを使用してServiceAccount AdmissionControllerを介してKubernetesによって自動的に管理されます。ベアラートークンは、よく知られた場所のポッドにマウントされ、トークンが保護されていない場合は、クラスターの外部から使用できます。このため、ポッドシークレットへのアクセスは、KubernetesRBACを使用して表示する必要があるユーザーに制限する必要があります。通常のユーザーと管理者アカウントの場合、ユーザーの自動認証方法はありません。管理者は、認証および承認メカニズムを実装するために、クラスターに認証方法を追加する必要があります。 Kubernetesは、クラスターに依存しないサービスがユーザー認証を管理することを前提としています。 Kubernetesのドキュメントには、クライアント証明書、ベアラートークン、認証プラグイン、その他の認証プロトコルなど、ユーザー認証を実装するいくつかの方法が記載されています。少なくとも1つのユーザー認証方法を実装する必要があります。複数の認証方法が実装されている場合、要求を正常に認証する最初のモジュールが評価を短絡させます。管理者は、静的パスワードファイルなどの弱い方法を使用しないでください。弱い認証方法では、サイバー攻撃者が正当なユーザーとして認証できる可能性があります。

匿名リクエストは、他の設定された認証方法によって拒否され、個々のユーザーまたはポッドに関連付けられていないリクエストです。匿名リクエストが有効になっているトークン認証用に設定されたサーバーでは、トークンが存在しないリクエストは匿名リクエストとして実行されます。 Kubernetes 1.6以降では、匿名リクエストはデフォルトで有効になっています。 RBACが有効になっている場合、匿名要求には、system:anonymousユーザーまたはsystem:unauthenticatedグループの明示的な承認が必要です。 --anonymous-auth = falseオプションをAPIサーバーに渡して、匿名リクエストを無効にする必要があります。匿名リクエストを有効のままにしておくと、サイバーアクターが認証なしでクラスターリソースにアクセスできるようになる可能性があります。

Role-based access control

RBACは、組織内の個人の役割に基づいてクラスターリソースへのアクセスを制御する1つの方法です。 Kubernetesバージョン1.6以降では、RBACはデフォルトで有効になっています。 kubectlを使用してクラスターでRBACが有効になっているかどうかを確認するには、kubectlapi-versionを実行します。有効になっている場合は、.rbac.authorization.k8s.io / v1のAPIバージョンを一覧表示する必要があります。 Cloud Kubernetesサービスでは、クラスターでRBACが有効になっているかどうかを確認する方法が異なる場合があります。 RBACが有効になっていない場合は、次のコマンドで--authorization-modeフラグを使用してAPIサーバーを起動します。kube-apiserver--authorization-mode= RBAC AlwaysAllowなどの承認モードフラグをそのままにしておくと、すべての承認要求が許可されます。すべての承認を効果的に無効にし、アクセスに最小限の特権を適用する機能を制限します。ロールとClusterRolesの2種類の権限を設定できます。ロールは特定の名前空間のアクセス許可を設定しますが、ClusterRolesは名前空間に関係なくすべてのクラスターリソースにアクセス許可を設定します。ロールとClusterRolesは、権限を追加するためにのみ使用できます。拒否ルールはありません。クラスターがRBACを使用するように構成されていて、匿名アクセスが無効になっている場合、KubernetesAPIサーバーは明示的に許可されていないアクセス許可を拒否します。 RBACの役割の例を付録J:ポッドリーダーのRBACの役割の例に示します。

RoleまたはClusterRoleは権限を定義しますが、権限をユーザーに結び付けません。 RoleBindingsおよびClusterRoleBindingsは、RoleまたはClusterRoleをユーザー、グループ、またはサービスアカウントに関連付けるために使用されます。 RoleBindingsは、定義された名前空間内のユーザー、グループ、またはサービスアカウントにRolesまたはClusterRolesのアクセス許可を付与します。 ClusterRolesは名前空間から独立して作成され、RoleBindingを使用して個人に付与して名前空間のスコープを制限できます。 ClusterRoleBindingsは、すべてのクラスターリソースにわたってユーザー、グループ、またはサービスアカウントにClusterRolesを付与します。 RBAC RoleBindingとClusterRoleBindingの例を付録K:RBACRoleBindingとClusterRoleBindingの例に示します。

RolesとClusterRolesを作成または更新するには、ユーザーは同じスコープで新しいロールに含まれる権限を持っているか、rbac.authorization.k8s.ioAPIグループのRolesまたはClusterRolesリソースでエスカレーション動詞を実行する明示的な権限を持っている必要があります。バインディングが作成された後、RoleまたはClusterRoleは不変です。ロールを変更するには、バインディングを削除する必要があります。ユーザー、グループ、およびサービスアカウントに割り当てられる特権は、最小特権の原則に従い、リソースに必要なアクセス許可のみを付与する必要があります。ユーザーまたはユーザーグループは、必要なリソースが存在する特定の名前空間に制限できます。デフォルトでは、ポッドがKubernetesAPIにアクセスするための名前空間ごとにサービスアカウントが作成されます。 RBACポリシーを使用して、各名前空間のサービスアカウントから許可されるアクションを指定できます。 Kubernetes APIへのアクセスは、適切なAPIリクエスト動詞とアクションを適用できる目的のリソースを使用してRBACロールまたはClusterRoleを作成することで制限されます。ユーザー、グループ、およびサービスアカウントを、関連付けられた割り当てられたロールとClusterRolesとともに印刷することにより、RBACポリシーの監査に役立つツールが存在します。

Log auditing

ログはクラスター内のアクティビティをキャプチャします。ログの監査は、サービスが意図したとおりに動作および構成されていることを確認するためだけでなく、システムのセキュリティを確保するためにも必要です。体系的な監査要件では、セキュリティ設定を一貫して徹底的にチェックして、侵害を特定することが義務付けられています。 Kubernetesは、クラスターアクションの監査ログをキャプチャし、基本的なCPUとメモリの使用状況情報を監視できます。ただし、詳細な監視またはアラートサービスをネイティブに提供するわけではありません。

キーポイント
異常なアクティビティの識別を可能にするために、作成時にポッドベースラインを確立します。
ホストレベル、アプリケーションレベル、および該当する場合はクラウドでロギングを実行します。
既存のネットワークセキュリティツールを統合して、スキャン、監視、アラート、および分析を集約します。
通信障害が発生した場合の損失を防ぐために、ローカルログストレージを設定します

Kubernetes内でアプリケーションを実行しているシステム管理者は、環境に合わせて効果的なロギング、モニタリング、アラートシステムを確立する必要があります。 Kubernetesイベントをログに記録するだけでは、システムで発生するアクションの全体像を把握するのに十分ではありません。ロギングは、ホストレベル、アプリケーションレベル、および該当する場合はクラウドでも実行する必要があります。これらのログは、必要に応じて外部認証およびシステムログと関連付けて、セキュリティ監査人およびインシデント対応者が使用するために環境全体で実行されたアクションの全体像を提供できます。 Kubernetes環境内で、管理者は以下を監視/ログに記録する必要があります。
API request history
• Performance metrics
• Deployments
• Resource consumption
• Operating system calls
• Protocols, permission changes
• Network traffic Pod scaling

ポッドが作成または更新されると、管理者はネットワーク通信、応答時間、要求、リソース消費、およびその他の関連するメトリックの詳細なログをキャプチャして、ベースラインを確立する必要があります。前のセクションで詳しく説明したように、匿名アカウントは無効にする必要がありますが、ログポリシーでは、異常なアクティビティを特定するために匿名アカウントが実行したアクションを記録する必要があります。 RBACポリシー構成は、定期的に、および組織のシステム管理者に変更が発生するたびに監査する必要があります。そうすることで、アクセス制御が、役割ベースのアクセス制御セクションで概説されているRBACポリシー強化ガイダンスに準拠して調整されることが保証されます。監査には、現在のログと通常のアクティビティのベースライン測定値との比較を含めて、ログに記録されたメトリックとイベントのいずれかの重要な変更を特定する必要があります。システム管理者は、アプリケーションの使用状況の変更やクリプトマイナーなどの悪意のあるプロセスのインストールなどの重要な変更を調査して、根本的な原因を特定する必要があります。内部および外部のトラフィックログの監査を実施して、接続で意図されているすべてのセキュリティ制約が適切に構成され、意図したとおりに機能していることを確認する必要があります。管理者は、システムの進化に合わせてこれらの監査を使用して、外部アクセスが不要になり、制限できる時期を特定することもできます。ログを外部のログサービスにストリーミングして、クラスター外のセキュリティプロフェッショナルが利用できるようにし、異常を可能な限りリアルタイムで特定し、侵害が発生した場合にログが削除されないように保護できます。この方法を使用する場合、サイバー攻撃者が転送中のログにアクセスして環境に関する貴重な情報を取得できないように、転送中にログをTLS1.2または1.3で暗号化する必要があります。外部ログサーバーを利用する際のもう1つの予防策は、外部ストレージへの追加専用アクセスを使用してKubernetes内のログフォワーダーを構成することです。これにより、外部に保存されたログがクラスター内から削除または上書きされるのを防ぐことができます。

Kubernetes native audit logging configuration

kube-apiserverはKubernetesコントロールプレーン上にあり、フロントエンドとして機能し、クラスターの内部および外部のリクエストを処理します。各要求は、ユーザー、アプリケーション、またはコントロールプレーンによって生成されたかどうかに関係なく、実行の各段階で監査イベントを生成します。監査イベントが登録されると、kube-apiserverは監査ポリシーファイルと該当するルールをチェックします。そのようなルールが存在する場合、サーバーは最初に一致したルールによって定義されたレベルでイベントをログに記録します。 Kubernetesの組み込みの監査機能はデフォルトで有効になっていないため、監査ポリシーが記述されていない場合、何もログに記録されません。クラスター管理者は、監査ポリシーYAMLファイルを作成してルールを確立し、各タイプの監査イベントをログに記録する目的の監査レベルを指定する必要があります。次に、この監査ポリシーファイルは、適切なフラグとともにkube-apiserverに渡されます。ルールが有効であると見なされるには、4つの監査レベル(None、Metadata、Request、またはRequestResponse)のいずれかを指定する必要があります。付録L:監査ポリシーは、RequestResponseレベルですべてのイベントをログに記録する監査ポリシーファイルの内容を示しています。付録M:監査ポリシーファイルをkube-apiserverに送信するためのフラグの例は、kube-apiserver構成ファイルの場所を示し、監査ポリシーファイルをkube-apiserverに渡すためのフラグの例を示します。付録Mには、ボリュームをマウントし、必要に応じてホストパスを構成する方法についての説明もあります。 kube-apiserverには、監査ログ用の構成可能なログとWebhookバックエンドが含まれています。ロギングバックエンドは、指定された監査イベントをログファイルに書き込み、Webhookバックエンドはファイルを外部HTTPAPIに送信するように構成できます。付録Mの例で設定されている--audit-log-pathフラグと--audit-log-maxageフラグは、監査イベントをファイルに書き込むログバックエンドを構成するために使用できるフラグの2つの例です。 log-pathフラグは、ロギングを有効にするために必要な最小構成であり、ロギングバックエンドに必要な唯一の構成です。これらのログファイルのデフォルトの形式はJSONですが、必要に応じて変更することもできます。ロギングバックエンドの追加の設定オプションは、Kubernetesのドキュメントに記載されています。

監査ログを組織のSIEMプラットフォームにプッシュするために、kube-apiserverに送信されたYAMLファイルを介してWebhookバックエンドを手動で構成できます。 Webhook構成ファイルの例と、ファイルをkubeapiserverに渡してWebhookバックエンドを接続するために必要なフラグは、付録N:Webhook構成にあります。 Webhookバックエンドのkube-apiserverで設定できる構成オプションの完全なリストは、Kubernetesのドキュメントにあります。

Worker node and container logging

Kubernetesアーキテクチャ内でロギング機能を設定する方法はたくさんあります。組み込みのログ管理方法では、各ノードのkubeletがログの管理を担当します。個々のファイルの長さ、保存期間、およびストレージ容量のポリシーに基づいて、ログファイルをローカルに保存およびローテーションします。これらのログはkubeletによって制御され、コマンドラインからアクセスできます。

NSACISAは、ノードに障害が発生した場合にログを保持するようにリモートログソリューションを構成することを推奨しています。
・すべてのノードでロギングエージェントを実行して、ログをバックエンドにプッシュします
・各ポッドでサイドカーコンテナを使用してログを出力ストリームにプッシュする
・各ポッドでログエージェントサイドカーを使用してログをバックエンドにプッシュする
・アプリケーション内からバックエンドにログを直接プッシュする

サイドカーコンテナは、他のコンテナと一緒にポッドで実行され、ログをログファイルまたはログバックエンドにストリーミングするように構成できます。サイドカーコンテナは、パッケージ化および展開される別の標準機能コンテナのトラフィックプロキシとして機能するように構成することもできます。

ワーカーノード間でこれらのロギングエージェントの継続性を確保するために、DaemonSetとして実行するのが一般的です。このメソッド用にDaemonSetを構成すると、すべてのノードに常にロギングエージェントのコピーが存在し、ロギングエージェントに加えられた変更がクラスター全体で一貫していることが保証されます。

Seccomp: audit mode

上記のノードとコンテナのログに加えて、システムコールをログに記録することは非常に有益です。 Kubernetesでコンテナシステムコールを監査する1つの方法は、セキュアコンピューティングモード(seccomp)ツールを使用することです。このツールはデフォルトで無効になっていますが、コンテナのシステムコール能力を制限するために使用できるため、カーネルの攻撃対象領域が低くなります。 Seccompは、監査プロファイルを使用して、行われている呼び出しをログに記録することもできます。カスタムseccompプロファイルを使用して、許可されるシステムコールと、指定されていないコールのデフォルトアクションを定義します。ポッド内でカスタムseccompプロファイルを有効にするには、Kubernetes管理者はseccompプロファイルのJSONファイルを/ var / lib / kubelet / seccomp /ディレクトリに書き込み、seccompProfileをポッドのsecurityContextに追加できます。カスタムseccompProfileには、次の2つのフィールドも含める必要があります。タイプ:LocalhostおよびlocalhostProfile:myseccomppolicy.json。すべてのシステムコールをログに記録すると、管理者は標準操作に必要なシステムコールを知ることができ、システム機能を失うことなくseccompプロファイルをさらに制限できます。

SYSLOG

Kubernetesは、デフォルトで、サービスが利用可能な場合、kubeletログとコンテナランタイムログをジャーナルに書き込みます。組織がデフォルトでsyslogユーティリティを使用しないシステムにsyslogユーティリティを利用する場合、またはクラスタ全体からログを収集してsyslogサーバーまたは他のログストレージおよび集約プラットフォームに転送する場合は、その機能を手動で構成できます。 Syslogプロトコルは、ログメッセージフォーマット標準を定義します。 Syslogメッセージには、タイムスタンプ、ホスト名、アプリケーション名、プロセスID(PID)で構成されるヘッダーと、プレーンテキストで記述されたメッセージが含まれます。 syslog-ng®やrsyslogなどのSyslogサービスは、システム全体から統一された形式でログを収集および集約することができます。多くのLinuxオペレーティングシステムは、デフォルトでrsyslogまたはjournaldを使用します。これはイベントログデーモンであり、ログストレージを最適化し、journalctlを介してsyslog形式でログを出力します。特定のLinuxディストリビューションを実行しているノード上のsyslogユーティリティは、デフォルトでオペレーティングシステムレベルでイベントをログに記録します。これらのLinuxディストリビューションを実行しているコンテナーは、デフォルトで、syslogを使用してログも収集します。 Syslogユーティリティによって収集されたログは、ログ集約プラットフォームがそれらを収集するように構成されていない限り、該当する各ノードまたはコンテナのローカルファイルシステムに保存されます。

Alerting

Kubernetesはアラートをネイティブにサポートしていません。ただし、アラート機能を備えたいくつかのモニタリングツールはKubernetesと互換性があります。 Kubernetes管理者がKubernetes環境内で機能するようにアラートツールを設定することを選択した場合、管理者がアラートを監視および設定する必要があるいくつかの指標があります。

アラートをトリガーする可能性のあるケースの例には、次のものが含まれますが、これらに限定されません。
•環境内のいずれかのマシンのディスク容量が少ない、
•ロギングボリュームの使用可能なストレージ容量が不足している、
•外部ロギングサービスがオフラインになる、
•ポッドまたはアプリケーションがroot権限で実行されている、
•リソースのアカウントによって要求が行われている
•使用されている、または特権を取得している匿名アカウント、
•ポッド作成要求のソースIDとしてリストされているポッドまたはワーカーノードのIPアドレス
•異常なシステムコールまたは失敗したAPI呼び出し、
•ユーザー/管理者の動作これは異常です(つまり、異常な時間または異常な場所から)、および
•標準の運用メトリックベースラインからの大幅な逸脱。

ストレージが不足しているときにアラートを出すと、限られたリソースによるパフォーマンスの問題やログの損失を回避し、悪意のある暗号ジャックの試みを特定するのに役立ちます。特権ポッドの実行のケースを調査して、管理者がミスを犯したのか、本物のユースケースで特権の昇格が必要なのか、悪意のある攻撃者が特権ポッドを展開したのかを判断できます。疑わしいポッド作成元のIPアドレスは、悪意のあるサイバー攻撃者がコンテナから抜け出し、悪意のあるポッドを作成しようとしていることを示している可能性があります。

Kubernetesを組織の既存のSIEMプラットフォーム、特に機械学習/ビッグデータ機能を備えたプラットフォームと統合すると、監査ログの不規則性を特定し、誤ったアラートを削減するのに役立ちます。このようなツールをKubernetesで動作するように構成する場合は、これらのケースとユースケースに適用可能なその他のケースがアラートをトリガーするように構成されるように構成する必要があります。侵入の疑いが発生したときに自動的に動作できるシステムは、管理者がアラートに応答している間、侵害を軽減するための措置を講じるように構成できる可能性があります。ポッド作成リクエストのソースIDとしてポッドIPがリストされている場合、アプリケーションを利用可能に保ちながらクラスターの侵害を一時的に停止するために実装できる1つの緩和策は、ポッドを自動的に削除することです。そうすることで、ポッドのクリーンバージョンをノードの1つに再スケジュールできるようになります。次に、調査担当者はログを調べて、違反が発生したかどうかを判断し、発生した場合は、悪意のある攻撃者がどのように侵害を実行してパッチを展開できるかを判断できます。

Service meshes

サービスメッシュは、これらの通信のロジックを各マイクロサービス内ではなくサービスメッシュにコード化できるようにすることで、アプリケーション内のマイクロサービス通信を合理化するプラットフォームです。この通信ロジックを個々のマイクロサービスにコーディングすることは、拡張が難しく、障害が発生したときにデバッグするのが難しく、保護するのが困難です。サービスメッシュを使用すると、開発者にとってこれを簡素化できます。
•サービスがダウンしたときにトラフィックをリダイレクトします。
•通信を最適化するためのパフォーマンスメトリックを収集します。
•サービス間通信の暗号化の管理を許可します。
•サービス間通信のログを収集します。
•各サービスからログを収集します。
•ヘルプ開発者は、マイクロサービスまたは通信メカニズムの問題と障害を診断します。

サービスメッシュは、ハイブリッド環境またはマルチクラウド環境へのサービスの移行にも役立ちます。サービスメッシュは必須ではありませんが、Kubernetes環境に非常に適したオプションです。マネージドKubernetesサービスには、多くの場合、独自のサービスメッシュが含まれています。ただし、他のいくつかのプラットフォームも利用可能であり、必要に応じて高度にカスタマイズできます。これらの一部には、証明書を生成およびローテーションする認証局が含まれており、サービス間の安全なTLS認証を可能にします。管理者は、サービスメッシュを使用してKubernetesクラスタのセキュリティを強化することを検討する必要があります。

Fault tolerance

ロギングサービスの可用性を確保するために、フォールトトレランスポリシーを導入する必要があります。これらのポリシーは、特定のKubernetesユースケースによって異なる場合があります。設定できるポリシーの1つは、ストレージ容量を超えた場合にどうしても必要な場合に、新しいログが最も古いログファイルを上書きできるようにすることです。
ログが外部サービスに送信されている場合、通信の損失または外部サービスの障害が発生した場合にログをローカルに保存するためのメカニズムを導入する必要があります。外部サービスへの通信が復元されたら、ローカルに保存されたログを外部サーバーにプッシュするためのポリシーを設定する必要があります。

Upgrading and application security practices

このドキュメントで概説されている強化ガイダンスに従うことは、Kubernetesオーケストレーションコンテナで実行されているアプリケーションのセキュリティを確保するためのステップです。ただし、セキュリティは継続的なプロセスであり、パッチ、更新、およびアップグレードについていくことが重要です。特定のソフトウェアコンポーネントは個々の構成によって異なりますが、システム全体の各部分は可能な限り安全に保つ必要があります。これには、Kubernetes、ハイパーバイザー、仮想化ソフトウェア、プラグイン、環境が実行されているオペレーティングシステム、サーバーで実行されているアプリケーション、およびKubernetes環境でホストされているその他のソフトウェアの更新が含まれます。 Center for Internet Security(CIS)は、ソフトウェアを保護するためのベンチマークを公開しています。管理者は、Kubernetesおよびその他の関連するシステムコンポーネントのCISベンチマークを順守する必要があります。管理者は定期的にチェックして、システムのセキュリティがベストプラクティスに関する現在のセキュリティ専門家のコンセンサスに準拠していることを確認する必要があります。さまざまなシステムコンポーネントに対して定期的な脆弱性スキャンと侵入テストを実行して、安全でない構成とゼロデイ脆弱性を事前に探す必要があります。潜在的サイバー攻撃者が発見して悪用する前に、発見はすぐに修正する必要があります。更新が展開されると、管理者は、環境から不要になった古いコンポーネントを削除することにも対応する必要があります。マネージドKubernetesサービスを使用すると、Kubernetesオペレーティングシステム、ネットワークプロトコルのアップグレードとパッチを自動化できます。ただし、管理者はコンテナ化されたアプリケーションにパッチを適用してアップグレードする必要があります。

Trivy and hadolint:Checking for misconfiguration of Dockerfile

脆弱性スキャナのTrivyが設定ミスも検出できるようになったらしいので、少し触ってみました。

Dockerfileのスキャンツール

hadolint

github.com
hadolintはDockerfileのスキャナで、ポリシーはDL(hadolint由来)とSC(ShellCheck由来)で構成されています。
前者は、Dockerfile best practicesをベースにしていてDockerfile固有の記述をスキャンしてくれます。後者は、脆弱なシェルコマンドを検出してくれます。

ポリシーの例

Rule Default Severity Description
DL3000 Error Use absolute WORKDIR.
DL3001 Info For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig.
DL3002 Warning Last user should not be root.
DL3003 Warning Use WORKDIR to switch to a directory.
DL3004 Error Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root.
DL3005 Error Do not use apt-get dist-upgrade.
DL3006 Warning Always tag the version of an image explicitly.
DL3007 Warning Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag.
DL3008 Warning Pin versions in apt-get install.
DL3009 Info Delete the apt-get lists after installing something.
DL3010 Info Use ADD for extracting archives into an image.
DL3011 Error Valid UNIX ports range from 0 to 65535.
DL3012 Error Multiple HEALTHCHECK instructions.
DL3013 Warning Pin versions in pip.
DL3014 Warning Use the -y switch.
SC1000 $ is not used specially and should therefore be escaped.
SC1001 This \c will be a regular 'c' in this context.
SC1007 Remove space after = if trying to assign a value (or for empty string, use var='' ...).
SC1010 Use semicolon or linefeed before done (or quote to make it literal).
SC1018 This is a unicode non-breaking space. Delete it and retype as space.
SC1035 You need a space here
SC1045 It's not foo &; bar, just foo & bar.
SC1065 Trying to declare parameters? Don't. Use () and refer to params as $1, $2 etc.
SC1066 Don't use $ on the left side of assignments.
SC1068 Don't put spaces around the = in assignments.
SC1077 For command expansion, the tick should slant left (` vs ´).

Trivy

f:id:FallenPigeon:20210805152042p:plain
aquasecurity.github.io
TrivyはVulnerability ScanningとMisconfigurationの二つの機能があります。
Vulnerability Scanning機能は、コンテナイメージのみならず、ホストOSのファイルシステム、Gitリポジトリも対象としています。
仕組みとしては、apt/yum/rpm/dpkg等のOSパッケージマネージャやgem/pip等の言語パッケージマネージャから依存関係を抽出し、脆弱性データベースと照合しているようです。

Misconfiguration機能は、最近追加された機能でDockerfile、Kubernetes、TerraformといったIaC(Infrastructure as Code)のスキャン機能です。
DockerfileとKubernetesのポリシーはAppshieldリポジトリで管理されていて、hadolint、kubesec、tfsecといったスキャンツールのノウハウが統合されているようです。今回は、新機能のMisconfiguration機能に焦点を当てます。

https://github.com/aquasecurity/appshield
https://github.com/hadolint/hadolint
https://github.com/Checkmarx/kics
https://github.com/controlplaneio/kubesec
https://github.com/aquasecurity/tfsec
https://kubernetes.io/docs/concepts/security/pod-security-standards/
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

インストール

OS:Ubuntu 21.04

hadolint

git clone https://github.com/hadolint/hadolint
cd hadolint
sudo apt install haskell-stack
stack install
nano ~/.profile 
PATH="$PATH:/home/user/.local/bin"
source ~/.profile

使い方

#Trivy:IaCファイルのディレクトリを指定
trivy config [YOUR_IaC_DIRECTORY]

#hadolint:Dockerfileを指定
hadolint Dockerfile

比較

検証1

#スキャン対象のDockerfile
$ cat Dockerfile 
FROM busybox
WORKDIR usr/src/app
$ trivy conf -f json $(pwd)
2021-08-05T16:33:46.620+0900	INFO	Detected config files: 1
2021-08-05T16:33:46.620+0900	WARN	DEPRECATED: the current JSON schema is deprecated, check https://github.com/aquasecurity/trivy/discussions/1050 for more information.

[
  {
    "Target": "Dockerfile",
    "Class": "config",
    "Type": "dockerfile",
    "MisconfSummary": {
      "Successes": 20,
      "Failures": 3,
      "Exceptions": 0
    },
    "Misconfigurations": [
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS001",
        "Title": "':latest' tag used",
        "Description": "When using a 'FROM' statement you should use a specific tag to avoid uncontrolled behavior when the image is updated.",
        "Message": "Specify a tag in the 'FROM' statement for image 'busybox'",
        "Namespace": "appshield.dockerfile.DS001",
        "Query": "data.appshield.dockerfile.DS001.deny",
        "Resolution": "Add a tag to the image in the 'FROM' statement",
        "Severity": "MEDIUM",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds001",
        "References": [
          "https://avd.aquasec.com/appshield/ds001"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:358203d0551db478185a6443612240b7fc2a6e0cf692bdcaf576ebca134958e1"
        }
      },
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS002",
        "Title": "root user",
        "Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
        "Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument",
        "Namespace": "appshield.dockerfile.DS002",
        "Query": "data.appshield.dockerfile.DS002.deny",
        "Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile",
        "Severity": "HIGH",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds002",
        "References": [
          "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
          "https://avd.aquasec.com/appshield/ds002"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:358203d0551db478185a6443612240b7fc2a6e0cf692bdcaf576ebca134958e1"
        }
      },
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS009",
        "Title": "WORKDIR path not absolute",
        "Description": "For clarity and reliability, you should always use absolute paths for your WORKDIR.",
        "Message": "WORKDIR path 'usr/src/app' should be absolute",
        "Namespace": "appshield.dockerfile.DS009",
        "Query": "data.appshield.dockerfile.DS009.deny",
        "Resolution": "Use absolute paths for your WORKDIR",
        "Severity": "HIGH",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds009",
        "References": [
          "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#workdir",
          "https://avd.aquasec.com/appshield/ds009"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:358203d0551db478185a6443612240b7fc2a6e0cf692bdcaf576ebca134958e1"
        }
      }
    ]
  }
]
$ hadolint Dockerfile 
Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:2 DL3000 error: Use absolute WORKDIR

Trivyでは下記すべてが検出されましたがhadolintは2が漏れています。
1.latestタグ(バージャン管理が困難)
2.コンテナ内のrootユーザ(コンテナ内の過剰な権限)
3.WORKDIRの絶対パス(意図しないパスの参照)

検証2

$ cat Dockerfile 
FROM python:3.4
ADD requirements.txt /usr/src/app/
$ trivy conf -f json $(pwd)
2021-08-05T16:50:05.497+0900	INFO	Detected config files: 1
2021-08-05T16:50:05.497+0900	WARN	DEPRECATED: the current JSON schema is deprecated, check https://github.com/aquasecurity/trivy/discussions/1050 for more information.
[
  {
    "Target": "Dockerfile",
    "Class": "config",
    "Type": "dockerfile",
    "MisconfSummary": {
      "Successes": 21,
      "Failures": 2,
      "Exceptions": 0
    },
    "Misconfigurations": [
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS002",
        "Title": "root user",
        "Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
        "Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument",
        "Namespace": "appshield.dockerfile.DS002",
        "Query": "data.appshield.dockerfile.DS002.deny",
        "Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile",
        "Severity": "HIGH",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds002",
        "References": [
          "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
          "https://avd.aquasec.com/appshield/ds002"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:16165dee25682bccd2a150512b6971582a57652bfd051590a7eac075e4da8d84"
        }
      },
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS005",
        "Title": "ADD instead of COPY",
        "Description": "You should use COPY instead of ADD unless you want to extract a tar file. Note that an ADD command will extract a tar file, which adds the risk of Zip-based vulnerabilities. Accordingly, it is advised to use a COPY command, which does not extract tar files.",
        "Message": "Consider using 'COPY requirements.txt /usr/src/app/' command instead of 'ADD requirements.txt /usr/src/app/'",
        "Namespace": "appshield.dockerfile.DS005",
        "Query": "data.appshield.dockerfile.DS005.deny",
        "Resolution": "Use COPY instead of ADD",
        "Severity": "LOW",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds005",
        "References": [
          "https://docs.docker.com/engine/reference/builder/#add",
          "https://avd.aquasec.com/appshield/ds005"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:16165dee25682bccd2a150512b6971582a57652bfd051590a7eac075e4da8d84"
        }
      }
    ]
  }
]
$ hadolint Dockerfile 
Dockerfile:2 DL3020 error: Use COPY instead of ADD for files and folders

ADDの使用については両方のツールで拾えています。

検証3

$ cat Dockerfile 
FROM debian
RUN wget http://google.com
RUN curl http://bing.com
$ trivy conf -f json $(pwd)
...
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS014",
        "Title": "RUN using 'wget' and 'curl'",
        "Description": "Avoid using both 'wget' and 'curl' since these tools have the same effect.",
        "Message": "Shouldn't use both curl and wget",
        "Namespace": "appshield.dockerfile.DS014",
        "Query": "data.appshield.dockerfile.DS014.deny",
        "Resolution": "Pick one util, either 'wget' or 'curl'",
        "Severity": "LOW",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds014",
        "References": [
          "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run",
          "https://avd.aquasec.com/appshield/ds014"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:b33501c59c91430addd5b3e3ecb960911c78c4cc04662b926b5303dc30440262"
        }
      }
    ]
  }
]
$ hadolint Dockerfile 
Dockerfile:2 DL3047 info: Avoid use of wget without progress bar. Use `wget --progress=dot:giga `.Or consider using `-q` or `-nv` (shorthands for `--quiet` or `--no-verbose`).
Dockerfile:3 DL4001 warning: Either use Wget or Curl but not both
Dockerfile:3 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.

wgetcurlの併用はどちらも拾えていますが、hadolintでは追加情報も出力されています。

検証4

$ cat Dockerfile 
FROM debian:10
RUN $greeting="Hello World" 
RUN ls -l $(getfilename)
$ trivy conf -f json $(pwd)
"Misconfigurations": [
      {
        "Type": "Dockerfile Security Check",
        "ID": "DS002",
        "Title": "root user",
        "Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
        "Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument",
        "Namespace": "appshield.dockerfile.DS002",
        "Query": "data.appshield.dockerfile.DS002.deny",
        "Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile",
        "Severity": "HIGH",
        "PrimaryURL": "https://avd.aquasec.com/appshield/ds002",
        "References": [
          "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
          "https://avd.aquasec.com/appshield/ds002"
        ],
        "Status": "FAIL",
        "Layer": {
          "DiffID": "sha256:5776f76fe0c52302603355ee575cd5be70d7b0891ab85903c3de9516f49ff027"
        }
      }
    ]
  }
]
$ hadolint Dockerfile 
Dockerfile:2 SC1066 error: Don't use $ on the left side of assignments.
Dockerfile:3 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:3 SC2046 warning: Quote this to prevent word splitting.

ShellCheck関連はhadolintだけ検出しています。

まとめ

Dockerfile固有の設定はTrivyでもhadolint並みに拾えています。ShellCheckの機能は未実装なので、hadolintの上位互換ではなさそうです。
ポリシーが多少異なっているので、両方使う選択肢もありな気がします。
ただ、カバー範囲や将来性ではTrivyに期待です。
TrivyのMisconfigurationポリシはポリシ検査(Policy as Code)に特化したRego言語をサポートしているため、KubernetesやCICDと親和性が高そうです。
Rego言語は多少癖がありますが、IaC(Infrastructure as Code)セキュリティの感心が高まれば存在感が増してくるのではないかと思います。

Open Policy Agent | Policy Language

DS002 | Vulnerability Database | Aqua Security

package appshield.dockerfile.DS002

import data.lib.docker

__rego_metadata__ := {
	"id": "DS002",
	"title": "root user",
	"version": "v1.0.0",
	"severity": "HIGH",
	"type": "Dockerfile Security Check",
	"description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.",
	"recommended_actions": "Add 'USER ' line to the Dockerfile",
	"url": "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/",
}

__rego_input__ := {
	"combine": false,
	"selector": [{"type": "dockerfile"}],
}

# get_user returns all the usernames from
# the USER command.
get_user[username] {
	user := docker.user[_]
	username := user.Value[_]
}

# fail_user_count is true if there is no USER command.
fail_user_count {
	count(get_user) < 1
}

# fail_last_user_root is true if the last USER command
# value is "root"
fail_last_user_root {
	user := cast_array(get_user)
	len := count(get_user)
	user[minus(len, 1)] == "root"
}

deny[msg] {
	fail_user_count
	msg = "Specify at least 1 USER command in Dockerfile with non-root user as argument"
}

deny[res] {
	fail_last_user_root
	res := "Last USER command in Dockerfile should not be 'root'"
}

住まい探しメモ

インターン生や新入社員からよく聞かれる住まい探しのメモ。
f:id:FallenPigeon:20210724233034p:plain

引越しプラン

スケジュール決め

引越し時期と引越しにかかる時間を見積もりましょう。
費用も大事ですが、焦っていい物件を逃してしまったり、ルーズすぎて引越しで慌てたりしないよう、最初にスケジュールを決めましょう。

住居探し

賃貸の住居探しは、賃貸業者のページから探す、または、賃貸業者に直接相談して紹介してもらう、の2パターンです。
上記に大きな利点欠点はないので、内見や契約時間を考慮して早めに済ませましょう。
ただし、不動産会社が紹介した家しか見ることができない状態だと、不動産会社が得するような物件しか出てこない可能性があります。
賃貸業者に不信感を感じた場合は相談を切り上げましょう。

内見

カタログスペックで把握できない居住性を確認するために重要です。
後述の居住性を考慮して後悔のない住居選びをしましょう。
ただし、社会人が内見に避ける時間は限られているので、2,3件が限界かと思います。
よさげな物件を見つけたらすぐに内見の希望しましょう。
場合によっては唾を付けることもできるので、他人に先を越されるのを防げる可能性もあります。

契約

住居を決定してもすぐ住めるわけではありません。
契約内容の説明と承諾。契約書類関連の手続き(賃貸業者+大家さん)。鍵の受渡し。が完了してやっと住めるようになります。
賃貸業者側の処理速度は運なのでどうしようもありませんが、おおよそ2週間-1か月程度かかるかと思います。

最近はリモート相談や書類の郵送ですべての契約フローを完了できるパターンもありますが、そうでない場合は、賃貸業者の店舗に複数回足を運ぶことになります。前者では、書類のコピーなどをメール送付するため、スキャナプリンタがあると便利です。

予算

家賃に加えて、敷金・礼金や保証金、仲介手数料などの諸経費があることを忘れてはいけません。賃貸物件には初期費用として以下の費用が原則かかります。特に契約時は、家賃に加えて敷金、礼金もかかるため、ひと月分の手取りは軽く吹き飛びます。また、管理費や保険料など、余計にかかる費用もあるため、金銭面の条項は注意深く確認しましょう。

1)敷金……賃料の1~2カ月程度
2)礼金……賃料の1~2カ月程度
3)仲介手数料……一般的には賃料の1カ月分
4)管理費……物件により異なる
5)保険料……万が一の際の家財補償や賠償責任補
6)鍵交換費用……昔の入居者が使っていた鍵の交換

住居種別

個人寮と集合寮

社員寮の場合は、ワンルームと集合寮の2パターンがあり、会社によっては希望を出せるケースもあります。
ワンルーム寮はVM、集合寮はコンテナという印象です。
ワンルームであれば、水回りや家電などの設備が個室に整備されているので、部屋からでなくとも(他人と関わらずとも)生活できます。一方、集合寮では、生活の大半を共同設備に依存することになります。具体的には洗濯機、冷蔵庫、風呂、電子レンジ、トイレ、洗面所といった設備が共用になります。他人と関わりたくない人はワンルームがおすすめですが、集合寮は寮費が安く、食堂付きというメリットもあるので、共同設備に抵抗がなければ悪くないでしょう。

鉄筋と木造

住居のつくりには鉄筋と木造があります。鉄筋は防音性や耐久性に優れていますが、断熱性は木造が優れていると言えます。また、鉄筋の方が費用が高くなり、予算が同じであれば鉄筋の方が間取りが狭くなる傾向があります。室温は一軒家かマンション型かによって多少左右されますが空調で解決できます。また、耐久性についても寮や賃貸であれば責任範囲外のため、それほど気にする必要はありません。
そのため、防音性と面積が大きな判断基準になります。特にテレワーク中心の方は、どちらを優先するか考えておきましょう。

立地条件

立地条件は地理的な性質が強く、一度決めたら変更が効きません。
そのため、優先度を高めに設定して条件を絞り込みましょう。
特に通勤条件が共通することから、勤務地が同じ社員は特定地域に集中する傾向もあります。
社員に知り合いがいれば聞いてみるとよいでしょう。

駅、勤務地からの所要時間

通勤時間との兼ね合いを考えましょう。
テレワーク主体を前提に勤務地から遠いところを選ぶ方もいますが、基本的にはアクセスしやすいところを選ぶのが無難かと思います。

私の場合、下記の条件で駅を選びました。
・乗車時間15分
・乗り換えなし
・勤務地への路線が二つあること
・隣駅が車庫のため始発列車で座れること
・12時以降も終電があること

店舗へのアクセス

店舗や市役所へのアクセスも確認しましょう。
(スーパー、コンビニ、ポスト、薬局、病院、市役所、警察署)

窓、方角、階層

在宅時間が長い場合は太陽の差し込む南や東向きがよいかと思います。

北向きの部屋は寒く、湿気がこもりやすくなります。そのために住む人によってはカビを発生させてしまったり、洗濯物が乾かずに悩まされたりと後悔することになるでしょう。けれどもそのようなデメリットを理解したうえで、あえて北向きが良いという人もいます。

窓のある方角や立地にもよるものの、自分の生活スタイルを考えたうえで、どの向きであれば生活しやすいのかを決めるとよいでしょう。

室外設備

ゴミ置き場や駐輪場など共用部分は事前にチェックしましょう。
ゴミ回収については、ローカルルールを確認しましょう。例えば、指定のゴミ袋や分類、掃除当番がないかなどです。また、カラスの被害や夜間にごみを捨てれるかどうかも確認しておくとよいでしょう。これらを軽視すると近隣住民とトラブルになり戦争状態に突入する恐れがあります。
移動や荷物の搬入がラクかどうかも確認しましょう。エレベーターの有無や階段など、買出しの搬入が可能かどうか確認しておきましょう。特に飲料は重いので、階段であれば2階が限界かと思います。

災害耐性

滅多にありませんが災害にも目を向けておきましょう。地震はどうにもなりませんが、浸水はあり得るのでハザードマップも一度は目を通しておきましょう。極端な話、2階に住めば1階の方が犠牲になっても生き残れます。

治安

自転車盗難の発生率などを確認しましょう。後述する事故物件サイトも参考になります。
内見ではどの程度の人通りがあるか、街灯が多く夜道も安心して歩けるかなどを確認しましょう。
1階に住むうえで、どうしても気になるのは防犯性といえます。
格子やブロック塀などで囲いがつくられている物件も多いものの、侵入のしやすさを考えれば、上階よりも不安が大きいのは確かです。
また、道路に面している立地では、部屋の中が見られやすいといったプライバシーの問題もあります。
特に女性の場合は、外に干している洗濯物への不安などもあるため、何らかの対策が必要となるかと思います。
上述のようなデメリットから1階は家賃が安いものの、できれば避けたいところです。

必須条件と優先順位の決定

イメージするのは常に最強の住居です。ただし、費用と性能はトレードオフのため、条件に優先順位をつけ、最低限の妥協ラインをあらかじめ決めておくことも重要です。

契約と責任範囲

エアコンや照明、コンロなど、自分で取り付ける必要があるものをチェックしておきましょう。
ウォシュレットを利用したいトイレの神様は、入居後にウォシュレットの設置を検討できるかなど、トイレも忘れずチェックしましょう。

物件選びのポイント

カタログスペックも重要ですが、必ず内見で物件情報を確認しましょう。

部屋の広さや窓のサイズ

どこに何をおくのか想像しながら確認しましょう。

収納

収納が狭いと荷物が出しっぱなしになります。ゴミ屋敷の一歩を踏み出さないためにも容量を確認しましょう。

キッチン

自炊する方は下記を確認しましょう。
・調理台のスペースが十分に確保されているか
・ガス台と流しの位置関係
・コンロはIHかガスか。何口設置されているか
・手持ちの調理器具や調味料が収まる広さや形状の収納があるか
・下水や排水溝からの嫌な臭いがないか
・水漏れしていないか
・シンクが狭くて洗い物しづらくないか

洗濯機

家賃が安いと思いきや、置き場が外というトラップがあります。
洗濯機置き場のスペースが狭いと大きい洗濯機が入らないパターンもあります。
衣類程度であればそれほど気にする必要はありませんが、布団などを選択する場合は確認が必要です。

風呂とトイレ

ユニットバスか否かを確認しましょう。
風呂の換気扇がまともに動作するか内見で確認しましょう。機能がしょぼいと黒カビ風呂になります。
トイレは、外見だけでなく、給水タンクの中も綺麗か確認しましょう。

日当たり

窓から差し込む日光を確認しておきましょう。遮光カーテンで調整できるのであるに越したことはないかと思います。

玄関の広さとシューズボックスの内容量

靴をたくさん保有する方は確認しておきましょう。

インターホン

インターホンについては音だけか映像付きか確認しましょう。

換気

居室に湿気がたまると過ごしにくく感じたり、カビの原因にもなります。部屋の換気がスムーズにおこなえる換気扇の有無もチェックしましょう。

エアコン

備え付けられている場合、古過ぎる機種は電気代がかさみやすくなるため、年式を聞いておきましょう。エアコンが付いてない場合は設置できるかどうか、室外機の置き場所があるかどうかも確認しておくと良いでしょう。

コンセント

位置や数をメモしておきましょう。
コンセントは、位置と併せて床からの高さも測っておきましょう。コンセントの位置次第では、想像していた通りの家具の置き方ができないことがあります。居室だけでなくキッチンやトイレ、洗面室なども確認を忘れずにしましょう。また、家電製品やデジタル機器を多く使う方はコンセントが足りなくならないよう数の確認もしておくと安心です。コンセントが足りないと、延長コードを使うことで見た目を損ねたりします。

ベランダ

実用性と防犯面を吟味しましょう。
ベランダは、洗濯物を干すのに不便がない実用性に加え、防犯面での不安がないかも確認しましょう。
物干しが設置されている位置によっては丈が長い洗濯物を引きずってしまう可能性もあるため、不便がないかシミュレーションしておきましょう。干せるスペースがない場合は、浴室に干せるかどうかや浴室乾燥機が付いているか見ておくのをおすすめします。
防犯面に関しては、隣接している建物や電柱から侵入されたり、向かい側の建物から覗かれたりする危険性がないか、ベランダに出て周辺を見ておくと安心です。
最近はベランダなしの物件もありますが、一人暮らしであれば室内干で事足りるため、あまり気にならないかと思います。

騒音

隣や上下の住民の生活音がどの程度聞こえるか、周辺環境が騒がしくないか、実際にチェックしておきましょう。大通りに面している場合や子どもが多いエリアでは、時間帯によって騒がしく感じるかもしれません。生活音を気にする場合は、他の入居者が帰って来る夜の時間帯に内見するのも一つの方法です。壁に耳を当てるのが、なんだかんだで一番判断できると思います。
隣や上階の音がさほど気にならない方は、そこまでしなくてもよいかもしれません。

通信環境

インターネット回線については、物件に導入されているか、ない場合は自分の好きな回線を引けるかを不動産会社に聞いておきましょう。スマートフォンは部屋によって電波レベルが変わる可能性もあるため、各部屋で状況をチェックしてみましょう。特に鉄筋では電波が遮断されるので気を付けましょう。

固定回線の光回線は、戸建ても集合住宅も最終的に各家庭に使われている回線は最大32分岐のうちの1分岐になります。唯一異なるのは、集合住宅タイプは最終的な32分岐を建物内で行っているという点だけです。したがって、各家庭がフルで利用した場合は、1Gbpsの回線であっても31.25Mbpsしか速度が出ません。ただ、全家庭が一斉に使うことはないため、実測は100-200Mbps程度になるかと思います。
また、回線速度に影響する要因としてIPv4におけるPPPoE認証サーバの輻輳があります。こちらは容量が潤沢なIPv6回線を利用することで緩和できる可能性があります。いずれにせよ、根本的な要因は通信事業者が握っていて利用者は関与できないため、基本運ゲーです。

f:id:FallenPigeon:20210724224654j:plain

水道

水道には、貯水槽水道方式と直結給水方式があります。特に高い階層では水圧の関係で前者が採用されます。メンテナンスの行き届いた貯水槽であればそこまで問題ありませんが、そうでない場合は水質が極端に悪化します。私が住んでいた8階の寮は貯水槽式でしたが、水道水がくそまずでした。水道水は飲料用から料理にも使用するため、直結給水方式であるかを確認すべきです。

また、水圧についても確認しましょう。直結給水方式は水道水が比較的おいしいという利点がありますが、階層が上がるたびに水圧が弱くなる欠点もあります。シャワーやキッチンの水圧が貧弱だとイライラするため、内見で確認しましょう。

虫対策

内見で虫の死骸がないかを確認しましょう。賃貸では、以前の入居者が退去した段階で清掃が入りますが、虫の死骸は清掃後に虫が侵入した形跡になります。虫の死骸が多い場合は入居後に悩まされる可能性もあるので気を付けましょう。

ブレーカー

一人暮らしであれば30アンペアあれば十分で、節約が好きな人は20Aでも問題ないでしょう。しかし、うっかりするとブレーカーが落ちてしまうこともあります。
アンペアの変更は入居後でも変更することは出来ますが、大家や不動産会社へ確認が必要ですし、手続きにおける費用も掛かります。事前に必要なアンペア数か、あるいは入居にあたってアンペア数の変更が可能か打診してみましょう。

事故物件

事故物件とは、事件や事故、自殺で人が亡くなった物件です。
この事故物件ですが、入居者に伝えるかに明確なルールはなく、教えてくれないケースがあります。
過去の事件などが気になってしまうという方は、申し込み前に「大島てる」という事故物件公示サイトで確認をしておくことがおすすめです。
特に死因を確認すると地域の治安把握にも活用できます。孤独死程度であればありがちですが、自殺の多発や銃殺などの死因が見つかる地域は避けた方がよいでしょう。

f:id:FallenPigeon:20210724225156p:plain

在宅便利グッズ

社会人になって一年以上経つにもかかわらず出社回数が両手の指で足りる状態の鳩ぽっぽ。
そんな引籠りエバンジェリストが在宅便利グッズ TOP 10を紹介。

電撃殺虫機

コバエどもはどこからでも湧いてきます。特にディスプレイ前を飛ばれると殺意MAX。
半信半疑で電撃殺虫機を導入したところ、まばゆい閃光とともにコバエを殲滅。
紫の光でおびき寄せる機能も効いているらしく、部屋の暗がりに転がしておけば、あとは時間の問題。
電気代も(発動がなければ)一時間5円程度らしく、コスパも最強。

f:id:FallenPigeon:20210719205853j:plain
f:id:FallenPigeon:20210719205736j:plain

アルコールスプレー

日中は電撃殺虫機のおびき寄せ効果が半減するため、PC周りをハエが飛び回ることも。
小さい虫はだいたいアルコールスプレーで撃ち落とせるので緊急殺虫剤になります。
消毒用でも使いますが殺虫用途の方が多いです。

f:id:FallenPigeon:20210719210043j:plain

スイッチマイク

有効無効のスイッチがついてるマイク。zoomなどで常時マイクオン状態にしても、物理スイッチで切り替えができるため、発言せずにやる気があるように見せるという小賢しいテクニックが使えます。

f:id:FallenPigeon:20210719210006j:plain

ソーラー監視カメラ

有線コンセントによる給電が必要なく、無線接続で映像を投影できるため、完全にスタンドアロンで稼働する監視カメラです。
動体センサがトリガされた際のみ起動するため、日中の充電だけで夜間も動作します。
室外にコンセントがなくても動作するため、どこにも設置できるメリットがあります。
玄関にあれば録画できるので訪問販売員に対して強気になれたりします。(私はドアすら開けませんが)

f:id:FallenPigeon:20210719210121j:plain

宅配box

通販の受取り作業を効率化できます。仕事中であろうとなかろうと受け取れていちいち配達員と会話しなくてもいいのは大きなメリットです。南京錠や番号錠も置いておけば使ってくれるので公開鍵の要領で窃盗も防げます。

f:id:FallenPigeon:20210719210154j:plain

米びつ

炊飯機を陰で支える計量器具です。
計量カップで米を測る作業は地味に時間がかかります。
機能はシンプルで地味ですが、無洗米と組み合わせることで炊事を30秒程度で完了できます。
フルテレワークで自炊する場合は必須です。

f:id:FallenPigeon:20210719210227j:plain

スマホアーム

個人スマホ用途で購入しましたが社用スマホを装着すると通知が見やすくて便利。
スマホに通知がいっぱい来るような方は一つはあってもよいかと思います。

f:id:FallenPigeon:20210719210254j:plain

中華ルンバ

ジャップ製は高いので手が届きませんが、中華式のルンバであれば1,2万で買えます。
半端な手動掃除機を買うよりはおすすめです。想像していた以上に賢くて感動しました。

f:id:FallenPigeon:20210719212711j:plain

L字デスク

引っ越してきた際に悩んだ机。部屋の角にL字机を採用して、広大な作業スペースを確保。
テレワークだとプライベートと仕事環境が同一デスクになるため、作業スペースがQOLに直結。
スタンダードの机しか頭にない方は間取りと相談してぜひ選択肢に。

f:id:FallenPigeon:20210719210409j:plain

加湿器

冬はエアコン暖房がフル稼働。のどが渇くので必須のお供。
超音波式であれば電気代もかかりません。

f:id:FallenPigeon:20210719210439j:plain

クラウドKubernetesのセキュリティ

今回はクラウドKubernetesサービス(別名フルマネージドKubernetes)は、通常のKubernetesよりも構築運用の負担が少なく、使い勝手のいいコンテナオーケストレータです。今回はクラウドKubernetesのセキュリティについてです。

責任共有モデル

f:id:FallenPigeon:20210630222756j:plain
f:id:FallenPigeon:20210630222814j:plain

上記はAmazon Elastic Kubernetes Serviceの責任共有モデルです。
Kubernetesはコンテナが動作するNodeとそれらを集中制御するMasterで構成されています。
フルマネージドKubernetesでは、Master側のインフラからサービスまでほぼすべてがクラウドベンダの管轄となります。
そのため、ファイルパーミッションやサービスのオプション指定などはクラウドベンダの責任範囲となり、ユーザ側のセキュリティ範囲は限定されます。ただし、すべてがクラウドベンダ側の責任というわけではなく、ネットワーク設定やユーザアクセス制御など、ユーザに委ねられている設定に関しては、ユーザが責任を負うことになります。また、フルマネージドKubernetesでは、IaaSやサーバレスコンテナなど、Nodeに複数の選択肢があり、構成に応じた責任範囲を理解する必要があります。

Self Managed Workers

OS以上のレイヤがすべてユーザの責任範囲となるため、kubeletからコンテナまですべてが対象。
自由度の高い選択肢であるものの、kubeletやコンテナランタイムも責任範囲となるため、ユーザ側の対策事項が多くなります。
kubeletの要塞化はそれなりに複雑なので、理解が怪しい場合は、EKS FargateやMangaed Node Groupsを選択した方が安心かもしれません。

Mangaed Node Groups

OS、kubelet、コンテナランタイムはAWS側で管理されます。OSにはアクセスできるものの、変更は自由に行えないため、IaaSよりも自由度が下がります。
ただ、要塞化にコストのかかるOS、kubelet、コンテナランタイムがベンダ管轄となります。アンチウィルス製品が対応していないなどの問題が出てきますが、OSレイヤの設定ミスによる感染リスクが大きく軽減できることを考慮すればそれほど大きな問題ではないかもしれません。

EKS Fargate

コンテナランタイムやkubeletはAWS側で管理されるため、ユーザのインフラ管理はコンテナ本体のみとなります。
Nodeについてはコンテナ本体のみを考慮すれば良いため、負担は最も少なくなります。ただ、OSの操作権限がほとんどないため、コンテナ外でソフトウェアを動作させることはできません。コンテナランタイムの機能でコンテナを参照することもできないため、小回りが利かなくなり煩わしく感じることもあります。

セキュリティ考慮

ユーザアクセス制御

Kubernetesには、ロール ベース アクセス制御 (RBAC)の機能があり、認証されていないユーザによるアクセスを制限することが可能です。また、EKSやAKSではIAMのようなクラウド固有のRBACが用意されており、KubernetesのRBACと統合されています。基本的には、従来の機能を利用するだけでも十分なアクセス制御が実現できますが、クラウドネイティブな機能であるため、他のクラウドサービスと親和性が高いという利点があります。ただ、KubernetesのRBAC単体でもそれなりにややこしいので両方利用すると何が何やら状態に陥ります。

ネットワーク

Kubernetesシステムでは、コンテナ単体のバインドポート、コンテナノードの解放ポート、コンテナの論理集合であるポッド単位のネットワークポリシーがネットワークフィルタリングとして機能します。特に、ネットワークポリシーはKubernetes固有の仕組みになります。IPアドレスが動的に変化する問題をネットワークポリシーが解決してくれます。

インフラ

コンテナを実行するOSの要塞化がメインになります。特にコンテナを動作させるのに最低限の機能のみを提供するマシンイメージも存在します。 例例としてはCoreOSやBottlerocketが該当します。これらをIaaSで利用することでOSへの操作性を維持したまま強固なコンテナインフラを運用することができます。コンテナ以外でアプリケーションを動かす予定がない場合は選択肢に入るかと思います。

Kubernetes masterコンポーネント

Kubernetesのmasterノードは、api server、etcd、scheduler等のサービス群で構成されています。それらのほとんどがベンダ管轄となるため、ユーザはほとんどセキュリティを考慮する必要がありません。ただし、これらの一部はユーザに操作機能が提供されているため、その範囲内で適切に制御する必要があります。例として、api serverのエンドポイント設定、マスターノードのロギング設定、管理データの暗号化機能などがあります。

ポッド

コンテナ本体のセキュリティ設定は、コンテナの設定が記載されたポッド構成ファイル、ポッド構成ファイルの設定を強制するポッドセキュリティポリシが該当します。これらのファイルには、コンテナの公開ポート、特権コンテナ、権限昇格、ホストOSのユーザバインドのような実行コンテナに適用されるセキュリティ設定が集約されています。そのため、これさえ覚えておけば、コンテナ本体のセキュリティの半分ぐらいはカバーできると言っても過言ではありません。
ちなみに通常のKubernetesでは、デフォルトのポリシが存在しませんが、EKSのポッドセキュリティポリシは特権ポリシがデフォルトで割り当てられます。そのため、ノーガード状態と同じ状態となります。これは、ポッドセキュリティポリシを有効化する機能自体がMasterノードの機能としてロックされているため、機能を提供しながらユーザにMasterノードを操作させないための仕様と思われます。一方、Azure側では、後述するGatekeeperベースのAzure Policyが推奨されている印象です。
その他の機能には、ReplicaSetのような冗長性を維持する機能もKubernetesで提供されています。

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: eks.privileged
  annotations:
    kubernetes.io/description: 'privileged allows full unrestricted access to
      pod features, as if the PodSecurityPolicy controller was not enabled.'
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
  labels:
    kubernetes.io/cluster-service: "true"
    eks.amazonaws.com/component: pod-security-policy
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'
  readOnlyRootFilesystem: false

アドミッションコントロール

先ほど紹介したポッドセキュリティポリシですが、実は非推奨化が決定しています。もともと設計がよろしくなかったようで廃止されるようです。ただし、代替機能も開発が進んでいるらしく、それほど心配する必要はないのかもしれません。
それまでの乗り換え先候補となるのがアドミッションコントロールという機能になります。
Kubernetesでは、コンテナの操作はapi serverを経由して行われます。このapi serverでapiリクエストを実行する前に先程紹介したRBAC、ポッドセキュリティポリシ、さらにアドミッションコントロールによるアクセス制御が実行されます。イメージとしては現場猫3人がコンテナノードの門番をしているような状態となります。

f:id:FallenPigeon:20210701002327j:plain

このアドミッションコントロールがなんなのかというと主にwebhookの機能を利用して様々なチェック処理を走らせることができます。例えば、デプロイ寸前のコンテイメージの脆弱性診断を実施したり、実装次第ではポッドセキュリティポリシ以上の機能を提供することができます。ただ、実装にはそれなりの知識が求められるため、敷居の高い機能となっています。
そこで頼りになるのがGatekeeperというテンプレートアドミッションコントロールとなります。従来であれば一から実装すべき機能をフレームワークとして提供しており、ポッドセキュリティポリシの上位互換機能を組み込むことができます。EKSではagentをポッドとして容易にデプロイすることができます。AKSではAzure Policyという機能に統合されており、直接操作はできませんが、間接的にこの機能を利用することが可能です。

参考

フルマネージドKubernetesのセキュリティはベンダが提供するベストプラクティスやCIS benchmarkが参考になります。
基本はKubernetesの対策がメインですが、クラウドサービス固有の機能や責任共有モデルが絡んでくるため、自由度とセキュリティを天秤にかけながらアーキテクチャを決定し、セキュリティを担保するとよいでしょう。

f:id:FallenPigeon:20210701004050p:plain
aws.amazon.com
aws.github.io