michimani.net

Terraform と OpenSearch CLI で OpenSearch Service に入門する

2022-07-04

Terraform とOpenSearch CLI で OpenSearch Service に入門してみたので、そのメモです。

目次

やること

とりあえず試したい

以降は↑の内容を順番に書いていくだけなので、もし実際に試したいという方は下記リポジトリを clone して試してみてください。 terraform/README.md を読んでもらえれば諸々試せます。

michimani/get-started-opensearch: Hello OpenSearch.

Terraform で OpenSearch Service のドメインを定義する

下記ドキュメントを参考に、 OpenSearch Service のドメインを定義します。

aws_opensearch_domain | Resources | hashicorp/aws | Terraform Registry

ドメイン

最小構成だとこんな感じになります。

resource "aws_opensearch_domain" "first_opensearch" {
  domain_name    = "first-opensearch"
  engine_version = "OpenSearch_1.2"

  cluster_config {
    instance_type = "t3.small.search"
  }

  ebs_options {
    ebs_enabled = true
    volume_size = 10
  }
}

OpenSearch Service で指定できるエンジンは aws opensearch list-versions で確認できます。2022/07/04 現在だと下記のエンジンが指定可能です。

{
  "Versions": [
    "OpenSearch_1.2",
    "OpenSearch_1.1",
    "OpenSearch_1.0",
    "Elasticsearch_7.10",
    "Elasticsearch_7.9",
    "Elasticsearch_7.8",
    "Elasticsearch_7.7",
    "Elasticsearch_7.4",
    "Elasticsearch_7.1",
    "Elasticsearch_6.8",
    "Elasticsearch_6.7",
    "Elasticsearch_6.5",
    "Elasticsearch_6.4",
    "Elasticsearch_6.3",
    "Elasticsearch_6.2",
    "Elasticsearch_6.0",
    "Elasticsearch_5.6",
    "Elasticsearch_5.5",
    "Elasticsearch_5.3",
    "Elasticsearch_5.1",
    "Elasticsearch_2.3",
    "Elasticsearch_1.5"
  ]
}

ドメインポリシー

今回は、構築する OpenSearch クラスターに対して Read (GET アクセス) のみ可能な IAM ロールと Write (POST, PUT, DELETE アクセス) も可能な IAM ロールを作成し、それらによるアクセス制御を行うためのドメインポリシーを設定します。 Terraform での定義は下記のようになります。

resource "aws_opensearch_domain_policy" "first_domain_policy" {
  domain_name = aws_opensearch_domain.first_opensearch.domain_name

  access_policies = <<POLICIES
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AccessByAdministrator",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/demo/opensearch-admin"
        ]
      },
      "Action": [
        "es:*"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "${var.my_ip}"
          ]
        }
      },
      "Resource": "arn:aws:es:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:domain/first-opensearch/*"
    },
    {
      "Sid": "AccessByReadOnlyUser",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/demo/opensearch-read-only"
        ]
      },
      "Action": [
        "es:ESHttpGet"
      ],
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "${var.my_ip}"
          ]
        }
      },
      "Resource": "arn:aws:es:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:domain/first-opensearch/*"
    }
  ]
}
POLICIES
}

opensearch-admin というロールには es:* ですべてのアクションを許可し、 opensearch-read-only ロールには es:ESHttpGetGET アクセスのみ許可します。また、今回は Condition で特定の IP からのアクセスのみ許可するようにしています。

IAM ロールの方の定義はこちら。

