セキュリティー

イントロダクション

LXD は root ユーザーで実行するデーモンです。

デフォルトではデーモンへのアクセスはローカルの UNIX ソケット経由でのみ 可能です。設定によって、 TLS ソケット上でネットワーク越しに同じ API を 公開することが可能です。

警告: UNIX ソケット経由でのローカルアクセスは LXD へのフルのアクセスを 常に許可します。これはあらゆるファイルシステムのパスやデバイスをインスタンス にアタッチする能力やインスタンスの全てのセキュリティの機能を変更することも 含みます。あなたのシステムに root 権限でアクセスを許可するほど信頼できる人 だけにそのようなアクセスを許可するべきです。

リモート API は TLS クライアント証明書か Candid ベースの認証のどちらかを 使用します。 Canonical RBAC のサポートは Canded ベースの認証と組み合わせて API クライアントが LXD で何を出来るかを制限するのに使えます。

TLS configuration

LXD デーモンとのリモートの通信は HTTPS 上の JSON を使って行います。 サポートしているプロトコルは TLS 1.2 以上です。

全ての通信は完全な前方秘匿性 (Perfect Forward Secrecy; PFS) を使う必要があり、 暗号は強力な楕円曲線のもの (ECDHE-RSA や ECDHE-ECDSA など)に限定されます。

生成されるキーは最低でも 4096 ビットのRSAでなければならず、 EC384 が好ましいです。 署名を使う場合は SHA-2 の署名だけが信頼されます。

LXD を導入する際はクライアントとサーバの両方を管理するので、後方互換性の ために弱いプロトコルや暗号をサポートする理由はありません。

クライアントとサーバの両方が初回起動時に証明書とキーのペアを生成します。 サーバは LXD ソケットとの全ての https 通信にそれを使用し、クライアントは その証明書をクライアント・サーバ間の通信にクライアント証明書として使用します。

証明書を再生成するには単に古い証明書を消すだけです。次に接続する際に 新しい証明書が生成されます。

Role Based Access Control (RBAC)

LXD は Canonical RBAC サービスとの統合をサポートします。

これは Candid ベースの認証を用い、 RBAC サービスがユーザー/グループと ロールの関係を管理します。ロールは個々のプロジェクト、全てのプロジェクト、 あるいは LXD インスタンス全体に割り当てることができます。

ロールはプロジェクトに割り当てられると以下のような意味を持ちます。

  • auditor: プロジェクトへの読み取り専用のアクセス
  • user: 通常のライフサイクルアクション(起動、停止、…)の実行、インスタンス内でのコマンドの実行、コンソールへのアタッチ、スナップショットの管理、… を行う能力
  • operator: 上記のすべてに加えてインスタンスとイメージを作成、再設定、そして削除する能力
  • admin: 上記のすべてに加えてプロジェクト自体を再構成する能力

警告: これらのロールのうち現状では auditoruser だけが ホストへの root 権限のアクセスを渡す信頼が持てないユーザーに適した ロールです。

Container security

LXD コンテナーはかなり広い範囲のセキュリティの機能を利用可能です。

デフォルトではコンテナーは非特権 (unprivileged) です。これはコンテナーが ユーザー名前空間で稼働することを意味し、コンテナー内のユーザーの能力をホスト上の 通常ユーザーの能力に制限し、コンテナーが所有するデバイスにも限定した権限しか 与えないことを意味します。

コンテナー間のデータ共有が不要であれば、 security.idmap.isolated を 有効にすることで各コンテナーに対する uid/gid のマッピングをオーバーラップ しないようにでき、他のコンテナーへの潜在的な DoS 攻撃を防ぐことができます。

もし望む場合は LXD は特権 (privileged) コンテナーを実行することもできます。 ただし、これらは root 権限を取得しようとする行為に対して安全ではないこと、 特権コンテナー内の root 権限を持つユーザーはホストに DoS をすることができ、 コンテナー内への監禁から脱出する方法を見つけるかもしれないことに注意してください。

