Support HTTP basic auth and switch bifrost-cli to it

The voting jobs also use the new mode (since it requires more OS
coverage), the DHCP jobs are left with no-auth.

Change-Id: I8c8ee112edbc1e61b54aff13c06c518c7daa9700
This commit is contained in:
Dmitry Tantsur 2020-07-20 17:06:04 +02:00
parent 02fb11fcc7
commit bcda97b630
20 changed files with 164 additions and 18 deletions

View File

@ -158,7 +158,7 @@ def cmd_install(args):
network_interface=args.network_interface, network_interface=args.network_interface,
enable_keystone=args.enable_keystone, enable_keystone=args.enable_keystone,
use_public_urls=args.enable_keystone, use_public_urls=args.enable_keystone,
noauth_mode=not args.enable_keystone, noauth_mode='false',
enabled_hardware_types=args.hardware_types, enabled_hardware_types=args.hardware_types,
cleaning_disk_erase=args.cleaning_disk_erase, cleaning_disk_erase=args.cleaning_disk_erase,
testing=args.testenv, testing=args.testenv,

View File

@ -31,6 +31,7 @@ oslo.i18n==3.20.0
oslo.log==3.36.0 oslo.log==3.36.0
oslo.serialization==2.25.0 oslo.serialization==2.25.0
oslo.utils==3.36.0 oslo.utils==3.36.0
passlib==1.7.2
pbr==2.0.0 pbr==2.0.0
prettytable==0.7.2 prettytable==0.7.2
pyasn1==0.4.2 pyasn1==0.4.2

View File

@ -15,3 +15,4 @@
ZUUL_BRANCH: "{{ zuul.branch }}" ZUUL_BRANCH: "{{ zuul.branch }}"
BOOT_MODE: "{{ boot_mode | default('') }}" BOOT_MODE: "{{ boot_mode | default('') }}"
TEST_VM_NODE_DRIVER: "{{ test_driver | default('ipmi') }}" TEST_VM_NODE_DRIVER: "{{ test_driver | default('ipmi') }}"
NOAUTH_MODE: "{{ noauth_mode | default(false) | bool | lower }}"

View File

@ -25,6 +25,14 @@ bifrost-ironic-install role.
testing: false testing: false
Enables no-authentication mode where no authentication is used for accessing
API services. Setting it to ``false`` will make ironic and ironic-inspector
either use keystone (if ``enable_keystone`` is true) or HTTP basic auth
(use ``admin_username``/``admin_password`` and
``default_username``/``default_password`` to configure).
noauth_mode: true
Node cleaning, which was a feature added to ironic during the Kilo cycle, Node cleaning, which was a feature added to ironic during the Kilo cycle,
removes the previous contents of a node once it has been moved from an removes the previous contents of a node once it has been moved from an
active to available state, such as setting the provision state to deleted. active to available state, such as setting the provision state to deleted.

View File

@ -33,6 +33,7 @@ required_packages:
- python-pip - python-pip
- gcc - gcc
- dnsmasq - dnsmasq
- apache2-utils
# NOTE(TheJulia): The above entry for dnsmasq must be the last entry in the # NOTE(TheJulia): The above entry for dnsmasq must be the last entry in the
# package list as the installation causes name resolution changes that can # package list as the installation causes name resolution changes that can
# temporarily block packages following it while the system is being # temporarily block packages following it while the system is being

View File

@ -10,6 +10,7 @@ required_packages:
- dnsmasq - dnsmasq
- gcc - gcc
- genisoimage - genisoimage
- httpd-tools
- ipmitool - ipmitool
- ipxe-bootimgs - ipxe-bootimgs
- kpartx - kpartx

View File

@ -36,6 +36,7 @@ required_packages:
- socat - socat
- firewalld - firewalld
- python3-firewall - python3-firewall
- httpd-tools
iscsi_required_packages: iscsi_required_packages:
- iscsi-initiator-utils - iscsi-initiator-utils
- gdisk - gdisk

View File

@ -41,6 +41,7 @@ required_packages:
- python-pip - python-pip
- gcc - gcc
- python-PyMySQL - python-PyMySQL
- apache2-utils
iscsi_required_packages: iscsi_required_packages:
- open-iscsi - open-iscsi
- gptfdisk - gptfdisk

View File

@ -27,6 +27,7 @@ required_packages:
- uuid-runtime - uuid-runtime
- curl - curl
- dnsmasq - dnsmasq
- apache2-utils
# NOTE(TheJulia): The above entry for dnsmasq must be the last entry in the # NOTE(TheJulia): The above entry for dnsmasq must be the last entry in the
# package list as the installation causes name resolution changes that can # package list as the installation causes name resolution changes that can
# temporarily block packages following it while the system is being # temporarily block packages following it while the system is being

View File

@ -134,6 +134,26 @@
command: cp -r "{{ ironic_git_folder }}/etc/ironic/rootwrap.d/" "/etc/ironic/rootwrap.d" command: cp -r "{{ ironic_git_folder }}/etc/ironic/rootwrap.d/" "/etc/ironic/rootwrap.d"
when: not skip_install | bool when: not skip_install | bool
- name: "Generate admin htpasswd for ironic"
htpasswd:
path: /etc/ironic/htpasswd
crypt_scheme: bcrypt
name: "{{ admin_username }}"
password: "{{ admin_password }}"
when:
- not noauth_mode | bool
- not enable_keystone | bool
- name: "Generate user htpasswd for ironic"
htpasswd:
path: /etc/ironic/htpasswd
crypt_scheme: bcrypt
name: "{{ default_username }}"
password: "{{ default_password }}"
when:
- not noauth_mode | bool
- not enable_keystone | bool
- name: "Populate keystone for Bifrost" - name: "Populate keystone for Bifrost"
include: keystone_setup.yml include: keystone_setup.yml
when: enable_keystone is defined and enable_keystone | bool == true when: enable_keystone is defined and enable_keystone | bool == true

View File

@ -61,6 +61,26 @@
- name: "Copy rootwrap.d contents from ironic-inspector source folder" - name: "Copy rootwrap.d contents from ironic-inspector source folder"
command: cp -r "{{ ironicinspector_git_folder }}/rootwrap.d/" "/etc/ironic-inspector/rootwrap.d" command: cp -r "{{ ironicinspector_git_folder }}/rootwrap.d/" "/etc/ironic-inspector/rootwrap.d"
- name: "Generate admin htpasswd for ironic-inspector"
htpasswd:
path: /etc/ironic-inspector/htpasswd
crypt_scheme: bcrypt
name: "{{ admin_username }}"
password: "{{ admin_password }}"
when:
- not noauth_mode | bool
- not enable_keystone | bool
- name: "Generate user htpasswd for ironic-inspector"
htpasswd:
path: /etc/ironic-inspector/htpasswd
crypt_scheme: bcrypt
name: "{{ default_username }}"
password: "{{ default_password }}"
when:
- not noauth_mode | bool
- not enable_keystone | bool
- name: "Populate keystone for ironic-inspector " - name: "Populate keystone for ironic-inspector "
include: keystone_setup_inspector.yml include: keystone_setup_inspector.yml
when: enable_keystone is defined and enable_keystone | bool == true when: enable_keystone is defined and enable_keystone | bool == true

View File

@ -1,10 +1,13 @@
# {{ ansible_managed }} # {{ ansible_managed }}
[DEFAULT] [DEFAULT]
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
auth_strategy = keystone auth_strategy = keystone
{% else %} {% elif noauth_mode | bool %}
auth_strategy = noauth auth_strategy = noauth
{% else %}
auth_strategy = http_basic
http_basic_auth_user_file = /etc/ironic-inspector/htpasswd
{% endif %} {% endif %}
debug = {{ inspector_debug | bool }} debug = {{ inspector_debug | bool }}
@ -29,7 +32,7 @@ driver = iptables
{% endif %} {% endif %}
[ironic] [ironic]
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
os_region = {{ keystone.bootstrap.region_name | default('RegionOne') }} os_region = {{ keystone.bootstrap.region_name | default('RegionOne') }}
project_name = baremetal project_name = baremetal
username = {{ ironic_inspector.keystone.default_username }} username = {{ ironic_inspector.keystone.default_username }}
@ -38,10 +41,14 @@ auth_url = {{ ironic_inspector.service_catalog.auth_url }}
auth_type = password auth_type = password
user_domain_id = default user_domain_id = default
project_domain_id = default project_domain_id = default
{% elif noauth_mode | bool %}
{% else %}
auth_type = none auth_type = none
endpoint_override = {{ ironic_api_url }} endpoint_override = {{ ironic_api_url }}
{% else %}
auth_type = http_basic
endpoint_override = {{ ironic_api_url }}
username = {{ admin_username }}
password = {{ admin_password }}
{% endif %} {% endif %}
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone is defined and enable_keystone | bool == true %}

View File

@ -27,10 +27,13 @@ transport_url = rabbit://ironic:{{ironic_db_password }}@{{ message_queue_host |
rpc_transport = json-rpc rpc_transport = json-rpc
{% endif %} {% endif %}
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
auth_strategy = keystone auth_strategy = keystone
{% else %} {% elif noauth_mode | bool %}
auth_strategy = noauth auth_strategy = noauth
{% else %}
auth_strategy = http_basic
http_basic_auth_user_file = /etc/ironic/htpasswd
{% endif %} {% endif %}
{% if ironic_log_dir | default("") != "" %} {% if ironic_log_dir | default("") != "" %}
@ -106,7 +109,7 @@ use_web_server_for_images = true
[inspector] [inspector]
power_off = {{ power_off_after_inspection }} power_off = {{ power_off_after_inspection }}
extra_kernel_params = {{ inspector_extra_kernel_options | default('') }} extra_kernel_params = {{ inspector_extra_kernel_options | default('') }}
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
auth_type = password auth_type = password
auth_url = {{ ironic.service_catalog.auth_url }} auth_url = {{ ironic.service_catalog.auth_url }}
username = {{ ironic.service_catalog.username }} username = {{ ironic.service_catalog.username }}
@ -116,9 +119,14 @@ project_name = {{ ironic.service_catalog.project_name }}
project_domain_id = default project_domain_id = default
region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}} region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}}
callback_endpoint_override = http://{{ internal_ip }}:5050 callback_endpoint_override = http://{{ internal_ip }}:5050
{% else %} {% elif noauth_mode | bool %}
auth_type=none auth_type=none
endpoint_override = http://{{ internal_ip }}:5050 endpoint_override = http://{{ internal_ip }}:5050
{% else %}
auth_type = http_basic
endpoint_override = http://{{ internal_ip }}:5050
username = {{ admin_username }}
password = {{ admin_password }}
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -134,7 +142,7 @@ project_domain_id = default
{% endif %} {% endif %}
[service_catalog] [service_catalog]
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
auth_url = {{ ironic.service_catalog.auth_url }} auth_url = {{ ironic.service_catalog.auth_url }}
auth_type = password auth_type = password
project_name = {{ ironic.service_catalog.project_name }} project_name = {{ ironic.service_catalog.project_name }}
@ -143,13 +151,18 @@ password = {{ ironic.service_catalog.password }}
user_domain_id = default user_domain_id = default
project_domain_id = default project_domain_id = default
region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}} region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}}
{% else %} {% elif noauth_mode | bool %}
auth_type = none auth_type = none
{% else %}
auth_type = http_basic
username = {{ admin_username }}
password = {{ admin_password }}
{% endif %} {% endif %}
endpoint_override = http://{{ internal_ip }}:6385 endpoint_override = http://{{ internal_ip }}:6385
[json_rpc] [json_rpc]
{% if enable_keystone is defined and enable_keystone | bool == true %} {% if enable_keystone | bool %}
auth_strategy = keystone
auth_url = {{ ironic.service_catalog.auth_url }} auth_url = {{ ironic.service_catalog.auth_url }}
auth_type = password auth_type = password
project_name = {{ ironic.service_catalog.project_name }} project_name = {{ ironic.service_catalog.project_name }}
@ -157,6 +170,16 @@ username = {{ ironic.service_catalog.username }}
password = {{ ironic.service_catalog.password }} password = {{ ironic.service_catalog.password }}
user_domain_id = default user_domain_id = default
project_domain_id = default project_domain_id = default
{% else %} {% elif noauth_mode | bool %}
auth_strategy = noauth
auth_type = none auth_type = none
{% else %}
auth_strategy = http_basic
auth_type = http_basic
http_basic_auth_user_file = /etc/ironic/htpasswd
username = {{ admin_username }}
password = {{ admin_password }}
# Deprecated - compatibility
http_basic_username = {{ admin_username }}
http_basic_password = {{ admin_password }}
{% endif %} {% endif %}

