[ English | English (United Kingdom) | Deutsch | русский ]
Использование точек доступа на основе домена (или пути) вместо точек входа на основе порта¶
По умолчанию OpenStack-Ansible использует точки доступа на основе портов. Это означает, что каждая служба будет обслуживаться на своем собственном уникальном порту как для публичных, так и для внутренних точек доступа. Например, Keystone будет добавлен как https://domain.com:5000/v3
, а Nova как https://domain.com:8774/v2.1
и так далее.
Хотя это самый простой подход, так как он не требует дополнительной настройки и прост в запуске, он также имеет некоторые недостатки. Например, некоторым клиентам или организациям может быть запрещено подключаться к произвольным портам, что полностью отключает возможность их использования в таких развертываниях.
Чтобы обойти эти ограничения, начиная с версии 2023.1 (Antelope), можно использовать точки доступа на основе домена или пути.
Настройка точек доступа на основе домена (рекомендуется)¶
Доменные точки доступа выполняют отдельные прямые запросы к определенным службам на основе полных доменных имен. Обычно для этой цели используются поддомены. Например, точка доступа Keystone может выглядеть как https://identity.domain.com
, а конечная точка Nova может выглядеть как https://compute.domain.com
.
В качестве предварительного условия для этого типа настройки вам необходимо убедиться, что соответствующие записи A или CNAME присутствуют для вашего домена. Также вам необходимо убедиться, что у вас есть действительные wildcard или сертификаты SAN для публичных/внутренних точек доступа.
Конфигурация HAProxy¶
Для того чтобы HAProxy передавал определенное полное доменное имя в свой собственный бекэнд, мы воспользуемся функциональностью файлов карты.
Нам необходимо внести изменения в каждое определение службы HAProxy, чтобы:
Предотвратить создание фронтенда на службу. Поскольку теперь мы ожидаем, что трафик будет поступать только на порты по умолчанию 80 и 443, нет необходимости иметь отдельный фронтенд на службу. Файл карты HAProxy прикреплен к «базовому» фронтенду, который развернут с ролью
haproxy_server
и не зависит от каких-либо служб. Файл карты можно использовать для направления входящих запросов на определенные бэкенды с помощью правил, определенных в файле карты, для сопоставления с заголовками запросов хоста.Примечание
В случае каких-либо изменений в переменной
haproxy_base_service_overrides
необходимо повторно запуститьopenstack-ansible openstack.osa.haproxy --tags haproxy-service-config
.haproxy_base_service_overrides: haproxy_maps: - 'use_backend %[req.hdr(host),map_dom(/etc/haproxy/base_domain.map)]'
Заполните файл «базового» сопоставления шаблонов поиска для каждого бэкенда служб. Поскольку каждый сервис будет использовать свой собственный FQDN, нам нужно сообщить HAProxy, какой бэкенд следует использовать, когда запрос поступает на FQDN.
Пример конфигурации для Keystone и Nova будет выглядеть следующим образом:
Примечание
После внесения изменений в переменную
haproxy_<service>_service_overrides
вам необходимо повторно запустить сценарий для конкретной службы с тегом haproxy-service-config, напримерopenstack-ansible openstack.osa.keystone --tags haproxy-service-config
.haproxy_keystone_service_overrides: haproxy_backend_only: True haproxy_map_entries: - name: base_domain entries: - "identity.{{ external_lb_vip_address }} keystone_service-back" - "identity.{{ internal_lb_vip_address }} keystone_service-back" haproxy_nova_api_compute_service_overrides: haproxy_backend_only: True haproxy_map_entries: - name: base_domain entries: - "compute.{{ external_lb_vip_address }} nova_api_os_compute-back" - "compute.{{ internal_lb_vip_address }} nova_api_os_compute-back" haproxy_nova_novnc_console_service_overrides: haproxy_backend_only: True haproxy_map_entries: - name: base_domain entries: - "novnc.{{ external_lb_vip_address }} nova_novnc_console-back"
Конфигурация службы¶
Наряду с конфигурацией HAProxy нам также необходимо убедиться, что каталог точек доступа будет заполнен правильными URI. У каждой службы есть набор переменных, которые необходимо переопределить. Обычно такие переменные имеют следующий формат:
<service>_service_publicuri
<service>_service_internaluri
<service>_service_adminuri
Ниже вы можете найти пример определения точек доступа для Keystone и Nova:
keystone_service_publicuri: "{{ openstack_service_publicuri_proto }}://identity.{{ external_lb_vip_address }}"
keystone_service_internaluri: "{{ openstack_service_internaluri_proto }}://identity.{{ internal_lb_vip_address }}"
keystone_service_adminuri: "{{ openstack_service_adminuri_proto }}://identity.{{ internal_lb_vip_address }}"
nova_service_publicuri: "{{ openstack_service_publicuri_proto }}://compute.{{ external_lb_vip_address }}"
nova_service_internaluri: "{{ openstack_service_internaluri_proto }}://compute.{{ internal_lb_vip_address }}"
nova_service_adminuri: "{{ openstack_service_adminuri_proto }}://compute.{{ internal_lb_vip_address }}"
nova_novncproxy_base_uri: "{{ nova_novncproxy_proto }}://novnc.{{ external_lb_vip_address }}"
Использование Let’s Encrypt¶
Хотя вы можете рассмотреть возможность использования wildcard сертификата или сертификата SAN TLS для домена, чтобы охватить все точки доступа службы в этой настройке, по-прежнему можно использовать сертификаты Let’s Encrypt с аутентификацией dns-01 или предоставить список поддоменов, на которые будет распространяться выданный сертификат.
Итак, ваша конфигурация Let’s Encrypt может выглядеть следующим образом:
haproxy_ssl_letsencrypt_enable: True
haproxy_ssl_letsencrypt_email: "root@{{ external_lb_vip_address }}"
haproxy_ssl_letsencrypt_domains:
- "{{ external_lb_vip_address }}"
- "identity.{{ external_lb_vip_address }}"
- "compute.{{ external_lb_vip_address }}"
Примечание
Обратите внимание, что внутренние полные доменные имена по-прежнему будут защищены самоподписанными сертификатами, поскольку в большинстве случаев Let’s Encrypt не сможет проверить право собственности на домен для внутренних VIP-адресов, если только не используется аутентификация dns-01.
Вам также может понадобиться позаботиться о расширении имен CN для выданных SAN-сертификатов ролью PKI. Для этого вам придется переопределить переменную haproxy_vip_binds
, как в примере ниже:
haproxy_vip_binds:
- address: "{{ haproxy_bind_external_lb_vip_address }}"
interface: "{{ haproxy_bind_external_lb_vip_interface }}"
type: external
- address: "{{ haproxy_bind_internal_lb_vip_address }}"
interface: "{{ haproxy_bind_internal_lb_vip_interface }}"
type: internal
pki_san_records:
- "{{ internal_lb_vip_address }}"
- "identity.{{ internal_lb_vip_address }}"
- "compute.{{ internal_lb_vip_address }}"
Вы также можете захотеть настроить заголовки HSTS, определенные переменной haproxy_security_headers_csp
. Хотя правила по умолчанию разрешают поддомены из коробки, вы можете захотеть ограничить записи немного больше, чтобы запретить доступ к произвольным портам.
Примечание
Переменные haproxy_security_child_src_records
и haproxy_security_connect_src_records
доступны только начиная с версии 2024.2 (Dalmatian). Для более ранних версий вам необходимо полностью переопределить haproxy_security_headers_csp
haproxy_security_child_src_records:
- "novnc.{{ external_lb_vip_address }}"
haproxy_security_connect_src_records:
- "{{ external_lb_vip_address }}
haproxy_security_frame_ancestors_records:
- "{{ external_lb_vip_address }}
Настройка точек доступа на основе пути¶
Точки доступа на основе пути подразумевают обслуживание служб на одном и том же полном доменном имени, но дифференциацию их на основе URI.
Например, Keystone можно настроить как https://domain.com/identity/v3
, а Nova как https://domain.com/compute/v2.1
Предупреждение
Обратите внимание, что Horizon использует /identity для своей панели Keystone, поэтому, если вы обслуживаете Horizon на / (по умолчанию) и используете /identity для пересылки трафика на бекэнд Keystone, управление пользователями, ролями и проектами внутри Horizon будет нарушено из-за конфликта.
Хотя точки доступа на основе пути могут выглядеть заманчиво из-за использования FQDN и, таким образом, отсутствия необходимости в wildcard TLS, их сложнее поддерживать и настраивать. Также стоит упомянуть, что не все службы готовы поддерживать точки доступа на основе пути, несмотря на то, что этот подход используется в devstack.
Хорошим примером исключений, которые в данный момент не поддерживают точки доступа на основе путей, являются консоли VNC для виртуальных машин (будут реализованы с помощью blueprint), Magnum (отчет об ошибке <https://launchpad.net/bugs/2083168>) и Ceph Rados Gateway.
Конфигурация HAProxy¶
Подобно доменным точкам доступа мы полагаемся на функциональность карт HAProxy. Но вместо map_dom
мы будем использовать map_reg
.
Итак, нам нужно определить файл карты, который будет использоваться, и способ его анализа. Для этого нам нужно применить переопределение для службы base.
haproxy_base_service_overrides:
haproxy_maps:
- 'use_backend %[path,map_reg(/etc/haproxy/base_regex.map)]'
Если вам действительно нужен Ceph RGW или вы хотите объединить подходы на основе домена и пути, вы можете сделать это, определив два файла карт:
Примечание
В случае каких-либо изменений в переменной haproxy_base_service_overrides
необходимо повторно запустить openstack-ansible openstack.osa.haproxy --tags haproxy-service-config
.
haproxy_base_service_overrides:
haproxy_maps:
- 'use_backend %[req.hdr(host),map_dom(/etc/haproxy/base_domain.map)] if { req.hdr(host),map_dom(/etc/haproxy/base_domain.map) -m found }'
- 'use_backend %[path,map_reg(/etc/haproxy/base_regex.map)]'
Если ни один домен не будет совпадать, HAProxy продолжит работу с точками доступа на основе пути.
Далее нам необходимо убедиться, что конфигурация HAProxy для каждой службы содержит наполнение карты HAProxy с соответствующим условием, например:
Примечание
После внесения изменений в переменную haproxy_<service>_service_overrides
вам необходимо повторно запустить сценарий для конкретной службы с тегом haproxy-service-config, например openstack-ansible openstack.osa.keystone --tags haproxy-service-config
.
haproxy_keystone_service_overrides:
haproxy_backend_only: True
haproxy_map_entries:
- name: base_regex
entries:
- "^/identity keystone_service-back"
haproxy_nova_api_compute_service_overrides:
haproxy_backend_only: True
haproxy_map_entries:
- name: base_regex
entries:
- "^/compute nova_api_os_compute-back"
Конфигурация службы¶
Подобно доменным точкам доступа нам нужно переопределить значение точек доступа для каждой службы. Точки доступа обычно определяются со следующими переменными:
<service>_service_publicuri
<service>_service_internaluri
<service>_service_adminuri
Ниже вы можете найти пример определения точек доступа для Keystone и Nova:
keystone_service_publicuri: "{{ openstack_service_publicuri_proto }}://{{ external_lb_vip_address }}/identity"
keystone_service_internaluri: "{{ openstack_service_internaluri_proto }}://{{ internal_lb_vip_address }}/identity"
keystone_service_adminuri: "{{ openstack_service_adminuri_proto }}://{{ internal_lb_vip_address }}/identity"
nova_service_publicuri: "{{ openstack_service_publicuri_proto }}://{{ external_lb_vip_address }}/compute"
nova_service_internaluri: "{{ openstack_service_internaluri_proto }}://{{ internal_lb_vip_address }}/compute"
nova_service_adminuri: "{{ openstack_service_adminuri_proto }}://{{ internal_lb_vip_address }}/compute"
Однако есть еще одна важная часть конфигурации, требуемая для каждой службы, которая не является случаем для настройки на основе домена. Все службы предполагают, что они обслуживаются по корневому пути (т. е. /), тогда как в подходе на основе пути мы используем уникальный путь для каждой службы.
Итак, теперь нам нужно заставить службу уважать путь и правильно реагировать на него. Одним из способов сделать это может быть использование rewrite механизма в uWSGI, например:
Предупреждение
Пример ниже не отображает правильный подход к настройке точки доступа на основе пути для большинства служб
keystone_uwsgi_ini_overrides:
uwsgi:
route: '^/identity(.*)$ rewrite:$1'
Но этот подход не является правильным и приведет к проблемам в некоторых клиентах или вариантах использования, несмотря на то, что служба выглядит полностью функциональной. Проблема с подходом выше связана с тем, как службы возвращают self URL, когда его запрашивают. Большинство служб ответят своей текущей микроверсией и URI этой микроверсии в ответе.
Если вы используете rewrite uWSGI, как показано выше, вы получите такой ответ:
curl https://cloud.com/identity/ | jq
{
"versions": {
"values": [
{
"id": "v3.14",
"status": "stable",
"updated": "2020-04-07T00:00:00Z",
"links": [
{
"rel": "self",
"href": "https://cloud.com/v3/"
}
],
"media-types": [
{
"base": "application/json",
"type": "application/vnd.openstack.identity-v3+json"
}
]
}
]
}
}
Как вы могли заметить, href указывает не на ожидаемое местоположение. Хотя некоторые клиенты могут не ссылаться на ссылку href, предоставленную сервисом, другие могут использовать ее как источник истины, что приведет к сбоям.
Некоторые службы, такие как keystone, имеют параметры конфигурации, которые могут контролировать, как определяется href. Например, keystone имеет параметр [DEFAULT]/public_endpoint, но этот подход не является единообразным для всех служб. Более того, keystone вернет предоставленный public_endpoint для всех точек доступа, включая административную и внутреннюю.
При этом единственным правильным подходом здесь будет настройка api-paste.ini
для каждой соответствующей службы. Но, в частности, Keystone не поддерживает файлы api-paste.ini. Поэтому единственный способ обойти это — фактически использовать uWSGI rewrite и определить public_endpoint в keystone.conf:
keystone_keystone_conf_overrides:
DEFAULT:
public_endpoint: "{{ keystone_service_publicuri }}"
Для других служб применение api-paste.ini
можно сделать с переменными, но у каждой службы там довольно уникальный контент, поэтому подход нельзя легко обобщить. Ниже вы можете найти переопределения, сделанные для некоторых служб в качестве примера:
_glance_api_paste_struct:
/: {}
/healthcheck: {}
/image: api
/image/healthcheck: healthcheck
glance_glance_api_paste_ini_overrides:
composite:glance-api: "{{ _glance_api_paste_struct }}"
composite:glance-api-caching: "{{ _glance_api_paste_struct }}"
composite:glance-api-cachemanagement: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone+caching: "{{ _glance_api_paste_struct }}"
composite:glance-api-keystone+cachemanagement: "{{ _glance_api_paste_struct }}"
neutron_api_paste_ini_overrides:
composite:neutron:
/: {}
/v2.0: {}
/network/: neutronversions_composite
/network/v2.0: neutronapi_v2_0
nova_api_paste_ini_overrides:
composite:osapi_compute:
/: {}
/v2: {}
/v2.1: {}
/v2/+: {}
/v2.1/+: {}
/compute: oscomputeversions
/compute/v2: oscomputeversion_legacy_v2
/compute/v2.1: oscomputeversion_v2
/compute/v2/+: openstack_compute_api_v21_legacy_v2_compatible
/compute/v2.1/+: openstack_compute_api_v21
Мы рекомендуем обратиться к api-paste.ini каждой службы для получения более подробной информации о том, как правильно настроить переопределения.