AWS: створення OpenSearch Service cluster та налаштування аутентифікації і авторизації

Автор |  29/08/2025
 

В попередній частині – AWS: знайомство з OpenSearch Service в ролі vector store – подивились на AWS OpenSearch Service взагалі, трохи розібрались з тим, як в ньому організовані дані, що таке shards та nodes, і які нам власне типи інстансів для data nodes треба.

Наступний крок – створити кластер і подивитись на аутентифікацію, яка, як на мене, в чомусь навіть складніша за AWS EKS. Хоча, можливо, просто діло звички.

Що будемо робити сьогодні – вручну створимо кластер AWS OpenSearch Service, глянемо на основні опції при створенні кластеру, а потім копнемо в налаштування доступу до кластеру і до OpenSearch Dashboards з AWS IAM та Fine-grained access control самого OpenSearch і його Security plugin.

А вже в наступній частині, якщо буде час на написання, то дійдемо до Terraform.

Ручне створення кластера в AWS Console

Робити будемо мінімальний PoC аби погратись, тобто з t3 інстансами і в одній Availability Zone та без Master Nodes.

В Production у нас теж планується один маленький кластер з трьома індексами dev/staging/prod в ролі vector store для AWS Bedrock Knowledge Base.

Документація від AWS – Creating OpenSearch Service domains.

Переходимо в Amazon OpenSearch Service > Domains, клікаємо “Create domain”.

Задаємо ім’я, вибираємо “Standart create”, аби мати доступ до всіх опцій:

В “Templates” вибираємо “Dev/”test – тоді можна буде вибрати конфіг без Master Nodes і можна буде деплоїти в одній Availability Zone.

В “Deployment option(s)” вибираємо “Domain without standby” – тоді нам будуть доступні інстанси t3:

Справа нам зручненько відразу показує весь сетап.

Storage

Питання кількості шардів на кластер розбирали в попередньому пості, будемо вважати, що у нас планується даних максимум 20-30 GiB, тому будемо створювати 1 primary шард та 1 replica. Але шарди налаштовуються пізніше, коли будемо робити індекси з Terraform і opensearch_index_template.

І для цих двох шардів будемо робити дві Data Nodes – одна для primary шарду, одна для репліки.

“Engine options” описані в Features by engine version in Amazon OpenSearch Service, просто залишаємо дефолтне значення, останню версію.

“Instance family” вибираємо “General puprose”, в “Instance type” – t3.small.search.

“EBS storage size per node” візьмемо 50 GiB – 20-30 гігабайт під дані, і трохи запасу для самої операційної системи:

Nodes

“Number of master nodes” та “Dedicated coordinator nodes” залишаємо без змін, тобто без них:

Network

В “Custom endpoint” поки теж нічого не міняємо, але потім тут можна додати який власний домен із Route53 з сертифікатом з AWS Certificate Manager для доступу до кластеру, див. Creating a custom endpoint for Amazon OpenSearch Service.

В “Network” – поки робимо найпростіший варіант, з “Public access”, але для Production будемо робити всередині VPC:

Але треба буде потестити доступ до Dashboards, бо якщо кластер створюється в сабнетах VPC, то до нього не можна застосувати IP-based policies, див. About access policies on VPC domains. Про IP-based policies будемо говорити тут далі.

Access && permissions

“Fine-grained access control” (FGAC) – поки відключаємо, далі детальніше подивимось на цей механізм. Хоча я не впевнений, що він буде потрібен, бо розділити доступ до різних індексів в одному кластері можна і просто з IAM.

SAML, JWT та IAM Identity Center залежать від FGAC, тому теж скіпаємо, і надалі я їх використовувати не планую, не наш кейс.

Cognito теж мимо – ми ним не користуємось (хоча пізніше, можливо, подивлюсь в сторону інтеграції з Auth0 чи Cognito для Dashboards):

“Access policy” можна порівняти з S3 Access Policy, або з IAM Policy для EKS яка дозволяє IAM-юзеру доступ до кластеру.

Детальніше поговоримо в частині про аутентифікацію, поки просто залишаємо дефолтний “Do not set domain level access policy”:

“Off-peak window” – час найменшого навантаження для встановлення апдейтів і виконання Auto-tune операцій.

У нас off-peak буде вночі по США, тому в Production тут буде Central Time (CT) 05:00 UTC.

Але так як зараз тестовий PoC – то теж скіпаємо.

Auto-Tune власне теж нормально описана, і недоступна для наших інстансів t3.

Automatic software update – корисна штука для Production, і буде виконуватись в час, заданий в Off-peak window:

В “Advanced cluster settings” можна відключити rest.action.multi.allow_explicit_index, але не знаю, як у нас будуть будуватись запити, і начебто десь зустрічав, що може поламати Dashboard – тому нехай залишиться дефолтне enabled:

Ну і все, в результаті маємо такий сетап:

Клікаємо “Create”, і йдемо пити чай, бо створюється кластер довго – довше, ніж EKS, і створення OpenSearch зайняло хвилин 20.

Аутентифікація та авторизація

Тепер, мабуть, саме цікаве – про юзерів і доступи.

Після створення кластера по дефолту ми маємо обмежені права доступу до самого OpenSearch API:

Бо в “Security Configuration” у нас є явний Deny:

Доступ до AWS OpenSearch Service має три таких собі “рівня” – мережа, IAM, та Security Plugin самого OpenSearch.

При цьому в IAM у нас є дві сутності – Domain Access Policy, який ми бачимо в Security Configuration > Access Policy (атрибут access_policies в Terraform), та Identity-based policies – які є звичайними AWS IAM Policies.

Якщо говорити про ці рівні більш детально, то вони виглядають якось так:

  • мережа: параметр Network > VPC access або Public access: задаємо ліміт доступу на рівні мережі (див. Launching your Amazon OpenSearch Service domains within a VPC)
    • або, якщо брати аналогію з EKS – То це Public та Private API endpoint, або з RDS – створювати інстанс в публічних чи приватних сабнетах
  • AWS IAM:
    • Domain Access Policies:
      • Resource-based policies: політики, які описуються безпосередньо в налаштуваннях самого кластеру
        • доступ задається для IAM Role, IAM User, AWS Accounts  до конкретного OpenSearch domain
      • IP-based policies: фактично ті самі Resource-based policies, але з можливістю дозволити доступ без аутентифікації для конкретних IP (тільки якщо тип доступу Public, див. VPC versus public domains)
    • Identity-based policies: якщо Resource-based policies є частиною налаштувань security-політик кластера – то Identity-based policies є звичайними AWS IAM Policies, які додаються конкретному юзеру чи ролі
  • Fine-grained access control (FGAC): Security Plugin самого OpenSearch – атрибут advanced_security_options в Terraform
    • якщо в Resource-based policies і Identity-based policies ми задаємо правила на рівні кластеру (домену) і індексів, то в FGAC можна додатково описати обмеження на конкретні документи або поля
    • і навіть якщо в Resource-based policies і Identity-based policies дозволено доступ до ресурсу в кластері – через Fine-grained access control його можна “обрізати”

Тобто authentification та  authorization flow буде таким:

  1. AWS API отримує запит від юзера, наприклад es:ESHttpGet
    1. AWS IAM виконує аутентифікацію – перевіряє ACCESS:SECRET ключі або Session token
    2. AWS IAM виконує авторизацію:
      • перевіряє IAM Policy юзера (Identity-based policy), якщо тут є явний дозвіл – пропускаємо
      • перевіряє Domain Access Policy (Resource-based policy) кластеру, якщо тут явний дозвіл – пропускаємо
  2. запит приходить до самого OpenSearch
    1. якщо Fine-grained access control не включений – дозволяємо
    2. якщо є налаштований Fine-grained access control – перевіряємо внутрішні ролі, і якщо юзеру дозволено – то виконуємо запит

Давайте робити доступи, подивимось, як воно все працює.

Налаштування Domain Access policy

Базовий варіант – додати IAM User доступ до кластеру.

Resource-based policy

Редагуємо “Access policy”, і вказуємо свого юзера, типи API-операцій, та домен:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::492***148:user/arseny.zinchenko"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*"
    }
  ]
}

