diff --git a/bifrost/cli.py b/bifrost/cli.py index 1984705a8..a9502ff64 100644 --- a/bifrost/cli.py +++ b/bifrost/cli.py @@ -158,7 +158,7 @@ def cmd_install(args): network_interface=args.network_interface, enable_keystone=args.enable_keystone, use_public_urls=args.enable_keystone, - noauth_mode=not args.enable_keystone, + noauth_mode='false', enabled_hardware_types=args.hardware_types, cleaning_disk_erase=args.cleaning_disk_erase, testing=args.testenv, diff --git a/lower-constraints.txt b/lower-constraints.txt index 6e32b761e..dcdda972d 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -31,6 +31,7 @@ oslo.i18n==3.20.0 oslo.log==3.36.0 oslo.serialization==2.25.0 oslo.utils==3.36.0 +passlib==1.7.2 pbr==2.0.0 prettytable==0.7.2 pyasn1==0.4.2 diff --git a/playbooks/ci/run.yaml b/playbooks/ci/run.yaml index 701b3f980..ab64b7655 100644 --- a/playbooks/ci/run.yaml +++ b/playbooks/ci/run.yaml @@ -15,3 +15,4 @@ ZUUL_BRANCH: "{{ zuul.branch }}" BOOT_MODE: "{{ boot_mode | default('') }}" TEST_VM_NODE_DRIVER: "{{ test_driver | default('ipmi') }}" + NOAUTH_MODE: "{{ noauth_mode | default(false) | bool | lower }}" diff --git a/playbooks/roles/bifrost-ironic-install/README.md b/playbooks/roles/bifrost-ironic-install/README.md index 24dc8d099..ca7bad8f4 100644 --- a/playbooks/roles/bifrost-ironic-install/README.md +++ b/playbooks/roles/bifrost-ironic-install/README.md @@ -25,6 +25,14 @@ bifrost-ironic-install role. 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, 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. diff --git a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Debian_family.yml b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Debian_family.yml index fdd2a2b70..d48cf1907 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Debian_family.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Debian_family.yml @@ -33,6 +33,7 @@ required_packages: - python-pip - gcc - dnsmasq + - apache2-utils # 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 # temporarily block packages following it while the system is being diff --git a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Fedora.yml b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Fedora.yml index 9d8f5c6dc..578227c3f 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Fedora.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Fedora.yml @@ -10,6 +10,7 @@ required_packages: - dnsmasq - gcc - genisoimage + - httpd-tools - ipmitool - ipxe-bootimgs - kpartx diff --git a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_RedHat_family.yml b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_RedHat_family.yml index af51b4f8e..ca2fa0d0a 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_RedHat_family.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_RedHat_family.yml @@ -36,6 +36,7 @@ required_packages: - socat - firewalld - python3-firewall + - httpd-tools iscsi_required_packages: - iscsi-initiator-utils - gdisk diff --git a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Suse_family.yml b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Suse_family.yml index ac1d88507..23cbe65b1 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Suse_family.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Suse_family.yml @@ -41,6 +41,7 @@ required_packages: - python-pip - gcc - python-PyMySQL + - apache2-utils iscsi_required_packages: - open-iscsi - gptfdisk diff --git a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Ubuntu.yml b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Ubuntu.yml index 17b5ee2f0..896ffc3c1 100644 --- a/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Ubuntu.yml +++ b/playbooks/roles/bifrost-ironic-install/defaults/required_defaults_Ubuntu.yml @@ -27,6 +27,7 @@ required_packages: - uuid-runtime - curl - dnsmasq + - apache2-utils # 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 # temporarily block packages following it while the system is being diff --git a/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml b/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml index 0e271060c..016938972 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/bootstrap.yml @@ -134,6 +134,26 @@ command: cp -r "{{ ironic_git_folder }}/etc/ironic/rootwrap.d/" "/etc/ironic/rootwrap.d" 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" include: keystone_setup.yml when: enable_keystone is defined and enable_keystone | bool == true diff --git a/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml b/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml index 6dda47c10..0a3fa5a62 100644 --- a/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml +++ b/playbooks/roles/bifrost-ironic-install/tasks/inspector_bootstrap.yml @@ -61,6 +61,26 @@ - name: "Copy rootwrap.d contents from ironic-inspector source folder" 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 " include: keystone_setup_inspector.yml when: enable_keystone is defined and enable_keystone | bool == true diff --git a/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 b/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 index ad71313cc..7de58e8bd 100644 --- a/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 +++ b/playbooks/roles/bifrost-ironic-install/templates/ironic-inspector.conf.j2 @@ -1,10 +1,13 @@ # {{ ansible_managed }} [DEFAULT] -{% if enable_keystone is defined and enable_keystone | bool == true %} +{% if enable_keystone | bool %} auth_strategy = keystone -{% else %} +{% elif noauth_mode | bool %} auth_strategy = noauth +{% else %} +auth_strategy = http_basic +http_basic_auth_user_file = /etc/ironic-inspector/htpasswd {% endif %} debug = {{ inspector_debug | bool }} @@ -29,7 +32,7 @@ driver = iptables {% endif %} [ironic] -{% if enable_keystone is defined and enable_keystone | bool == true %} +{% if enable_keystone | bool %} os_region = {{ keystone.bootstrap.region_name | default('RegionOne') }} project_name = baremetal username = {{ ironic_inspector.keystone.default_username }} @@ -38,10 +41,14 @@ auth_url = {{ ironic_inspector.service_catalog.auth_url }} auth_type = password user_domain_id = default project_domain_id = default - -{% else %} +{% elif noauth_mode | bool %} auth_type = none endpoint_override = {{ ironic_api_url }} +{% else %} +auth_type = http_basic +endpoint_override = {{ ironic_api_url }} +username = {{ admin_username }} +password = {{ admin_password }} {% endif %} {% if enable_keystone is defined and enable_keystone | bool == true %} diff --git a/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 b/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 index 48ea0183b..c52ae38d8 100644 --- a/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 +++ b/playbooks/roles/bifrost-ironic-install/templates/ironic.conf.j2 @@ -27,10 +27,13 @@ transport_url = rabbit://ironic:{{ironic_db_password }}@{{ message_queue_host | rpc_transport = json-rpc {% endif %} -{% if enable_keystone is defined and enable_keystone | bool == true %} +{% if enable_keystone | bool %} auth_strategy = keystone -{% else %} +{% elif noauth_mode | bool %} auth_strategy = noauth +{% else %} +auth_strategy = http_basic +http_basic_auth_user_file = /etc/ironic/htpasswd {% endif %} {% if ironic_log_dir | default("") != "" %} @@ -106,7 +109,7 @@ use_web_server_for_images = true [inspector] power_off = {{ power_off_after_inspection }} 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_url = {{ ironic.service_catalog.auth_url }} username = {{ ironic.service_catalog.username }} @@ -116,9 +119,14 @@ project_name = {{ ironic.service_catalog.project_name }} project_domain_id = default region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}} callback_endpoint_override = http://{{ internal_ip }}:5050 -{% else %} +{% elif noauth_mode | bool %} auth_type=none 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 %} @@ -134,7 +142,7 @@ project_domain_id = default {% endif %} [service_catalog] -{% if enable_keystone is defined and enable_keystone | bool == true %} +{% if enable_keystone | bool %} auth_url = {{ ironic.service_catalog.auth_url }} auth_type = password project_name = {{ ironic.service_catalog.project_name }} @@ -143,13 +151,18 @@ password = {{ ironic.service_catalog.password }} user_domain_id = default project_domain_id = default region_name = {{ keystone.bootstrap.region_name | default('RegionOne')}} -{% else %} +{% elif noauth_mode | bool %} auth_type = none +{% else %} +auth_type = http_basic +username = {{ admin_username }} +password = {{ admin_password }} {% endif %} endpoint_override = http://{{ internal_ip }}:6385 [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_type = password project_name = {{ ironic.service_catalog.project_name }} @@ -157,6 +170,16 @@ username = {{ ironic.service_catalog.username }} password = {{ ironic.service_catalog.password }} user_domain_id = default project_domain_id = default -{% else %} +{% elif noauth_mode | bool %} +auth_strategy = noauth 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 %} diff --git a/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml b/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml index 7efbbe9f2..211b37e4e 100644 --- a/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml +++ b/playbooks/roles/bifrost-keystone-client-config/defaults/main.yml @@ -6,3 +6,16 @@ ironic_inspector_api_url: "http://localhost:5050" enable_venv: true 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' }}" + +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') }}" diff --git a/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 b/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 index ff932adaa..4be188458 100644 --- a/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 +++ b/playbooks/roles/bifrost-keystone-client-config/templates/clouds.yaml.j2 @@ -14,7 +14,7 @@ clouds: user_domain_id: "{{ cloud.1.config_user_domain_id | default('default') }}" identity_api_version: "3" {% endfor %} -{% else %} +{% elif noauth_mode | default(true) | bool %} bifrost: auth_type: "none" baremetal_endpoint_override: {{ ironic_api_url }} @@ -23,4 +23,19 @@ clouds: bifrost-inspector: auth_type: "none" 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 %} diff --git a/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 b/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 index 3c807073e..e1275f107 100644 --- a/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 +++ b/playbooks/roles/bifrost-keystone-client-config/templates/openrc.j2 @@ -1,7 +1,7 @@ #!/usr/bin/env bash # WARNING: This file is managed by bifrost. -{% if (enable_keystone | default(false) | bool) %} +{% if enable_keystone | bool %} case "$1" in {% for cloud in clouds | default({}) | dictsort %} # Section for {{ cloud.0 }} @@ -17,7 +17,22 @@ case "$1" in {% endfor %} *) echo -e "\nERROR unsupported or unspecified profile: $1\nMust be one of {{ clouds | default({}) | dictsort | map(attribute='0') | join(',') }}";; esac -{% else %} +{% elif noauth_mode | bool %} export OS_AUTH_TYPE=none 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 %} diff --git a/releasenotes/notes/http-basic-40df399ea63956aa.yaml b/releasenotes/notes/http-basic-40df399ea63956aa.yaml new file mode 100644 index 000000000..ea9939fcb --- /dev/null +++ b/releasenotes/notes/http-basic-40df399ea63956aa.yaml @@ -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. diff --git a/requirements.txt b/requirements.txt index 9186f7f13..70f3ed948 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 PyYAML>=3.12 # MIT +passlib>=1.7.2 # BSD # TODO(dtantsur): remove pyOpenSSL when we no longer support Bionic and # openSUSE updates its version to at least 18.0.0. pyOpenSSL>=18.0.0 # Apache-2.0 diff --git a/scripts/test-bifrost.sh b/scripts/test-bifrost.sh index 1c49ca513..c7dab22ad 100755 --- a/scripts/test-bifrost.sh +++ b/scripts/test-bifrost.sh @@ -41,7 +41,7 @@ DOWNLOAD_IPA=true CREATE_IPA_IMAGE=false WRITE_INTERFACES_FILE=true PROVISION_WAIT_TIMEOUT=${PROVISION_WAIT_TIMEOUT:-900} -NOAUTH_MODE=true +NOAUTH_MODE=${NOAUTH_MODE:-true} CLOUD_CONFIG="" WAIT_FOR_DEPLOY=true diff --git a/zuul.d/bifrost-jobs.yaml b/zuul.d/bifrost-jobs.yaml index e103356bf..6d8fab7ea 100644 --- a/zuul.d/bifrost-jobs.yaml +++ b/zuul.d/bifrost-jobs.yaml @@ -34,6 +34,7 @@ timeout: 7200 vars: use_dhcp: true + noauth_mode: true - job: name: bifrost-integration-dhcp-ubuntu-bionic @@ -66,6 +67,8 @@ name: bifrost-integration-tinyipa parent: bifrost-base timeout: 3600 + vars: + noauth_mode: false - job: name: bifrost-integration-tinyipa-ubuntu-bionic