Начиная с Kubernetes 1.6, RBAC-политики включены по умолчанию. К тому же использование этих политик помогает безопасно управлять вашим кластером. Раньше нам приходилось вручную создавать подобные политики, сервисные аккаунты и пользователей. Для каждого нового проекта мы проделывали ручные операции, которые отнимали много времени. Особенно создание пользователей, так как требует множества мелких манипуляций. Готового решения, удовлетворяющего нашим требованиям, нам найти не удалось, поэтому мы написали terraform-модуль, который упрощает этот процесс. Данный модуль позволяет создавать сервисные аккаунты и пользователей, а затем генерировать готовые конфигурационные файлы (kubeconfig) для них. Также при помощи данного модуля можно создавать роли, кластерные роли и привязывать их к определенным сервисным аккаунтам, пользователям или группам. Модуль можно найти в нашем GitHub репозитории. Для использования модуля потребуется terraform >= 1.0.0 и kubeconfig, с административными правами.
Описание переменных
output_files_path: путь до директории где будут сохранены сгенерированные TLS файлы и kubeconfig для пользователей и сервисных аккаунтов. По умолчанию: "./files".
k8s_api_endpoint: имя хоста (в URI формате) для Kubernetes API.
k8s_insecure: следует ли обращаться к серверу без проверки сертификата TLS. По умолчанию: false.
k8s_cluster_name: название Kubernetes кластера.
k8s_auth_cluster_ca_certificate: PEM-кодированный корневой сертификат для TLS аутентификации.
raw(опционально): сертификат в неизменном виде. Пример: "-----BEGIN CERTIFICATE-----\nMIIELDCCApSgAwIBAgIQcLahmhzRbVMSRZX2cQXtuTANBgkqhkiG9w0BAQsFADAv\n ... \n-----END CERTIFICATE-----\n".
encoded(опционально): закодированный в base64 сертификат.
Должно быть задано одно из полей raw или encoded. Если заданы оба, будет использовано поле raw.
Переменные k8s_api_endpoint, k8s_auth_cluster_ca_certificate и k8s_cluster_name необходимы для генерации конфигурационных файлов (kubeconfig).
k8s_config_path: путь до kubeconfig с административными правами.
k8s_config_context(опционально): контекст для kubeconfig.
roles_list: список создаваемых ролей.
name: название роли.
namespace: окружение.
rules: список правил для роли.
api_groups: список субъектов.
resources: список ресурсов.
verbs: список глаголов (verb).
cluster_roles_list: список создаваемых кластерных ролей.
name: название кластерной роли.
rules: список правил для роли.
api_groups: список субъектов.
resources: список ресурсов.
verbs: список глаголов (verb).
sa_list: список создаваемых сервисных аккаунтов.
name: имя сервисного аккаунта.
namespace: окружение.
bindings: список RoleBinding и ClusterRoleBinding.
type: тип (role_binding или cluster_role_binding).
prefix: уникальная строка, которая используется для формирования названия RoleBinding и ClusterRoleBinding.
namespaces(опционально): список окружений, в которых создается RoleBinding. Используется только для RoleBinding.
sa_list(опционально): список сервисных аккаунтов.
name: имя сервисного аккаунта.
namespace: окружение.
users(опционально): список пользователей. Пользователи создаются из данного списка.
name: имя пользователя.
group: группа (организация) пользователя.
groups(опционально): список групп.
roles(опционально): список ролей.
cluster_roles(опционально): список кластерных ролей.
Значение для переменной type может быть следующим: role_binding или cluster_role_binding.
Переменная namespaces используется только для RoleBinding. Если переменная будет пустая для RoleBinding, деплой не пройдет.
Одна из переменных sa_list, users, groups должна быть задана для RoleBinding и ClusterRoleBinding.
Пользователи создаются и списка users.
Переменные roles или cluster_roles должны быть установлены для RoleBinding.
Переменная cluster_roles должна быть установлена для ClusterRoleBinding.
Пример использования модуля
Рассмотрим конкретный пример использования данного модуля:
Код
module "k8s-rbac-controller" {
source = "../k8s-rbac-controller"
output_files_path = "./files"
k8s_api_endpoint = "https://172.20.1.2"
k8s_auth_cluster_ca_certificate = {
raw = "-----BEGIN CERTIFICATE-----\nMIIELDCCApSgAwIBAgIQcLahmhzRbVMSRZX2cQXtuTANBgkqhkiG9w0BAQsFADAv\n ... 8hZp/GUpn6jahcXxmuKaAQ==\n-----END CERTIFICATE-----\n"
encoded = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS..."
}
k8s_cluster_name = "cluster-name"
k8s_config_path = "~/.kube/config"
k8s_config_context = "config-context"
roles_list = [
{
name = "role-1",
namespace = "default",
rules = [
{
api_groups = [""],
resources = ["pods"],
verbs = ["get", "list"],
}
]
}
]
cluster_roles_list = [
{
name = "cluster-role-1",
rules = [
{
api_groups = [""],
resources = ["namespaces"],
verbs = ["get", "list", "watch", "create"],
}
]
},
{
name = "cluster-role-2",
rules = [
{
api_groups = [""],
resources = ["namespaces"],
verbs = ["get"]
},
{
api_groups = [""],
resources = ["namespaces"],
verbs = ["list"]
}
]
}
]
sa_list = [
{
name = "sa-1"
namespace = "kube-system"
},
{
name = "sa-2"
namespace = "default"
},
]
bindings = [
{
type = "role_binding"
prefix = "prefix-1"
namespaces = ["default"]
sa = [
{
name = "sa-1",
namespace = "kube-system",
}
],
users = [
{
name = "user-1",
group = "group-1",
},
{
name = "user-2",
group = "group-2",
}
]
groups = ["group-1"]
roles = ["role-1"]
cluster_roles = ["cluster-role-2"]
},
{
type = "cluster_role_binding"
prefix = "prefix-2"
sa = [
{
name = "sa-2",
namespace = "default",
}
],
users = [
{
name = "user-2",
group = "group-2",
}
],
cluster_roles = ["cluster-role-1"]
},
]
}
Данный пример можно найти в GitHub репозитории. В этом примере создается роль role-1 в default окружении со следующими правами:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-1
namespace: default
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- get
Также создается кластерная роль cluster-role-1 со следующими правами:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-role-1
rules:
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- create
и cluster-role-2 со следующими правами:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-role-2
rules:
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- namespaces
verbs:
- list
Дополнительно создаются сервисные аккаунты sa-1 в окружении kube-system и sa-2 в окружении default. Список пользователей для создания берется из поля users в переменной bindings. То есть в данном случае будут созданы 2 пользователя user-1 и user-2 в группах group-1 и group-2 соответственно. Также будут созданы 2 RoleBinding (соответствует количеству элементов в списках roles и cluster_roles, если type = role_binding) cluster-role-2-prefix-1-cluster-role:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cluster-role-2-prefix-1-cluster-role
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ns-viewer
subjects:
- kind: ServiceAccount
name: sa-1
namespace: kube-system
- apiGroup: rbac.authorization.k8s.io
kind: User
name: user-1
namespace: default
- apiGroup: rbac.authorization.k8s.io
kind: User
name: user-2
namespace: default
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: group-1
namespace: default
и role-1-prefix-1-role следующего вида:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-1-prefix-1-role
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-1
subjects:
- kind: ServiceAccount
name: sa-1
namespace: kube-system
- apiGroup: rbac.authorization.k8s.io
kind: User
name: user-1
namespace: default
- apiGroup: rbac.authorization.k8s.io
kind: User
name: user-2
namespace: default
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: group-1
namespace: default
Обратите внимание, что в RoleBinding можно указывать ClusterRole.
Также будет создан ClusterRoleBinding (соответствует количеству элементов в списке cluster_roles, если type = cluster_role_binding) cluster-role-1-prefix-2:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-role-1-prefix-2
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ns-creator
subjects:
- kind: ServiceAccount
name: sa-2
namespace: default
- apiGroup: rbac.authorization.k8s.io
kind: User
name: user-2
namespace: default
Название для RoleBinding формируется по следующему принципу:[${Role}|${ClusterRole}]-[${prefix}]-["role"|"cluster-role"]
Приписка ["role"|"cluster-role"] нужна, так как в RoleBinding можно использовать Role и ClusterRole, которые могут совпадать по названию.
Название для ClusterRoleBinding формируется по следующему принципу:[${ClusterRole}]-[${prefix}]
Что планируется дальше
В будущем планируется добавить следующий функционал:
Возможность выбирать output для сгенерированных kubeconfig - как минимум добавить vault помимо локальных файлов.
Возможность задавать для пользователя не одну группу, а передавать список. Для этого необходимо законтребьютить в репозиторий hashicorp/terraform-provider-tls, добавив возможность передавать список в поле organization для ресурса tls_cert_request.
Возможность генерировать kubeconfig для существующих сервисных аккаунтов.
Добавить модуль в terraform registry, чтобы было удобнее его использовать.
Выводы
Данный модуль помог повысить нашу эффективность. Раньше мы могли тратить от 20 до 60 минут на создание RBAC политик. Сейчас же на это уходит от 5 до 10 минут. То есть наша эффективность возрасла в ~4~6 раз. Как правило для проектов мы используем одни и те же роли и кластерные роли. Поэтому модуль позволяет унифицировать данный процесс, так как описание RBAC политик осуществляется на стороне кода. То есть можно повторно использовать зараннее созданные файлы с заполненными переменными.
P.S.: На текущий момент данный модуль прошел обкатку на небольшом количестве проектов, поэтому возможно будут обнаруживаться какие-то баги. Если вы заметите какой-то баг, пожалуйста, создайте issue на GitHub, мы будем оперативно обрабатывать их.
Кстати, мы также делимся полезной информацией по теме DevOps и не только в telegram-канале DevOps FM, присоединяйтесь.