resource "aws_iam_role" "opensearch_ro_role" {
  name = "opensearch-read-only"
  path = "/demo/"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "${var.iam_user_arn}"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role" "opensearch_admin_role" {
  name = "opensearch-admin"
  path = "/demo/"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "${var.iam_user_arn}"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

特定の IAM ユーザーからスイッチロールして使うことを想定しています。 ${var.iam_user_arn} には、実際にこれらのロールにスイッチする IAM ユーザーの ARN を variable で指定します。

variables.tf を作成して apply

variables.tf は下記のような形で作成しておきます。

variable "my_ip" {
  default = "203.0.113.0" // Your machine's global IP address.
}

variable "iam_user_arn" {
  default = "arn:aws:iam::000000000000:user/hoge" // ARN of the IAM User for which AWS CLI credentials have been set up.
}

自身の IP アドレスの確認には、

curl -X GET 'https://checkip.amazonaws.com/'

がシンプルなのでおすすめです。

あとは terraform apply でリソースを作成します。初回の apply には 15 分程度かかります。

OpenSearch CLI でリクエストを投げる

OpenSearch のクラスタが構築できたら、 OpenSearch CLI を使ってリクエストを投げてみます。

OpenSearch CLI のインストール

下記ページからダウンロードします。

Opensearch 2.0.1 · OpenSearch

macOS の場合はインストーラ (pkg) 形式なので、ダウンロードしてインストーラの指示に従ってインストールします。

プロファイルの設定

OpenSearch CLI はいくつかプロファイルを作成して使い分ける事ができます。 AWS CLI でいう --profile オプションみたいなのがあります。
プロファイルの作成には OpenSearch クラスタの URL と、認証方法を指定する必要があります。

まず、 OpenSearch クラスタの URL を取得します。

今回は first-opensearch という名前でドメインを作成したので、下記の AWS CLI コマンドで URL (エンドポイント) を取得します。

ENDPOINT=$(
  aws opensearch describe-domain \
  --domain-name 'first-opensearch' \
  --query 'DomainStatus.join(``,[`https://`,Endpoint])' \
  --output text
) && echo "${ENDPOINT}"

続いて認証方法についてですが、 OpenSearch CLI では下記の 3 の認証方法が指定できます。

今回は、 IAM ロールでの認証を行うので aws-iam を認証方法として指定します。

プロファイルの作成には下記コマンドを実行します。

opensearch-cli profile create \
--auth-type aws-iam \
--endpoint "${ENDPOINT}" \
--name env-role

aws-iam を指定した場合、認証に使用する AWS のプロファイルと AWS のサービス名を聞かれます。

プロファイルに関しては特定のプロファイルを指定する事もできますが、指定しない場合は実行環境の環境変数をもとに認証に使用するプロファイルが選択されます。今回は環境変数によって使用するプロファイルを使い分けることにします。

AWS のサービス名は、 es または ec2 で指定します。今回は es を指定します。 (OpenSearch Service ですが es です)

Read Only なロールでリクエストを投げてみる

まずは Read Only なロール opensearch-read-only にスイッチしてリクエストを投げてみます。

スイッチロールするには下記のコマンドを実行します。

SWITCH_CMD=$( \
  aws sts assume-role \
  --role-arn $(aws iam list-roles \
  --path-prefix '/demo' \
  --query 'Roles[?contains(to_string(RoleName), `opensearch-read-only`)].Arn' \
  --output text) \
  --role-session-name 'opensearch-read-only' \
  --query 'Credentials.join(``,[`export AWS_ACCESS_KEY_ID=\"`,AccessKeyId,`\" AWS_SECRET_ACCESS_KEY=\"`,SecretAccessKey,`\" AWS_SESSION_TOKEN=\"`,SessionToken,`\"`])' \
  --output text) \
&& eval ${SWITCH_CMD} \
&& aws sts get-caller-identity

ちなみに、このコマンドを AWS CLI のエイリアスとしていい感じに設定しておくことで、 AWS CLI でのスイッチロールがめちゃくちゃ簡単になるよ というのを別記事で書いているので参考にしてみてください。

AWS CLI のエイリアスを使ってメチャクチャ簡単にスイッチロールできるようにしてみた - michimani.net

opensearch-read-only ロールにスイッチできたら、 GET のリクエストを投げてみます。

$ opensearch-cli curl get \
--path '_cluster/health' \
--output-format yaml \
--profile env-role

---
cluster_name: "000000000000:first-opensearch"
status: "green"
timed_out: false
number_of_nodes: 1
number_of_data_nodes: 1
discovered_master: true
active_primary_shards: 1
active_shards: 1
relocating_shards: 0
initializing_shards: 0
unassigned_shards: 0
delayed_unassigned_shards: 0
number_of_pending_tasks: 0
number_of_in_flight_fetch: 0
task_max_waiting_in_queue_millis: 0
active_shards_percent_as_number: 100.0

続いて PUT リクエストを投げてみます。

$ opensearch-cli curl put \
--path 'demoindex' \
--profile env-role

{
  "Message": "User: arn:aws:sts::000000000000:assumed-role/opensearch-read-only/opensearch-read-only is not authorized to perform: es:ESHttpPut because no identity-based policy allows the es:ESHttpPut action"
}

こちらはエラーになりました。

ここまで終わったらスイッチロールを解除するために下記コマンドを実行します。

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

Write も許可されてたロールでリクエストを投げてみる

次に Write も許可されたロール opensearch-admin にスイッチしてリクエストを投げてみます。

opensearch-admin にスイッチするには下記コマンドを実行します。

SWITCH_CMD=$( \
  aws sts assume-role \
  --role-arn $(aws iam list-roles \
  --path-prefix '/demo' \
  --query 'Roles[?contains(to_string(RoleName), `opensearch-admin`)].Arn' \
  --output text) \
  --role-session-name 'opensearch-admin' \
  --query 'Credentials.join(``,[`export AWS_ACCESS_KEY_ID=\"`,AccessKeyId,`\" AWS_SECRET_ACCESS_KEY=\"`,SecretAccessKey,`\" AWS_SESSION_TOKEN=\"`,SessionToken,`\"`])' \
  --output text) \
&& eval ${SWITCH_CMD} \
&& aws sts get-caller-identity

Read Only のときに失敗した PUT リクエストを投げてみます。

$ opensearch-cli curl put \
--path 'demoindex' \
--profile env-role

{"acknowledged":true,"shards_acknowledged":true,"index":"demoindex"}

今度は成功しました。続いて DELETE リクエストも投げてみます。

$ opensearch-cli curl delete \
--path 'demoindex' \
--profile env-role

{"acknowledged":true}

こちらも成功しました。

ここまで終わったら、忘れずにスイッチロールを解除しておきます。

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

まとめ


comments powered by Disqus