diff --git a/ic-os/components/conformance_tests/check_file_references.py b/ic-os/components/conformance_tests/check_file_references.py index cd91b8723e7..6009ff44384 100755 --- a/ic-os/components/conformance_tests/check_file_references.py +++ b/ic-os/components/conformance_tests/check_file_references.py @@ -11,12 +11,7 @@ import tarfile import tempfile -ALLOWED_UNDECLARED_DEPENDENCIES = { - "ic-os/components/misc/fetch-property.sh": { - # fetch-property.sh checks existence of metrics.sh - "/opt/ic/bin/metrics.sh", - } -} +ALLOWED_UNDECLARED_DEPENDENCIES = {} # Check file patterns /opt/ic/... COMPONENT_FILE_PATTERN = r"/opt/ic/[^\s'\"},)]+" diff --git a/ic-os/components/hostos-scripts/build-bootstrap-config-image.sh b/ic-os/components/hostos-scripts/build-bootstrap-config-image.sh index ec1f21cfd1e..2f759d4e199 100755 --- a/ic-os/components/hostos-scripts/build-bootstrap-config-image.sh +++ b/ic-os/components/hostos-scripts/build-bootstrap-config-image.sh @@ -63,7 +63,7 @@ options may be specified: (make sure to quote the argument string so it appears as a single argument to the script, e.g. --elasticsearch_tags "testnet1 slo") - --nns_url url + --nns_urls urls URL of NNS nodes for sign up or registry access. Can be multiple nodes separated by commas. @@ -122,7 +122,7 @@ function build_ic_bootstrap_tar() { local IPV6_ADDRESS IPV6_GATEWAY DOMAIN HOSTNAME local IC_CRYPTO IC_STATE IC_REGISTRY_LOCAL_STORE - local NNS_URL NNS_PUBLIC_KEY NODE_OPERATOR_PRIVATE_KEY + local NNS_URLS NNS_PUBLIC_KEY NODE_OPERATOR_PRIVATE_KEY local BACKUP_RETENTION_TIME_SECS BACKUP_PURGING_INTERVAL_SECS local ELASTICSEARCH_HOSTS ELASTICSEARCH_TAGS local ACCOUNTS_SSH_AUTHORIZED_KEYS @@ -170,8 +170,8 @@ function build_ic_bootstrap_tar() { --elasticsearch_tags) ELASTICSEARCH_TAGS="$2" ;; - --nns_url) - NNS_URL="$2" + --nns_urls) + NNS_URLS="$2" ;; --nns_public_key) NNS_PUBLIC_KEY="$2" @@ -237,8 +237,8 @@ EOF if [ "${NNS_PUBLIC_KEY}" != "" ]; then cp "${NNS_PUBLIC_KEY}" "${BOOTSTRAP_TMPDIR}/nns_public_key.pem" fi - if [ "${NNS_URL}" != "" ]; then - echo "nns_url=${NNS_URL}" >"${BOOTSTRAP_TMPDIR}/nns.conf" + if [ "${NNS_URLS}" != "" ]; then + echo "nns_url=${NNS_URLS}" >"${BOOTSTRAP_TMPDIR}/nns.conf" fi if [ "${BACKUP_RETENTION_TIME_SECS}" != "" ] || [ "${BACKUP_PURGING_INTERVAL_SECS}" != "" ]; then echo "backup_retention_time_secs=${BACKUP_RETENTION_TIME_SECS}" >"${BOOTSTRAP_TMPDIR}/backup.conf" diff --git a/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh b/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh index 49276f8719b..667ad867150 100755 --- a/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh +++ b/ic-os/components/hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh @@ -4,29 +4,18 @@ set -e # Generate the GuestOS configuration. +source /opt/ic/bin/logging.sh # Source the functions required for writing metrics source /opt/ic/bin/metrics.sh -SCRIPT="$(basename $0)[$$]" - # Get keyword arguments for argument in "${@}"; do case ${argument} in - -c=* | --config=*) - CONFIG="${argument#*=}" - shift - ;; - -d=* | --deployment=*) - DEPLOYMENT="${argument#*=}" - shift - ;; -h | --help) echo 'Usage: Generate GuestOS Configuration Arguments: - -c=, --config= specify the config.ini configuration file (Default: /boot/config/config.ini) - -d=, --deployment= specify the deployment.json configuration file (Default: /boot/config/deployment.json) -h, --help show this help message and exit -i=, --input= specify the input template file (Default: /opt/ic/share/guestos.xml.template) -m=, --media= specify the config media image file (Default: /run/ic-node/config.img) @@ -54,47 +43,36 @@ Arguments: done function validate_arguments() { - if [ "${CONFIG}" == "" -o "${DEPLOYMENT}" == "" -o "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then + if [ "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then $0 --help fi } # Set arguments if undefined -CONFIG="${CONFIG:=/boot/config/config.ini}" -DEPLOYMENT="${DEPLOYMENT:=/boot/config/deployment.json}" INPUT="${INPUT:=/opt/ic/share/guestos.xml.template}" MEDIA="${MEDIA:=/run/ic-node/config.img}" OUTPUT="${OUTPUT:=/var/lib/libvirt/guestos.xml}" -write_log() { - local message=$1 - - if [ -t 1 ]; then - echo "${SCRIPT} ${message}" >/dev/stdout - fi - - logger -t ${SCRIPT} "${message}" -} - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - "ipv4_address") ipv4_address="${value}" ;; - "ipv4_prefix_length") ipv4_prefix_length="${value}" ;; - "ipv4_gateway") ipv4_gateway="${value}" ;; - "domain") domain="${value}" ;; - esac - done <"${CONFIG}" +function read_config_variables() { + ipv6_prefix=$(get_config_value '.network_settings.ipv6_config.Deterministic.prefix') + ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway') + ipv4_address=$(get_config_value '.network_settings.ipv4_config.address') + ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length') + ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway') + domain=$(get_config_value '.network_settings.ipv4_config.domain') + elasticsearch_hosts=$(get_config_value '.icos_settings.logging.elasticsearch_hosts') + nns_public_key=$(get_config_value '.icos_settings.nns_public_key_path') + nns_urls=$(get_config_value '.icos_settings.nns_urls') + node_operator_private_key=$(get_config_value '.icos_settings.node_operator_private_key_path') + vm_memory=$(get_config_value '.hostos_settings.vm_memory') + vm_cpu=$(get_config_value '.hostos_settings.vm_cpu') + ssh_authorized_keys=$(get_config_value '.icos_settings.ssh_authorized_keys_path') } function assemble_config_media() { cmd=(/opt/ic/bin/build-bootstrap-config-image.sh ${MEDIA}) - cmd+=(--nns_public_key "/boot/config/nns_public_key.pem") - cmd+=(--elasticsearch_hosts "$(/opt/ic/bin/fetch-property.sh --key=.logging.hosts --metric=hostos_logging_hosts --config=${DEPLOYMENT})") + cmd+=(--nns_public_key "$nns_public_key") + cmd+=(--elasticsearch_hosts "$elasticsearch_hosts") cmd+=(--ipv6_address "$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type GuestOS)") cmd+=(--ipv6_gateway "${ipv6_gateway}") if [[ -n "$ipv4_address" && -n "$ipv4_prefix_length" && -n "$ipv4_gateway" && -n "$domain" ]]; then @@ -102,13 +80,13 @@ function assemble_config_media() { cmd+=(--ipv4_gateway "${ipv4_gateway}") cmd+=(--domain "${domain}") fi + # todo: can I use the fetch-mgmt-mac in hostos tool? cmd+=(--hostname "guest-$(/opt/ic/bin/fetch-mgmt-mac.sh | sed 's/://g')") - cmd+=(--nns_url "$(/opt/ic/bin/fetch-property.sh --key=.nns.url --metric=hostos_nns_url --config=${DEPLOYMENT})") - if [ -f "/boot/config/node_operator_private_key.pem" ]; then - cmd+=(--node_operator_private_key "/boot/config/node_operator_private_key.pem") + cmd+=(--nns_urls "$nns_urls") + if [ -f "$node_operator_private_key" ]; then + cmd+=(--node_operator_private_key "$node_operator_private_key") fi - - cmd+=(--accounts_ssh_authorized_keys "/boot/config/ssh_authorized_keys") + cmd+=(--accounts_ssh_authorized_keys "$ssh_authorized_keys") # Run the above command "${cmd[@]}" @@ -116,22 +94,18 @@ function assemble_config_media() { } function generate_guestos_config() { - RESOURCES_MEMORY=$(/opt/ic/bin/fetch-property.sh --key=.resources.memory --metric=hostos_resources_memory --config=${DEPLOYMENT}) MAC_ADDRESS=$(/opt/ic/bin/hostos_tool generate-mac-address --node-type GuestOS) - # NOTE: `fetch-property` will error if the target is not found. Here we - # only want to act when the field is set. - CPU_MODE=$(jq -r ".resources.cpu" ${DEPLOYMENT}) CPU_DOMAIN="kvm" CPU_SPEC="/opt/ic/share/kvm-cpu.xml" - if [ "${CPU_MODE}" == "qemu" ]; then + if [ "${vm_cpu}" == "qemu" ]; then CPU_DOMAIN="qemu" CPU_SPEC="/opt/ic/share/qemu-cpu.xml" fi if [ ! -f "${OUTPUT}" ]; then mkdir -p "$(dirname "$OUTPUT")" - sed -e "s@{{ resources_memory }}@${RESOURCES_MEMORY}@" \ + sed -e "s@{{ resources_memory }}@${vm_memory}@" \ -e "s@{{ mac_address }}@${MAC_ADDRESS}@" \ -e "s@{{ cpu_domain }}@${CPU_DOMAIN}@" \ -e "/{{ cpu_spec }}/{r ${CPU_SPEC}" -e "d" -e "}" \ @@ -152,9 +126,8 @@ function generate_guestos_config() { } function main() { - # Establish run order validate_arguments - read_variables + read_config_variables assemble_config_media generate_guestos_config } diff --git a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh b/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh index cca55130bf4..ae1613a4756 100755 --- a/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh +++ b/ic-os/components/hostos-scripts/generate-guestos-config/generate-guestos-config.sh @@ -8,26 +8,14 @@ source /opt/ic/bin/logging.sh # Source the functions required for writing metrics source /opt/ic/bin/metrics.sh -SCRIPT="$(basename $0)[$$]" - # Get keyword arguments for argument in "${@}"; do case ${argument} in - -c=* | --config=*) - CONFIG="${argument#*=}" - shift - ;; - -d=* | --deployment=*) - DEPLOYMENT="${argument#*=}" - shift - ;; -h | --help) echo 'Usage: Generate GuestOS Configuration Arguments: - -c=, --config= specify the config.ini configuration file (Default: /boot/config/config.ini) - -d=, --deployment= specify the deployment.json configuration file (Default: /boot/config/deployment.json) -h, --help show this help message and exit -i=, --input= specify the input template file (Default: /opt/ic/share/guestos.xml.template) -m=, --media= specify the config media image file (Default: /run/ic-node/config.img) @@ -55,37 +43,35 @@ Arguments: done function validate_arguments() { - if [ "${CONFIG}" == "" -o "${DEPLOYMENT}" == "" -o "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then + if [ "${INPUT}" == "" -o "${OUTPUT}" == "" ]; then $0 --help fi } # Set arguments if undefined -CONFIG="${CONFIG:=/boot/config/config.ini}" -DEPLOYMENT="${DEPLOYMENT:=/boot/config/deployment.json}" INPUT="${INPUT:=/opt/ic/share/guestos.xml.template}" MEDIA="${MEDIA:=/run/ic-node/config.img}" OUTPUT="${OUTPUT:=/var/lib/libvirt/guestos.xml}" -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - "ipv4_address") ipv4_address="${value}" ;; - "ipv4_prefix_length") ipv4_prefix_length="${value}" ;; - "ipv4_gateway") ipv4_gateway="${value}" ;; - "domain") domain="${value}" ;; - esac - done <"${CONFIG}" +function read_config_variables() { + ipv6_prefix=$(get_config_value '.network_settings.ipv6_config.Deterministic.prefix') + ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway') + ipv4_address=$(get_config_value '.network_settings.ipv4_config.address') + ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length') + ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway') + domain=$(get_config_value '.network_settings.ipv4_config.domain') + elasticsearch_hosts=$(get_config_value '.icos_settings.logging.elasticsearch_hosts') + nns_public_key=$(get_config_value '.icos_settings.nns_public_key_path') + nns_urls=$(get_config_value '.icos_settings.nns_urls') + node_operator_private_key=$(get_config_value '.icos_settings.node_operator_private_key_path') + vm_memory=$(get_config_value '.hostos_settings.vm_memory') + vm_cpu=$(get_config_value '.hostos_settings.vm_cpu') } function assemble_config_media() { cmd=(/opt/ic/bin/build-bootstrap-config-image.sh ${MEDIA}) - cmd+=(--nns_public_key "/boot/config/nns_public_key.pem") - cmd+=(--elasticsearch_hosts "$(/opt/ic/bin/fetch-property.sh --key=.logging.hosts --metric=hostos_logging_hosts --config=${DEPLOYMENT})") + cmd+=(--nns_public_key "$nns_public_key") + cmd+=(--elasticsearch_hosts "$elasticsearch_hosts") cmd+=(--ipv6_address "$(/opt/ic/bin/hostos_tool generate-ipv6-address --node-type GuestOS)") cmd+=(--ipv6_gateway "${ipv6_gateway}") if [[ -n "$ipv4_address" && -n "$ipv4_prefix_length" && -n "$ipv4_gateway" && -n "$domain" ]]; then @@ -93,10 +79,11 @@ function assemble_config_media() { cmd+=(--ipv4_gateway "${ipv4_gateway}") cmd+=(--domain "${domain}") fi + # todo: can I use the fetch-mgmt-mac in hostos tool? cmd+=(--hostname "guest-$(/opt/ic/bin/fetch-mgmt-mac.sh | sed 's/://g')") - cmd+=(--nns_url "$(/opt/ic/bin/fetch-property.sh --key=.nns.url --metric=hostos_nns_url --config=${DEPLOYMENT})") - if [ -f "/boot/config/node_operator_private_key.pem" ]; then - cmd+=(--node_operator_private_key "/boot/config/node_operator_private_key.pem") + cmd+=(--nns_urls "$nns_urls") + if [ -f "$node_operator_private_key" ]; then + cmd+=(--node_operator_private_key "$node_operator_private_key") fi # Run the above command @@ -105,22 +92,18 @@ function assemble_config_media() { } function generate_guestos_config() { - RESOURCES_MEMORY=$(/opt/ic/bin/fetch-property.sh --key=.resources.memory --metric=hostos_resources_memory --config=${DEPLOYMENT}) MAC_ADDRESS=$(/opt/ic/bin/hostos_tool generate-mac-address --node-type GuestOS) - # NOTE: `fetch-property` will error if the target is not found. Here we - # only want to act when the field is set. - CPU_MODE=$(jq -r ".resources.cpu" ${DEPLOYMENT}) CPU_DOMAIN="kvm" CPU_SPEC="/opt/ic/share/kvm-cpu.xml" - if [ "${CPU_MODE}" == "qemu" ]; then + if [ "${vm_cpu}" == "qemu" ]; then CPU_DOMAIN="qemu" CPU_SPEC="/opt/ic/share/qemu-cpu.xml" fi if [ ! -f "${OUTPUT}" ]; then mkdir -p "$(dirname "$OUTPUT")" - sed -e "s@{{ resources_memory }}@${RESOURCES_MEMORY}@" \ + sed -e "s@{{ resources_memory }}@${vm_memory}@" \ -e "s@{{ mac_address }}@${MAC_ADDRESS}@" \ -e "s@{{ cpu_domain }}@${CPU_DOMAIN}@" \ -e "/{{ cpu_spec }}/{r ${CPU_SPEC}" -e "d" -e "}" \ @@ -141,9 +124,8 @@ function generate_guestos_config() { } function main() { - # Establish run order validate_arguments - read_variables + read_config_variables assemble_config_media generate_guestos_config } diff --git a/ic-os/components/hostos-scripts/misc/fetch-mgmt-mac.sh b/ic-os/components/hostos-scripts/misc/fetch-mgmt-mac.sh index c469920801a..80ee8abde7b 100755 --- a/ic-os/components/hostos-scripts/misc/fetch-mgmt-mac.sh +++ b/ic-os/components/hostos-scripts/misc/fetch-mgmt-mac.sh @@ -58,8 +58,6 @@ function fetch_mgmt_mac() { function main() { # Establish run order - # NOTE: `fetch-property` will error if the target is not found. Here we - # only want to act when the field is set. MGMT_MAC=$(jq -r ".deployment.mgmt_mac" ${DEPLOYMENT}) if [ -z "${MGMT_MAC}" ] || [ "${MGMT_MAC}" = "null" ]; then diff --git a/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.sh b/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.sh index a8ff2e9b2cd..979464d0777 100644 --- a/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.sh +++ b/ic-os/components/hostos-scripts/verbose-logging/verbose-logging.sh @@ -1,18 +1,8 @@ #!/bin/bash -CONFIG="${CONFIG:=/boot/config/config.ini}" +source /opt/ic/bin/config.sh -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "verbose") verbose="${value}" ;; - esac - done <"${CONFIG}" -} - -read_variables +verbose=$(get_config_value '.hostos_settings.verbose') if [[ "${verbose,,}" == "true" ]]; then echo "##########################################" >/dev/tty1 diff --git a/ic-os/components/hostos.bzl b/ic-os/components/hostos.bzl index c40901fbcaa..424df253a0c 100644 --- a/ic-os/components/hostos.bzl +++ b/ic-os/components/hostos.bzl @@ -48,9 +48,9 @@ component_files = { Label("early-boot/initramfs-tools/hostos/set-machine-id/set-machine-id"): "/etc/initramfs-tools/scripts/init-bottom/set-machine-id/set-machine-id", # misc + Label("misc/config/hostos-config.sh"): "/opt/ic/bin/config.sh", Label("misc/logging.sh"): "/opt/ic/bin/logging.sh", Label("misc/metrics.sh"): "/opt/ic/bin/metrics.sh", - Label("misc/fetch-property.sh"): "/opt/ic/bin/fetch-property.sh", Label("misc/vsock/vsock-agent.service"): "/etc/systemd/system/vsock-agent.service", Label("misc/vsock/10-vhost-vsock.rules"): "/etc/udev/rules.d/10-vhost-vsock.rules", Label("misc/chrony/chrony.conf"): "/etc/chrony/chrony.conf", diff --git a/ic-os/components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh b/ic-os/components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh index 5459b895e03..bafdff6cca3 100755 --- a/ic-os/components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh +++ b/ic-os/components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh @@ -3,8 +3,6 @@ # Provision a node based on an injected "ic-bootstrap.tar" file. This script # is meant to be run as a prerequisite before launching orchestrator/replica. # -# The configuration format is described in guestos/docs/ConfigStore.adoc -# # The tar file can be supplied using one of two methods: # - as "ic-bootstrap.tar" stored on a (virtual) removable media attached # on first boot diff --git a/ic-os/components/misc/config/hostos-config.sh b/ic-os/components/misc/config/hostos-config.sh new file mode 100644 index 00000000000..831d04cbc28 --- /dev/null +++ b/ic-os/components/misc/config/hostos-config.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Shared config utilities. + +# Retrieves a value from the config.json file using a JSON path. +# Arguments: +# $1 - JSON path to the desired value (e.g., '.icos_settings.node_operator_private_key_path') +function get_config_value() { + local CONFIG_FILE="/boot/config/config.json" + local key=$1 + jq -r "${key}" "${CONFIG_FILE}" +} diff --git a/ic-os/components/misc/config/setupos-config.sh b/ic-os/components/misc/config/setupos-config.sh new file mode 100644 index 00000000000..21151813cc8 --- /dev/null +++ b/ic-os/components/misc/config/setupos-config.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Shared config utilities. + +# Retrieves a value from the config.json file using a JSON path. +# Arguments: +# $1 - JSON path to the desired value (e.g., '.icos_settings.node_operator_private_key_path') +function get_config_value() { + local CONFIG_FILE="/var/ic/config/config.json" + local key=$1 + jq -r "${key}" "${CONFIG_FILE}" +} diff --git a/ic-os/components/misc/fetch-property.sh b/ic-os/components/misc/fetch-property.sh deleted file mode 100755 index 46c6d2a2eeb..00000000000 --- a/ic-os/components/misc/fetch-property.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -# Fetch configuration property - -source /opt/ic/bin/logging.sh - -SCRIPT="$(basename $0)[$$]" - -# Get keyword arguments -for argument in "${@}"; do - case ${argument} in - -c=* | --config=*) - CONFIG="${argument#*=}" - shift - ;; - -h | --help) - echo 'Usage: -Fetch Configuration Property - -Arguments: - -c=, --config= mandatory: specify the configuration file to read from - -h, --help show this help message and exit - -k=, --key= mandatory: specify the property key - -m=, --metric= optional: specify the metric name (required if metrics.sh exists) -' - exit 1 - ;; - -k=* | --key=*) - KEY="${argument#*=}" - shift - ;; - -m=* | --metric=*) - METRIC="${argument#*=}" - shift - ;; - *) - echo "Error: Argument is not supported." - exit 1 - ;; - esac -done - -function validate_arguments() { - if [ -z "${CONFIG}" ] || [ -z "${KEY}" ]; then - $0 --help - fi - - if [ -f "/opt/ic/bin/metrics.sh" ] && [ -z "${METRIC:-}" ]; then - echo "Error: METRIC is required when metrics.sh exists." - exit 1 - fi -} - -try_write_metric() { - local name=$1 - local value=$2 - local help=$3 - local type=$4 - - # metrics.sh is required for writing metrics - # metrics.sh only exists on HostOS and GuestOS, not SetupOS - if [ -f "/opt/ic/bin/metrics.sh" ]; then - source "/opt/ic/bin/metrics.sh" - write_metric "${name}" "${value}" "${help}" "${type}" - fi -} - -function fetch_property() { - PROPERTY=$(jq -r "$(echo ${KEY})" ${CONFIG}) - - if [ -z "${PROPERTY}" ] || [ "${PROPERTY}" == "null" ]; then - write_log "ERROR: Unable to fetch property: ${KEY}" - try_write_metric "$(echo ${METRIC:-})" \ - "1" \ - "Property: $(echo ${KEY})" \ - "gauge" - exit 1 - else - write_log "Using property: ${PROPERTY}" - try_write_metric "$(echo ${METRIC:-})" \ - "0" \ - "Property: $(echo ${KEY})" \ - "gauge" - echo "${PROPERTY}" - fi -} - -function main() { - # Establish run order - validate_arguments - fetch_property -} - -main diff --git a/ic-os/components/setupos-scripts/check-config.sh b/ic-os/components/setupos-scripts/check-config.sh new file mode 100644 index 00000000000..fa20310e9fc --- /dev/null +++ b/ic-os/components/setupos-scripts/check-config.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -o nounset +set -o pipefail + +source /opt/ic/bin/config.sh +source /opt/ic/bin/functions.sh + +SHELL="/bin/bash" +PATH="/sbin:/bin:/usr/sbin:/usr/bin" + +check_config_file() { + echo "* Checking Config..." + local CONFIG_FILE="/var/ic/config/config.json" + + if [ -f "${CONFIG_FILE}" ]; then + local config_contents=$(cat "${CONFIG_FILE}") + echo -e "Configuration file '${CONFIG_FILE}' exists.\n" + echo -e "File contents:\n${config_contents}" + else + local service_logs=$(journalctl -u config.service --no-pager) + local log_message="Error creating SetupOS configuration. Configuration file '${CONFIG_FILE}' does not exist.\n\nConfig.service logs:\n${service_logs}" + + log_and_halt_installation_on_error 1 "${log_message}" + fi +} + +# Establish run order +main() { + log_start "$(basename $0)" + check_config_file + log_end "$(basename $0)" +} + +main diff --git a/ic-os/components/setupos-scripts/check-hardware.sh b/ic-os/components/setupos-scripts/check-hardware.sh index 97770a9d8bd..3d77b7ac2cb 100644 --- a/ic-os/components/setupos-scripts/check-hardware.sh +++ b/ic-os/components/setupos-scripts/check-hardware.sh @@ -3,6 +3,9 @@ set -o nounset set -o pipefail +source /opt/ic/bin/config.sh +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" @@ -30,8 +33,6 @@ GEN2_MINIMUM_AGGREGATE_DISK_SIZE=32000000000000 GEN1_MINIMUM_DISK_SIZE=3200000000000 GEN1_MINIMUM_AGGREGATE_DISK_SIZE=32000000000000 -CONFIG_DIR="/var/ic/config" - function check_generation() { echo "* Checking Generation..." @@ -247,7 +248,10 @@ function verify_disks() { function verify_deployment_path() { echo "* Verifying deployment path..." - if [[ ${GENERATION} == 2 ]] && [[ ! -f "${CONFIG_DIR}/node_operator_private_key.pem" ]]; then + + local node_operator_key_path=$(get_config_value '.icos_settings.node_operator_private_key_path') + + if [[ ${GENERATION} == 2 ]] && [[ ! -f "${node_operator_key_path}" ]]; then echo -e "\n\n\n\n\n\n" echo -e "\033[1;31mWARNING: Gen2 hardware detected but no Node Operator Private Key found.\033[0m" echo -e "\033[1;31mGen2 hardware should be deployed using the Gen2 Node Deployment method.\033[0m" @@ -261,7 +265,6 @@ function verify_deployment_path() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" check_generation verify_cpu diff --git a/ic-os/components/setupos-scripts/check-network.sh b/ic-os/components/setupos-scripts/check-network.sh index 7d6b5ad7624..e008004cdc3 100755 --- a/ic-os/components/setupos-scripts/check-network.sh +++ b/ic-os/components/setupos-scripts/check-network.sh @@ -3,25 +3,19 @@ set -o nounset set -o pipefail +source /opt/ic/bin/config.sh +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" -CONFIG="${CONFIG:=/var/ic/config/config.ini}" -DEPLOYMENT="${DEPLOYMENT:=/data/deployment.json}" - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - "ipv4_address") ipv4_address="${value}" ;; - "ipv4_prefix_length") ipv4_prefix_length="${value}" ;; - "ipv4_gateway") ipv4_gateway="${value}" ;; - "domain") domain="${value}" ;; - esac - done <"${CONFIG}" +function read_config_variables() { + ipv6_prefix=$(get_config_value '.network_settings.ipv6_config.Deterministic.prefix') + ipv6_gateway=$(get_config_value '.network_settings.ipv6_config.Deterministic.gateway') + ipv4_address=$(get_config_value '.network_settings.ipv4_config.address') + ipv4_prefix_length=$(get_config_value '.network_settings.ipv4_config.prefix_length') + ipv4_gateway=$(get_config_value '.network_settings.ipv4_config.gateway') + domain=$(get_config_value '.network_settings.ipv4_config.domain') } # WARNING: Uses 'eval' for command execution. @@ -121,20 +115,20 @@ function validate_domain_name() { IFS='.' read -ra domain_parts <<<"${domain}" if [ ${#domain_parts[@]} -lt 2 ]; then - log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain" + log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain: ${domain}" fi for domain_part in "${domain_parts[@]}"; do if [ -z "$domain_part" ] || [ ${#domain_part} -gt 63 ]; then - log_and_halt_installation_on_error 1 "Domain validation error: domain part length violation" + log_and_halt_installation_on_error 1 "Domain validation error: domain part length violation: ${domain_part}" fi if [[ $domain_part == -* ]] || [[ $domain_part == *- ]]; then - log_and_halt_installation_on_error 1 "Domain validation error: domain part starts or ends with a hyphen" + log_and_halt_installation_on_error 1 "Domain validation error: domain part starts or ends with a hyphen: ${domain_part}" fi if ! [[ $domain_part =~ ^[a-zA-Z0-9-]+$ ]]; then - log_and_halt_installation_on_error 1 "Domain validation error: invalid characters in domain part" + log_and_halt_installation_on_error 1 "Domain validation error: invalid characters in domain part: ${domain_part}" fi done } @@ -168,17 +162,13 @@ function ping_ipv6_gateway() { echo " " } -function assemble_nns_nodes_list() { - NNS_URL_STRING=$(/opt/ic/bin/fetch-property.sh --key=.nns.url --config=${DEPLOYMENT}) - IFS=',' read -r -a NNS_URL_LIST <<<"$NNS_URL_STRING" -} - function query_nns_nodes() { echo "* Querying NNS nodes..." + local nns_urls=($(get_config_value '.icos_settings.nns_urls' | jq -r '.[]')) local success=false - # At least one of the provided URLs needs to work. - for url in "${NNS_URL_LIST[@]}"; do + + for url in "${nns_urls[@]}"; do # When running against testnets, we need to ignore self signed certs # with `--insecure`. This check is only meant to confirm from SetupOS # that NNS urls are reachable, so we do not mind that it is "weak". @@ -200,9 +190,8 @@ function query_nns_nodes() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" - read_variables + read_config_variables get_network_settings print_network_settings @@ -213,7 +202,6 @@ main() { fi ping_ipv6_gateway - assemble_nns_nodes_list query_nns_nodes log_end "$(basename $0)" } diff --git a/ic-os/components/setupos-scripts/config.service b/ic-os/components/setupos-scripts/config.service index 2db6cdf7dc0..f35bbae5eb3 100644 --- a/ic-os/components/setupos-scripts/config.service +++ b/ic-os/components/setupos-scripts/config.service @@ -6,9 +6,9 @@ Before=setupos.service [Service] Type=oneshot RemainAfterExit=true -ExecStart=/opt/ic/bin/output-wrapper.sh /dev/ttyS0 /opt/ic/bin/config.sh -StandardOutput=tty -StandardError=tty +ExecStart=/opt/ic/bin/config create-setupos-config +StandardOutput=journal+console +StandardError=journal+console [Install] WantedBy=multi-user.target diff --git a/ic-os/components/setupos-scripts/config.sh b/ic-os/components/setupos-scripts/config.sh deleted file mode 100755 index 0b13fb29296..00000000000 --- a/ic-os/components/setupos-scripts/config.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash - -set -o nounset -set -o pipefail - -SHELL="/bin/bash" -PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -CONFIG_DIR="/config" -CONFIG_TMP="/var/ic/config" -CONFIG_INI="${CONFIG_DIR}/config.ini" -CONFIG_INI_CLONE="${CONFIG_TMP}/config.ini" -SSH_AUTHORIZED_KEYS="${CONFIG_DIR}/ssh_authorized_keys" -SSH_AUTHORIZED_KEYS_CLONE="${CONFIG_TMP}/ssh_authorized_keys" - -# Define empty variables so they are not unset -ipv6_prefix="" -ipv6_gateway="" - -function print_config_file() { - if [ -e "${CONFIG_INI}" ]; then - echo "Found ${CONFIG_INI}. Contents:" - cat "${CONFIG_INI}" - else - log_and_halt_installation_on_error "1" "config.ini not found. Please copy a valid config.ini to the SetupOS installer config partition." - fi - -} - -function create_config_tmp() { - if [ ! -e "${CONFIG_TMP}" ]; then - # Create fresh config tmp directory - mkdir -p "${CONFIG_TMP}" - log_and_halt_installation_on_error "${?}" "Unable to create new '${CONFIG_TMP}' directory." - fi -} - -function clone_config() { - cp "${CONFIG_INI}" "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to copy 'config.ini' configuration file." - - if [ ! -f "${CONFIG_INI_CLONE}" ]; then - log_and_halt_installation_on_error "1" "Cloned 'config.ini' configuration file does not exist." - fi - - if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then - cp ${CONFIG_DIR}/node_operator_private_key.pem ${CONFIG_TMP}/node_operator_private_key.pem - log_and_halt_installation_on_error "${?}" "Unable to copy 'node_operator_private_key.pem' configuration file." - fi - - if [ -d "${SSH_AUTHORIZED_KEYS}" ]; then - cp -r "${SSH_AUTHORIZED_KEYS}" "${CONFIG_TMP}" - log_and_halt_installation_on_error "${?}" "Unable to copy 'ssh_authorized_keys' directory." - else - log_and_halt_installation_on_error "1" "Unable to read 'ssh_authorized_keys' directory." - fi - - if [ ! -d "${SSH_AUTHORIZED_KEYS_CLONE}" ]; then - log_and_halt_installation_on_error "1" "Cloned 'ssh_authorized_keys' directory does not exist." - fi -} - -function normalize_config() { - CONFIG_VAR=$(cat "${CONFIG_INI_CLONE}" | tr '\r' '\n') - echo "${CONFIG_VAR}" >"${CONFIG_INI_CLONE}" - - sed -i 's/#.*$//g' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to remove comments from 'config.ini'." - - sed -i 's/"//g' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to replace double-quote characters in 'config.ini'." - - sed -i "s/'//g" "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to replace single-quote characters in 'config.ini'." - - sed -i 's/.*/\L&/' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to convert upper- to lower-case in 'config.ini'." - - sed -i '/^$/d' "${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to remove empty lines in 'config.ini'." - - echo -e '\n' >>"${CONFIG_INI_CLONE}" - log_and_halt_installation_on_error "${?}" "Unable to inject extra new-line at the end of 'config.ini'." -} - -function read_variables() { - # Read limited set of keys. Be extra-careful quoting values as it could - # otherwise lead to executing arbitrary shell code! - while IFS="=" read -r key value; do - case "$key" in - "ipv6_prefix") ipv6_prefix="${value}" ;; - "ipv6_gateway") ipv6_gateway="${value}" ;; - esac - done <"${CONFIG_INI_CLONE}" -} - -function verify_variables() { - if [ -z "${ipv6_prefix}" ]; then - log_and_halt_installation_on_error "1" "Variable 'ipv6_prefix' is not defined in 'config.ini'." - fi - - if [ -z "${ipv6_gateway}" ]; then - log_and_halt_installation_on_error "1" "Variable 'ipv6_gateway' is not defined in 'config.ini'." - fi -} - -# Establish run order -main() { - source /opt/ic/bin/functions.sh - log_start "$(basename $0)" - print_config_file - create_config_tmp - clone_config - normalize_config - read_variables - verify_variables - log_end "$(basename $0)" -} - -main diff --git a/ic-os/components/setupos-scripts/functions.sh b/ic-os/components/setupos-scripts/functions.sh index c058b1885c2..4e63bf0782e 100755 --- a/ic-os/components/setupos-scripts/functions.sh +++ b/ic-os/components/setupos-scripts/functions.sh @@ -23,7 +23,7 @@ function log_and_halt_installation_on_error() { echo " ERROR" echo "--------------------------------------------------------------------------------" echo -e "\n\n" - echo "${log_message}" + echo -e "${log_message}" echo -e "\n\n" echo "--------------------------------------------------------------------------------" echo " ERROR" diff --git a/ic-os/components/setupos-scripts/install-guestos.sh b/ic-os/components/setupos-scripts/install-guestos.sh index 2311c3ba9c3..3e24773234d 100755 --- a/ic-os/components/setupos-scripts/install-guestos.sh +++ b/ic-os/components/setupos-scripts/install-guestos.sh @@ -3,6 +3,8 @@ set -o nounset set -o pipefail +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" @@ -34,7 +36,6 @@ function install_guestos() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" install_guestos log_end "$(basename $0)" diff --git a/ic-os/components/setupos-scripts/install-hostos.sh b/ic-os/components/setupos-scripts/install-hostos.sh index bb3d3b2f424..4557a02957c 100755 --- a/ic-os/components/setupos-scripts/install-hostos.sh +++ b/ic-os/components/setupos-scripts/install-hostos.sh @@ -3,6 +3,8 @@ set -o nounset set -o pipefail +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" @@ -101,7 +103,6 @@ function resize_partition() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" install_hostos configure_efi diff --git a/ic-os/components/setupos-scripts/setup-disk.sh b/ic-os/components/setupos-scripts/setup-disk.sh index f7546bec499..38437fcf20b 100755 --- a/ic-os/components/setupos-scripts/setup-disk.sh +++ b/ic-os/components/setupos-scripts/setup-disk.sh @@ -3,6 +3,8 @@ set -o nounset set -o pipefail +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" @@ -60,7 +62,6 @@ function setup_storage() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" purge_partitions setup_storage diff --git a/ic-os/components/setupos-scripts/setup-hostos-config.sh b/ic-os/components/setupos-scripts/setup-hostos-config.sh index 1fc04a959fc..d1308c49495 100755 --- a/ic-os/components/setupos-scripts/setup-hostos-config.sh +++ b/ic-os/components/setupos-scripts/setup-hostos-config.sh @@ -3,9 +3,12 @@ set -o nounset set -o pipefail +source /opt/ic/bin/config.sh +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" -CONFIG_DIR="/var/ic/config" +CONFIG_DIR="/config" function mount_config_partition() { echo "* Mounting hostOS config partition..." @@ -27,19 +30,27 @@ function copy_config_files() { fi echo "* Copying SSH authorized keys..." - if [ -d "${CONFIG_DIR}/ssh_authorized_keys" ]; then - cp -r ${CONFIG_DIR}/ssh_authorized_keys /media/ - log_and_halt_installation_on_error "${?}" "Unable to copy SSH authorized keys to hostOS config partition." + ssh_authorized_keys=$(get_config_value '.icos_settings.ssh_authorized_keys_path') + if [ -n "${ssh_authorized_keys}" ] && [ "${ssh_authorized_keys}" != "null" ]; then + if [ -d "${ssh_authorized_keys}" ]; then + cp -a "${ssh_authorized_keys}" /media/ + log_and_halt_installation_on_error "${?}" "Unable to copy SSH authorized keys to hostOS config partition." + else + log_and_halt_installation_on_error "1" "Directory '${ssh_authorized_keys}' does not exist." + fi else - log_and_halt_installation_on_error "1" "Directory 'ssh_authorized_keys' does not exist." + echo >&2 "Warning: SSH authorized keys path is not configured." fi echo "* Copying node operator private key..." - if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then - cp ${CONFIG_DIR}/node_operator_private_key.pem /media/ + node_operator_private_key_path=$(get_config_value '.icos_settings.node_operator_private_key_path') + if [ "${node_operator_private_key_path}" != "null" ] && [ -f "${node_operator_private_key_path}" ]; then + cp "${node_operator_private_key_path}" /media/ log_and_halt_installation_on_error "${?}" "Unable to copy node operator private key to hostOS config partition." + elif [ "${node_operator_private_key_path}" = "null" ]; then + echo >&2 "Warning: Node operator private key path is not configured." else - echo "node_operator_private_key.pem does not exist, requiring HSM." + echo >&2 "Warning: node_operator_private_key.pem does not exist, requiring HSM." fi echo "* Copying deployment.json to config partition..." @@ -47,8 +58,21 @@ function copy_config_files() { log_and_halt_installation_on_error "${?}" "Unable to copy deployment.json to hostOS config partition." echo "* Copying NNS public key to hostOS config partition..." - cp /data/nns_public_key.pem /media/ + nns_public_key_path=$(get_config_value '.icos_settings.nns_public_key_path') + cp "${nns_public_key_path}" /media/ log_and_halt_installation_on_error "${?}" "Unable to copy NNS public key to hostOS config partition." + + echo "* Converting 'config.json' to hostOS config file 'config-hostos.json'..." + /opt/ic/bin/config generate-hostos-config + log_and_halt_installation_on_error "${?}" "Unable to generate hostos configuration." + + echo "* Copying 'config-hostos.json' to hostOS config partition..." + if [ -f "/var/ic/config/config-hostos.json" ]; then + cp /var/ic/config/config-hostos.json /media/config.json + log_and_halt_installation_on_error "${?}" "Unable to copy 'config-hostos.json' to hostOS config partition." + else + log_and_halt_installation_on_error "1" "Configuration file 'config-hostos.json' does not exist." + fi } function insert_hsm_if_necessary() { @@ -82,7 +106,6 @@ function unmount_config_partition() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" mount_config_partition copy_config_files diff --git a/ic-os/components/setupos-scripts/setupos.sh b/ic-os/components/setupos-scripts/setupos.sh index 413f6453c1e..67fcbeedc77 100755 --- a/ic-os/components/setupos-scripts/setupos.sh +++ b/ic-os/components/setupos-scripts/setupos.sh @@ -3,6 +3,8 @@ set -o nounset set -o pipefail +source /opt/ic/bin/functions.sh + SHELL="/bin/bash" PATH="/sbin:/bin:/usr/sbin:/usr/bin" @@ -34,7 +36,6 @@ function reboot_setupos() { # Establish run order main() { - source /opt/ic/bin/functions.sh log_start "$(basename $0)" start_setupos /opt/ic/bin/check-setupos-age.sh diff --git a/ic-os/components/setupos.bzl b/ic-os/components/setupos.bzl index b05ad77eaa3..1c2afa576b3 100644 --- a/ic-os/components/setupos.bzl +++ b/ic-os/components/setupos.bzl @@ -5,7 +5,7 @@ Enumerate every component file dependency for SetupOS component_files = { # setupos-scripts Label("setupos-scripts/check-setupos-age.sh"): "/opt/ic/bin/check-setupos-age.sh", - Label("setupos-scripts/config.sh"): "/opt/ic/bin/config.sh", + Label("setupos-scripts/check-config.sh"): "/opt/ic/bin/check-config.sh", Label("setupos-scripts/setup-hostos-config.sh"): "/opt/ic/bin/setup-hostos-config.sh", Label("setupos-scripts/setup-disk.sh"): "/opt/ic/bin/setup-disk.sh", Label("setupos-scripts/functions.sh"): "/opt/ic/bin/functions.sh", @@ -26,9 +26,9 @@ component_files = { # misc Label("misc/logging.sh"): "/opt/ic/bin/logging.sh", + Label("misc/config/setupos-config.sh"): "/opt/ic/bin/config.sh", Label("misc/chrony/chrony.conf"): "/etc/chrony/chrony.conf", Label("misc/chrony/chrony-var.service"): "/etc/systemd/system/chrony-var.service", - Label("misc/fetch-property.sh"): "/opt/ic/bin/fetch-property.sh", Label("misc/serial-getty@/setupos/serial-getty@.service"): "/etc/systemd/system/serial-getty@.service", Label("monitoring/journald.conf"): "/etc/systemd/journald.conf", diff --git a/ic-os/docs/Configuration.adoc b/ic-os/docs/Configuration.adoc index d505d0359b3..8d42ce11442 100644 --- a/ic-os/docs/Configuration.adoc +++ b/ic-os/docs/Configuration.adoc @@ -2,27 +2,25 @@ Each IC-OS has a 100 MB config partition. All IC-OS config partitions are initialized to be empty, except for SetupOS. -In production, configuration is propagated from a partition on the USB installer through each of SetupOS, HostOS and GuestOS: +In production, configuration is propagated from a partition on the USB installer through each of SetupOS, HostOS and GuestOS. +This process is controlled by the (link:../../rs/ic_os/config/README.md[ic-os config tool]) and an assortment of bash scripts. -* SetupOS reads and validates its configuration files from `/config/` -* SetupOS copies sanitized configuration files from `/config/` to `/var/ic/config/` -* SetupOS copies its configuration files from `/var/ic/config/` to the HostOS config partition. -* HostOS reads the configuration files from `/boot/config`. These files are used to populate the GuestOS config partition through a more complicated process described below. +All access to the config partition should be done through the ic-os config tool. -== Detailed configuration steps +== User-facing configuration files -=== SetupOS -> HostOS - -SetupOS validates, sanitizes, and copies all of its configuration files to the HostOS config partition: +SetupOS constructs its config struct from the following user-facing configuration files: config.ini # Data center-specific network settings ssh_authorized_keys # SSH private keys node_operator_private_key.pem # Node Operator private key created in the Node Provider onboarding deployment.json # Deployment-specific configurations nns_public_key.pem # NNS public key -Refer to link:../../rs/ic_os/config/README.md[rs/ic_os/config] & link:../components/setupos-scripts/setup-hostos-config.sh[setup-hostos-config.sh] +Refer to link:../../rs/ic_os/config/README.md[rs/ic_os/config] and link:../components/setupos-scripts/setup-hostos-config.sh[setup-hostos-config.sh] for more details. + +== HostOS -> GuestOS -=== HostOS -> GuestOS +TODO: update... HostOS builds the "bootstrap config image". Refer to link:../components/hostos-scripts/build-bootstrap-config-image.sh[build-bootstrap-config-image.sh] @@ -44,14 +42,32 @@ GuestOS only reads a predefined set of files from the bootstrap config image (i. [NOTE] The reason for the bootstrap config image redirection is to ensure that GuestOS maintains control over what goes onto its config partition. Theoretically, a malicious Node Technician could modify their HostOS image and place any file onto the bootstrap config image. However, GuestOS will only copy a predefined set of files to its config partition. -== GuestOS configuration files +== GuestOS config partition + +TODO: update... + +The config partition stores information that must be preserved across system upgrades and needs to be available during early boot time. Consequently, this information cannot reside within the encrypted payload data partition. + +Currently, all contents in the config partition are stored as plain-text without integrity protection. + +These files are stored in `/boot/config` or `/var/lib/ic`. To see where each configuration file is stored, refer to link:../../components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh[bootstrap-ic-node] -To learn more about the GuestOS configuration files, link:../guestos/docs/ConfigStore.adoc[see the GuestOS ConfigStore.adoc] +=== CONFIGURED file + +This file serves as a tag to indicate that the one-time bootstrap configuration has been completed. If the `/boot/config/CONFIGURED` file is not present, the boot sequence will search for a virtual USB stick (the bootstrap config image) containing the injected configuration files, and create the file. + +=== store.keyfile + +This file contains the key material used to derive the wrapping key for all block device encryption. The `store.keyfile` is created during the first boot, and encrypted partitions are configured with it. + +In the absence of a sealing key (which will be available in SEV-protected trusted execution environments), the `store.keyfile` is stored as plain-text. Once a sealing key becomes available, it should be used to wrap the contents of this file. == Implementation notes === Guidance for adding configuration bits +TODO: update... + To add a new configuration file/directory: 1. Add handling to `build-bootstrap-config-image.sh` to include the new file/directory in the bootstrap config image. @@ -72,10 +88,10 @@ Consider that values may be controlled by an attacker on boot. Bootstrapping a n *Interpretation of configuration bits*: Any script or service in the system may pull configuration bits out of /boot/config to customize its behavior. E.g. if adding parameter-driven customization of ic.json5, then augment the generate-replica-config.sh script to pull the configuration values and substitute them into the generated configuration. -*Documentation*: Please keep documentation up-to-date (link:ConfigStore-SetupOSHostOS.adoc[SetupOS/HostOS config store], link:../guestos/docs/ConfigStore.adoc[GuestOS config store]) - === Testing +TODO: update... + * *bootstrap-ic-node.sh* can be temporarily tweaked (internally adapt paths, then run the process_bootstrap function): ** run stand-alone ** verify that the config image is unpacked @@ -86,3 +102,22 @@ Consider that values may be controlled by an attacker on boot. Bootstrapping a n * *generate-replica-config.sh* can be run stand-alone to verify that it produces the intended ic.json5 configuration from the template. After all is done, it is advised to prepare a configuration for a single node and boot it in a VM before conducting testnet deployments. + +=== Injecting external state + +TODO: update... + +*Typical bootstrap process:* On first boot, the system will perform technical initialization (filesystems, etc.) and afterwards, initialize itself to act as a node in the IC. The node is initialized using key generation on the node itself (such that the private key never leaves the node) and through joining the IC (the node gets the rest of its state via joining the IC). "Registration" to the target IC is initiated by the node itself by sending a Node Operator-signed "join" request to its NNS. + +However, the typical bootstrap process can be modified such that the node is initialized using externally generated private keys and an externally generated initial state. All "registration" to the target IC is assumed to have been performed by other means. + +The behavior is triggered through the presence of the following files: + +- ic_crypto +- ic_registry_local_store + +This behavior is suitable for the following use cases: + +- Bootstrapping an IC instance: In this case, suitable state for all nodes is generated by ic-prep and then distributed across multiple nodes. This is used, for example, during testnet setup. + +- Externally controlled join of a node to a subnet: In this case, ic-prep is used to prepare key material to the node, while ic-admin is used to modify the target NNS such that it "accepts" the new node as part of the IC. diff --git a/ic-os/docs/README.adoc b/ic-os/docs/README.adoc index 966475c5c75..09ef5d3c9a8 100644 --- a/ic-os/docs/README.adoc +++ b/ic-os/docs/README.adoc @@ -7,4 +7,3 @@ Refer to detailed documentation on: * link:Components{outfilesuffix}[Components] * link:SELinux{outfilesuffix}[SELinux security policy] * link:Configuration{outfilesuffix}[Configuration] -* link:ConfigStore-SetupOSHostOS{outfilesuffix}[SetupOS and HostOS config store] diff --git a/ic-os/guestos/docs/Boot.adoc b/ic-os/guestos/docs/Boot.adoc index a744509ec75..aa4e4803926 100644 --- a/ic-os/guestos/docs/Boot.adoc +++ b/ic-os/guestos/docs/Boot.adoc @@ -57,7 +57,7 @@ not held in +/etc/fstab+ but is generated by the shell script Afterwards, the first three partitions are mounted as +/boot/efi+, +/boot/grub+ and +/boot/config+, respectively. The +config+ partition is used as (small) store for data that is preserved across upgrades -and is available at early boot time already (see link:ConfigStore{outfilesuffix}[config store]). +and is available at early boot time already. == Save machine-id @@ -167,8 +167,8 @@ depends on mount of all filesystems. This is only executed once on first boot after provisioning. It looks for a "virtual USB stick" attached to the VM that contains a tar file with initial configuration -for parts of the system (see link:ConfigStore{outfilesuffix}[config store] for a description). Required -files in the +config+ partition as well as payload store are created. +for parts of the system. Required files in the +config+ partition as well as +payload store are created. == Deploy updated ssh account keys diff --git a/ic-os/guestos/docs/ConfigStore.adoc b/ic-os/guestos/docs/ConfigStore.adoc deleted file mode 100644 index f0d0c8e382d..00000000000 --- a/ic-os/guestos/docs/ConfigStore.adoc +++ /dev/null @@ -1,45 +0,0 @@ -= GuestOS Config Store - -This document calls out some of the contents of the GuestOS *config* partition (*/dev/vda3* in the GuestOS disk image). The config partition stores information that must be preserved across system upgrades and needs to be available during early boot time. Consequently, this information cannot reside within the encrypted payload data partition. - -Currently, all contents in the config partition are stored as plain-text without integrity protection. - -These files are stored in `/boot/config` or `/var/lib/ic`. To see where each configuration file is stored, refer to link:../../components/init/bootstrap-ic-node/guestos/bootstrap-ic-node.sh[bootstrap-ic-node] - -== Production configuration files - -Not all configuration files and directories are required for GuestOS to run in production, as certain configuration files exist solely for testing and development purposes. - -The following files and directories *are* required for GuestOS to run in production. - -=== CONFIGURED - -This file serves as a tag to indicate that the one-time bootstrap configuration has been completed. If the `/boot/config/CONFIGURED` file is not present, the boot sequence will search for a virtual USB stick (the bootstrap config image) containing the injected configuration files, and create the file. - -=== store.keyfile - -This file contains the key material used to derive the wrapping key for all block device encryption. The `store.keyfile` is created during the first boot, and encrypted partitions are configured with it. - -In the absence of a sealing key (which will be available in SEV-protected trusted execution environments), the `store.keyfile` is stored as plain-text. Once a sealing key becomes available, it should be used to wrap the contents of this file. - -== Development configuration files - -These configuration files should only be used for development and testing purposes. - -== Injecting external state - -*Typical bootstrap process:* On first boot, the system will perform technical initialization (filesystems, etc.) and afterwards, initialize itself to act as a node in the IC. The node is initialized using key generation on the node itself (such that the private key never leaves the node) and through joining the IC (the node gets the rest of its state via joining the IC). "Registration" to the target IC is initiated by the node itself by sending a Node Operator-signed "join" request to its NNS. - -However, the typical bootstrap process can be modified such that the node is initialized using externally generated private keys and an externally generated initial state. All "registration" to the target IC is assumed to have been performed by other means. - -The behavior is triggered through the presence of the following files: - -- ic_crypto -- ic_registry_local_store - -This behavior is suitable for the following use cases: - -- Bootstrapping an IC instance: In this case, suitable state for all nodes is generated by ic-prep and then distributed across multiple nodes. This is used, for example, during testnet setup. - -- Externally controlled join of a node to a subnet: In this case, ic-prep is used to prepare key material to the node, while ic-admin is used to modify the target NNS such that it "accepts" the new node as part of the IC. - diff --git a/ic-os/guestos/docs/DiskLayout.adoc b/ic-os/guestos/docs/DiskLayout.adoc index 8a690680b9d..13dbcbd99f5 100644 --- a/ic-os/guestos/docs/DiskLayout.adoc +++ b/ic-os/guestos/docs/DiskLayout.adoc @@ -41,8 +41,6 @@ tampering). == *config* System config store Contains the config store persisted across system upgrades. -See link:ConfigStore{outfilesuffix}[config store] for a -specification of its contents. == *A_boot* / *B_boot* Boot partition for system A/B diff --git a/ic-os/guestos/docs/README.adoc b/ic-os/guestos/docs/README.adoc index 4caa2719d2c..92e95a07169 100644 --- a/ic-os/guestos/docs/README.adoc +++ b/ic-os/guestos/docs/README.adoc @@ -3,7 +3,6 @@ Refer to detailed documentation on: * link:DiskLayout{outfilesuffix}[Disk layout] -* link:ConfigStore{outfilesuffix}[GuestOS config store] * link:Boot{outfilesuffix}[Boot sequence] * link:SELinux{outfilesuffix}[SELinux security policy] * link:Interface{outfilesuffix}[GuestOS input/output interface] \ No newline at end of file diff --git a/ic-os/setupos/defs.bzl b/ic-os/setupos/defs.bzl index 325ebb995fa..6483645fd41 100644 --- a/ic-os/setupos/defs.bzl +++ b/ic-os/setupos/defs.bzl @@ -31,6 +31,7 @@ def image_deps(mode, _malicious = False): "bootfs": {}, "rootfs": { "//rs/ic_os/release:setupos_tool": "/opt/ic/bin/setupos_tool:0755", + "//rs/ic_os/release:config": "/opt/ic/bin/config:0755", }, # Set various configuration values diff --git a/rs/ic_os/config/src/lib.rs b/rs/ic_os/config/src/lib.rs index d10ef35b8e4..40014d8e590 100644 --- a/rs/ic_os/config/src/lib.rs +++ b/rs/ic_os/config/src/lib.rs @@ -18,6 +18,7 @@ pub static DEFAULT_SETUPOS_NODE_OPERATOR_PRIVATE_KEY_PATH: &str = pub static DEFAULT_SETUPOS_HOSTOS_CONFIG_OBJECT_PATH: &str = "/var/ic/config/config-hostos.json"; +pub static DEFAULT_HOSTOS_CONFIG_OBJECT_PATH: &str = "/boot/config/config.json"; pub static DEFAULT_HOSTOS_CONFIG_INI_FILE_PATH: &str = "/boot/config/config.ini"; pub static DEFAULT_HOSTOS_DEPLOYMENT_JSON_PATH: &str = "/boot/config/deployment.json"; diff --git a/rs/ic_os/network/src/info.rs b/rs/ic_os/network/src/info.rs deleted file mode 100644 index 21ac15e688c..00000000000 --- a/rs/ic_os/network/src/info.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::net::Ipv6Addr; - -use anyhow::{bail, Context, Result}; - -use config::config_ini::ConfigMap; - -#[derive(Debug)] -pub struct NetworkInfo { - pub ipv6_prefix: String, - pub ipv6_subnet: u8, - pub ipv6_gateway: Ipv6Addr, -} - -fn is_valid_prefix(ipv6_prefix: &str) -> bool { - ipv6_prefix.len() <= 19 && format!("{ipv6_prefix}::").parse::().is_ok() -} - -impl NetworkInfo { - pub fn from_config_map(config_map: &ConfigMap) -> Result { - // Per PFOPS - this will never not be 64 - let ipv6_subnet = 64_u8; - - let ipv6_prefix = config_map - .get("ipv6_prefix") - .context("Missing config parameter: ipv6_prefix") - .and_then(|prefix| { - if is_valid_prefix(prefix) { - Ok(prefix.clone()) - } else { - bail!("Invalid ipv6 prefix: {}", prefix) - } - })?; - - let ipv6_gateway = config_map - .get("ipv6_gateway") - .context("Missing config parameter: ipv6_gateway")?; - let ipv6_gateway = ipv6_gateway - .parse::() - .context(format!("Invalid ipv6 address: {}", ipv6_gateway))?; - - Ok(NetworkInfo { - ipv6_prefix, - ipv6_subnet, - ipv6_gateway, - }) - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashMap; - - use super::*; - #[test] - fn test_is_valid_prefix() { - assert!(is_valid_prefix("2a00:1111:1111:1111")); - assert!(is_valid_prefix("2a00:111:11:11")); - assert!(is_valid_prefix("2602:fb2b:100:10")); - } - - #[test] - fn test_from_config_map() { - // Example config.ini - let config_map = HashMap::from([ - ("ipv6_prefix".to_string(), "2a00:fb01:400:100".to_string()), - ( - "ipv6_gateway".to_string(), - "2a00:fb01:400:100::1".to_string(), - ), - ]); - assert!(NetworkInfo::from_config_map(&config_map).is_ok()); - - // No subnet - let config_map = HashMap::from([ - ("ipv6_prefix".to_string(), "2a00:fb01:400:100".to_string()), - ( - "ipv6_gateway".to_string(), - "2a00:fb01:400:100::1".to_string(), - ), - ]); - assert!(NetworkInfo::from_config_map(&config_map).is_ok()); - - // Need prefix or address, gateway - let config_map = HashMap::from([( - "ipv6_gateway".to_string(), - "2a00:fb01:400:100::1".to_string(), - )]); - assert!(NetworkInfo::from_config_map(&config_map).is_err()); - let config_map = - HashMap::from([("ipv6_prefix".to_string(), "2a00:fb01:400:100".to_string())]); - assert!(NetworkInfo::from_config_map(&config_map).is_err()); - } -} diff --git a/rs/ic_os/network/src/interfaces.rs b/rs/ic_os/network/src/interfaces.rs index 2b7895be1bf..3f041e3856f 100644 --- a/rs/ic_os/network/src/interfaces.rs +++ b/rs/ic_os/network/src/interfaces.rs @@ -22,11 +22,11 @@ pub struct Interface { pub fn has_ipv6_connectivity( interface: &Interface, generated_ipv6: &Ipv6Addr, - ipv6_subnet: u8, + ipv6_prefix_length: u8, ping_target: &str, ) -> Result { // Format with the prefix length - let ip = format!("{}/{}", generated_ipv6, ipv6_subnet); + let ip = format!("{}/{}", generated_ipv6, ipv6_prefix_length); let interface_down_func = || { eprintln!("Removing ip address and bringing interface down"); get_command_stdout("ip", ["addr", "del", &ip, "dev", &interface.name])?; diff --git a/rs/ic_os/network/src/lib.rs b/rs/ic_os/network/src/lib.rs index c64b77b4dec..01147f7a06e 100644 --- a/rs/ic_os/network/src/lib.rs +++ b/rs/ic_os/network/src/lib.rs @@ -1,14 +1,13 @@ use std::path::Path; -use anyhow::Result; +use anyhow::{anyhow, Result}; use crate::mac_address::UnformattedMacAddress; use crate::systemd::generate_systemd_config_files; -use info::NetworkInfo; +use config::types::{Ipv6Config, NetworkSettings}; use ipv6::generate_ipv6_address; use mac_address::FormattedMacAddress; -pub mod info; pub mod interfaces; pub mod ipv6; pub mod mac_address; @@ -18,19 +17,28 @@ pub mod systemd; /// Write SetupOS or HostOS systemd network configuration. /// Requires superuser permissions to run `ipmitool` and write to the systemd directory pub fn generate_network_config( - network_info: &NetworkInfo, + network_settings: &NetworkSettings, generated_mac: UnformattedMacAddress, output_directory: &Path, ) -> Result<()> { eprintln!("Generating ipv6 address"); - let ipv6_address = generate_ipv6_address(&network_info.ipv6_prefix, &generated_mac)?; - eprintln!("Using ipv6 address: {}", ipv6_address); - let formatted_mac = FormattedMacAddress::from(&generated_mac); - generate_systemd_config_files( - output_directory, - network_info, - Some(&formatted_mac), - &ipv6_address, - ) + match &network_settings.ipv6_config { + Ipv6Config::RouterAdvertisement => { + Err(anyhow!("IC-OS router advertisement is not yet supported")) + } + Ipv6Config::Fixed(_) => Err(anyhow!("Fixed IP configuration is not yet supported")), + Ipv6Config::Deterministic(ipv6_config) => { + let ipv6_address = generate_ipv6_address(&ipv6_config.prefix, &generated_mac)?; + eprintln!("Using ipv6 address: {}", ipv6_address); + + let formatted_mac = FormattedMacAddress::from(&generated_mac); + generate_systemd_config_files( + output_directory, + ipv6_config, + Some(&formatted_mac), + &ipv6_address, + ) + } + } } diff --git a/rs/ic_os/network/src/systemd.rs b/rs/ic_os/network/src/systemd.rs index d2e1c2685c6..e2d49cf247b 100644 --- a/rs/ic_os/network/src/systemd.rs +++ b/rs/ic_os/network/src/systemd.rs @@ -5,9 +5,9 @@ use std::process::Command; use anyhow::{Context, Result}; -use crate::info::NetworkInfo; use crate::interfaces::{get_interfaces, has_ipv6_connectivity, Interface}; use crate::mac_address::FormattedMacAddress; +use config::types::DeterministicIpv6Config; pub static DEFAULT_SYSTEMD_NETWORK_DIR: &str = "/run/systemd/network"; @@ -149,7 +149,7 @@ fn generate_and_write_systemd_files( pub fn generate_systemd_config_files( output_directory: &Path, - network_info: &NetworkInfo, + ipv6_config: &DeterministicIpv6Config, generated_mac: Option<&FormattedMacAddress>, ipv6_address: &Ipv6Addr, ) -> Result<()> { @@ -157,13 +157,13 @@ pub fn generate_systemd_config_files( interfaces.sort_by(|a, b| a.speed_mbps.cmp(&b.speed_mbps)); eprintln!("Interfaces sorted by speed: {:?}", interfaces); - let ping_target = network_info.ipv6_gateway.to_string(); + let ping_target = ipv6_config.gateway.to_string(); // old nodes are still configured with a local IPv4 interface connection // local IPv4 interfaces must be filtered out let ipv6_interfaces: Vec<&Interface> = interfaces .iter() .filter(|i| { - match has_ipv6_connectivity(i, ipv6_address, network_info.ipv6_subnet, &ping_target) { + match has_ipv6_connectivity(i, ipv6_address, ipv6_config.prefix_length, &ping_target) { Ok(result) => result, Err(e) => { eprintln!("Error testing connectivity on {}: {}", &i.name, e); @@ -183,13 +183,17 @@ pub fn generate_systemd_config_files( eprintln!("Using fastest interface: {:?}", fastest_interface); // Format the ip address to include the subnet length. See `man systemd.network`. - let ipv6_address = format!("{}/{}", &ipv6_address.to_string(), network_info.ipv6_subnet); + let ipv6_address = format!( + "{}/{}", + &ipv6_address.to_string(), + ipv6_config.prefix_length + ); generate_and_write_systemd_files( output_directory, fastest_interface, generated_mac, &ipv6_address, - &network_info.ipv6_gateway.to_string(), + &ipv6_config.gateway.to_string(), )?; print!("Restarting systemd networkd"); diff --git a/rs/ic_os/os_tools/hostos_tool/src/main.rs b/rs/ic_os/os_tools/hostos_tool/src/main.rs index 3ed2ab183b3..dd2ea5c60f6 100644 --- a/rs/ic_os/os_tools/hostos_tool/src/main.rs +++ b/rs/ic_os/os_tools/hostos_tool/src/main.rs @@ -1,13 +1,14 @@ use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; -use config::config_ini::config_map_from_path; -use config::deployment_json::get_deployment_settings; -use config::{DEFAULT_HOSTOS_CONFIG_INI_FILE_PATH, DEFAULT_HOSTOS_DEPLOYMENT_JSON_PATH}; +use config::types::{HostOSConfig, Ipv6Config}; +use config::{ + deserialize_config, DEFAULT_HOSTOS_CONFIG_INI_FILE_PATH, DEFAULT_HOSTOS_CONFIG_OBJECT_PATH, + DEFAULT_HOSTOS_DEPLOYMENT_JSON_PATH, +}; use network::generate_network_config; -use network::info::NetworkInfo; use network::ipv6::generate_ipv6_address; use network::mac_address::{generate_mac_address, get_ipmi_mac, FormattedMacAddress}; use network::node_type::NodeType; @@ -56,23 +57,20 @@ pub fn main() -> Result<()> { match opts.command { Some(Commands::GenerateNetworkConfig { output_directory }) => { - let config_map = config_map_from_path(Path::new(&opts.config)).context(format!( - "Failed to get config.ini settings for path: {}", - &opts.config - ))?; - eprintln!("Using config: {:?}", config_map); - - let network_info = NetworkInfo::from_config_map(&config_map)?; - eprintln!("Network info config: {:?}", &network_info); - - let deployment_settings = get_deployment_settings(Path::new(&opts.deployment_file)) - .context(format!( - "Failed to get deployment settings for file: {}", - &opts.deployment_file - ))?; - eprintln!("Deployment config: {:?}", deployment_settings); - - let mgmt_mac = match deployment_settings.deployment.mgmt_mac { + let hostos_config: HostOSConfig = + deserialize_config(DEFAULT_HOSTOS_CONFIG_OBJECT_PATH)?; + + eprintln!( + "Network settings config: {:?}", + &hostos_config.network_settings + ); + + let mgmt_mac = match hostos_config + .icos_settings + .icos_dev_settings + .mgmt_mac + .as_ref() + { Some(config_mac) => { let mgmt_mac = FormattedMacAddress::try_from(config_mac.as_str())?; eprintln!( @@ -83,33 +81,35 @@ pub fn main() -> Result<()> { } None => get_ipmi_mac()?, }; + let generated_mac = generate_mac_address( &mgmt_mac, - deployment_settings.deployment.name.as_str(), + &hostos_config.icos_settings.hostname, &NodeType::HostOS, )?; + eprintln!("Using generated mac (unformatted) {}", generated_mac); - generate_network_config(&network_info, generated_mac, Path::new(&output_directory)) + generate_network_config( + &hostos_config.network_settings, + generated_mac, + Path::new(&output_directory), + ) } Some(Commands::GenerateIpv6Address { node_type }) => { - let config_map = config_map_from_path(Path::new(&opts.config)).context(format!( - "Failed to get config.ini settings for path: {}", - &opts.config - ))?; - eprintln!("Using config: {:?}", config_map); - - let network_info = NetworkInfo::from_config_map(&config_map)?; - eprintln!("Network info config: {:?}", &network_info); - - let deployment_settings = get_deployment_settings(Path::new(&opts.deployment_file)) - .context(format!( - "Failed to get deployment settings for file: {}", - &opts.deployment_file - ))?; - eprintln!("Deployment config: {:?}", deployment_settings); - - let node_type = node_type.parse::()?; - let mgmt_mac = match deployment_settings.deployment.mgmt_mac { + let hostos_config: HostOSConfig = + deserialize_config(DEFAULT_HOSTOS_CONFIG_OBJECT_PATH)?; + + eprintln!( + "Network settings config: {:?}", + &hostos_config.network_settings + ); + + let mgmt_mac = match hostos_config + .icos_settings + .icos_dev_settings + .mgmt_mac + .as_ref() + { Some(config_mac) => { let mgmt_mac = FormattedMacAddress::try_from(config_mac.as_str())?; eprintln!( @@ -120,34 +120,42 @@ pub fn main() -> Result<()> { } None => get_ipmi_mac()?, }; - let generated_mac = generate_mac_address( - &mgmt_mac, - deployment_settings.deployment.name.as_str(), - &node_type, - )?; - let ipv6_address = generate_ipv6_address(&network_info.ipv6_prefix, &generated_mac)?; - println!("{}", to_cidr(ipv6_address, network_info.ipv6_subnet)); + let node_type = node_type.parse::()?; + + let generated_mac = + generate_mac_address(&mgmt_mac, &hostos_config.icos_settings.hostname, &node_type)?; + eprintln!("Using generated mac (unformatted) {}", generated_mac); + + let ipv6_config = if let Ipv6Config::Deterministic(ipv6_config) = + &hostos_config.network_settings.ipv6_config + { + ipv6_config + } else { + return Err(anyhow!( + "Ipv6Config is not of type Deterministic. Cannot generate IPv6 address." + )); + }; + + let ipv6_address = generate_ipv6_address(&ipv6_config.prefix, &generated_mac)?; + println!("{}", to_cidr(ipv6_address, ipv6_config.prefix_length)); + Ok(()) } Some(Commands::GenerateMacAddress { node_type }) => { - let config_map = config_map_from_path(Path::new(&opts.config)).context(format!( - "Failed to get config.ini settings for path: {}", - &opts.config - ))?; - eprintln!("Using config: {:?}", config_map); - - let network_info = NetworkInfo::from_config_map(&config_map)?; - eprintln!("Network info config: {:?}", &network_info); - - let deployment_settings = get_deployment_settings(Path::new(&opts.deployment_file)) - .context(format!( - "Failed to get deployment settings for file: {}", - &opts.deployment_file - ))?; - eprintln!("Deployment config: {:?}", deployment_settings); - - let node_type = node_type.parse::()?; - let mgmt_mac = match deployment_settings.deployment.mgmt_mac { + let hostos_config: HostOSConfig = + deserialize_config(DEFAULT_HOSTOS_CONFIG_OBJECT_PATH)?; + + eprintln!( + "Network settings config: {:?}", + &hostos_config.network_settings + ); + + let mgmt_mac = match hostos_config + .icos_settings + .icos_dev_settings + .mgmt_mac + .as_ref() + { Some(config_mac) => { let mgmt_mac = FormattedMacAddress::try_from(config_mac.as_str())?; eprintln!( @@ -158,13 +166,12 @@ pub fn main() -> Result<()> { } None => get_ipmi_mac()?, }; - let generated_mac = generate_mac_address( - &mgmt_mac, - deployment_settings.deployment.name.as_str(), - &node_type, - )?; + let node_type = node_type.parse::()?; + + let generated_mac = + generate_mac_address(&mgmt_mac, &hostos_config.icos_settings.hostname, &node_type)?; + eprintln!("Using generated mac (unformatted) {}", generated_mac); - let generated_mac = FormattedMacAddress::from(&generated_mac); println!("{}", generated_mac); Ok(()) } diff --git a/rs/ic_os/os_tools/setupos_tool/src/main.rs b/rs/ic_os/os_tools/setupos_tool/src/main.rs index dd929f842cf..152c6c48e24 100644 --- a/rs/ic_os/os_tools/setupos_tool/src/main.rs +++ b/rs/ic_os/os_tools/setupos_tool/src/main.rs @@ -1,13 +1,14 @@ use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; -use config::config_ini::config_map_from_path; -use config::deployment_json::get_deployment_settings; -use config::{DEFAULT_SETUPOS_CONFIG_INI_FILE_PATH, DEFAULT_SETUPOS_DEPLOYMENT_JSON_PATH}; +use config::types::{Ipv6Config, SetupOSConfig}; +use config::{ + deserialize_config, DEFAULT_SETUPOS_CONFIG_INI_FILE_PATH, DEFAULT_SETUPOS_CONFIG_OBJECT_PATH, + DEFAULT_SETUPOS_DEPLOYMENT_JSON_PATH, +}; use network::generate_network_config; -use network::info::NetworkInfo; use network::ipv6::generate_ipv6_address; use network::mac_address::{generate_mac_address, get_ipmi_mac, FormattedMacAddress}; use network::node_type::NodeType; @@ -51,23 +52,20 @@ pub fn main() -> Result<()> { match opts.command { Some(Commands::GenerateNetworkConfig { output_directory }) => { - let config_map = config_map_from_path(Path::new(&opts.config)).context(format!( - "Failed to get config.ini settings for path: {}", - &opts.config - ))?; - eprintln!("Using config: {:?}", config_map); - - let network_info = NetworkInfo::from_config_map(&config_map)?; - eprintln!("Network info config: {:?}", &network_info); - - let deployment_settings = get_deployment_settings(Path::new(&opts.deployment_file)) - .context(format!( - "Failed to get deployment settings for file: {}", - &opts.deployment_file - ))?; - eprintln!("Deployment config: {:?}", deployment_settings); - - let mgmt_mac = match deployment_settings.deployment.mgmt_mac { + let setupos_config: SetupOSConfig = + deserialize_config(DEFAULT_SETUPOS_CONFIG_OBJECT_PATH)?; + + eprintln!( + "Network settings config: {:?}", + &setupos_config.network_settings + ); + + let mgmt_mac = match setupos_config + .icos_settings + .icos_dev_settings + .mgmt_mac + .as_ref() + { Some(config_mac) => { let mgmt_mac = FormattedMacAddress::try_from(config_mac.as_str())?; eprintln!( @@ -80,32 +78,28 @@ pub fn main() -> Result<()> { }; let generated_mac = generate_mac_address( &mgmt_mac, - deployment_settings.deployment.name.as_str(), + &setupos_config.icos_settings.hostname, &NodeType::SetupOS, )?; eprintln!("Using generated mac (unformatted) {}", generated_mac); - generate_network_config(&network_info, generated_mac, Path::new(&output_directory)) + generate_network_config( + &setupos_config.network_settings, + generated_mac, + Path::new(&output_directory), + ) } Some(Commands::GenerateIpv6Address { node_type }) => { - let config_map = config_map_from_path(Path::new(&opts.config)).context(format!( - "Failed to get config.ini settings for path: {}", - &opts.config - ))?; - eprintln!("Using config: {:?}", config_map); - - let network_info = NetworkInfo::from_config_map(&config_map)?; - eprintln!("Network info config: {:?}", &network_info); - - let deployment_settings = get_deployment_settings(Path::new(&opts.deployment_file)) - .context(format!( - "Failed to get deployment settings for file: {}", - &opts.deployment_file - ))?; - eprintln!("Deployment config: {:?}", deployment_settings); + let setupos_config: SetupOSConfig = + deserialize_config(DEFAULT_SETUPOS_CONFIG_OBJECT_PATH)?; + + eprintln!( + "Network settings config: {:?}", + &setupos_config.network_settings + ); let node_type = node_type.parse::()?; - let mgmt_mac = match deployment_settings.deployment.mgmt_mac { + let mgmt_mac = match setupos_config.icos_settings.icos_dev_settings.mgmt_mac { Some(config_mac) => { let mgmt_mac = FormattedMacAddress::try_from(config_mac.as_str())?; eprintln!( @@ -118,11 +112,24 @@ pub fn main() -> Result<()> { }; let generated_mac = generate_mac_address( &mgmt_mac, - deployment_settings.deployment.name.as_str(), + &setupos_config.icos_settings.hostname, &node_type, )?; - let ipv6_address = generate_ipv6_address(&network_info.ipv6_prefix, &generated_mac)?; - println!("{}", to_cidr(ipv6_address, network_info.ipv6_subnet)); + eprintln!("Using generated mac (unformatted) {}", generated_mac); + + let ipv6_config = if let Ipv6Config::Deterministic(ipv6_config) = + &setupos_config.network_settings.ipv6_config + { + ipv6_config + } else { + return Err(anyhow!( + "Ipv6Config is not of type Deterministic. Cannot generate IPv6 address." + )); + }; + + let ipv6_address = generate_ipv6_address(&ipv6_config.prefix, &generated_mac)?; + println!("{}", to_cidr(ipv6_address, ipv6_config.prefix_length)); + Ok(()) } None => Err(anyhow!(