コンテナーのセキュリティとカーネル機能についてのより詳細は LXC のセキュリティページ. を参照してください。

TLS クライアント証明書での認証を使ってリモートを追加する

デフォルトのセットアップでは、ユーザーが lxd remote add で新しいサーバを 追加する際、サーバに https で通信し、証明書がダウンロードされ、 フィンガープリントがユーザーに表示されます。

ユーザーは、これが本当にサーバのフィンガープリントなのかの確認を求められます。 これは接続してみて手動で確認するか、既にそのサーバに接続可能になっている 別のユーザーに info コマンドを実行してもらい、表示されたフィンガープリント と比較することで確認できます。

その後ユーザーはそのサーバのトラスト・パスワード (訳注: サーバに接続できる権限があることを確認するためのパスワード) を 入力する必要があります。正しいパスワードを入力すると、クライアント証明書が サーバのトラスト・ストア (訳注: 信頼済みクライアントストア) に追加され、 今後はクライアントは追加の機密情報を提供することなく、サーバに接続できます。

このワークフローは SSH が未知のサーバに初めて接続したときにプロンプトが 表示されるのと非常に似ています。

PKI ベースのセットアップで TLS クライアントを使ってリモートを追加する

PKI ベースのセットアップではシステム管理者は中心となる PKI を運営します。 その PKI が全ての lxc クライアント用のクライアント証明書と全ての LXD デーモンのサーバ証明書を発行します。

それらの証明書と鍵はさまざまなマシンに手動で配置され、自動生成された 証明書と鍵を置き換えます。

CA 証明書も全てのマシンに追加します。

このモードでは、 LXD デーモンへの通信は予め配置しておいた CA 証明書を 使って行われます。サーバ証明書が CA によって署名されていなければ、 通信は単に通常の認証機構 (訳注: 上記のデフォルトのセットアップでリモート を追加する際の手順) を通ることになります。

サーバ証明書が有効で CA によって署名されていれば、その証明書について ユーザーにプロンプトを表示することなく接続は続行します。

その後、ユーザーはそのサーバのトラスト・パスワード を入力する必要があります。 正しいパスワードを入力すると、クライアント証明書がサーバのトラスト・ストアに追加され、 今後はクライアントは追加の機密情報を提供することなく、サーバに接続できます。

PKI モードを有効にするには、クライアントの設定ディレクトリ (~/.config/lxc) に client.ca ファイルを追加し、サーバの設定ディレクトリ (/var/lib/lxd) に server.ca ファイルを追加します。さらにクライアント用にクライアント証明書を CA によって発行し、サーバ用にサーバ証明書を発行します。それらの証明書で 事前に自動生成されたファイルを置き換える必要があります。

この後、サーバを再起動すると PKI モードで起動します。

Candid 認証を使ってでリモートを追加する

LXD を Candid を使うように設定した場合、 LXD はクライアントが candid.api.url の設定に指定した認証サーバから Discharge トークンを 取得して認証を試みるように依頼します。

認証サーバの証明書は LXD サーバに信頼される必要があります。

Macaroon 認証を設定された LXD にリモートを追加するには lxd remote add REMOTE ENDPOINT --auth-type=candid を実行します。クライアントはユーザーを検証するために認証サーバに要求される 機密情報を入力するためのプロンプトを表示します。認証が成功したら、 認証サーバから受け取ったトークンを LXD サーバに渡して接続します。 LXD サーバはトークンを検証し、リクエストを認証します。トークンはクッキーとして 保存され、クライアントが LXD にリクエストを送る度にサーバに渡されます。

信頼された TLS クライントを管理する

LXD サーバが信頼している TLS 証明書の一覧は lxc config trust list で 取得できます。

クライアントは lxc config trust add <file> を使用して手動で追加できます。 これにより既存の管理者が新しいクライアント証明書を直接トラスト・ストアに 追加することによって共有されたトラスト・パスワードの必要性を無くします。

クライアントへの信頼を取り消すには lxc config trust remove FINGERPRINT を 実行すると証明書が削除されます。

