Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(node): Configuration revamp (SetupOS integration) #3270

Merged
merged 16 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ic-os/components/misc/config/setupos/config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/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.nns_urls')
# Note: If the key is not found, this function will return null.
function get_config_value() {
frankdavid marked this conversation as resolved.
Show resolved Hide resolved
local CONFIG_FILE="/var/ic/config/config.json"
local key=$1
jq -r "${key}" "${CONFIG_FILE}"
}
38 changes: 38 additions & 0 deletions ic-os/components/setupos-scripts/check-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash

# check-config.sh verifies the existence of the configuration JSON file created by config.service,
# halting the installation if not found.

set -o nounset
set -o pipefail

SHELL="/bin/bash"
PATH="/sbin:/bin:/usr/sbin:/usr/bin"

source /opt/ic/bin/config.sh
andrewbattat marked this conversation as resolved.
Show resolved Hide resolved
source /opt/ic/bin/functions.sh

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
31 changes: 2 additions & 29 deletions ic-os/components/setupos-scripts/check-hardware.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set -o pipefail
SHELL="/bin/bash"
PATH="/sbin:/bin:/usr/sbin:/usr/bin"

source /opt/ic/bin/config.sh
andrewbattat marked this conversation as resolved.
Show resolved Hide resolved
source /opt/ic/bin/functions.sh

GENERATION=
Expand All @@ -32,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..."

Expand Down Expand Up @@ -249,6 +248,7 @@ function verify_disks() {

function verify_deployment_path() {
echo "* Verifying deployment path..."

if [[ ${GENERATION} == 2 ]] && [[ ! -f "${CONFIG_DIR}/node_operator_private_key.pem" ]]; 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"
Expand All @@ -261,32 +261,6 @@ function verify_deployment_path() {
fi
}

# TODO(NODE-1477): delete in configuration revamp integration
CONFIG="${CONFIG:=/var/ic/config/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
"node_reward_type") node_reward_type="${value}" ;;
esac
done <"${CONFIG}"
}

function validate_node_reward() {
read_variables
if [[ -z "$node_reward_type" ]]; then
log_and_halt_installation_on_error 1 "Configuration error: node_reward_type is not set"
fi

if [[ ! "$node_reward_type" =~ ^type[0-9]+(\.[0-9])?$ ]]; then
log_and_halt_installation_on_error 1 "Configuration error: node_reward_type is invalid: ${node_reward_type}"
fi

echo "Valid node reward type: ${node_reward_type}"
}
andrewbattat marked this conversation as resolved.
Show resolved Hide resolved