View File

@ -6,3 +6,16 @@ ironic_inspector_api_url: "http://localhost:5050"
enable_venv: true enable_venv: true
bifrost_venv_dir: "{{ lookup('env', 'VENV') or '/opt/stack/bifrost' }}" bifrost_venv_dir: "{{ lookup('env', 'VENV') or '/opt/stack/bifrost' }}"
ansible_python_interpreter: "{{ bifrost_venv_dir + '/bin/python3' if enable_venv | bool else '/usr/bin/python3' }}" ansible_python_interpreter: "{{ bifrost_venv_dir + '/bin/python3' if enable_venv | bool else '/usr/bin/python3' }}"
enable_keystone: false
noauth_mode: true
# Directory (on the controller) to keep the passwords
password_dir: "{{ lookup('env', 'HOME') }}/.config/bifrost"
# Various credentials
default_username: bifrost_user
default_password: "{{ lookup('password', password_dir + '/default_password') }}"
admin_username: admin
admin_password: "{{ lookup('password', password_dir + '/admin_password') }}"

View File

@ -14,7 +14,7 @@ clouds:
user_domain_id: "{{ cloud.1.config_user_domain_id | default('default') }}" user_domain_id: "{{ cloud.1.config_user_domain_id | default('default') }}"
identity_api_version: "3" identity_api_version: "3"
{% endfor %} {% endfor %}
{% else %} {% elif noauth_mode | default(true) | bool %}
bifrost: bifrost:
auth_type: "none" auth_type: "none"
baremetal_endpoint_override: {{ ironic_api_url }} baremetal_endpoint_override: {{ ironic_api_url }}
@ -23,4 +23,19 @@ clouds:
bifrost-inspector: bifrost-inspector:
auth_type: "none" auth_type: "none"
endpoint: {{ ironic_inspector_api_url }} endpoint: {{ ironic_inspector_api_url }}
{% else %}
bifrost:
auth_type: "http_basic"
auth:
username: "{{ default_username }}"
password: "{{ default_password }}"
endpoint: {{ ironic_api_url }}
baremetal_introspection_endpoint_override: {{ ironic_inspector_api_url }}
bifrost-admin:
auth_type: "http_basic"
auth:
username: "{{ admin_username }}"
password: "{{ admin_password }}"
endpoint: {{ ironic_api_url }}
baremetal_introspection_endpoint_override: {{ ironic_inspector_api_url }}
{% endif %} {% endif %}

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# WARNING: This file is managed by bifrost. # WARNING: This file is managed by bifrost.
{% if (enable_keystone | default(false) | bool) %} {% if enable_keystone | bool %}
case "$1" in case "$1" in
{% for cloud in clouds | default({}) | dictsort %} {% for cloud in clouds | default({}) | dictsort %}
# Section for {{ cloud.0 }} # Section for {{ cloud.0 }}
@ -17,7 +17,22 @@ case "$1" in
{% endfor %} {% endfor %}
*) echo -e "\nERROR unsupported or unspecified profile: $1\nMust be one of {{ clouds | default({}) | dictsort | map(attribute='0') | join(',') }}";; *) echo -e "\nERROR unsupported or unspecified profile: $1\nMust be one of {{ clouds | default({}) | dictsort | map(attribute='0') | join(',') }}";;
esac esac
{% else %} {% elif noauth_mode | bool %}
export OS_AUTH_TYPE=none export OS_AUTH_TYPE=none
export OS_ENDPOINT={{ ironic_api_url }} export OS_ENDPOINT={{ ironic_api_url }}
{% else %}
export OS_AUTH_TYPE=http_basic
export OS_ENDPOINT={{ ironic_api_url }}
unset OS_AUTH_URL
case "${1:-bifrost}" in
"bifrost")
export OS_USERNAME="{{ default_username }}"
export OS_PASSWORD="{{ default_password }}"
;;
"bifrost-admin")
export OS_USERNAME="{{ admin_username }}"
export OS_PASSWORD="{{ admin_password }}"
;;
*) echo -e "\nERROR unsupported or unspecified profile: $1\nMust be one of bifrost, bifrost-admin";;
esac
{% endif %} {% endif %}

