Чи може ваша платформа реалізовувати політики? Прискорте команди завдяки функціональності платформних політик L7

Чи є політика вашою основною компетенцією? Ймовірно, ні, але важливо зробити все правильно. Зробіть це один раз з Istio та OPA і поверніть команді фокус на те, що має найбільше значення.

Oct 14, 2024 | Від Antonio Berben - Solo.io, Charlie Egan - Styra

Спільні обчислювальні платформи надають ресурси та функціональність для команд орендарів, щоб їм не доводилося створювати все з нуля. Хоча часом буває важко збалансувати всі запити від орендарів, важливо, щоб платформа ставила питання: яку найціннішу функцію ми можемо запропонувати нашим орендарям?

Часто роботу доручають безпосередньо командам що створюють застосунки, але деякі функції найкраще реалізувати один раз і надавати їх як сервіс для всіх команд. Однією з таких функцій, яку може запропонувати більшість команд, що опікуються платформами, є надання стандартної, гнучкої системи політики авторизації для рівня застосунків L7. Політика як код дозволяє командам переносити рішення щодо авторизації з рівня застосунків у легку та ефективну розподілену систему. Це може здатися складним завданням, але з правильними інструментами воно не обов’язково є таким.

Ми розглянемо, як Istio та Open Policy Agent (OPA) можуть використовуватися для забезпечення політик рівня L7 у вашій платформі. Ми покажемо, як почати з простого прикладу. Ви побачите, як ця комбінація є надійним варіантом для швидкого і прозорого надання політик командам розробки застосунків у бізнесі, а також забезпечує дані, необхідні командам безпеки для аудиту та дотримання стандартів.

Спробуйте самі

Коли OPA інтегровано з Istio, він може використовуватися для забезпечення детальних політик контролю доступу для мікросервісів. У цьому посібнику описано, як забезпечити політики контролю доступу для простого мікросервісного застосунку.

Попередні вимоги

Встановіть Istio і налаштуйте параметри mesh, щоб увімкнути OPA:

$ istioctl install -y -f - <<'EOF'
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: meshConfig: accessLogFile: /dev/stdout accessLogFormat: | [OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%" extensionProviders: - name: "opa.local" envoyExtAuthzGrpc: service: "opa.opa.svc.cluster.local" port: "9191" EOF

Зверніть увагу, що в конфігурації ми визначаємо розділ extensionProviders, який вказує на самостійне встановлення OPA.

Розгорніть приклад застосунків. Httpbin — відомий застосунок, який можна використовувати для тестування HTTP-запитів; він швидко демонструє, як можна працювати з атрибутами запиту та відповіді.

$ kubectl create ns my-app $ kubectl label namespace my-app istio-injection=enabled
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml -n my-app

Розгорніть OPA. Це не вдасться, оскільки очікується configMap, що містить стандартне правило Rego для використання. Цей configMap буде розгорнуто пізніше у нашому прикладі.

$ kubectl create ns opa $ kubectl label namespace opa istio-injection=enabled
$ kubectl apply -f - <<EOF apiVersion: apps/v1 kind: Deployment metadata: labels: app: opa name: opa namespace: opa spec: replicas: 1 selector: matchLabels: app: opa template: metadata: labels: app: opa spec: containers: - image: openpolicyagent/opa:0.61.0-envoy name: opa args: - "run" - "--server" - "--disable-telemetry" - "--config-file=/config/config.yaml" - "--log-level=debug" # Розкоментуйте цей рядок, щоб увімкнути журнали налагодження - "--diagnostic-addr=0.0.0.0:8282" - "/policy/policy.rego" # Стандартна політика volumeMounts: - mountPath: "/config" name: opa-config - mountPath: "/policy" name: opa-policy volumes: - name: opa-config configMap: name: opa-config - name: opa-policy configMap: name: opa-policy --- apiVersion: v1 kind: ConfigMap metadata: name: opa-config namespace: opa data: config.yaml: | # Тут ви знайдете конфігурацію OPA, яку ви можете знайти в офіційній документації decision_logs: console: true plugins: envoy_ext_authz_grpc: addr: ":9191" path: mypackage/mysubpackage/myrule # Default path for grpc plugin # Тут ви можете додати власну конфігурацію з сервісами та пакетами --- apiVersion: v1 kind: Service metadata: name: opa namespace: opa labels: app: opa spec: ports: - port: 9191 protocol: TCP name: grpc selector: app: opa --- EOF

Розгорніть AuthorizationPolicy, щоб визначити, які служби будуть захищені OPA.

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: my-opa-authz namespace: istio-system # Цей рядок застосовує політику до всіх мереж в просторі назв налаштувань istio-system spec: selector: matchLabels: ext-authz: enabled action: CUSTOM provider: name: "opa.local" rules: [{}] # Порожнє правило, буде застосовуватися до селекторів з міткою ext-authz: enabled EOF

Позначімо застосунок міткою, щоб впровадити політику:

$ kubectl patch deploy httpbin -n my-app --type=merge -p='{
"spec": { "template": { "metadata": { "labels": { "ext-authz": "enabled" } } } } }'

Зверніть увагу, що в цьому ресурсі ми визначаємо OPA extensionProvider, який ви встановили в конфігурації Istio:

[...]
  provider:
    name: "opa.local"
[...]

Як це працює

При застосуванні AuthorizationPolicy панель управління Istio (istiod) надсилає необхідні конфігурації до sidecar проксі (Envoy) вибраних сервісів, зазначених у політиці. Envoy потім відправляє запит на сервер OPA, щоб перевірити, чи дозволено цей запит.

Istio та OPA

Проксі Envoy працює шляхом налаштування фільтрів у ланцюгу. Одним із таких фільтрів є ext_authz, який реалізує зовнішню службу авторизації з певним повідомленням. Будь-який сервер, що реалізує відповідний protobuf, може підʼєднатися до проксі Envoy та надати рішення щодо авторизації; OPA є одним з таких серверів.

Фільтри

Раніше, коли ви встановлювали сервер OPA, ви використовували версію сервера Envoy. Цей образ дозволяє налаштувати втулок gRPC, який впроваджує службу ext_authz protobuf.

[...]
      containers:
      - image: openpolicyagent/opa:0.61.0-envoy # Це версія образу OPA з втулком Envoy
        name: opa
[...]

У конфігурації ви увімкнули втулок Envoy та порт, на якому він слухає:

[...]
    decision_logs:
      console: true
    plugins:
      envoy_ext_authz_grpc:
        addr: ":9191" # Це порт, на якому буде слухати втулок Envoy
        path: mypackage/mysubpackage/myrule # Стандартний шлях для втулка grpc
    # Тут ви можете додати власну конфігурацію з сервісами та наборами даних
[...]

Переглядаючи документацію про службу авторизації Envoy, можна побачити, що повідомлення має такі атрибути:

OkHttpResponse
{
  "status": {...},
  "denied_response": {...},
  "ok_response": {
      "headers": [],
      "headers_to_remove": [],
      "dynamic_metadata": {...},
      "response_headers_to_add": [],
      "query_parameters_to_set": [],
      "query_parameters_to_remove": []
    },
  "dynamic_metadata": {...}
}

Це означає, що на основі відповіді від сервера authz Envoy може додавати або видаляти заголовки, параметри запиту та навіть змінювати статус відповіді. OPA також може це робити, як описано в його документації.

Тестування

Протестуймо просте використання (авторизацію), а потім створимо більш розширене правило, щоб показати, як можна використовувати OPA для зміни запиту та відповіді.

Розгорніть застосунок для запуску команд curl до тестового застосунку httpbin:

$ kubectl -n my-app run --image=curlimages/curl curl -- /bin/sleep 100d

Застосуйте перше правило Rego і перезапустіть розгортання OPA:

$ kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: opa-policy namespace: opa data: policy.rego: | package mypackage.mysubpackage import rego.v1 default myrule := false myrule if { input.attributes.request.http.headers["x-force-authorized"] == "enabled" } myrule if { input.attributes.request.http.headers["x-force-authorized"] == "true" } EOF
$ kubectl rollout restart deployment -n opa

Простий сценарій передбачає дозвіл запитів, якщо вони містять заголовок x-force-authorized зі значенням enabled або true. Якщо заголовок відсутній або має інше значення, запит буде відхилено.

Існує кілька способів створити правило Rego. У цьому випадку ми створили два різні правила. Виконуються вони у порядку, і перше правило, яке задовольняє всі умови, буде застосоване.

Просте правило

Результатом наступного запиту буде відповідь 403:

$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get

Наступний запит поверне 200 та тіло відповіді:

$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-authorized: enabled"

Складніші маніпуляції

Тепер складніше правило. Застосуйте друге правило Rego і перезапустіть розгортання OPA:

$ kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: opa-policy namespace: opa data: policy.rego: | package mypackage.mysubpackage import rego.v1 request_headers := input.attributes.request.http.headers force_unauthenticated if request_headers["x-force-unauthenticated"] == "enabled" default allow := false allow if { not force_unauthenticated request_headers["x-force-authorized"] == "true" } default status_code := 403 status_code := 200 if allow status_code := 401 if force_unauthenticated default body := "Unauthorized Request" body := "Authentication Failed" if force_unauthenticated myrule := { "body": body, "http_status": status_code, "allowed": allow, "headers": {"x-validated-by": "my-security-checkpoint"}, "response_headers_to_add": {"x-add-custom-response-header": "added"}, "request_headers_to_remove": ["x-force-authorized"], "dynamic_metadata": {"my-new-metadata": "my-new-value"}, } EOF
$ kubectl rollout restart deployment -n opa

В цьому правилі ви можете побачити:

myrule["allowed"] := allow # Зверніть увагу, що `allowed` є обовʼязковим при поверненні обʼєкта, як тут `myrule`.
myrule["headers"] := headers
myrule["response_headers_to_add"] := response_headers_to_add
myrule["request_headers_to_remove"] := request_headers_to_remove
myrule["body"] := body
myrule["http_status"] := status_code

Це значення, які будуть повернуті проксі-серверу Envoy від OPA-сервера. Envoy буде використовувати ці значення для модифікації запиту і відповіді.

Зверніть увагу, що при поверненні JSON-обʼєкта потрібно вказувати allowed, а не тільки true/false. Це можна знайти в документації OPA.

Зміна тіла відповіді

Випробуємо нові можливості:

$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get

Тепер ми можемо змінити тіло відповіді. Значення 403 змінює тіло в правилі Rego на «Unauthorized Request» (Несанкціонований запит). За допомогою попередньої команди ви повинні отримати:

Unauthorized Request
http_code=403

Зміна тіла, що повертається і коду статусу

Запустивши запит із заголовком x-force-authorized: enabled ви повинні отримати тіло «Authentication Failed» і помилку «401»:

$ kubectl exec -n my-app curl -c curl -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-unauthenticated: enabled"

Додавання заголовків до запиту

Запустивши відповідний запит, ви повинні отримати тіло відповіді з новим заголовком x-validated-by: my-security-checkpoint і видаленим заголовком x-force-authorized:

$ kubectl exec -n my-app curl -c curl -- curl -s httpbin:8000/get -H "x-force-authorized: true"

Додавання заголовків до відповіді

Запустивши той самий запит, але показавши лише заголовок, ви побачите заголовок відповіді, доданий під час перевірки Authz x-add-custom-response-header: added:

$ kubectl exec -n my-app curl -c curl -- curl -s -I httpbin:8000/get -H "x-force-authorized: true"

Обмін даними між фільтрами

Останнім кроком є передача даних іншим фільтрам Envoy за допомогою dynamic_metadata. Це корисно, коли потрібно передати дані іншому фільтру ext_authz у ланцюзі або вивести їх у логи застосунку.

Metadata

Для цього перегляньте формат журналу доступу, який ви налаштували раніше:

[...]
    accessLogFormat: |
      [OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%"
[...]

DYNAMIC_METADATA — це зарезервоване ключове слово для доступу до обʼєкта метаданих. Далі вказується назва фільтра, до якого ви хочете звернутися. У вашому випадку, імʼя envoy.filters.http.ext_authz автоматично створюється Istio. Ви можете перевірити це, вивівши конфігурацію Envoy:

$ istioctl pc all deploy/httpbin -n my-app -oyaml | grep envoy.filters.http.ext_authz

Ви побачите конфігурації для фільтра.

Тепер перевіримо динамічні метадані. У розширеному правилі ви створюєте новий запис метаданих: {"my-new-metadata": "my-new-value"}.

Виконайте запит і перевірте логи застосунку:

$ kubectl exec -n my-app curl -c curl -- curl -s -I httpbin:8000/get -H "x-force-authorized: true" $ kubectl logs -n my-app deploy/httpbin -c istio-proxy --tail 1

У виводі ви побачите нові атрибути, налаштовані за допомогою правил OPA Rego:

[...]
 my-new-dynamic-metadata: "{"my-new-metadata":"my-new-value","decision_id":"8a6d5359-142c-4431-96cd-d683801e889f","ext_authz_duration":7}"
[...]

Підсумки

У цьому посібнику ми показали, як інтегрувати Istio та OPA для впровадження політик для простого мікросервісного застосунку. Ми також продемонстрували, як використовувати Rego для модифікації атрибутів запиту та відповіді. Це основний приклад для побудови системи політик на платформі, яку можуть використовувати всі команди розробників застосунків.

Share this post