zaki work log

作業ログやら生活ログやらなんやら

NetBoxのSecretを使って暗号化された情報を登録・参照する

NetBoxは、デバイスVMの情報に暗号化されたSecret情報を持たせることができるので、例えばログイン用のアカウント情報などもNetBoxに登録しておくことができます。
このSecretはbase64エンコードされるだけのもの、、ではなく、256ビットのAES共通鍵で暗号化されて保存されます。(ドキュメントより)

The plaintext value of a secret is encrypted to a ciphertext immediately prior to storage within the database using a 256-bit AES master key

netbox.readthedocs.io

鍵作成

鍵形式は以下。

Supported Key Format

Public key formats supported

  • PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY)
  • X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
  • OpenSSH line format is not supported.

Private key formats supported (unencrypted) - PKCS#1 RSAPrivateKey* (PEM header: BEGIN RSA PRIVATE KEY) - PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY)

https://netbox.readthedocs.io/en/stable/core-functionality/secrets/#supported-key-format

PKCS#1形式のRSAキーペアを用意するのが簡単でopenssl genrsaで作れる、、、と思ったけど、NetBoxのUIでもキーペアが作成できます。

鍵の作成は画面一番右上ユーザーメニューの[Profile]から。

f:id:zaki-hmkc:20210402070824p:plain

User Profile画面のサイドメニューの[User Key]で鍵管理の画面になります。

f:id:zaki-hmkc:20210402071030p:plain

初回アクセス時はまだ登録された鍵情報がないため、[Create User Key]押下し、鍵の作成を行います。
別途作成したキーペアの公開鍵の内容をテキストフィールドに入力するか、[Generate a New Key Pair]押下してここでキーペアを作成します。
今回はここでキーペアを作成してみます。

f:id:zaki-hmkc:20210402071224p:plain

[Generate a New Key Pair]押下するとキーペアが作成され、その情報が表示されます。

f:id:zaki-hmkc:20210402071409p:plain

ここで表示された秘密鍵はこの画面を閉じると再表示はできない(NetBox上には残らない)ので、テキストの内容を手元にコピーし、netbox_private.keyなどのファイル名で保存しておきます。
保存したら[I have saved my new private key]押下。 すると元の鍵管理画面に戻って作成された公開鍵情報が表示された状態になるので[Save]を押下。

f:id:zaki-hmkc:20210402071738p:plain

これで鍵情報が登録されました。

Secretの暗号化は共通鍵で暗号化されると書かれてたのに何でRSA公開鍵作るのかというと、暗号化・復号に使うAES共通鍵を、ここで作ったRSAキーペアの公開鍵で暗号化したものがNetBox上に保存され、Secret情報の暗号化・復号にはRSAキーペアの秘密鍵で復号したAES共通鍵を使用するみたい。(ドキュメントを読む限り、そうなる)
webの画面で作成されたキーペアは見た感じ2048ビット長になる模様(NetBox v2.10.4時点)。

web画面でSecretの作成

Secret Roleの作成

Secretを作成するにはその準備としてSecret Roleを作成しておく必要があります。 (Secretの必須パラメタにSecret Roleがある)

Secret Roleの作成は、メニューの[Secrets] -> [Secret Roles]から。

f:id:zaki-hmkc:20210402070727p:plain

Secret Roleには、暗号化するSecretが何の情報なのかメタ情報的な名称を付けてやるとよさげ。 例えば「login credentials」など。

f:id:zaki-hmkc:20210402070711p:plain

Secretの作成

VM情報を開くとダッシュボードに「Secrets」のパートの[+ Add secret]を押下。

f:id:zaki-hmkc:20210402071948p:plain

Secretの作成画面になるので、Secret Roleを選択しデータを入力、[Create]押下。

f:id:zaki-hmkc:20210402072713p:plain

そうすると、現在のNetBoxのweb操作のためのセッション情報に秘密鍵が保持されてない場合、秘密鍵を要求されるので、ここにキーペア作成時に保存した秘密鍵のテキストデータを入力し[Request session key]を押下します。

f:id:zaki-hmkc:20210402072844p:plain

秘密鍵を入力するとセッション情報として保持されたという通知がブラウザのダイアログで表示されるので[OK]押下。

f:id:zaki-hmkc:20210402073223p:plain

で、これで秘密鍵を使用可能になったので、[Create]押下してSecretの登録を再開します。

f:id:zaki-hmkc:20210402073452p:plain

