ONTAPのSecurity機能について11【Dynamic Authorization for SSH connections】

# はじめに
本記事ではONTAP 9.15.1以降で利用可能となった、**Dynamic Authorizationについて記載**します。

Dynamic Authorizationは、場所/時刻/信頼できるデバイス/ユーザ認証履歴などの属性を元に**ユーザーに信頼スコアを割り当て、アクティビティに応じた許可や追加認証を行い**、信頼できるデバイスからログインしたユーザーにはスムーズに操作が許可されますが、**リスクの高いユーザーには追加のチェックや操作の拒否が行われます。**
この機能によってリモートアクセスのセキュリティを強化する事ができますが、<font color="Red">**ONTAP 9.15.1ではSSHに対してのみ有効な機能**</font>となります。

管理者はDynamic Authorizationに対してルールを作成し、**信頼スコアに基づいて特定のコマンドを制限することが可能で、悪意のある攻撃者による損害を軽減し、ユーザーのアクティビティが疑わしい場合には追加の認可チェックを行います。**
設定を行う際はクラスタ全体または個々のSVMに対して有効にでき、信頼できるデバイスの定義や基準スコアの重み付けなどのカスタマイズも可能です。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/2f7d2c19-7f70-275b-9393-36a50dddea08.png)


## 何をしたい？できる？
- **Dynamic Authorizationを有効化する**
- **公開鍵認証とパスワード認証で信頼スコアが変わるか確認する**
- **コマンド実行時刻を変えてスコアが変化するか確認する**



## 記事における環境情報
本記事では、以下の環境で実施した内容となります。
**分かり易くするために、Network構成は単純化しています。**
- ONTAP  : 9.15.1
- Dynamic Authorizationの設定自体は管理Networkから実施
- **外部サービスとしてコマンド操作の時間によってリスク値を返すコンテナを作成**
- **Windows端末からユーザ向けSVMに直接SSHログインして信頼スコアを確認**

<img width="550" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/e2d1137b-4ce7-8755-d130-f7406020d7cb.png>


:::note warn
本記事ではクラスタ全体管理のユーザ向けではなく、個別に作成したSVMへのSSH利用時の確認を実施しています
:::


## 設定内容に関して

### 1. 公開鍵認証とパスワード認証の違いを確認

**最初から用意されているスコアリングの為のコンポーネント(要素)は以下の2つ**になります。
最初に**公開鍵認証かパスワード認証によるアクセスで、信頼スコアが変わるか確認**します。
次に**パスワード認証によるアクセス失敗でスコアが下がるかの確認**も行います。

|コンポーネント名  |説明  |
|---|---|
|trusted_device|ONTAP9.15.1は公開鍵利用時に信頼できるデバイス扱い|
|authentication_history_policy|認証の合否の履歴を記録し、認証失敗でスコアが低下|

外部サービスにREST APIで属性による判定を実施したい場合は、コンポーネントの作成及び設定が必要となります。


#### 1-1. SVMに公開鍵認証用とパスワード認証を行うユーザをそれぞれ作成

Dynamic Authorizationを有効にするSVMに対して、公開鍵認証とパスワード認証でSSHログインできるユーザをそれぞれ作成します。

- ssh_user150 ＝＞公開鍵認証
- ssh_user200 ＝＞パスワード認証


```bash:
# 公開鍵認証を行うSSH用ユーザ作成
> security login create -vserver vs_nfs10 -user-or-group-name ssh_user150 -role vsadmin -application ssh -authentication-method publickey

# パスワード認証を行うSSH用ユーザ作成
> security login create -vserver vs_nfs10 -user-or-group-name ssh_user200 -role vsadmin -application ssh -authentication-method password

Please enter a password for user 'ssh_user200':
Please enter it again:
```