View File

@ -0,0 +1,14 @@
---
features:
- |
HTTP basic authentication for API services is now supported in addition
to no authentication and Keystone. It is triggered by setting
``noauth_mode=false`` with ``enable_keystone=false``.
- |
Installations with ``bifrost-cli`` now use HTTP basic authentication if
Keystone is disabled.
deprecations:
- |
Bifrost will switch to HTTP basic authentication by default in the future.
If you want to avoid it, please set ``noauth_mode`` to ``false``
explicitly.

View File

@ -5,6 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0
PyYAML>=3.12 # MIT PyYAML>=3.12 # MIT
passlib>=1.7.2 # BSD
# TODO(dtantsur): remove pyOpenSSL when we no longer support Bionic and # TODO(dtantsur): remove pyOpenSSL when we no longer support Bionic and
# openSUSE updates its version to at least 18.0.0. # openSUSE updates its version to at least 18.0.0.
pyOpenSSL>=18.0.0 # Apache-2.0 pyOpenSSL>=18.0.0 # Apache-2.0

View File

@ -41,7 +41,7 @@ DOWNLOAD_IPA=true
CREATE_IPA_IMAGE=false CREATE_IPA_IMAGE=false
WRITE_INTERFACES_FILE=true WRITE_INTERFACES_FILE=true
PROVISION_WAIT_TIMEOUT=${PROVISION_WAIT_TIMEOUT:-900} PROVISION_WAIT_TIMEOUT=${PROVISION_WAIT_TIMEOUT:-900}
NOAUTH_MODE=true NOAUTH_MODE=${NOAUTH_MODE:-true}
CLOUD_CONFIG="" CLOUD_CONFIG=""
WAIT_FOR_DEPLOY=true WAIT_FOR_DEPLOY=true

View File

@ -34,6 +34,7 @@
timeout: 7200 timeout: 7200
vars: vars:
use_dhcp: true use_dhcp: true
noauth_mode: true
- job: - job:
name: bifrost-integration-dhcp-ubuntu-bionic name: bifrost-integration-dhcp-ubuntu-bionic
@ -66,6 +67,8 @@
name: bifrost-integration-tinyipa name: bifrost-integration-tinyipa
parent: bifrost-base parent: bifrost-base
timeout: 3600 timeout: 3600
vars:
noauth_mode: false
- job: - job:
name: bifrost-integration-tinyipa-ubuntu-bionic name: bifrost-integration-tinyipa-ubuntu-bionic