このとき、秘密鍵がセッション情報に登録されているので、[Unlock]を押下すると内容を確認できます。(内容はダミーですw)

f:id:zaki-hmkc:20210402073637p:plain

RESTでSecretの参照

RESTの仕様は例によってNetBoxのweb画面下部の「{} API」で見れるswaggerから。
secretsのセクションにエンドポイント一覧があるのでそこで確認できます。

登録したSecretの情報を取得するには、/secrets/secrets/{id}/を使用します。(IDはwebで見たときのURLで確認)

[zaki@cloud-dev netbox-key]$ curl -X GET http://192.168.0.19:28080/api/secrets/secrets/1/ -H "Accept: application/json; indent=4" -H "Authorization: Token ${NETBOX_TOKEN}"
{
    "id": 1,
    "url": "http://192.168.0.19:28080/api/secrets/secrets/1/",
    "assigned_object_type": "virtualization.virtualmachine",
    "assigned_object_id": 6,
    "assigned_object": {
        "id": 6,
        "url": "http://192.168.0.19:28080/api/virtualization/virtual-machines/6/",
        "name": "client-dev"
    },
    "role": {
        "id": 1,
        "url": "http://192.168.0.19:28080/api/secrets/secret-roles/1/",
        "name": "login credentials",
        "slug": "login-credentials"
    },
    "name": "zaki",
    "plaintext": null,
    "hash": "pbkdf2_sha256$1000$jA10MuGv10l3$K586baQvjZQCl10idEA0WbeuWwHqotrOuG0Oz/7pAzQ=",
    "tags": [],
    "custom_fields": {},
    "created": "2021-04-01",
    "last_updated": "2021-04-01T22:32:14.236537Z"
}

ただし、普通にやってもデータが入っているplaintextのところは(暗号化データを復号できずに)nullとなっている。
この値を取得するには、webの画面でセッションとして秘密鍵情報を登録したのと同じように、RESTで秘密鍵を送信してセッションキーを発行しておく必要があります。

[zaki@cloud-dev netbox-key]$ ls -F
gen_by_openssl/  netbox_register_private.key

カレントディレクトリに秘密鍵を保存したnetbox_register_private.keyファイルがある場合、--data-urlencode@でファイル指定して以下のRESTでセッションキーを取得します。

[zaki@cloud-dev netbox-key]$ curl -X POST -H "Authorization: Token ${NETBOX_TOKEN}" -H "Accept: application/json; indent=4" --data-urlencode "private_key@netbox_register_private.key" http://192.168.0.19:28080/api/secrets/get-session-key/
{
    "session_key": "8zJfic3TpR/WcJhDtuIWChZQrdkuJeIsfVI3JmDmqhc="
}

これでセッションキーを取得できたので、X-Session-Keyヘッダにこの値を追加して再度SecretのAPIを叩く。

[zaki@cloud-dev netbox-key]$ curl -X GET http://192.168.0.19:28080/api/secrets/secrets/1/ -H "Accept: application/json; indent=4" -H "Authorization: Token ${NETBOX_TOKEN}" -H "X-Session-Key: 8zJfic3TpR/WcJhDtuIWChZQrdkuJeIsfVI3JmDmqhc="
{
    "id": 1,
    "url": "http://192.168.0.19:28080/api/secrets/secrets/1/",
    "assigned_object_type": "virtualization.virtualmachine",
    "assigned_object_id": 6,
    "assigned_object": {
        "id": 6,
        "url": "http://192.168.0.19:28080/api/virtualization/virtual-machines/6/",
        "name": "client-dev"
    },
    "role": {
        "id": 1,
        "url": "http://192.168.0.19:28080/api/secrets/secret-roles/1/",
        "name": "login credentials",
        "slug": "login-credentials"
    },
    "name": "zaki",
    "plaintext": "curry_tabetai",
    "hash": "pbkdf2_sha256$1000$jA10MuGv10l3$K586baQvjZQCl10idEA0WbeuWwHqotrOuG0Oz/7pAzQ=",
    "tags": [],
    "custom_fields": {},
    "created": "2021-04-01",
    "last_updated": "2021-04-01T22:32:14.236537Z"
}

無事に"plaintext": "curry_tabetai" という値を取得できました。
ちなみにAnsibleのlookup pluginを使う場合はkey_fileに秘密鍵ファイルのパスを指定すれば、セッション処理は自動でやってくれるので楽です。(あとでまとめる)

curlのオプションはこの辺も参照。 qiita.com

環境

Docker版NetBox (v2.10.4)で確認。