AWS: створення OpenSearch Service cluster та налаштування аутентифікації і авторизації
В попередній частині — 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
Питання кількості шардів на кластер розбирали в попередньому пості, будемо вважати, що у нас планується даних максимум
І для цих двох шардів будемо робити дві 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 —

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)
- Resource-based policies: політики, які описуються безпосередньо в налаштуваннях самого кластеру
- Identity-based policies: якщо Resource-based policies є частиною налаштувань security-політик кластера — то Identity-based policies є звичайними AWS IAM Policies, які додаються конкретному юзеру чи ролі
- Domain Access 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 буде таким:
- AWS API отримує запит від юзера, наприклад es:ESHttpGet
- AWS IAM виконує аутентифікацію — перевіряє ACCESS:SECRET ключі або Session token
- AWS IAM виконує авторизацію:
- перевіряє IAM Policy юзера (Identity-based policy), якщо тут є явний дозвіл — пропускаємо
- перевіряє Domain Access Policy (Resource-based policy) кластеру, якщо тут явний дозвіл — пропускаємо
- запит приходить до самого OpenSearch
- якщо Fine-grained access control не включений — дозволяємо
- якщо є налаштований 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" : [ ]
}
}
Готово.
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів