Kubectl-r[exe]c: Плагін для kubectl для аудиту команд kubectl exec

Автор: Мартон Натко, Інженер з інфраструктури, Adyen

pic

З переходом до контейнеризованих навантажень в Adyen ми значно збільшили швидкість виконання задач. Ми також прийняли одну з наших основних формул — запускати швидко та ітераційно — оскільки контейнери надають оптимальний спосіб для швидких циклів випуску. Однак ми розуміємо, що швидкість не повинна ставати на заваді дотриманню регуляцій.

Ми обрали Kubernetes як оркестратор контейнерів для багатьох наших проектів з нуля і, щоб забезпечити належну інженерну автономію, надаємо нашим інженерам доступ до прямої взаємодії з ним. Автономія особливо корисна для налагодження, оскільки інженерні команди володіють власними застосунками.

Вступ до kubectl exec

Якщо ви знайомі з екосистемою Kubernetes, то команда kubectl exec спадає на думку, коли потрібно налагоджувати працюючий застосунок в Kubernetes.

Якщо ви не знайомі з нею, то коротке пояснення: ця команда дозволяє користувачеві виконувати команди всередині контейнера, що працює в Kubernetes, або навіть, можна отримати TTY (teletype) в цьому контейнері.

У нашій галузі важливо мати аудиторські журнали таких подій. Проблема в тому, що не так багато рішень пропонують цю функцію.

Зануримося глибше

Щоб вирішити цю проблему, давайте детальніше розглянемо потік даних. Ми охопимо лише частину між користувачем та apiserver, яка є найзручнішою для розробки рішення.

Коли користувач виконує команду kubectl exec, ми можемо легко перехопити параметр команди з запиту. Але припустімо, що користувач також запитує TTY і передає свій STDIN до контейнера, він отримає TTY всередині контейнера, і будь-яка команда, яка буде виконана, пройде через оновлений протокол. Для версій Kubernetes нижче v1.30 це був SPDY, а у v1.30 Websocket став стандартом. На цьому етапі все ускладнюється, оскільки аудиторський запис того, що виконується всередині контейнера через ці сесії, стає менш зручним для спостереження.

Наше рішення — kubectl-rexec

Ми вирішили зберегти всі можливості, які надає kubectl exec, при розгляді можливих рішень. Тому ми сформулювали наступну задачу: що, якщо ми будемо мати ту саму команду як плагін для kubectl, але викликатимемо інший API-ендпоінт, який потрапляє до компонента, яким ми керуємо?
Kubernetes має ресурс APIService, який дозволяє нам розширювати сервер API Kubernetes новими ендпоінтами.

Ми впровадили APIService, що показаний у коді нижче. Він дозволяє запити з шляхом /apis/audit.adyen.internal/v1beta1/… перенаправляти до служби під назвою rexec в просторі імен kube-system.

apiVersion: apiregistration.k8s.io/v1  
kind: APIService  
metadata:  
 name: v1beta1.audit.adyen.internal  
spec:  
 group: audit.adyen.internal  
 groupPriorityMinimum: 100  
 caBundle: caCertAsBase64…   
 service:  
 name: rexec  
 namespace: kube-system  
 port: 8443  
 version: v1beta1  
 versionPriority: 100

Ми створили плагін kubectl, який реалізує все те, що робить exec, за винятком одного: замість виклику api/v1/namespaces/{{ namespace }}/pods/{{ pod }}/exec, він викликає apis/audit.adyen.internal/v1beta1/namespaces/{{ namespace }}/pods/{{ pod }}/exec. Цей запит потрапляє — через APIService — до компонента, яким ми керуємо; давайте назвемо його "proxy rexec".

В rexec проксі ми робимо дві речі: по-перше, переписуємо шлях на рідний шлях exec, а потім проксіруємо його назад до API сервера Kubernetes. Ми повинні зазначити, що це проксирування відбувається через імперсонацію, оскільки на цьому рівні ми вже не маємо оригінальних даних користувача для автентифікації; ми маємо лише користувача та групи, які можемо передати через імперсонацію.

Проксирування може відбуватись двома способами:

1.
1. Коли користувач не запитує TTY, ми реєструємо параметри команди з запиту як аудиторську подію та проксируємо запит безпосередньо до API сервера Kubernetes.
2. Коли запитується TTY, ситуація ускладнюється. У цьому випадку проксі не перенаправляє запит безпосередньо до API сервера Kubernetes. Замість цього він проксирує запит на себе через TCP слухач на Unix-сокеті, який запускається для кожного запиту exec. Кожен з цих TCP слухачів діє як TCP проксі, проксируючи запити до API сервера Kubernetes. Маючи TCP проксі, ми можемо досліджувати сирий трафік TCP, аналізувати фрейми Websocket і захоплювати кожен натискання клавіші, яке термінал користувача відправляє до контейнера в TTY. Додатково, наявність TCP сесії для кожного користувача — це простий спосіб відслідковувати ідентичність користувача та сесію, залишаючись на рівні TCP.

Тепер, коли ми вирішили половину проблеми — ми маємо аудиторське логування — нам все ще потрібно забезпечити, щоб користувачі не використовували рідну команду. Для цього ми додали ValidatingWebhookConfiguration.

apiVersion: admissionregistration.k8s.io/v1  
kind: ValidatingWebhookConfiguration  
metadata:  
 name: deny-pod-exec  
webhooks:  
- name: deny-pod-exec.k8s.io  
 clientConfig:  
 service:  
 name: rexec  
 namespace: kube-system  
 port: 8443  
 path: /validate-exec  
 caBundle: caCertAsBase64...  
 rules:  
 - apiGroups: [""]  
 apiVersions: ["v1"]  
 operations: ["CONNECT"]  
 resources: ["pods/exec"]  
 admissionReviewVersions: ["v1"]  
 sideEffects: None  
 failurePolicy: Fail

Ця конфігурація робить одну річ: кожного разу, коли API сервер Kubernetes отримує запит, націлений на api/v1/namespaces/{{ namespace }}/pods/{{ pod }}/exec, API викликає проксі rexec з шляхом /validate-exec.
Тепер це означає, що ми також маємо спосіб контролювати запити до рідної команди exec; нам потрібно лише знайти спосіб відмовити всьому, що не проходить через сам проксі rexec. Для цього, на запитах exec, що проходять через наш проксі, ми додаємо заголовок з значенням, яке діє як спільний ключ між двома ендпоінтами, які ми надаємо. Якщо заголовок присутній і значення однакове, ми можемо дозволити запит рухатись далі.

Висновок

З цим мінімалістичним застосунком ми можемо легко аудіювати команди exec, і нам потрібно лише встановити кілька манифестів на стороні Kubernetes, при цьому розповсюджуючи наш плагін серед наших інженерів.

Якщо вам цікаво спробувати, ознайомтесь з ним на Github: https://github.com/adyen/kubectl-rexec.

Перекладено з: Kubectl-r[exe]c: A kubectl plugin for auditing kubectl exec commands

Leave a Reply

Your email address will not be published. Required fields are marked *