TLS 認証でのパスワード・プロンプト

管理者によって事前に信頼関係がセットアップされていない場合に 新しい信頼関係を確立するには、サーバにパスワードを設定し、クライアントが 自身をサーバに登録する際にそのパスワードを送る必要があります。

ですので、リモートを追加する操作は次のようになります。

  1. API の GET /1.0 を呼びます。
  2. PKI のセットアップをしていなければ、フィンガープリントを確認するプロンプトがユーザーに表示されます。
  3. サーバから返された dict を見て、 "auth" が "untrusted" だった場合、ユーザーにサーバのパスワードを入力させ、 /1.0/certificatesPOST のリクエストを送り、その後再び /1.0 のリクエストを送って本当に信頼されたかを確認します。
  4. これでリモートが準備完了になりました。

失敗のシナリオ

サーバ証明書が変更されていた場合

典型的には次の 2 つの場合があるでしょう。

  • サーバが完全に再インストールされたため証明書が変わった
  • 接続がマン・イン・ザ・ミドル (MITM) 攻撃によりインターセプトされた

これらのケースでは、サーバ証明書のフィンガープリントが (訳注: ローカルに保存されていた) このリモート用の設定に含まれる フィンガープリントと一致しないため、クライアントはサーバへの接続を拒否します。

後はユーザーの責任でサーバ管理者に連絡し、サーバ証明書が本当に変更されたのか 確認する必要があります。変更されたのであれば証明書を新しいもので置き換えるか、 リモートを一旦削除して再度追加できます。

サーバ上の信頼関係が取り消された

このケースでは、サーバは同じ証明書をまだ使っていますが、全ての API 呼び出しは クライアントが信頼されていないことを示す 403 エラーを返します。

これは別の信頼されたクライアントかローカルのサーバ管理者がサーバ上の 信頼エントリーを削除したときに起こります。

プロダクションのセットアップ

プロダクション環境のセットアップでは、全てのクライアントを追加した後、 core.trust_password の設定を削除することを推奨します。削除することにより パスワードを推測しようとするブルート・フォース攻撃を防ぐことができます。

さらに core.https_address をサーバにアクセス可能な単一のアドレスに設定し (ホスト上の任意のアドレスではなく) 、許可されたホストやサブネットからのみ LXD のポートへのアクセスを許可するようにファイアウォールのルールを設定すべきです。

ネットワークのセキュリティ

bridged NIC のセキュリティ

LXD のデフォルトのネットワークのモードはそれぞれのインスタンスが接続する「管理された」プライベートなネットワークブリッジを提供するためのものです。 このモードではホスト上に lxdbr0 と呼ばれるインターフェースが存在し、それぞれのインスタンスに対してブリッジとして振る舞います。

ホストはそれぞれの管理されたブリッジに対して dnsmasq のインスタンスを稼働します。 それが IP アドレスを割り当て、 DNS の権威サーバーとキャッシュサーバーの両方のサービスを提供します。

DHCPv4 を使うインスタンスには IPv4 アドレスが割り当てられ、インスタンス名に対する DNS レコードが作成されます。 これによりインスタンスが DHCP リクエスト内に虚偽のホスト名を含めて DNS レコードをスプーフィングできないようにしています。

さらに dnsmasq サービスは IPv6 のルーター広告の機能も提供します。 これはインスタンスが SLAAC を使って自身の IPv6 アドレスを自動設定することを意味し、 dnsmasq による割り当ては行いません。 しかしインスタンスは同等の SLAAC IPv6 アドレスに対して作成された AAAA DNS レコードを DHCPv4 を使って取得することもできます。 これにはインスタンスが IPv6 アドレスを生成する際に IPv6 のプライバシー拡張を使っていないことが前提となります。

このデフォルトの設定では DNS の名前はスプーフィングできませんが、インスタンスは Ethernet ブリッジに接続しており、 Layer 2 の希望するトラフィックを送信できますので、信頼できないインスタンスが実質的にはブリッジ上の MAC アドレスあるいは IP アドレスをスプーフィングできることを意味します。

デフォルトの設定ではブリッジに接続されたインスタンスがブリッジに (場合によっては悪意のある) IPv6 ルーター広告を送ることで LXD ホストの IPv6 ルーティングテーブルを変更することも可能です。 これは lxdbr0 インターフェースが /proc/sys/net/ipv6/conf/lxdbr0/accept_ra2 に設定して作られており、それは LXD ホストが forwarding を有効にしているときでさえもルーター広告を受け付けることを意味しているからです(より詳細な情報については https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt を参照)。

しかし LXD はいくつかの bridged NIC セキュリティ機能を提供しており、インスタンスがネットワークに送信できるトラフィックの種類を制限するのに使用できます。 これらの NIC 設定はインスタンスが使用するプロファイルに設定するべきですが、以下に示すように個々のインスタンスに追加することもできます。

bridged NIC に対して以下のセキュリティ機能が利用可能です。

Key Type Default Required Description
security.mac_filtering boolean false no インスタンスが別のインスタンスの MAC アドレスを詐称するのを防ぐ
security.ipv4_filtering boolean false no インスタンスが別のインスタンスの IPv4 アドレスを詐称するのを防ぐ(これを有効にすると mac_filtering も有効になります)
security.ipv6_filtering boolean false no インスタンスが別のインスタンスの IPv6 アドレスを詐称するのを防ぐ(これを有効にすると mac_filtering も有効になります)

プロファイルに設定されたデフォルトの bridged NIC 設定を以下のコマンドでインスタンスごとにオーバーライドできます。

lxc config device override <instance> <NIC> security.mac_filtering=true

これらの機能を合わせて使うとブリッジに接続されたインスタンスが MAC アドレスや IP アドレスを詐称するのを防ぐことができます。 これらは xtables (iptables, ip6tables そして ebtables) あるいは nftables のいずれかを使って実装されていて、どちらが使われるかはホストでどちらが利用可能かによって決まります。

これらのオプションを使うと、ネストしたコンテナーでは異なる MAC アドレス (例えばブリッジされているか macvlan の NICを使うなど) を持つ親のネットワークを実質的に使えなくなることに注意が必要です。 ネストしたコンテナー、少なくとも親と同じネットワーク上のネストしたコンテナーを実質的に使えなくなることに注意が必要です。

IP フィルタリング機能は詐称されたソースアドレスを含む全てのパケットをブロックするだけでなく、詐称された IP を含む ARP と NDP 広告もブロックします。

security.ipv4\_filteringsecurity.ipv6\_filtering が有効で ( ipvX.address=none であるかブリッジで DHCP サービスが有効になっていないために)インスタンスに IP アドレスが割り当てられない場合、そのインスタンスからの(訳注: security.ipv4\_filtering なら IPv4 、 security.ipv6\_filtering なら IPv6 と)設定に対応するプロトコルの全ての IP トラフィックはブロックされます。

security.ipv6\_filtering が有効な場合、インスタンスからの IPv6 ルーター広告はブロックされます。

security.ipv4\_filteringsecurity.ipv6\_filtering が有効な場合は ARP、IPv4、IPv6 以外の全ての Ethernet フレームがドロップされます。 これはスタックされた VLAN QinQ (802.1ad) のフレームが IP フィルタリングをバイパスするのを防ぎます。

routed NIC のセキュリティ

routed という別のネットワークモードが使用でき、これはコンテナーとホストの間に veth のペアを提供します。 このネットワークモードでは LXD ホストはルーターとして機能し、ホストに静的ルートが追加され、コンテナの IP アドレスへのトラフィックはコンテナーの veth インタフェースに向けられます。

デフォルトではホスト上に作成される veth インタフェースは accept_ra 設定が無効になっています。 これは LXD ホストの IPv6 ルーティングテーブルをコンテナーからのルーター広告で変更されないようにするためです。 さらにホスト上の rp_filter1 に設定されます。 これはコンテナーが持っているとホストが知らない IP アドレスに対してソースアドレスのスプーフィングを防ぐためです。