Apache Solr にBasic認証プラグインを利用して認証を追加する方法

# はじめに
代表的なオープンソース全文検索エンジンであるSolrですが、
初期構築されたSolrの管理画面やAPIでは、認証を求められることはありません。
この状態では、誰でもアクセスできてしまうため、ネットワーク環境によっては、セキュリティ上の問題が生じます。
この問題への対応として、Solrの認証プラグインを利用した認証の追加を紹介します。
なお、今回の説明では、Basic認証の追加について説明します。

# Solr Basic認証プラグインに関するドキュメントについて
Solr Basic認証プラグインに関するドキュメントは、「 [Apache Solr Reference Guide](https://solr.apache.org/guide/solr/latest/index.html "Apache Solr Reference Guide") 」の下記のページに記載されています。

* バージョン9以降
https\://solr.apache.org/guide/solr/[バージョン]/deployment-guide/basic-authentication-plugin.html
　　
  * バージョン9.3の場合
https://solr.apache.org/guide/solr/9_3/deployment-guide/basic-authentication-plugin.html
　　
* バージョン8以前
https\://solr.apache.org/guide/[バージョン]/basic-authentication-plugin.html
　　
  * バージョン8.11の場合
https://solr.apache.org/guide/8_11/basic-authentication-plugin.html


# Basic認証追加手順
Basic認証を追加するには、認証情報が記載された「security.json」ファイルを作成し、Solrに反映する必要があります。

:::note warn
注意事項
本手順では、「Solr 8.11」の利用を前提に説明を進めて行きます。
:::


## 「security.json」ファイル作成
「[Solr Basic認証プラグインに関するドキュメントについて](#solr-basic認証プラグインに関するドキュメントについて)」にて記載したページ内の「Enable Basic Authentication」項に、「security.json」ファイルのサンプルが記載されているので、そのサンプルを元に「security.json」ファイルを作成します。

サンプルを何も編集せずにSolrに反映した場合のデフォルトの認証情報は、下記表に記載した通りとなります。
「ユーザーID」と「パスワード」を指定して反映する詳しい方法については、「[パスワードのハッシュ値の生成方法](#パスワードのハッシュ値の生成方法)」を参照してください。

* サンプルのユーザー情報

|ユーザー名|パスワード|
|:---:|:---:|
|solr|SolrRocks|

* サンプルの内容
```json-doc:security.json サンプル
{
  "authentication": {								// A.
    "blockUnknown": true,							// A-1.
    "class": "solr.BasicAuthPlugin",				// A-2.
    "credentials": {
      "solr": "IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0= Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c="
    },												// A-3. 
    "realm": "My Solr users",						// A-4. 
    "forwardCredentials": false						// A-5. 
  },
  "authorization": {								// B. 
    "class": "solr.RuleBasedAuthorizationPlugin",	// B-1. 
    "permissions": [
      {
        "name": "security-edit",
        "role": "admin"
      }
    ],												// B-2.
    "user-role": {
      "solr": "admin"
    }												// B-3.
  }
}
```

* 各項目の設定内容

|項番|説明|設定値|
|:---:|:---|:---|
| **A.** | **認証プラグインの定義ブロック** | **－** |
|A-1.|　認証のないアクセスを拒否するか許可するかを設定。<br>　　・拒否する場合：true<br>　　・許可する場合：false|true固定|
|A-2.|　認証で使用するプラグインクラス名を設定。|"solr.BasicAuthPlugin"固定（Basic認証）|
|A-3.|　ユーザー情報を設定。|"ユーザーID"："パスワード（ハッシュ値）"<br>※ハッシュ値の生成方法については、「[パスワードのハッシュ値の生成方法](#パスワードのハッシュ値の生成方法)」を参照。|
|A-4.|　ログイン画面に表示される認証の名称を設定|任意の文言|
|A-5.|　SolrのPKI 認証が、Basic認証ヘッダーを転送する代わりに分散リクエストを処理するか否かの設定（用途不明）。|「false」|
| **B.** | **認可プラグインの定義ブロック** | **－** |
|B-1.|　認証で使用するプラグインクラス名を設定。|"solr.RuleBasedAuthorizationPlugin"固定（ルールベース認可方式）|
|B-2.|　ロール毎の権限を設定。|"権限名"："ロール名"<br>※権限の詳細については、「 [Apache Solr Reference Guide](https://solr.apache.org/guide/solr/latest/index.html "Apache Solr Reference Guide") 」の「ルールベースの認可プラグイン」ページを参照。<br><br>・バージョン9以降<br>https\://solr.apache.org/guide/solr/[バージョン]/deployment-guide/rule-based-authorization-plugin.html<br><br>・バージョン8以前<br>https\://solr.apache.org/guide/[バージョン]/rule-based-authorization-plugin.html<br>|
|B-3.|　ユーザーに付与するロールを設定。|"ユーザーID"："ロール名"|



## 「security.json」ファイル配置と反映方法
「security.json」ファイルの配置先と反映方法は、Solrの稼働モードによって異なります。

* Standalone モードの場合
「$SOLR_HOME」ディレクトリに配置し、サービスを再起動します。
　　
* Cloud　モードの場合
ZooKeeperの「solr」ホームディレクトリにアップロードします。「Solr 8.11」ではサービスを再起動は不要です。
（※Solrのバージョンによっては、再起動が必要となる可能性があります。）

```shell-session:ZooKeeperへの配置コマンド例
$ zkcli.sh -zkhost xxx.xxx.xxx.xxx:2181 -cmd putfile /solr/security.json security.json
```

## APIによる認証・認可情報の変更
認証・認可情報を変更する方法は、再度、『「security.json」ファイルを再反映する方法』と、『APIを利用して変更する方法』があります。
ここでは、サンプルの「security.json」ファイルが反映されたことを前提として、APIによる認証・認可情報の変更方法を説明します。

### ユーザー追加・更新
ユーザーの追加と更新を行う場合は、同じ構文のコマンドを実行します。
「パスワード」は平文の入力で問題ありません。
```shell-session:ユーザー追加・更新
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authentication" 
-H "Content-type:application/json" 
-d "{\"set-user\": {\"「ユーザー名」\":\"「パスワード」\"}}"
```

### ユーザー削除
ユーザーの削除を行う場合は、下記コマンドを実行します。
```shell-session:ユーザー削除
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authentication" 
-H "Content-type:application/json" 
-d "{\"delete-user\": [\"「ユーザー名」\"]}"
```

### プロパティ更新
「realm」、「blockUnknown」、「forwardCredentials」などの項目を更新コマンドとなります。
ここでは、「blockUnknown」と「realm」の更新例を記載します。

```shell-session:「blockUnknown」更新
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authentication" 
-H "Content-type:application/json" 
-d "{\"set-property\": {\"blockUnknown\":false}}"
```

```shell-session:「realm」更新
 $ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authentication" 
-H "Content-type:application/json" 
-d "{\"set-property\": {\"realm\":\"「ログイン画面に表示される認証の名称」\"}}"
```

### 権限追加
権限追加を行う場合は、下記コマンドを実行します。
```shell-session:権限追加
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authorization" 
-H "Content-type:application/json" 
-d "{
    \"set-permission\": {\"name\": \"update\", \"role\":\"dev\"}, 
    \"set-permission\": {\"name\": \"read\", \"role\":\"guest\"}
}"
```

### 権限更新
権限を更新する場合は、事前に更新する権限の「表示順（index）」を確認して、「表示順（index）」を指定して更新します。
```shell-session:権限更新
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authorization" 
-H "Content-type:application/json" 
-d "{
    \"update-permission\": {\"index\": 3, \"role\": [\"admin\", \"dev\"]}
}"
```

### 権限削除
権限を削除する場合は、事前に削除する権限の「表示順（index）」を確認して、「表示順（index）」を指定して更新します。
```shell-session:権限削除
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authorization" 
-H "Content-type:application/json" 
-d "{
    \"delete-permission\": 3
}"
```

### ユーザーへのロール付与
ユーザーへのロール付与を行う場合は、下記コマンドを実行します。
```shell-session:ユーザーへのロール付与
$ curl -u solr:SolrRocks "http://localhost:8983/solr/admin/authorization" 
-H "Content-type:application/json" 
-d "{
    "set-user-role" : {\"solr\": [\"admin\",\"dev\"], \"testuser\": null}
}"
```

# パスワードのハッシュ値の生成方法
上記の「ユーザー追加・更新」コマンドを利用することで、ユーザー追加とパスワードの設定は可能ですが、「security.json」ファイルに直接ユーザーパスワードを設定することも可能です。
この場合は、パスワードを「SHA-256 + salt」で変換し、さらにBase64エンコードする必要があり、手動で作成することは非常に困難です。
そこで、パスワードのハッシュ値の生成する簡単なJavaプログラムを以下で紹介します。

:::note warn
注意事項
本プログラムの対象は、「Solr 9.4」以前、「Solr 8.11」以前となります。最新バージョンのSolrでは対応できない可能性があります。
:::

## パスワードハッシュ値生成用プログラム
```java:CreateSolrBasicPasswordHash.java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class CreateSolrBasicPasswordHash{
   public static void main(String[] args){

        if(args.length != 1 ) {
            System.out.println("Argument Error!!");
            System.exit(-1);
        }

        // ① 引数からパスワード取得
        String password = args[0];
     
        // ② 32byteのハッシュ化用ソルト(salt)値をランダム生成
        byte[] salt = new byte[32];
        new SecureRandom().nextBytes(salt);

        // ③ ハッシュ化用ソルト(salt)をBase64エンコード
        String saltBase64 = Base64.getEncoder().encodeToString(salt);

        // ④ パスワードをハッシュ化用ソルト(salt)を使用してSHA-256ハッシュ値を生成
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            System.out.println("NoSuchAlgorithmException!!");
            System.exit(-1);
        }
        digest.reset();
        digest.update(salt);
        byte[] passHash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
        digest.reset();
        passHash = digest.digest(passHash);

        // ⑤ 上記④のハッシュ値をBase64エンコード
        String passHashBase64 = Base64.getEncoder().encodeToString(passHash);
     
        // ⑥ security.jsonに設定する「上記⑤値 + " "（半角スペース） + 上記③値」を表示
        System.out.println(passHashBase64 + " " + saltBase64);

        System.exit(0);
    }
}
```

### コンパイルコマンド
```shell-session:コンパイル
$ javac CreateSolrBasicPasswordHash.java
```

### 実行コマンド
実行時には、引き数に設定したいパスワードを入力して実行します。
表示された文字列（スペースも含める）が「パスワードのハッシュ値」となります。
```shell-session:実行
$ java CreateSolrBasicPasswordHash P@ssw0rd
clN0uds+UvYCuViZ0sgkN0zNjP3FtgMuWJQ630UaReY= MBEatfApquA0DX7HxQUWREVgJTnEUoWTL/YQC7//NGw=
```

### 実行コマンド
「credentials」部分に、任意の「ユーザーID」と生成した「パスワードのハッシュ値」を設定、必要に応じてロールを設定してSolrに反映します。

```json:security.json
{
  "authentication": {
    "blockUnknown": true,
    "class": "solr.BasicAuthPlugin",
    "credentials": {
      "testAdmin": "clN0uds+UvYCuViZ0sgkN0zNjP3FtgMuWJQ630UaReY= MBEatfApquA0DX7HxQUWREVgJTnEUoWTL/YQC7//NGw="
    },
    "realm": "My Solr users",
    "forwardCredentials": false
  },
  "authorization": {
    "class": "solr.RuleBasedAuthorizationPlugin",
    "permissions": [
      {
        "name": "security-edit",
        "role": "admin"
      }
    ],
    "user-role": {
      "testAdmin": "admin"
    }
  }
}
```

# おわりに
以上が、Apache Solrに対してBasic認証を設定する方法となります。
環境によっては、認証機能は不要となる場合もあるとは思いますが、「 [Apache Solr Reference Guide](https://solr.apache.org/guide/solr/latest/index.html "Apache Solr Reference Guide") 」の「Configsets API」章（バージョン [9.4](https://solr.apache.org/guide/solr/9_4/configuration-guide/configsets-api.html) 、バージョン [8.11](https://solr.apache.org/guide/8_11/configsets-api.html) ）には、Configsets APIを利用して設定ファイルをアップロードする際には、認証が有効になっていないと機能が制限されるとの記載もありますので、認証の設定方法を把握しておくことに越したことはなさそうです。