Etcd

Материал из noname.com.ua
Версия от 11:43, 30 сентября 2022; Sirmax (обсуждение | вклад) (→‎Peer-to-peer SSL)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к навигацииПерейти к поиску

Etcd

Это часть моего изучения k8s the hard way
но может использоваться и отдельно

Требования

  • 3 ноды raspberry pi установленные и настроенные, имеющие коннективити друг с другом
  • установленный и настроенный PKI на основе Hashicorp Vault (в примере - по адресу http://vault.home)
  • Настроенный DNS и созданы записи для всех доменов в примере (в моем случае DNS поднят на MikroTik)

Замечания к тестовому окружению

  • для Vault используется корневой токен вместо отдельной авторизации по каждой роли
  • для Vault используется http вместо https
  • ???

Установка

  • мне НЕ удалось ни найти ни собрать более свежую версию чем была доступна в пакетах дистрибутива
  • установить на всех трех нодах
apt -y install etcd-server etcd-client etcd

Удалить юнит (свой создается ниже)

rm /usr/lib/systemd/system/etcd.service
dpkg -l | grep etcd
iU  etcd                           3.2.26+dfsg-3                       all          Transitional package for etcd-client and etcd-server
ii  etcd-client                    3.2.26+dfsg-3                       armhf        highly-available key value store -- client
iF  etcd-server                    3.2.26+dfsg-3                       armhf        highly-available key value store -- daemon
  • TODO: проверить кросс-компиляцию для ARM

Подготовка SSL сертификатов

Согласно документации (https://etcd.io/docs/v3.5/op-guide/security/) etcd сертификаты используются в двух независимых настройках:

  • Коммуникация между нодами кластера
  • Коммуникация между кластером и клиентом

Peer-to-peer SSL

Для того что бы настроить SSL потребуются сертификаты для соответствующих доменов. Так как сертификаты во взаимодействии нод кластера используются как клиентские и как серверные, то нужна соответствующая настройка PKI

настройка PKI для peer-to-peer SSL

  • Нужен клиент Vault
  • В примере используется рутовый токен (нужен ТОЛЬКО для создания "роли", но используется везде в примерах)

Для обновления сертификатов будет добавлен пользователь или другой способ авторизации и права будут ограничены

export VAULT_ADDR=http://vault.home:8200
export VAULT_TOKEN=<some-token-here>


vault write pki_intermediate_ca/roles/etc-az-k8s-home-server-crt \
    country="Ukraine" \
    locality="Kharkov" \
    street_address="Lui Pastera st 322 app. 311"\
    postal_code="61172" \
    organization="Home Network" \
    ou="IT" \
    allowed_domains="etcd1.home,etcd2.home,etcd3.home,etcd.home,etcd.az1.k8s.home,etcd.az2.k8s.home,etcd.az3.k8s.home" \
    allow_subdomains=false \
    max_ttl="87600h" \
    key_bits="2048" \
    key_type="rsa" \
    allow_any_name=false \
    allow_bare_domains=true \
    allow_glob_domain=false \
    allow_ip_sans=true \
    allow_localhost=false \
    client_flag=true \
    server_flag=true \
    enforce_hostnames=true \
    key_usage="DigitalSignature,KeyEncipherment" \
    ext_key_usage="ServerAuth,ClientAuth" \
    require_cn=true

В скрипте выще важно следующее

  • etc-az-k8s-home-server-crt имя роли - по сути произвольная строка которая просто определяет endpoint (используется ниже)
  • allowed_domains="etcd1.home,etcd2.home,etcd3.home,etcd.home,etcd.az1.k8s.home,etcd.az2.k8s.home,etcd.az3.k8s.home" - список доменов для которых можно выписывать сертефикаты используя этот endpoint. В моем случае изначально планировалось использовать домены etcdX.home но так как настройка "а-ля мультизонный кластер" то домер решил изменить

ВАЖНО: Домены пречислять через запятую без пробелов иначе не работает (как минимум в моей версии Vault)

  • allow_subdomains=false - не разрешать создавать сабдомены
  • client_flag=true - сертификат может использоваться как клиентский
  • server_flag=true - сертификат может использоваться как серверный
  • ext_key_usage="ServerAuth,ClientAuth" - расширения


Если попробовать создать только серверный сертификат то возникает ошибка SSL которая в общем-то об этом и говорит и ничего не работает


Создание сертификатов для peer-to-peer

Пример для одного домена, для остальных аналогично


export VAULT_ADDR=http://vault.home:8200
export VAULT_TOKEN=<some-token-here>


domain="etcd.az1.k8s.home"
vault \
    write \
    -format=json pki_intermediate_ca/issue/etc-az-k8s-home-server-crt \
    common_name="${domain}" \
    ttl="43800h" > ${domain}.crt.json
  • issue/etc-az-k8s-home-server-crt - имя роли которое указано на предыдущем шаге
  • используется рутовый токен который имеет все права (но это только для примера что бы пропустить "лишние" шаги)

Полученный файл etcd.az1.k8s.home.crt.json:

{
  "request_id": "0454e996-7bbc-d3ca-8830-cc2d544b2763",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "ca_chain": [
      "-----BEGIN CERTIFICATE----- пропущено -----END CERTIFICATE-----"
    ],
    "certificate": "-----BEGIN CERTIFICATE----- пропущено -----END CERTIFICATE-----",
    "expiration": 1792583899,
    "issuing_ca": "-----BEGIN CERTIFICATE----- пропущено -----END CERTIFICATE-----",
    "private_key": "-----BEGIN RSA PRIVATE KEY----- пропущено -----END RSA PRIVATE KEY-----",
    "private_key_type": "rsa",
    "serial_number": "3b:f4:6d:6d:b8:47:38:9f:7b:4d:dc:11:89:d9:63:ff:74:e6:fd:ed"
  },
  "warnings": null
}

Из полученного файла подготовить отдельно файлы сертификатов:

#!/bin/bash

domain="etcd.az1.k8s.home"

mkdir -p /etc/etcd/certs/server

cat \
    ${domain}.crt.json \
    | jq -r '.data.private_key' > /etc/etcd/certs/server/${domain}.key

cat \
    ${domain}.crt.json \
    | jq -r '.data.certificate' > /etc/etcd/certs/server/${domain}.pem

cat \
    ${domain}.crt.json \
    | jq -r '.data.ca_chain[]' >> /etc/etcd/certs/server/${domain}.pem

Обратить внимание: так как сертефикат подписан НЕ корневым а промежуточным центром сертефикации то в файл сертификата требуется добавить промежуточный сертефикат (поле '.data.ca_chain[]' в json)


Тестовый запуск c peer-to-peer SSL

В целом этого достаточно что бы запустить etcd (но клиентские соединения будут нешифрованными и без авторизации)
Скрипт запуска (на всех трех нодах отличаются домены и адреса):

export ETCD_UNSUPPORTED_ARCH=arm

/usr/bin/etcd \
  --debug \
  --name=etcd-az-1 \
  --listen-peer-urls=https://10.240.1.2:2380 \
  --listen-client-urls=http://10.240.1.2:2379,http://127.0.0.1:2379 \
  --advertise-client-urls=http://etcd.az1.k8s.home:2379 \
  --initial-advertise-peer-urls=https://etcd.az1.k8s.home:2380 \
  --initial-cluster-token=etcd.az1.k8s.home \
  --initial-cluster etcd-az-1=https://etcd.az1.k8s.home:2380,etcd-az-2=https://etcd.az2.k8s.home:2380,etcd-az-3=https://etcd.az3.k8s.home:2380 \
  --initial-cluster-state=new \
  --data-dir=/var/lib/etcd \
  --peer-client-cert-auth \
  --peer-cert-file=/etc/etcd/certs/server/etcd.az1.k8s.home.pem \
  --peer-key-file=/etc/etcd/certs/server/etcd.az1.k8s.home.key \
  --peer-trusted-ca-file=/etc/etcd/certs/rootCA.pem

Запускать на всех трех нодах!

Проверка подключения клиента без SSL

ETCDCTL_API=3 etcdctl --debug --endpoints=http://etcd.az1.k8s.home:2379 member list

Вывод команды разбит на 2 части - дебаг и собственно вывод
из вывода команды видно что все три ноды в кластере

INFO: 2021/10/22 15:50:14 parsed scheme: ""
INFO: 2021/10/22 15:50:14 scheme "" not registered, fallback to default scheme
INFO: 2021/10/22 15:50:14 ccResolverWrapper: sending new addresses to cc: [{etcd.az1.k8s.home:2379 0  <nil>}]
INFO: 2021/10/22 15:50:14 balancerWrapper: got update addr from Notify: [{etcd.az1.k8s.home:2379 <nil>}]
INFO: 2021/10/22 15:50:14 clientv3/balancer: pin "etcd.az1.k8s.home:2379"
INFO: 2021/10/22 15:50:14 balancerWrapper: got update addr from Notify: [{etcd.az1.k8s.home:2379 <nil>}]
b7a875ba8d83c06,  started, etcd-az-3, https://etcd.az3.k8s.home:2380, http://etcd.az3.k8s.home:2379
489c4054ed342d1a, started, etcd-az-2, https://etcd.az2.k8s.home:2380, http://etcd.az2.k8s.home:2379
db4ea93d7a019672, started, etcd-az-1, https://etcd.az1.k8s.home:2380, http://etcd.az1.k8s.home:2379

Client SSL

Следующий шаг - добавить шифрование клиентских соединений
Тут есть путаница - "клиентский-серверный" и "совсем клиентский" сертефикаты

  • "клиентский-серверный" - тот что устанавливается на сервере ETCD для того что бы обеспечить SSL клиентам
  • "совсем клиентский" - тот с которым авторизуются клиенты

настройка PKI для Client SSL

Со стороны ETCD сетрефикаты - "серверные", со стороны клиентов - "клиентские", по тому требуется создать в Vault PKI два дополнительных endpoint


  • Для сертефиката который будет "клиентский-серверный":
vault write pki_intermediate_ca/roles/etc-az-k8s-home-server-only-crt \
    country="Ukraine" \
    locality="Kharkov" \
    street_address="Lui Pastera st 322 app. 311"\
    postal_code="61172" \
    organization="Home Network" \
    ou="IT" \
    allowed_domains="etcd1.home,etcd2.home,etcd3.home,etcd.home,etcd.az1.k8s.home,etcd.az2.k8s.home,etcd.az3.k8s.home" \
    allow_subdomains=false \
    max_ttl="87600h" \
    key_bits="2048" \
    key_type="rsa" \
    allow_any_name=false \
    allow_bare_domains=true \
    allow_glob_domain=false \
    allow_ip_sans=true \
    allow_localhost=false \
    client_flag=false \
    server_flag=true \
    enforce_hostnames=true \
    key_usage="DigitalSignature,KeyEncipherment" \
    ext_key_usage="ServerAuth" \
    require_cn=true

Обратить внимаене что это чисто серверный сертефикат:

  • client_flag=false
  • ext_key_usage="ServerAuth"


"совсем клиентский":

vault write pki_intermediate_ca/roles/etc-az-k8s-home-client-only-crt \
    country="Ukraine" \
    locality="Kharkov" \
    street_address="Lui Pastera st 322 app. 311"\
    postal_code="61172" \
    organization="Home Network" \
    ou="IT" \
    allowed_domains="etcd1.home,etcd2.home,etcd3.home,etcd.home,etcd.az1.k8s.home,etcd.az2.k8s.home,etcd.az3.k8s.home" \
    allow_subdomains=false \
    max_ttl="87600h" \
    key_bits="2048" \
    key_type="rsa" \
    allow_any_name=true \
    allow_bare_domains=false \
    allow_glob_domain=false \
    allow_ip_sans=false \
    allow_localhost=false \
    client_flag=true \
    server_flag=false \
    enforce_hostnames=false \
    key_usage="DigitalSignature" \
    ext_key_usage="ClientAuth" \
    require_cn=true

Тут - только клиентский сертефикат

  • client_flag=true
  • server_flag=false
  • ext_key_usage="ClientAuth"

Создание сертификатов для Client SSL

В целом полностью аналогично созданию для peer-to-peer за исключением использования других endpoint естественно

export VAULT_ADDR=http://vault.home:8200
export VAULT_TOKEN=...
domain="etcd.az1.k8s.home"
mkdir -p ./client_crt
vault \
    write \
    -format=json pki_intermediate_ca/issue/etc-az-k8s-home-server-only-crt \
    common_name="${domain}" \
    ttl="43800h" > ./client_crt/${domain}.crt.json
mkdir -p ./client_only_crt
vault \
    write \
    -format=json pki_intermediate_ca/issue/etc-az-k8s-home-client-only-crt \
    common_name="${domain}" \
    ttl="43800h" > ./client_only_crt/${domain}.crt.json

и аналогично с помощью jq достать сами сертификаты и ключи

Тестовый запуск c Client SSL

Systemd Unit File

Юнит файлы требуют доработки - вынести в конфиги весь хардкод

  • /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
Type=notify
LimitNOFILE=65536
Environment=ETCD_UNSUPPORTED_ARCH=arm
ExecStart=/usr/bin/etcd \
                --debug \
                --name=etcd-az-1 \
                --listen-peer-urls=https://10.240.1.2:2380 \
                --listen-client-urls=https://10.240.1.2:2379,https://127.0.0.1:2379 \
                --advertise-client-urls=https://etcd.az1.k8s.home:2379 \
                --initial-advertise-peer-urls=https://etcd.az1.k8s.home:2380 \
                --initial-cluster-token=etcd.az1.k8s.home \
                --initial-cluster etcd-az-1=https://etcd.az1.k8s.home:2380,etcd-az-2=https://etcd.az2.k8s.home:2380,etcd-az-3=https://etcd.az3.k8s.home:2380 \
                --initial-cluster-state=new \
                --data-dir=/var/lib/etcd \
                --peer-client-cert-auth \
                --peer-cert-file=/etc/etcd/certs/server/etcd.az1.k8s.home.pem \
                --peer-key-file=/etc/etcd/certs/server/etcd.az1.k8s.home.key \
                --peer-trusted-ca-file=/etc/etcd/certs/rootCA.pem \
                --trusted-ca-file=/etc/etcd/certs/rootCA.pem \
                --cert-file=/etc/etcd/certs/client/etcd.az1.k8s.home.pem \
                --key-file=/etc/etcd/certs/client/etcd.az1.k8s.home.key \
                --client-cert-auth

Restart=on-failure
RestartSec=60

[Install]
WantedBy=multi-user.target
Alias=etcd-server.service

Общие параметры

  • --debug
  • --name=etcd-az-1 Имя ноды кластера - должно быть одним из содержащихся в параметре --initial-cluster На каждой ноде это имя отличается
  • --data-dir =/var/lib/etcd

Параметры кластера

Параметры SSL для peer-to-peer

  • --peer-client-cert-auth
  • --peer-cert-file =/etc/etcd/certs/server/etcd.az1.k8s.home.pem
  • --peer-key-file =/etc/etcd/certs/server/etcd.az1.k8s.home.key
  • --peer-trusted-ca-file =/etc/etcd/certs/rootCA.pem

Параметры SSL для клиентских подключений

  • --trusted-ca-file=/etc/etcd/certs/rootCA.pem
  • --cert-file=/etc/etcd/certs/client/etcd.az1.k8s.home.pem
  • --key-file=/etc/etcd/certs/client/etcd.az1.k8s.home.key
  • --client-cert-auth

Проверка подключения клиента с использованием SSL

#!/bin/bash


export ETCDCTL_API=3\

etcdctl \
    --debug \
    --endpoints=https://etcd.az1.k8s.home:2379 \
    --cert=/root/k8s/etcd/client_only_crt/etcd.az1.k8s.home.pem \
    --key=/root/k8s/etcd/client_only_crt/etcd.az1.k8s.home.key \
    member list
INFO: 2021/10/23 14:16:04 parsed scheme: ""
INFO: 2021/10/23 14:16:04 scheme "" not registered, fallback to default scheme
INFO: 2021/10/23 14:16:04 ccResolverWrapper: sending new addresses to cc: [{etcd.az1.k8s.home:2379 0  <nil>}]
INFO: 2021/10/23 14:16:04 balancerWrapper: got update addr from Notify: [{etcd.az1.k8s.home:2379 <nil>}]
INFO: 2021/10/23 14:16:04 clientv3/balancer: pin "etcd.az1.k8s.home:2379"
INFO: 2021/10/23 14:16:04 balancerWrapper: got update addr from Notify: [{etcd.az1.k8s.home:2379 <nil>}]
b7a875ba8d83c06,  started, etcd-az-3, https://etcd.az3.k8s.home:2380, https://etcd.az3.k8s.home:2379
489c4054ed342d1a, started, etcd-az-2, https://etcd.az2.k8s.home:2380, https://etcd.az2.k8s.home:2379
db4ea93d7a019672, started, etcd-az-1, https://etcd.az1.k8s.home:2380, https://etcd.az1.k8s.home:2379

В целом можно сказать что авьторизация возможна

  • с клиентским сертификатом
  • c сертификатом сервера для peer-to-peer (кстати эту возможность вероятно надо отключить как-то)

С сертификатом для клиентских подключения, который используется со стороны сервера, клиент подключиться не сможет

WARNING: 2021/10/23 14:15:46 grpc: addrConn.createTransport failed to connect to {etcd.az2.k8s.home:2379 0  <nil>}. Err :connection error: desc = "transport: authentication handshake failed: x509: certificate specifies an incompatible key usage". Reconnecting...

Разграничение прав, роли пользователи и авторизация по сертефикатам

TBD