From 5a10f7e9e1851e8599f48ba25749d9cd403632f8 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 24 Jul 2025 10:08:20 +0000 Subject: [PATCH 01/12] Simplify the setting of host_ip in ironic.conf The value of host_ip is determined twice within the ironic.conf.j2 template file, by means of a relatively hard to read set of conditions. Avoid this duplication and improve readability by exporting the correct value once in scripts/configure-ironic.sh. This also leave more room for more complex evaluations should these be needed in the future (e.g. IPv6) Signed-off-by: Marco Chiappero --- ironic-config/ironic.conf.j2 | 4 ++-- scripts/configure-ironic.sh | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ironic-config/ironic.conf.j2 b/ironic-config/ironic.conf.j2 index ed48e9c5c..d3513718e 100644 --- a/ironic-config/ironic.conf.j2 +++ b/ironic-config/ironic.conf.j2 @@ -68,7 +68,7 @@ port = {{ env.IRONIC_PRIVATE_PORT }} {% endif %} public_endpoint = {{ env.IRONIC_BASE_URL }} {% else %} -host_ip = {% if env.LISTEN_ALL_INTERFACES | lower == "true" %}::{% else %}{{ env.IRONIC_IP }}{% endif %} +host_ip = {{ env.IRONIC_HOST_IP }} port = {{ env.IRONIC_LISTEN_PORT }} {% if env.IRONIC_TLS_SETUP == "true" %} enable_ssl_api = true @@ -186,7 +186,7 @@ cipher_suite_versions = 3,17 # containers are in host networking. auth_strategy = http_basic http_basic_auth_user_file = {{ env.IRONIC_RPC_HTPASSWD_FILE }} -host_ip = {% if env.LISTEN_ALL_INTERFACES | lower == "true" %}::{% else %}{{ env.IRONIC_IP }}{% endif %} +host_ip = {{ env.IRONIC_HOST_IP }} port = {{ env.IRONIC_JSON_RPC_PORT }} {% if env.IRONIC_TLS_SETUP == "true" %} use_ssl = true diff --git a/scripts/configure-ironic.sh b/scripts/configure-ironic.sh index 9671c4f81..66b4b4377 100755 --- a/scripts/configure-ironic.sh +++ b/scripts/configure-ironic.sh @@ -49,6 +49,12 @@ export IRONIC_IPA_COLLECTORS=${IRONIC_IPA_COLLECTORS:-default,logs} wait_for_interface_or_ip +if [[ "$(echo "${LISTEN_ALL_INTERFACES}" | tr '[:upper:]' '[:lower:]')" == "true" ]]; then + export IRONIC_HOST_IP="::" +else + export IRONIC_HOST_IP="${IRONIC_IP}" +fi + # Hostname to use for the current conductor instance. export IRONIC_CONDUCTOR_HOST=${IRONIC_CONDUCTOR_HOST:-${IRONIC_URL_HOST}} From b5e013b36dbe644a2d3aee4a27239ca91fc8bee9 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 13 Nov 2025 14:23:07 +0000 Subject: [PATCH 02/12] Introduce an address parsing utility function Whether IRONIC_IP or PROVISIONIG_IP is provided to ironic-image, any such address should be checked for validity and, if needed, properly formatted. For this reason, this commit introduces parse_ip_address to be consumed inside wait_for_interface_or_ip. Signed-off-by: Marco Chiappero --- scripts/ironic-common.sh | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index bf0ccf59f..981ea618d 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -69,6 +69,27 @@ export PROVISIONING_INTERFACE export LISTEN_ALL_INTERFACES="${LISTEN_ALL_INTERFACES:-true}" +parse_ip_address() +{ + local IP_ADDR + + if [[ $# -ne 1 ]]; then + echo "ERROR: ${FUNCNAME[0]}: please provide a single IP address as input" >&2 + return 1 + fi + + IP_ADDR="$1" + + if ipcalc "${IP_ADDR}" | grep ^INVALID &>/dev/null; then + echo "ERROR: ${FUNCNAME[0]}: Failed to parse ${IP_ADDR}" >&2 + return 2 + fi + + # Convert the address using ipcalc which strips out the subnet. + # For IPv6 addresses, this will give the short-form address + ipcalc "${IP_ADDR}" | grep "^Address:" | awk '{print $2}' +} + # Wait for the interface or IP to be up, sets $IRONIC_IP wait_for_interface_or_ip() { @@ -76,9 +97,12 @@ wait_for_interface_or_ip() # available on an interface, otherwise we look at $PROVISIONING_INTERFACE # for an IP if [[ -n "${PROVISIONING_IP}" ]]; then - # Convert the address using ipcalc which strips out the subnet. - # For IPv6 addresses, this will give the short-form address - IRONIC_IP="$(ipcalc "${PROVISIONING_IP}" | grep "^Address:" | awk '{print $2}')" + IRONIC_IP="$(parse_ip_address "${PROVISIONING_IP}")" + if [[ -z "${IRONIC_IP}" ]]; then + echo "ERROR: PROVISIONING_IP contains an invalid IP address, failed to start ironic" + exit 1 + fi + export IRONIC_IP until grep -F " ${IRONIC_IP}/" <(ip -br addr show); do echo "Waiting for ${IRONIC_IP} to be configured on an interface" From 25de33b8bdc41cbb98a44cdeb3e2064834a509db Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 23 Jul 2025 12:04:03 +0000 Subject: [PATCH 03/12] Introduce get_interface_of_ip to look PROVISIONING_IP up Add a new get_interface_of_ip function that returns the name of the interface of a given IP, and use it to determine if the PROVISIONING_IP address is actually present on a network interface. This improves the code readability and enables additional debugging output. Signed-off-by: Marco Chiappero --- scripts/ironic-common.sh | 44 +++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index 981ea618d..e8734a0d5 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -69,6 +69,33 @@ export PROVISIONING_INTERFACE export LISTEN_ALL_INTERFACES="${LISTEN_ALL_INTERFACES:-true}" +get_interface_of_ip() +{ + local IP_VERS + local IP_ADDR + + if [[ $# -gt 2 ]]; then + echo "ERROR: ${FUNCNAME[0]}: too many parameters" >&2 + return 1 + fi + + if [[ $# -eq 2 ]]; then + case "$2" in + 4|6) + IP_VERS="-$2" + ;; + *) + echo "ERROR: ${FUNCNAME[0]}: the second parameter should be [4|6] (or missing for both)" >&2 + return 2 + ;; + esac + fi + + IP_ADDR="$1" + + ip "${IP_VERS[@]}" -br addr show scope global | grep -i " ${IP_ADDR}/" | cut -f 1 -d ' ' | cut -f 1 -d '@' +} + parse_ip_address() { local IP_ADDR @@ -97,17 +124,24 @@ wait_for_interface_or_ip() # available on an interface, otherwise we look at $PROVISIONING_INTERFACE # for an IP if [[ -n "${PROVISIONING_IP}" ]]; then - IRONIC_IP="$(parse_ip_address "${PROVISIONING_IP}")" - if [[ -z "${IRONIC_IP}" ]]; then + local PARSED_IP + PARSED_IP="$(parse_ip_address "${PROVISIONING_IP}")" + if [[ -z "${PARSED_IP}" ]]; then echo "ERROR: PROVISIONING_IP contains an invalid IP address, failed to start ironic" exit 1 fi - export IRONIC_IP - until grep -F " ${IRONIC_IP}/" <(ip -br addr show); do - echo "Waiting for ${IRONIC_IP} to be configured on an interface" + local IFACE_OF_IP="" + until [[ -n "${IFACE_OF_IP}" ]]; do + echo "Waiting for ${PROVISIONING_IP} to be configured on an interface..." + IFACE_OF_IP="$(get_interface_of_ip "${PARSED_IP}")" sleep 1 done + + echo "Found ${PROVISIONING_IP} on interface \"${IFACE_OF_IP}\"!" + + export PROVISIONING_INTERFACE="${IFACE_OF_IP}" + export IRONIC_IP="${PARSED_IP}" else until [[ -n "$IRONIC_IP" ]]; do echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured" From 3c0fbf5dd1860c37dd737ef150904092e89df08e Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 23 Jul 2025 12:48:45 +0000 Subject: [PATCH 04/12] Revert 2742439 being now redundant Commit 2742439 added logic to tentatively identify the interface name in get_provisioning_interface if the PROVISIONING_IP is provided. However the same process in then repeated in wait_for_interface_or_ip. Signed-off-by: Marco Chiappero --- scripts/ironic-common.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index e8734a0d5..779072fa3 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -48,12 +48,6 @@ get_provisioning_interface() local interface="provisioning" - if [[ -n "${PROVISIONING_IP}" ]]; then - if ip -br addr show | grep -i " ${PROVISIONING_IP}/" &>/dev/null; then - interface="$(ip -br addr show | grep -i " ${PROVISIONING_IP}/" | cut -f 1 -d ' ' | cut -f 1 -d '@')" - fi - fi - for mac in ${PROVISIONING_MACS//,/ }; do if ip -br link show up | grep -i "$mac" &>/dev/null; then interface="$(ip -br link show up | grep -i "$mac" | cut -f 1 -d ' ' | cut -f 1 -d '@')" From bd05dc0c95b2b6cf8fbe360a064c3601833af9f1 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 24 Jul 2025 14:27:27 +0000 Subject: [PATCH 05/12] Improve validation of the input variables in wait_for_interface_or_ip Introduce an explicit check for IRONIC_IP, allowing PROVISIONING_INTERFACE to be tested separately, to improve quality and readability. Moreover, whenever PROVISIONING_INTERFACE is not set by the user, function get_provisioning_interface attempts to determine one, or provide "provisioning" as default value. However this can cause confusing errors down the line. So, also remove this default value and fail gracefully, with proper logging, if the PROVISIONING_INTERFACE value is not detected. Signed-off-by: Marco Chiappero --- scripts/ironic-common.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index 779072fa3..5a18eb368 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -46,7 +46,7 @@ get_provisioning_interface() return fi - local interface="provisioning" + local interface="" for mac in ${PROVISIONING_MACS//,/ }; do if ip -br link show up | grep -i "$mac" &>/dev/null; then @@ -136,13 +136,25 @@ wait_for_interface_or_ip() export PROVISIONING_INTERFACE="${IFACE_OF_IP}" export IRONIC_IP="${PARSED_IP}" - else + elif [[ -n "${IRONIC_IP}" ]]; then + local PARSED_IP + PARSED_IP="$(parse_ip_address "${IRONIC_IP}")" + if [[ -z "${PARSED_IP}" ]]; then + echo "ERROR: PROVISIONING_IP contains an invalid IP address, failed to start ironic" + exit 1 + fi + + export IRONIC_IP="${PARSED_IP}" + elif [[ -n "${PROVISIONING_INTERFACE}" ]]; then until [[ -n "$IRONIC_IP" ]]; do echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured" IRONIC_IP="$(ip -br add show scope global up dev "${PROVISIONING_INTERFACE}" | awk '{print $3}' | sed -e 's%/.*%%' | head -n 1)" export IRONIC_IP sleep 1 done + else + echo "ERROR: cannot determine an interface or an IP for binding and creating URLs" + return 1 fi # If the IP contains a colon, then it's an IPv6 address, and the HTTP From a3971ceb8eb0ce608b3a5723c3f0eb57fb597892 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 13 Nov 2025 13:20:42 +0000 Subject: [PATCH 06/12] Make IRONIC_IP higher priority than PROVISIONING_IP Up to now wait_for_interface_or_ip has parse the values in the following order: PROVISIONING_IP, IRONIC_IP, PROVISIONING_INTERFACE. However IRONIC_IP should likely be considered as overriding any PROVISIONING_* value. Thus, make sure IRONIC_IP is evaluated at the beginning of the chain. Signed-off-by: Marco Chiappero --- scripts/ironic-common.sh | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index 5a18eb368..78cc5925d 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -114,10 +114,20 @@ parse_ip_address() # Wait for the interface or IP to be up, sets $IRONIC_IP wait_for_interface_or_ip() { - # If $PROVISIONING_IP is specified, then we wait for that to become - # available on an interface, otherwise we look at $PROVISIONING_INTERFACE - # for an IP - if [[ -n "${PROVISIONING_IP}" ]]; then + # IRONIC_IP already defined overrides everything else + if [[ -n "${IRONIC_IP}" ]]; then + local PARSED_IP + PARSED_IP="$(parse_ip_address "${IRONIC_IP}")" + if [[ -z "${PARSED_IP}" ]]; then + echo "ERROR: PROVISIONING_IP contains an invalid IP address, failed to start ironic" + exit 1 + fi + + export IRONIC_IP="${PARSED_IP}" + elif [[ -n "${PROVISIONING_IP}" ]]; then + # If $PROVISIONING_IP is specified, then we wait for that to become + # available on an interface, otherwise we look at $PROVISIONING_INTERFACE + # for an IP local PARSED_IP PARSED_IP="$(parse_ip_address "${PROVISIONING_IP}")" if [[ -z "${PARSED_IP}" ]]; then @@ -135,15 +145,6 @@ wait_for_interface_or_ip() echo "Found ${PROVISIONING_IP} on interface \"${IFACE_OF_IP}\"!" export PROVISIONING_INTERFACE="${IFACE_OF_IP}" - export IRONIC_IP="${PARSED_IP}" - elif [[ -n "${IRONIC_IP}" ]]; then - local PARSED_IP - PARSED_IP="$(parse_ip_address "${IRONIC_IP}")" - if [[ -z "${PARSED_IP}" ]]; then - echo "ERROR: PROVISIONING_IP contains an invalid IP address, failed to start ironic" - exit 1 - fi - export IRONIC_IP="${PARSED_IP}" elif [[ -n "${PROVISIONING_INTERFACE}" ]]; then until [[ -n "$IRONIC_IP" ]]; do From c186334fd0883a7aeb77ff218a8e65dbbc213487 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 13 Nov 2025 16:37:56 +0000 Subject: [PATCH 07/12] Introduce IRONIC_IPV6 to bind on IPv6 sockets The ironic scripts either use PROVISIONING_IP as an input or try to determine an IP address to bind the sockets to. This results in IRONIC_IP being defined once the process is complete, and it can carry either an IPv4 or an IPv6 address. Likely, the assumption is that on Linux, by default, IPv4-mapped IPv6 addresses can be leveraged to serve both IPv4 and IPv6 through a single socket. However this is not a good practice and two separate sockets should be used instead, whenever possible. This change modifies such logic by - introducing the variable IRONIC_IPV6 alongside the existing IRONIC_IP - matching IRONIC_IP and attempting to populate both variables Please note that hostname based URLs, with both A and AAAA records, are also required for a fully working dual-stack configuration. Signed-off-by: Marco Chiappero --- scripts/configure-ironic.sh | 9 ++++- scripts/ironic-common.sh | 80 ++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/scripts/configure-ironic.sh b/scripts/configure-ironic.sh index 66b4b4377..ecf9743d8 100755 --- a/scripts/configure-ironic.sh +++ b/scripts/configure-ironic.sh @@ -136,4 +136,11 @@ render_j2_config "/etc/ironic/ironic.conf.j2" \ configure_json_rpc_auth # Make sure ironic traffic bypasses any proxies -export NO_PROXY="${NO_PROXY:-},$IRONIC_IP" +export NO_PROXY="${NO_PROXY:-}" + +if [[ -n "${IRONIC_IPV6}" ]]; then + export NO_PROXY="${NO_PROXY},${IRONIC_IPV6}" +fi +if [[ -n "${IRONIC_IP}" ]]; then + export NO_PROXY="${NO_PROXY},${IRONIC_IP}" +fi diff --git a/scripts/ironic-common.sh b/scripts/ironic-common.sh index 78cc5925d..492f67237 100644 --- a/scripts/ironic-common.sh +++ b/scripts/ironic-common.sh @@ -5,6 +5,7 @@ set -euxo pipefail # Export IRONIC_IP to avoid needing to lean on IRONIC_URL_HOST for consumption in # e.g. dnsmasq configuration export IRONIC_IP="${IRONIC_IP:-}" +export IRONIC_IPV6="" PROVISIONING_INTERFACE="${PROVISIONING_INTERFACE:-}" PROVISIONING_IP="${PROVISIONING_IP:-}" PROVISIONING_MACS="${PROVISIONING_MACS:-}" @@ -63,6 +64,33 @@ export PROVISIONING_INTERFACE export LISTEN_ALL_INTERFACES="${LISTEN_ALL_INTERFACES:-true}" +get_ip_of_interface() +{ + local IP_VERS + local IP_ADDR + + if [[ $# -gt 2 ]]; then + echo "ERROR: ${FUNCNAME[0]}: too many parameters" >&2 + return 1 + fi + + if [[ $# -eq 2 ]]; then + case "$2" in + 4|6) + IP_VERS="-$2" + ;; + *) + echo "ERROR: ${FUNCNAME[0]}: the second parameter should be [4|6] (or missing for both)" >&2 + return 2 + ;; + esac + fi + + IFACE="$1" + + ip "${IP_VERS[@]}" -br addr show scope global up dev "${IFACE}" | awk '{print $3}' | sed -e 's%/.*%%' | head -n 1 +} + get_interface_of_ip() { local IP_VERS @@ -123,7 +151,12 @@ wait_for_interface_or_ip() exit 1 fi - export IRONIC_IP="${PARSED_IP}" + if [[ "${PARSED_IP}" =~ .*:.* ]]; then + export IRONIC_IPV6="${PARSED_IP}" + export IRONIC_IP="" + else + export IRONIC_IP="${PARSED_IP}" + fi elif [[ -n "${PROVISIONING_IP}" ]]; then # If $PROVISIONING_IP is specified, then we wait for that to become # available on an interface, otherwise we look at $PROVISIONING_INTERFACE @@ -145,27 +178,46 @@ wait_for_interface_or_ip() echo "Found ${PROVISIONING_IP} on interface \"${IFACE_OF_IP}\"!" export PROVISIONING_INTERFACE="${IFACE_OF_IP}" - export IRONIC_IP="${PARSED_IP}" + # If the IP contains a colon, then it's an IPv6 address + if [[ "${PARSED_IP}" =~ .*:.* ]]; then + export IRONIC_IPV6="${PARSED_IP}" + else + export IRONIC_IP="${PARSED_IP}" + fi elif [[ -n "${PROVISIONING_INTERFACE}" ]]; then - until [[ -n "$IRONIC_IP" ]]; do - echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured" - IRONIC_IP="$(ip -br add show scope global up dev "${PROVISIONING_INTERFACE}" | awk '{print $3}' | sed -e 's%/.*%%' | head -n 1)" - export IRONIC_IP + until [[ -n "${IRONIC_IPV6}" ]] || [[ -n "${IRONIC_IP}" ]]; do + echo "Waiting for ${PROVISIONING_INTERFACE} interface to be configured..." + + IRONIC_IPV6="$(get_ip_of_interface "${PROVISIONING_INTERFACE}" 6)" + sleep 1 + + IRONIC_IP="$(get_ip_of_interface "${PROVISIONING_INTERFACE}" 4)" sleep 1 done + + # Add some debugging output + if [[ -n "${IRONIC_IPV6}" ]]; then + echo "Found ${IRONIC_IPV6} on interface \"${PROVISIONING_INTERFACE}\"!" + export IRONIC_IPV6 + fi + if [[ -n "${IRONIC_IP}" ]]; then + echo "Found ${IRONIC_IP} on interface \"${PROVISIONING_INTERFACE}\"!" + export IRONIC_IP + fi else echo "ERROR: cannot determine an interface or an IP for binding and creating URLs" return 1 fi - # If the IP contains a colon, then it's an IPv6 address, and the HTTP - # host needs surrounding with brackets - if [[ "$IRONIC_IP" =~ .*:.* ]]; then - export IPV=6 - export IRONIC_URL_HOST="[$IRONIC_IP]" - else - export IPV=4 - export IRONIC_URL_HOST="$IRONIC_IP" + # Define the URLs based on the what we have found, + # prioritize IPv6 for IRONIC_URL_HOST + if [[ -n "${IRONIC_IP}" ]]; then + export ENABLE_IPV4=yes + export IRONIC_URL_HOST="${IRONIC_IP}" + fi + if [[ -n "${IRONIC_IPV6}" ]]; then + export ENABLE_IPV6=yes + export IRONIC_URL_HOST="[${IRONIC_IPV6}]" # The HTTP host needs surrounding with brackets fi # Avoid having to construct full URL multiple times while allowing From 4c0c7eec927cdd40d78b5d888713abc5f4fbde09 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 23 Jul 2025 20:47:00 +0000 Subject: [PATCH 08/12] Use my_ipv6 when IRONIC_IPV6 is defined in ironic.conf As per the Ironic documentation: "This field [my_ip] does accept an IPv6 address as an override for templates and URLs, however it is recommended that [DEFAULT]my_ipv6 is used along with DNS names for service URLs for dual-stack environments." Fill my_ipv6 when an IPv6 address has been found for binding. Signed-off-by: Marco Chiappero --- ironic-config/ironic.conf.j2 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ironic-config/ironic.conf.j2 b/ironic-config/ironic.conf.j2 index d3513718e..df384f757 100644 --- a/ironic-config/ironic.conf.j2 +++ b/ironic-config/ironic.conf.j2 @@ -25,7 +25,13 @@ rpc_transport = none use_stderr = true # NOTE(dtantsur): the default md5 is not compatible with FIPS mode hash_ring_algorithm = sha256 +{% if env.ENABLE_IPV4 %} my_ip = {{ env.IRONIC_IP }} +{% endif %} +{% if env.ENABLE_IPV6 %} +my_ipv6 = {{ env.IRONIC_IPV6 }} +{% endif %} + host = {{ env.IRONIC_CONDUCTOR_HOST }} # If a path to a certificate is defined, use that first for webserver From 7f3797d1a48ea40309ec4b730483379674e9fb6e Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Thu, 24 Jul 2025 10:08:20 +0000 Subject: [PATCH 09/12] Set host_ip to an IPv6 address when found Prioritize IPv6 over IPv4 when available to set host_ip in ironic.conf when LISTEN_ALL_INTERFACES is not set to true. Signed-off-by: Marco Chiappero --- scripts/configure-ironic.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/configure-ironic.sh b/scripts/configure-ironic.sh index ecf9743d8..90adbe02f 100755 --- a/scripts/configure-ironic.sh +++ b/scripts/configure-ironic.sh @@ -51,6 +51,8 @@ wait_for_interface_or_ip if [[ "$(echo "${LISTEN_ALL_INTERFACES}" | tr '[:upper:]' '[:lower:]')" == "true" ]]; then export IRONIC_HOST_IP="::" +elif [[ -n "${ENABLE_IPV6}" ]]; then + export IRONIC_HOST_IP="${IRONIC_IPV6}" else export IRONIC_HOST_IP="${IRONIC_IP}" fi From cf2f85c18ece0fff34f49f0aad17d727c080565f Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 23 Jul 2025 21:41:53 +0000 Subject: [PATCH 10/12] Let Ironic API use IPv4 and IPv6 sockets when possible When LISTEN_ALL_INTERFACES is not set, Apache should make Ironic API avaiable on either or both IPv4 and IPv6 sockets, depending on the addresses requested or found on the system. Make sure to set the "Listen" directive according to ENABLE_IPV4 and ENABLE_IPV4, and the VirtualHost when IRONIC_URL_HOSTNAME is present. Signed-off-by: Marco Chiappero --- ironic-config/httpd-ironic-api.conf.j2 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ironic-config/httpd-ironic-api.conf.j2 b/ironic-config/httpd-ironic-api.conf.j2 index 3e7731b47..dfc248ef5 100644 --- a/ironic-config/httpd-ironic-api.conf.j2 +++ b/ironic-config/httpd-ironic-api.conf.j2 @@ -15,8 +15,14 @@ Listen {{ env.IRONIC_LISTEN_PORT }} {% else %} -Listen {{ env.IRONIC_URL_HOST }}:{{ env.IRONIC_LISTEN_PORT }} - +{% if env.ENABLE_IPV4 %} +Listen {{ env.IRONIC_IP }}:{{ env.IRONIC_LISTEN_PORT }} +{% endif %} +{% if env.ENABLE_IPV6 %} +Listen [{{ env.IRONIC_IPV6 }}]:{{ env.IRONIC_LISTEN_PORT }} +{% endif %} + +{% endif %} {% endif %} DocumentRoot "/shared/html" From 8dab2c5d1d798f0d84400f766893f5a9cbb0977f Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Mon, 28 Jul 2025 09:00:17 +0000 Subject: [PATCH 11/12] Update httpd.conf to bind to IPv4 and/or IPv6 sockets Enable the use of individual IPv4 and IPv6 sockets when the respective IP is detected and LISTEN_ALL_INTERFACES is not set to true. This allows to correctly bind to both the IPv4 and IPv6 addresses found and not just one of them. Signed-off-by: Marco Chiappero --- ironic-config/httpd.conf.j2 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ironic-config/httpd.conf.j2 b/ironic-config/httpd.conf.j2 index 7bebcdd1d..39f6954dd 100644 --- a/ironic-config/httpd.conf.j2 +++ b/ironic-config/httpd.conf.j2 @@ -2,7 +2,12 @@ ServerRoot {{ env.HTTPD_DIR }} {%- if env.LISTEN_ALL_INTERFACES | lower == "true" %} Listen {{ env.HTTP_PORT }} {% else %} -Listen {{ env.IRONIC_URL_HOST }}:{{ env.HTTP_PORT }} +{% if env.ENABLE_IPV4 %} +Listen {{ env.IRONIC_IP }}:{{ env.HTTP_PORT }} +{% endif %} +{% if env.ENABLE_IPV6 %} +Listen [{{ env.IRONIC_IPV6 }}]:{{ env.HTTP_PORT }} +{% endif %} {% endif %} Include /etc/httpd/conf.modules.d/*.conf User apache From 5e0ff03348c297d355d2eff5449a2b79f4b75b7d Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Wed, 23 Jul 2025 21:44:10 +0000 Subject: [PATCH 12/12] Let Apache use separate IPv4 and IPv6 sockets for listening to any Enable the use of two separate sockets for IPv4 and IPv6 when LISTEN_ALL_INTERFACES is set to true. While desirable, on Linux Apache uses IPv4-mapped IPv6 addresses by default, thus leveraging a single IPv6 socket for IPv4 connections as well. This behaviour is far from being desirable and can be disabled at compile time via the "--disable-v4-mapped" flag, so make sure both an ANY address Listen directive is present for both IPv4 and IPv6. When Apache is compiled with "--enable-v4-mapped", the IPv4 socket will be simply ignored. Please see https://httpd.apache.org/docs/2.4/bind.html for more information. Signed-off-by: Marco Chiappero --- ironic-config/apache2-ipxe.conf.j2 | 3 ++- ironic-config/apache2-vmedia.conf.j2 | 3 ++- ironic-config/httpd-ironic-api.conf.j2 | 3 ++- ironic-config/httpd.conf.j2 | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ironic-config/apache2-ipxe.conf.j2 b/ironic-config/apache2-ipxe.conf.j2 index f570e6ea1..9f4b41d92 100644 --- a/ironic-config/apache2-ipxe.conf.j2 +++ b/ironic-config/apache2-ipxe.conf.j2 @@ -1,4 +1,5 @@ -Listen {{ env.IPXE_TLS_PORT }} +Listen 0.0.0.0:{{ env.IPXE_TLS_PORT }} +Listen [::]:{{ env.IPXE_TLS_PORT }} ErrorLog /dev/stderr diff --git a/ironic-config/apache2-vmedia.conf.j2 b/ironic-config/apache2-vmedia.conf.j2 index 62114274f..742cfedf3 100644 --- a/ironic-config/apache2-vmedia.conf.j2 +++ b/ironic-config/apache2-vmedia.conf.j2 @@ -1,4 +1,5 @@ -Listen {{ env.VMEDIA_TLS_PORT }} +Listen 0.0.0.0:{{ env.VMEDIA_TLS_PORT }} +Listen [::]:{{ env.VMEDIA_TLS_PORT }} ErrorLog /dev/stderr diff --git a/ironic-config/httpd-ironic-api.conf.j2 b/ironic-config/httpd-ironic-api.conf.j2 index dfc248ef5..1e0128c7b 100644 --- a/ironic-config/httpd-ironic-api.conf.j2 +++ b/ironic-config/httpd-ironic-api.conf.j2 @@ -12,7 +12,8 @@ {% if env.LISTEN_ALL_INTERFACES | lower == "true" %} -Listen {{ env.IRONIC_LISTEN_PORT }} +Listen 0.0.0.0:{{ env.IRONIC_LISTEN_PORT }} +Listen [::]:{{ env.IRONIC_LISTEN_PORT }} {% else %} {% if env.ENABLE_IPV4 %} diff --git a/ironic-config/httpd.conf.j2 b/ironic-config/httpd.conf.j2 index 39f6954dd..015a85a73 100644 --- a/ironic-config/httpd.conf.j2 +++ b/ironic-config/httpd.conf.j2 @@ -1,6 +1,7 @@ ServerRoot {{ env.HTTPD_DIR }} {%- if env.LISTEN_ALL_INTERFACES | lower == "true" %} -Listen {{ env.HTTP_PORT }} +Listen 0.0.0.0:{{ env.HTTP_PORT }} +Listen [::]:{{ env.HTTP_PORT }} {% else %} {% if env.ENABLE_IPV4 %} Listen {{ env.IRONIC_IP }}:{{ env.HTTP_PORT }}