Чекаємо хвилину – і тепер маємо доступ до OpenSearch API (бо Cluster health в AWS Console отримується саме з OpenSearch – див. Cluster Health API):

І тепер можемо з curl та --aws-sigv4 отримати доступ до кластеру (див. Authenticating Requests (AWS Signature Version 4)):

$ curl --aws-sigv4 "aws:amz:us-east-1:es" \
>  --user "AKI***B7A:pAu***2gW" \
> https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty
{
  "cluster_name" : "492***148:test",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
  "number_of_data_nodes" : 2,
  "discovered_master" : true,
  "discovered_cluster_manager" : true,
  "active_primary_shards" : 5,
  "active_shards" : 10,
  "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
}

IP-based policies та доступ до OpenSearch Dashboards

Аналогічно, через Domain Access Policy можемо відкрити доступ до Dashboards – самий простий варіант, але працює тільки з Public domains. Якщо кластер буде в VPC – то треба буде робити додаткову аутентифікацію, див. Controlling access to Dashboards.

Редагуємо політику, додаємо умову IpAddress.aws:SourceIp:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::492***148:user/arseny.zinchenko"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:ESHttp*",
      "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": "178.***.***.184"
        }
      }
    }
  ]
}

І тепер маємо доступ до дашборди:

Identity-based policy

Тепер другий варіант – створимо окремого IAM User і йому підключити окрему IAM Policy.

В AWS IAM додаємо юзера:

Можемо взяти AWS managed policies for Amazon OpenSearch Service:

Далі просто створюємо ключі доступу для Command Line Interface (CLI), і – нічого не змінюючи в Access policy самого кластеру – перевіряємо доступ:

$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty
{
  "cluster_name" : "492***148:test",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
  "number_of_data_nodes" : 2,
  "discovered_master" : true,
  "discovered_cluster_manager" : true,
  "active_primary_shards" : 5,
  "active_shards" : 10,
  "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
}

Тобто тепер у нас є Domain Acces Policy – яка дозволяє доступ конкретно моєму юзеру, і є окрема IAM Ploicy – Identity-based policy – яка дозволяє доступ тестовому юзеру.

Але тут є один важливий момент: в IAM Policy ми вказуємо або весь домен – або тільки його subresources.