#### 1-2. SVM上のユーザへWindowsで作成した公開鍵の紐付け
Windowsクライアントで作成した公開鍵をSVMにおけるSSH用のユーザと紐づけます。
Windowsクライアントによる公開鍵の作成手順については、[過去の記事](https://qiita.com/s_yosh1d/items/7ed8af8d7e8f16ddf040)を参照して頂ければと思います。

```bash:
> security login publickey create -vserver vs_nfs10 -username ssh_user150 -index 1 -publickey "ssh-rsa AAAXXXXXXXXXXXXXH12345X ssh_user01@WIn2019-01"
```

#### 1-3. visibilityモードでDynamic Authorizationを設定
まずは、ログインを実施した後に**信頼性スコアの値について比較だけしたいので、信頼スコアの変化は見えるけれども、操作制限は発生しないvisibilityモードでDynamic Authorizationを設定**してみます。

また、操作制限に抵触する信頼スコアの閾値等については、まずはデフォルト値のままにしておきます。

|パラメータ  |説明  |
|---|---|
|suppression-interval|一度認証に成功すると、再認証が不要な期間で、デフォルトは10分|
|lower-challenge-boundary|指定割合以下の場合には、指定コマンドの実行が不可となり、デフォルトは0|
|upper-challenge-boundary|指定割合以上の場合には再認証なしで指定コマンドが実行可能で、デフォルトは90|

```bash:
# visibilityモードで設定
> security dynamic-authorization modify -state visibility -vserver vs_nfs10


# 設定状態の確認
> security dynamic-authorization show -vserver vs_nfs10

                                   Vserver: vs_nfs10
               Dynamic Authorization State: visibility
Dynamic Authorization Suppression Interval: 10m
              Lower MFA Challenge Boundary: 0%
              Upper MFA Challenge Boundary: 90%
```

#### 1-4. SVMへSSHログインを実行して信頼スコアを確認

SVMのSSH用のIPを確認します。(本記事だと末番191のアドレス)
```bash:
> net int show -vserver vs_nfs10
  (network interface show)
            Logical    Status     Network            Current       Current Is
Vserver     Interface  Admin/Oper Address/Mask       Node          Port    Home
----------- ---------- ---------- ------------------ ------------- ------- ----
vs_nfs10
            data01       up/up    172.16.20.190/24   PS-C250-01    a0a-20  true
            mgm01        up/up    172.16.20.191/24   PS-C250-01    a0a-20  true
2 entries were displayed.
```

まずは、Windowsクライアントから公開鍵認証を使用してログインを行い、信頼スコアを確認します。
**信頼できるデバイスを使用してログインする場合には、信頼性スコアが最大(100)となります。**(公開鍵認証=信頼できるデバイス)

<img width="550" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/1062a406-da0d-c698-aa58-91588d1d492a.png>


**パスワード認証を用いてログインすると、公開鍵認証と比較して信頼スコアが低くなっている事が確認できます。**

<img width="550" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/fd1fd91e-5dbc-aee4-5e47-e85141a9c8ff.png>


### 2. 信頼スコアが閾値以下の際の挙動について

次に**信頼スコアの下限を30に変更**して、パスワード認証において失敗をして**スコアが下がると今まで実施できていた操作が実施できなくなる点について確認**してみます。


#### 2-1. 信頼スコアの下限を変更

```bash:
# 信頼スコアの下限変更
> security dynamic-authorization modify -vserver vs_nfs10 -lower-challenge-boundary 30

> security dynamic-authorization show -vserver vs_nfs10

                                   Vserver: vs_nfs10
               Dynamic Authorization State: visibility
Dynamic Authorization Suppression Interval: 10m
              Lower MFA Challenge Boundary: 30%
              Upper MFA Challenge Boundary: 90%
```
#### 2-2. enforcedモードでDynamic Authorizationを設定
信頼スコア表示だけで無く、指定されたCLI操作を制限したい為、enforcedモードへ変更します。
なお、制限される操作はデフォルトのものをそのまま利用しています。
```bash:
# enforcedモードで設定
> security dynamic-authorization modify -state enforced  -vserver vs_nfs10


# 設定状態の確認
> security dynamic-authorization show -vserver vs_nfs10

> security dynamic-authorization show -vserver vs_nfs10

                                   Vserver: vs_nfs10
               Dynamic Authorization State: enforced
Dynamic Authorization Suppression Interval: 10m
              Lower MFA Challenge Boundary: 30%
              Upper MFA Challenge Boundary: 90%


# 制限ルールの確認
> security dynamic-authorization rule show -vserver vs_nfs10

Vserver: vs_nfs10

Operation                                                              Query
---------------------------------------- -----------------------------------
lun delete
security anti-ransomware volume attack clear-suspect
security anti-ransomware volume disable
security anti-ransomware volume event-log
security anti-ransomware volume pause
security anti-ransomware vserver event-log
security ipsec config
security ipsec policy
security login
security login password
security multi-admin-verify
security multi-admin-verify approval-group
security multi-admin-verify rule
set
snaplock legal-hold end
timezone
volume
volume delete
volume file privileged-delete
volume file retention set
volume flexcache delete
volume recovery-queue
volume recovery-queue purge
volume recovery-queue purge-all
volume snaplock
volume snapshot
volume snapshot autodelete
volume snapshot delete
volume snapshot policy
volume snapshot policy add-schedule
volume snapshot policy create
volume snapshot policy modify-schedule
volume snapshot policy remove-schedule
volume snapshot rename
volume snapshot restore
vserver
vserver audit
vserver audit delete
vserver audit disable
vserver audit rotate-log
vserver object-store-server audit
vserver object-store-server audit delete
vserver object-store-server audit disable
vserver object-store-server audit rotate-log
vserver options
vserver security file-directory apply
vserver security file-directory remove-slag
vserver vscan disable
vserver vscan on-access-policy
vserver vscan on-access-policy disable
vserver vscan scanner-pool
51 entries were displayed.            
```

#### 2-3. SSHログインを成功した状態でVolumeの削除
パスワード誤りを起こさずにSSHログインを行い、Volume削除が成功する事を確認します。
なお、SVM上には以下のVolumeがある状態です。
```bash:
# SVM内のVolume一覧
> vol show -vserver vs_nfs10
Vserver   Volume       Aggregate    State      Type       Size  Available Used%
--------- ------------ ------------ ---------- ---- ---------- ---------- -----
vs_nfs10  vol200       aggr1_node1  online     RW         10GB     9.50GB    0%
vs_nfs10  vol201       aggr1_node1  online     RW         10GB     9.50GB    0%
vs_nfs10  vs_nfs10_root
                       aggr1_node2  online     RW          1GB    970.3MB    0%
3 entries were displayed.
```
**<font color="Red">信頼スコアが50ある状態の為、Volumeのoffline及び削除に成功</font>する事が確認できます。**
<img width="650" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/f4d0783c-8cb5-3073-d8fb-4b874c0b436f.png>


#### 2-4. SSHログインを1度失敗し、信頼スコア下限を下回った状態でVolumeの削除

**<font color="Red">信頼スコアが30以下の為、Volumeのofflineに失敗</font>する事が確認できます。**

<img width="650" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/80246bd8-09f1-7506-6544-f4ab605f82fc.png>


### 3. 信頼/リスクスコアを外部サービスから取得
管理クライアントのIPアドレスの信頼性等によるスコア取得や操作時間によるスコア値の取得について外部の信頼スコアプロバイダーから取得する事が可能です。

[Rapidapi](https://rapidapi.com/hub)等でAPIを探しても良いのですが、時間によるリスク値を変えるものを探せなかったので、本記事では、以下の機能を持つコンテナを作成し、信頼スコアが変化するか確認してみます。

- **コマンド発行時にリスク値を返す**
- **コマンド実行の時間によってリスク値を変える**
- 営業時間(9:00～18:00)の操作はリスク0
- 営業時間外はリスク値を返信

<img width="600" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/061c7f13-55b8-9c5f-52f2-d78fabd227ac.png>


#### 3-1. リスク値を返すサービス(コンテナ)作成

##### 3-1-1. Dockerfileの作成
Dockerイメージを作成する為、Dockerfileの作成を実施します。

```bash:
> cat Dockerfile
# ベースイメージとしてPythonを使用
FROM python:3.9-slim

# 作業ディレクトリを設定
WORKDIR /app

# 必要なパッケージをインストール
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのソースコードをコピー
COPY . .

# Flaskサーバーを起動
CMD ["python", "app.py"]
```
利用するパッケージ一覧をrequirements.txtに記載します。
```bash:
> cat requirements.txt
Flask
pytz
```


##### 3-1-2. リスク値を返すコードをPythonで作成
**[マニュアル](https://docs.netapp.com/ja-jp/ontap/authentication/configure-dynamic-authorization.html#%E4%BF%A1%E9%A0%BC%E3%82%B9%E3%82%B3%E3%82%A2%E3%81%AE%E8%A1%A8%E7%A4%BA)では信頼スコアまたはリスクスコアをJSON形式で返答するように記載**されています。
その為、ONTAPからコマンド実行時に発生した外部サービスへの問い合わせに対し、**現在時刻を確認して営業時間(9:00～18:00)以外はリスク値をJSON形式で返す形にて作成**します。

```bash:
> cat app.py
from flask import Flask, jsonify
from datetime import datetime
import pytz

app = Flask(__name__)

@app.route('/risk', methods=['GET'])
def get_risk():
    # 日本のタイムゾーンを設定
    jst = pytz.timezone('Asia/Tokyo')
    current_time = datetime.now(jst).time()

    # 営業時間外にリスク値を返す
    if current_time >= datetime.strptime('09:00', '%H:%M').time() and current_time < datetime.strptime('18:00', '%H:%M').time():
        risk = 0
    else:
        risk = 50

    return jsonify(risk=risk)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001)
```
3つのファイルが作成されている事を確認します。
```bash:
> ls
app.py  Dockerfile  requirements.txt
```


##### 3-1-3. Dockerイメージのビルドとコンテナの起動
ファイルを作成したディレクトリ上で、Dockerイメージを作成します。


```bash:
> docker build -t risk-api .
[+] Building 10.4s (10/10) FINISHED                                                                     docker:default
 => [internal] load build definition from Dockerfile                                                              0.0s
 => => transferring dockerfile: 422B                                                                              0.0s
 => [internal] load metadata for docker.io/library/python:3.9-slim                                                2.2s
 => [internal] load .dockerignore
(中略)
  => => naming to docker.io/library/risk-api


> docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED              SIZE
risk-api     latest    d7c75f7568a4   About a minute ago   137MB
```
コンテナを起動します。
コンテナ起動後はhttp://LinuxのIP:5001/risk をONTAP上で問い合わせ先として登録します。

```bash:
> docker run -p 5001:5001 risk-api
 * Serving Flask app 'app'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5001
 * Running on http://172.16.0.2:5001
Press CTRL+C to quit
```

コンテナを動かしているLinuix上でcurlコマンドを実行し、営業時間帯はリスク値が0が返ってくる事を確認します。
```bash:
>date
Wed Oct  2 12:31:52 PM JST 2024


> curl -k -v http://localhost:5001/risk
* Host localhost:5001 was resolved.
(中略)
>
< HTTP/1.1 200 OK
< Server: Werkzeug/3.0.4 Python/3.9.20
< Date: Wed, 02 Oct 2024 03:31:57 GMT
< Content-Type: application/json
< Content-Length: 11
< Connection: close
<
{"risk":0}
* Closing connection
```


#### 3-2. ONTAP側でカスタムコンポーネント利用の設定
時間による信頼スコアの変化を確認したいので、パスワード認証のみで確認を実施します。

##### 3-2-1. 下がってしまった信頼スコアのリセット
まずは先程までの信頼スコアを初期状態にリセットします。
(初期状態で時間による信頼スコア値変化を確認したい為)

```bash:
> security dynamic-authorization user-trust-score reset -vserver vs_nfs10 -username ssh_user200 -component all
Warning: Resetting the trust score for trusted_device and custom trust score components is not supported.
```
##### 3-2-2. カスタムコンポーネントの作成
作成したコンテナ信頼スコアを確認できるように、**カスタムコンポーネントの作成を実施し、パスワード認証のみで確認したいので、trusted_device(公開鍵認証)を利用しないようにWeight値を0に変更**します。
**(初期で用意されているコンポーネントの削除は出来ない)**

また、コンテナで作成した外部サービスでは、リスク値を応答する形にしているので、score-typeやscore-fieldをリスク用に指定しておきます。
```bash:
# カスタムコンポーネントの作成
> security dynamic-authorization trust-score-component create -vserver vs_nfs10 -component comp1 -weight 20 -max-score 50 -provider-uri http://172.168.0.2:5001/risk -score-type risk-score -score-field risk


# trusted_deviceのweight値を変更
> security dynamic-authorization trust-score-component modify -vserver vs_nfs10 -component trusted_device -weight 0


> security dynamic-authorization trust-score-component show -vserver vs_nfs10

                                                                Max Percentage
Vserver           Component Name                 Score Weight    Score Weight
---------------   ----------------------------   ------------   --------------

vs_nfs10          authentication_history_policy            20            50.00
vs_nfs10          comp1                                    20            50.00
vs_nfs10          trusted_device                            0             0.00
3 entries were displayed.
```

##### 3-2-3. 時間によって信頼スコアが変化する事の確認

SSHログインしたままでも、コマンド実行時間によって信頼スコアが変化している事が確認できます。

- **営業時間(9:00～18:00)においてコマンド実行した場合**

<img width="600" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/673e29ad-d1e4-0f3c-7a54-5ba0a334a79d.png>


- **営業時間外にコマンド実行した場合**

<img width="600" alt="qiita-square" src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1268577/ad7b8865-f09f-d398-05ac-39a318810476.png>


## 参考及びリンク
[Dynamic authorization overview](https://docs.netapp.com/us-en/ontap/authentication/dynamic-authorization-overview.html)

[ONTAP 9.15 Intro & Overview | NetApp ONAIR](https://www.youtube.com/watch?v=Wl-G685qr1c)

[ONTAP 9.15.1 highlights](https://docs.netapp.com/us-en/ontap/release-notes/#ontap-9-15-1-highlights)