# Establish run order
main() {
log_start "$(basename $0)"
Expand All @@ -296,7 +270,6 @@ main() {
verify_memory
verify_disks
verify_deployment_path
validate_node_reward
else
echo "* Hardware checks skipped by request via kernel command line"
GENERATION=2
Expand Down
47 changes: 18 additions & 29 deletions ic-os/components/setupos-scripts/check-network.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,16 @@ set -o pipefail
SHELL="/bin/bash"
PATH="/sbin:/bin:/usr/sbin:/usr/bin"

source /opt/ic/bin/config.sh
source /opt/ic/bin/functions.sh

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_name=$(get_config_value '.network_settings.domain_name')
}

# WARNING: Uses 'eval' for command execution.
Expand Down Expand Up @@ -109,11 +101,13 @@ function print_network_settings() {
echo "* Printing user defined network settings..."
echo " IPv6 Prefix : ${ipv6_prefix}"
echo " IPv6 Gateway: ${ipv6_gateway}"
if [[ -v ipv4_address && -n ${ipv4_address} && -v ipv4_prefix_length && -n ${ipv4_prefix_length} && -v ipv4_gateway && -n ${ipv4_gateway} && -v domain && -n ${domain} ]]; then
if [[ -n ${ipv4_address} && -n ${ipv4_prefix_length} && -n ${ipv4_gateway} ]]; then
echo " IPv4 Address: ${ipv4_address}"
echo " IPv4 Prefix Length: ${ipv4_prefix_length}"
echo " IPv4 Gateway: ${ipv4_gateway}"
echo " Domain name : ${domain}"
fi
if [[ -n ${domain_name} ]]; then
echo " Domain name : ${domain_name}"
Bownairo marked this conversation as resolved.
Show resolved Hide resolved
fi
echo " "

Expand All @@ -134,10 +128,10 @@ function validate_domain_name() {
local domain_part
local -a domain_parts

IFS='.' read -ra domain_parts <<<"${domain}"
IFS='.' read -ra domain_parts <<<"${domain_name}"

if [ ${#domain_parts[@]} -lt 2 ]; then
log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain: ${domain}"
log_and_halt_installation_on_error 1 "Domain validation error: less than two domain parts in domain: ${domain_name}"
fi

for domain_part in "${domain_parts[@]}"; do
Expand Down Expand Up @@ -184,17 +178,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_url_list=($(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_url_list[@]}"; 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".
Expand All @@ -218,7 +208,7 @@ function query_nns_nodes() {
main() {
log_start "$(basename $0)"
if kernel_cmdline_bool_default_true ic.setupos.check_network; then
read_variables
read_config_variables
get_network_settings
print_network_settings

Expand All @@ -229,7 +219,6 @@ main() {
fi

ping_ipv6_gateway
assemble_nns_nodes_list
query_nns_nodes
else
echo "* Network checks skipped by request via kernel command line"
Expand Down
6 changes: 3 additions & 3 deletions ic-os/components/setupos-scripts/config.service
Original file line number Diff line number Diff line change
Expand Up @@ -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
64 changes: 49 additions & 15 deletions ic-os/components/setupos-scripts/setup-hostos-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ set -o pipefail

SHELL="/bin/bash"
PATH="/sbin:/bin:/usr/sbin:/usr/bin"
CONFIG_DIR="/var/ic/config"
CONFIG_DIR="/config"

source /opt/ic/bin/config.sh
source /opt/ic/bin/functions.sh

function mount_config_partition() {
Expand All @@ -20,6 +21,7 @@ function mount_config_partition() {
}

function copy_config_files() {
# TODO(NODE-1518): delete config.ini copying after switch to new icos config
echo "* Copying 'config.ini' to hostOS config partition..."
if [ -f "${CONFIG_DIR}/config.ini" ]; then
cp ${CONFIG_DIR}/config.ini /media/
Expand All @@ -28,29 +30,61 @@ function copy_config_files() {
log_and_halt_installation_on_error "1" "Configuration file 'config.ini' does not exist."
fi

# TODO(NODE-1518): delete deployment.json copying after switch to new icos config
echo "* Copying deployment.json to config partition..."
cp /data/deployment.json /media/
log_and_halt_installation_on_error "${?}" "Unable to copy deployment.json to hostOS config partition."

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."
use_ssh_authorized_keys=$(get_config_value '.icos_settings.use_ssh_authorized_keys')
if [[ "${use_ssh_authorized_keys,,}" == "true" ]]; then
if [ -d "${CONFIG_DIR}/ssh_authorized_keys" ]; then
cp -a "${CONFIG_DIR}/ssh_authorized_keys" /media/
log_and_halt_installation_on_error "${?}" "Unable to copy SSH authorized keys to hostOS config partition."
else
echo >&2 "Warning: SSH authorized keys are not configured."
Bownairo marked this conversation as resolved.
Show resolved Hide resolved
fi
else
log_and_halt_installation_on_error "1" "Directory 'ssh_authorized_keys' does not exist."
echo >&2 "SSH keys not in use."
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/
log_and_halt_installation_on_error "${?}" "Unable to copy node operator private key to hostOS config partition."
use_node_operator_private_key=$(get_config_value '.icos_settings.use_node_operator_private_key')
if [[ "${use_node_operator_private_key,,}" == "true" ]]; then
if [ -f "${CONFIG_DIR}/node_operator_private_key.pem" ]; then
cp "${CONFIG_DIR}/node_operator_private_key.pem" /media/
log_and_halt_installation_on_error "${?}" "Unable to copy node operator private key to hostOS config partition."
else
log_and_halt_installation_on_error "1" "use_node_operator_private_key set to true but not found"
fi
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..."
cp /data/deployment.json /media/
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/
log_and_halt_installation_on_error "${?}" "Unable to copy NNS public key to hostOS config partition."
use_nns_public_key=$(get_config_value '.icos_settings.use_nns_public_key')
if [[ "${use_nns_public_key,,}" == "true" ]]; then
if [ -f "/data/nns_public_key.pem" ]; then
cp /data/nns_public_key.pem /media/
log_and_halt_installation_on_error "${?}" "Unable to copy NNS public key to hostOS config partition."
else
log_and_halt_installation_on_error "1" "use_nns_public_key set to true but not found."
fi
else
log_and_halt_installation_on_error "1" "use_nns_public_key must be set to true."
frankdavid marked this conversation as resolved.
Show resolved Hide resolved
fi

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() {
Expand Down
1 change: 1 addition & 0 deletions ic-os/components/setupos-scripts/setupos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ main() {
log_start "$(basename $0)"
start_setupos
/opt/ic/bin/check-setupos-age.sh
/opt/ic/bin/check-config.sh
/opt/ic/bin/check-hardware.sh
/opt/ic/bin/check-network.sh
if kernel_cmdline_bool_default_true ic.setupos.perform_installation; then
Expand Down
4 changes: 2 additions & 2 deletions ic-os/components/setupos.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ component_files = {

# setupos-scripts
Label("//ic-os/components/setupos-scripts:check-setupos-age.sh"): "/opt/ic/bin/check-setupos-age.sh",
Label("//ic-os/components/setupos-scripts:config.sh"): "/opt/ic/bin/config.sh",
Label("//ic-os/components/setupos-scripts:check-config.sh"): "/opt/ic/bin/check-config.sh",
Label("//ic-os/components/setupos-scripts:setup-hostos-config.sh"): "/opt/ic/bin/setup-hostos-config.sh",
Label("//ic-os/components/setupos-scripts:setup-disk.sh"): "/opt/ic/bin/setup-disk.sh",
Label("//ic-os/components/setupos-scripts:functions.sh"): "/opt/ic/bin/functions.sh",
Expand All @@ -31,9 +31,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/override.conf"): "/etc/systemd/system/[email protected]/override.conf",
Label("monitoring/journald.conf"): "/etc/systemd/journald.conf",

Expand Down
6 changes: 3 additions & 3 deletions ic-os/guestos/docs/Boot.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 0 additions & 2 deletions ic-os/guestos/docs/DiskLayout.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 0 additions & 1 deletion ic-os/guestos/docs/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Loading
Loading