Тобто, якщо замість політики AmazonOpenSearchServiceFullAccess ми створимо власну полісі, в якій вкажемо "Resource":***:domain/test/*":

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "es:*"
            ],
            "Resource": "arn:aws:es:us-east-1:492***148:domain/test/*"
        }
    ]
}

То ми зможемо виконати es:ESHttpGet (GET _cluster/health) – але не зможемо виконати cluster-level операції, наприклад – es:AddTags, навіть при тому, що в Actions IAM-політики маємо дозвіл на всі виклики – es:*:

 $ aws --profile test-os opensearch add-tags --arn arn:aws:es:us-east-1:492***148:domain/test --tag-list Key=environment,Value=test

An error occurred (AccessDeniedException) when calling the AddTags operation: User: arn:aws:iam::492***148:user/test-opesearch-identity-based-policy is not authorized to perform: es:AddTags on resource: arn:aws:es:us-east-1:492***148:domain/test because no identity-based policy allows the es:AddTags action

Якщо ж ми хочемо дозволити взагалі всі операції з кластером – то "Resource" задаємо як "arn:aws:es:us-east-1:492***148:domain/test", і тоді можемо додати теги.

Всі API actions див. в Actions, resources, and condition keys for Amazon OpenSearch Service.

Fine-grained access control

Документація – Fine-grained access control in Amazon OpenSearch Service.

Основна ідея дуже схожа з Kubernetes RBAC.

В OpenSearch маємо три основних концепти:

  • users – як Kubernetes Users та ServiceAccounts
  • roles  – як Kubernetes RBAC Roles
  • mappings – як  Kubernetes Role Bindings

Юзери можуть бути як з AWS IAM, так і з внутрішньої бази OpenSearch.

Як і в Kubernetes, в OpenSearch є набір дефолтних ролей – див. Predefined roles.

При цьому ролі, як і в Kubernetes, можуть бути cluster-wide або index-specific – аналог ClusterRoleBinding та просто namespaced RoleBinding в Kubernetes, плюс в OpenSearch FGAC можна додатково мати document level або field level permissions.

Налаштування Fine-grained access control

Важливий момент: після включення FGAC не можна буде повернутись на стару схему. Але всі доступи з IAM залишаться, навіть якщо переключитись на internal database.

Редагуємо “Security configuration”, вмикаємо “Fine-grained access control”:

Спершу тут нам треба задати Master user, якого можна вказати з IAM – або створити локально в OpenSearch.

Якщо ми створюємо юзера через опцію “Create master user” – то вказуємо звичайний логін:пароль, і в такому випадку OpenSearch підключить internal user database (internal_user_database_enabled  в Terraform).

Якщо використовуємо внутрішню базу OpenSearch – то можемо мати звичайних юзерів і виконувати HTTP basic authentication, див. документацію AWS – Tutorial: Configure a domain with the internal user database and HTTP basic authentication та Defining users and roles в документації самого OpenSearch, бо це вже його внутрішні механізми.

Має сенс, якщо не хочеться крутити Cognito чи SAML, і якщо налаштування юзерів у кожного кластеру будуть власні.

Якщо задавати IAM-юзера, то схема буде схожою з AIM аутентифікацією для RDS і IAM database authentication – доступ до кластеру контролюється AWS IAM, але внутрішні першмішени до схем та баз – ролями PostgreSQL чи MariaDB, див. AWS: RDS з IAM database authentication, EKS Pod Identities та Terraform.

Тобто в такому випадку AWS IAM буде виконувати виключно аутентифікацію юзера, а авторизація (перевірка прав доступу) вже через Security plugin та ролі самого OpenSearch.

Спробуємо локальну базу, і, думаю, в Production ми теж візьмемо цю схему:

“Access Policy” можемо залишити як є:

Переключення на internal database займе час, бо викличе blue/green deployment нового кластеру – див. Making configuration changes in Amazon OpenSearch Service.

І зайняло це прям багато часу – більше години, при тому, що в кластері нема ніяких наших даних.

Після того як зміни застосовані – в Dashboards у нас тепер буде просити логін і пароль, використовуємо нашого Master user:

Master user отримує дві підключені ролі – all_access та security_manager.

І саме security_manager дає доступ до розділу Security та Users в дашборді:

При цьому у нас залишається доступ наших AIM-юзерів, і ми можемо далі використовувати curl: IAM users будуть мапитись на роль default_role, яка дозволяє виконувати GET/PUT на всі індекси – див. About the default_role:

Перевіряємо доступ нашого тестового юзера зараз:

$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty
{
  "cluster_name" : "492***148:test",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 2,
...

А тепер поріжемо доступи всім IAM-юзерам.

Створення OpenSearch Role

Аби подивитись, як воно працює – додамо тестовий індекс і замапимо нашого тестового юзера з доступом до цього індексу.

Додаємо індекс:

Переходимо в Securty > Roles, додаємо роль:

Задаємо Index permissions – повний доступ на індекс (crud):

Далі в цій ролі переходимо до Mapped users > Map users:

І додаємо ARN нашого тестового юзера:

Видаляємо дефолтну роль:

Тепер наш юзер не має доступ до GET _cluster/health – тут отримуємо помилку 403, no permissions:

$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/_cluster/health?pretty
{
  "error" : {
    ...
    "type" : "security_exception",
    "reason" : "no permissions for [cluster:monitor/health] and User [name=arn:aws:iam::492***148:user/test-opesearch-identity-based-policy, backend_roles=[], requestedTenant=null]"
  },
  "status" : 403
}

Але має доступ до тестового індексу:

$ curl --aws-sigv4 "aws:amz:us-east-1:es" --user "AKI***YUK:fXV***34I" https://search-test-***.us-east-1.es.amazonaws.com/test-allowed-index/_search?pretty   -d '{
    "query": {
      "match_all": {}
    }
  }' -H 'Content-Type: application/json'
{
  "took" : 78,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

Готово.