From: Michael Friedrich Date: Wed, 6 Sep 2017 15:33:54 +0000 (+0200) Subject: Refactor the node wizard/setup CLI commands X-Git-Tag: v2.8.0~87^2~15 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e424017c157060f113e06a1c633ebbbf500bd832;p=icinga2 Refactor the node wizard/setup CLI commands refs #5450 --- diff --git a/lib/cli/nodesetupcommand.cpp b/lib/cli/nodesetupcommand.cpp index c3422785b..302dccae8 100644 --- a/lib/cli/nodesetupcommand.cpp +++ b/lib/cli/nodesetupcommand.cpp @@ -131,14 +131,14 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v cn = vm["cn"].as(); /* check whether the user wants to generate a new certificate or not */ - String existing_path = ApiListener::GetCertsDir() + "/" + cn + ".crt"; + String existingPath = ApiListener::GetCertsDir() + "/" + cn + ".crt"; Log(LogInformation, "cli") - << "Checking for existing certificates for common name '" << cn << "'..."; + << "Checking in existing certificates for common name '" << cn << "'..."; - if (Utility::PathExists(existing_path)) { + if (Utility::PathExists(existingPath)) { Log(LogWarning, "cli") - << "Certificate '" << existing_path << "' for CN '" << cn << "' already exists. Not generating new certificate."; + << "Certificate '" << existingPath << "' for CN '" << cn << "' already exists. Not generating new certificate."; } else { Log(LogInformation, "cli") << "Certificates not yet generated. Running 'api setup' now."; @@ -157,13 +157,11 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v } /* write zones.conf and update with zone + endpoint information */ - Log(LogInformation, "cli", "Generating zone and object configuration."); NodeUtility::GenerateNodeMasterIcingaConfig(); /* update the ApiListener config - SetupMaster() will always enable it */ - Log(LogInformation, "cli", "Updating the APIListener feature."); String apipath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf"; @@ -263,7 +261,8 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, /* require master host information for auto-signing requests */ if (!vm.count("master_host")) { - Log(LogCritical, "cli", "Please pass the master host connection information for auto-signing using '--master_host '"); + Log(LogCritical, "cli", "Please pass the master host connection information for auto-signing using '--master_host '. This can also be a direct parent satellite since 2.8."); + return 1; } @@ -279,13 +278,13 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, master_port = tokens[1]; Log(LogInformation, "cli") - << "Verifying master host connection information: host '" << master_host << "', port '" << master_port << "'."; + << "Verifying parent host connection information: host '" << master_host << "', port '" << master_port << "'."; /* trusted cert must be passed (retrieved by the user with 'pki save-cert' before) */ if (!vm.count("trustedcert")) { Log(LogCritical, "cli") - << "Please pass the trusted cert retrieved from the master\n" + << "Please pass the trusted cert retrieved from the parent node (master or satellite)\n" << "(Hint: 'icinga2 pki save-cert --host --port <5665> --key local.key --cert local.crt --trustedcert master.crt')."; return 1; } @@ -337,10 +336,10 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << key << "'. Verify it yourself!"; } - Log(LogInformation, "cli", "Requesting a signed certificate from the master."); + Log(LogInformation, "cli", "Requesting a signed certificate from the parent Icinga node."); if (PkiUtility::RequestCertificate(master_host, master_port, key, cert, ca, trustedcert, ticket) != 0) { - Log(LogCritical, "cli", "Failed to request certificate from Icinga 2 master."); + Log(LogCritical, "cli", "Failed to request certificate from parent Icinga node."); return 1; } @@ -431,7 +430,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm, /* update constants.conf with NodeName = CN */ if (cn != Utility::GetFQDN()) { Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; + << "CN '" << cn << "' does not match the default FQDN '" << Utility::GetFQDN() << "'. Requires an update for the NodeName constant in constants.conf!"; } Log(LogInformation, "cli", "Updating constants.conf."); diff --git a/lib/cli/nodewizardcommand.cpp b/lib/cli/nodewizardcommand.cpp index 37c9d68bb..a88bef75f 100644 --- a/lib/cli/nodewizardcommand.cpp +++ b/lib/cli/nodewizardcommand.cpp @@ -78,12 +78,10 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundBlue) << "Welcome to the Icinga 2 Setup Wizard!\n" << "\n" - << "We'll guide you through all required configuration details.\n" + << "We will guide you through all required configuration details.\n" << "\n" << ConsoleColorTag(Console_Normal); - //TODO: Add sort of bash completion to path input? - /* 0. master or node setup? * 1. Ticket * 2. Master information for autosigning @@ -100,11 +98,9 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, */ std::string answer; - bool is_node_setup = true; - - /* master or node setup */ + /* master or satellite/client setup */ std::cout << ConsoleColorTag(Console_Bold) - << "Please specify if this is a satellite setup " + << "Please specify if this is a satellite/client setup " << "('n' installs a master setup)" << ConsoleColorTag(Console_Normal) << " [Y/n]: "; std::getline (std::cin, answer); @@ -113,496 +109,525 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, String choice = answer; + std::cout << "\n"; + + int res = 0; + if (choice.Contains("n")) - is_node_setup = false; + res = MasterSetup(); + else + res = ClientSetup(); - if (is_node_setup) { - /* node setup part */ - std::cout << "Starting the Node setup routine...\n"; + if (res != 0) + return res; - /* CN */ - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the common name (CN)" - << ConsoleColorTag(Console_Normal) - << " [" << Utility::GetFQDN() << "]: "; + std::cout << "\n"; + std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen) + << "Done.\n\n" + << ConsoleColorTag(Console_Normal); - std::getline(std::cin, answer); + std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundRed) + << "Now restart your Icinga 2 daemon to finish the installation!\n\n" + << ConsoleColorTag(Console_Normal); + + return 0; +} - if (answer.empty()) - answer = Utility::GetFQDN(); +int NodeWizardCommand::ClientSetup(void) const +{ + std::string answer; + String choice; - String cn = answer; - cn = cn.Trim(); + std::cout << "Starting the Client/Satellite setup routine...\n"; - std::vector endpoints; + /* CN */ + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the common name (CN)" + << ConsoleColorTag(Console_Normal) + << " [" << Utility::GetFQDN() << "]: "; - String endpoint_buffer; + std::getline(std::cin, answer); - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the master endpoint(s) this node should connect to:" - << ConsoleColorTag(Console_Normal) << "\n"; - String master_endpoint_name; + if (answer.empty()) + answer = Utility::GetFQDN(); + + String cn = answer; + cn = cn.Trim(); + + std::vector endpoints; + + String endpointBuffer; + + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the parent endpoint(s) (master or satellite) where this node should connect to:" + << ConsoleColorTag(Console_Normal) << "\n"; + String parentEndpointName; wizard_endpoint_loop_start: + std::cout << ConsoleColorTag(Console_Bold) + << "Master/Satellite Common Name" << ConsoleColorTag(Console_Normal) + << " (CN from your master/satellite node): "; + + std::getline(std::cin, answer); + + if (answer.empty()) { + Log(LogWarning, "cli", "Master/Satellite CN is required! Please retry."); + goto wizard_endpoint_loop_start; + } + + endpointBuffer = answer; + endpointBuffer = endpointBuffer.Trim(); + + std::cout << "Do you want to establish a connection to the parent node " + << ConsoleColorTag(Console_Bold) << "from this node?" + << ConsoleColorTag(Console_Normal) << " [Y/n]: "; + + std::getline (std::cin, answer); + boost::algorithm::to_lower(answer); + choice = answer; + + String parentEndpointPort = "5665"; + + if (choice.Contains("n")) { + Log(LogWarning, "cli", "Node to master/satellite connection setup skipped"); + std::cout << "Connection setup skipped. Please configure your parent node to connect to this node by setting the 'host' attribute for the node Endpoint object.\n"; + } else { std::cout << ConsoleColorTag(Console_Bold) - << "Master Common Name" << ConsoleColorTag(Console_Normal) - << " (CN from your master setup): "; + << "Please specify the master/satellite connection information:" + << ConsoleColorTag(Console_Normal) << "\n" + << ConsoleColorTag(Console_Bold) << "Master/Satellite endpoint host" + << ConsoleColorTag(Console_Normal) << " (IP address or FQDN): "; std::getline(std::cin, answer); if (answer.empty()) { - Log(LogWarning, "cli", "Master CN is required! Please retry."); + Log(LogWarning, "cli", "Please enter the parent endpoint (master/satellite) connection information."); goto wizard_endpoint_loop_start; } - endpoint_buffer = answer; - endpoint_buffer = endpoint_buffer.Trim(); + String tmp = answer; + tmp = tmp.Trim(); - std::cout << "Do you want to establish a connection to the master " - << ConsoleColorTag(Console_Bold) << "from this node?" - << ConsoleColorTag(Console_Normal) << " [Y/n]: "; + endpointBuffer += "," + tmp; + parentEndpointName = tmp; - std::getline (std::cin, answer); - boost::algorithm::to_lower(answer); - choice = answer; + std::cout << ConsoleColorTag(Console_Bold) + << "Master/Satellite endpoint port" << ConsoleColorTag(Console_Normal) + << " [" << parentEndpointPort << "]: "; - String tmpPort = "5665"; + std::getline(std::cin, answer); - if (choice.Contains("n")) { - Log(LogWarning, "cli", "Node to master connection setup skipped"); - std::cout << "Connection setup skipped. Please configure your master to connect to this node.\n"; - } else { - std::cout << ConsoleColorTag(Console_Bold) - << "Please fill out the master connection information:" - << ConsoleColorTag(Console_Normal) << "\n" - << ConsoleColorTag(Console_Bold) << "Master endpoint host" - << ConsoleColorTag(Console_Normal) << " (Your master's IP address or FQDN): "; + if (!answer.empty()) + parentEndpointPort = answer; - std::getline(std::cin, answer); + endpointBuffer += "," + parentEndpointPort.Trim(); + } - if (answer.empty()) { - Log(LogWarning, "cli", "Please enter the master's endpoint connection information."); - goto wizard_endpoint_loop_start; - } + endpoints.push_back(endpointBuffer); - String tmp = answer; - tmp = tmp.Trim(); + std::cout << ConsoleColorTag(Console_Bold) << "Add more master/satellite endpoints?" + << ConsoleColorTag(Console_Normal) << " [y/N]: "; + std::getline (std::cin, answer); - endpoint_buffer += "," + tmp; - master_endpoint_name = tmp; //store the endpoint name for later + boost::algorithm::to_lower(answer); - std::cout << ConsoleColorTag(Console_Bold) - << "Master endpoint port" << ConsoleColorTag(Console_Normal) - << " [" << tmpPort << "]: "; + choice = answer; - std::getline(std::cin, answer); + if (choice.Contains("y")) + goto wizard_endpoint_loop_start; - if (!answer.empty()) - tmpPort = answer; + String parentHost, parentPort; - endpoint_buffer += "," + tmpPort.Trim(); - } + for (const String& endpoint : endpoints) { + std::vector tokens = endpoint.Split(","); - endpoints.push_back(endpoint_buffer); + if (tokens.size() > 1) + parentHost = tokens[1]; - std::cout << ConsoleColorTag(Console_Bold) << "Add more master endpoints?" - << ConsoleColorTag(Console_Normal) << " [y/N]: "; - std::getline (std::cin, answer); + if (tokens.size() > 2) + parentPort = tokens[2]; + } - boost::algorithm::to_lower(answer); + /* workaround for fetching the master cert */ + String certsDir = ApiListener::GetCertsDir(); + Utility::MkDirP(certsDir, 0700); - String choice = answer; + String user = ScriptGlobal::Get("RunAsUser"); + String group = ScriptGlobal::Get("RunAsGroup"); - if (choice.Contains("y")) - goto wizard_endpoint_loop_start; + if (!Utility::SetFileOwnership(certsDir, user, group)) { + Log(LogWarning, "cli") + << "Cannot set ownership for user '" << user + << "' group '" << group + << "' on file '" << certsDir << "'. Verify it yourself!"; + } - String master_host, master_port; + String nodeCert = certsDir + "/" + cn + ".crt"; + String nodeKey = certsDir + "/" + cn + ".key"; - for (const String& endpoint : endpoints) { - std::vector tokens = endpoint.Split(","); + if (Utility::PathExists(nodeKey)) + NodeUtility::CreateBackupFile(nodeKey, true); + if (Utility::PathExists(nodeCert)) + NodeUtility::CreateBackupFile(nodeCert); - if (tokens.size() > 1) - master_host = tokens[1]; + if (PkiUtility::NewCert(cn, nodeKey, Empty, nodeCert) > 0) { + Log(LogCritical, "cli") + << "Failed to create new self-signed certificate for CN '" + << cn << "'. Please try again."; + return 1; + } - if (tokens.size() > 2) - master_port = tokens[2]; - } + /* fix permissions: root -> icinga daemon user */ + if (!Utility::SetFileOwnership(nodeKey, user, group)) { + Log(LogWarning, "cli") + << "Cannot set ownership for user '" << user + << "' group '" << group + << "' on file '" << nodeKey << "'. Verify it yourself!"; + } - /* workaround for fetching the master cert */ - String pki_path = ApiListener::GetCertsDir(); - Utility::MkDirP(pki_path, 0700); + boost::shared_ptr trustedParentCert; - String user = ScriptGlobal::Get("RunAsUser"); - String group = ScriptGlobal::Get("RunAsGroup"); + if (!parentHost.IsEmpty()) { + //save-cert and store the master certificate somewhere + Log(LogInformation, "cli") + << "Fetching public certificate from master (" + << parentHost << ", " << parentPort << "):\n"; - if (!Utility::SetFileOwnership(pki_path, user, group)) { - Log(LogWarning, "cli") - << "Cannot set ownership for user '" << user - << "' group '" << group - << "' on file '" << pki_path << "'. Verify it yourself!"; + trustedParentCert = PkiUtility::FetchCert(parentHost, parentPort); + if (!trustedParentCert) { + Log(LogCritical, "cli", "Peer did not present a valid certificate."); + return 1; } - String node_cert = pki_path + "/" + cn + ".crt"; - String node_key = pki_path + "/" + cn + ".key"; - - if (Utility::PathExists(node_key)) - NodeUtility::CreateBackupFile(node_key, true); - if (Utility::PathExists(node_cert)) - NodeUtility::CreateBackupFile(node_cert); + std::cout << ConsoleColorTag(Console_Bold) << "Parent certificate information:\n" + << ConsoleColorTag(Console_Normal) << PkiUtility::GetCertificateInformation(trustedParentCert) + << ConsoleColorTag(Console_Bold) << "\nIs this information correct?" + << ConsoleColorTag(Console_Normal) << " [y/N]: "; - if (PkiUtility::NewCert(cn, node_key, Empty, node_cert) > 0) { - Log(LogCritical, "cli") - << "Failed to create new self-signed certificate for CN '" - << cn << "'. Please try again."; + std::getline (std::cin, answer); + boost::algorithm::to_lower(answer); + if (answer != "y") { + Log(LogWarning, "cli", "Process aborted."); return 1; } - /* fix permissions: root -> icinga daemon user */ - if (!Utility::SetFileOwnership(node_key, user, group)) { - Log(LogWarning, "cli") - << "Cannot set ownership for user '" << user - << "' group '" << group - << "' on file '" << node_key << "'. Verify it yourself!"; - } + Log(LogInformation, "cli", "Received trusted parent certificate.\n"); + } - boost::shared_ptr trustedcert; +wizard_ticket: + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the request ticket generated on your Icinga 2 master (optional)." + << ConsoleColorTag(Console_Normal) << "\n" + << " (Hint: # icinga2 pki ticket --cn '" << cn << "'): "; + + std::getline(std::cin, answer); + + if (answer.empty()) { + std::cout << ConsoleColorTag(Console_Bold) << "\n" + << "No ticket was specified. Please approve the certificate signing request manually\n" + << "on the master (see 'icinga2 ca list' and 'icinga2 ca sign --help' for details)." + << ConsoleColorTag(Console_Normal) << "\n\n"; + } + + String ticket = answer; + ticket = ticket.Trim(); - if (!master_host.IsEmpty()) { - //save-cert and store the master certificate somewhere + if (!parentHost.IsEmpty()) { + if (ticket.IsEmpty()) { + Log(LogInformation, "cli") + << "Requesting certificate without a ticket."; + } else { Log(LogInformation, "cli") - << "Fetching public certificate from master (" - << master_host << ", " << master_port << "):\n"; - - trustedcert = PkiUtility::FetchCert(master_host, master_port); - if (!trustedcert) { - Log(LogCritical, "cli", "Peer did not present a valid certificate."); - return 1; - } - - std::cout << ConsoleColorTag(Console_Bold) << "Certificate information:\n" - << ConsoleColorTag(Console_Normal) << PkiUtility::GetCertificateInformation(trustedcert) - << ConsoleColorTag(Console_Bold) << "\nIs this information correct?" - << ConsoleColorTag(Console_Normal) << " [y/N]: "; - - std::getline (std::cin, answer); - boost::algorithm::to_lower(answer); - if (answer != "y") { - Log(LogWarning, "cli", "Process aborted."); - return 1; - } - - Log(LogInformation, "cli", "Received trusted master certificate.\n"); + << "Requesting certificate with ticket '" << ticket << "'."; } -wizard_ticket: - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the request ticket generated on your Icinga 2 master (optional)." - << ConsoleColorTag(Console_Normal) << "\n" - << " (Hint: # icinga2 pki ticket --cn '" << cn << "'): "; + String nodeCA = certsDir + "/ca.crt"; - std::getline(std::cin, answer); + if (Utility::PathExists(nodeCA)) + NodeUtility::CreateBackupFile(nodeCA); + if (Utility::PathExists(nodeCert)) + NodeUtility::CreateBackupFile(nodeCert); - if (answer.empty()) { - std::cout << ConsoleColorTag(Console_Bold) << "\n" - << "No ticket was specified. Please approve the certificate signing request manually\n" - << "on the master (see 'icinga2 ca list' and 'icinga2 ca sign --help' for details)." - << ConsoleColorTag(Console_Normal) << "\n\n"; + if (PkiUtility::RequestCertificate(parentHost, parentPort, nodeKey, + nodeCert, nodeCA, trustedParentCert, ticket) > 0) { + Log(LogCritical, "cli") + << "Failed to fetch signed certificate from master '" + << parentHost << ", " + << parentPort <<"'. Please try again."; + goto wizard_ticket; } - String ticket = answer; - ticket = ticket.Trim(); - - if (!master_host.IsEmpty()) { - if (ticket.IsEmpty()) { - Log(LogInformation, "cli") - << "Requesting certificate without a ticket."; - } else { - Log(LogInformation, "cli") - << "Requesting certificate with ticket '" << ticket << "'."; - } - - String target_ca = pki_path + "/ca.crt"; - - if (Utility::PathExists(target_ca)) - NodeUtility::CreateBackupFile(target_ca); - if (Utility::PathExists(node_cert)) - NodeUtility::CreateBackupFile(node_cert); - - if (PkiUtility::RequestCertificate(master_host, master_port, node_key, - node_cert, target_ca, trustedcert, ticket) > 0) { - Log(LogCritical, "cli") - << "Failed to fetch signed certificate from master '" - << master_host << ", " - << master_port <<"'. Please try again."; - goto wizard_ticket; - } - - /* fix permissions (again) when updating the signed certificate */ - if (!Utility::SetFileOwnership(node_cert, user, group)) { - Log(LogWarning, "cli") - << "Cannot set ownership for user '" << user - << "' group '" << group << "' on file '" - << node_cert << "'. Verify it yourself!"; - } + /* fix permissions (again) when updating the signed certificate */ + if (!Utility::SetFileOwnership(nodeCert, user, group)) { + Log(LogWarning, "cli") + << "Cannot set ownership for user '" << user + << "' group '" << group << "' on file '" + << nodeCert << "'. Verify it yourself!"; } + } - /* apilistener config */ - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the API bind host/port" - << ConsoleColorTag(Console_Normal) << " (optional):\n" - << ConsoleColorTag(Console_Bold) << "Bind Host" - << ConsoleColorTag(Console_Normal) << " []: "; + std::cout << "\n"; - std::getline(std::cin, answer); + /* apilistener config */ + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the API bind host/port" + << ConsoleColorTag(Console_Normal) << " (optional):\n" + << ConsoleColorTag(Console_Bold) << "Bind Host" + << ConsoleColorTag(Console_Normal) << " []: "; - String bind_host = answer; - bind_host = bind_host.Trim(); + std::getline(std::cin, answer); - std::cout << "Bind Port []: "; + String bindHost = answer; + bindHost = bindHost.Trim(); - std::getline(std::cin, answer); + std::cout << "Bind Port []: "; - String bind_port = answer; - bind_port = bind_port.Trim(); + std::getline(std::cin, answer); - std::cout << ConsoleColorTag(Console_Bold) << "\n" - << "Accept config from master?" << ConsoleColorTag(Console_Normal) - << " [y/N]: "; - std::getline(std::cin, answer); - boost::algorithm::to_lower(answer); - choice = answer; + String bindPort = answer; + bindPort = bindPort.Trim(); - String accept_config = choice.Contains("y") ? "true" : "false"; + std::cout << ConsoleColorTag(Console_Bold) << "\n" + << "Accept config from parent node?" << ConsoleColorTag(Console_Normal) + << " [y/N]: "; + std::getline(std::cin, answer); + boost::algorithm::to_lower(answer); + choice = answer; - std::cout << ConsoleColorTag(Console_Bold) - << "Accept commands from master?" << ConsoleColorTag(Console_Normal) - << " [y/N]: "; - std::getline(std::cin, answer); - boost::algorithm::to_lower(answer); - choice = answer; + String acceptConfig = choice.Contains("y") ? "true" : "false"; - String accept_commands = choice.Contains("y") ? "true" : "false"; + std::cout << ConsoleColorTag(Console_Bold) + << "Accept commands from parent node?" << ConsoleColorTag(Console_Normal) + << " [y/N]: "; + std::getline(std::cin, answer); + boost::algorithm::to_lower(answer); + choice = answer; - /* disable the notifications feature on client nodes */ - Log(LogInformation, "cli", "Disabling the Notification feature."); + String acceptCommands = choice.Contains("y") ? "true" : "false"; - std::vector disable; - disable.push_back("notification"); - FeatureUtility::DisableFeatures(disable); + std::cout << "\n"; - Log(LogInformation, "cli", "Enabling the ApiListener feature."); + /* disable the notifications feature on client nodes */ + Log(LogInformation, "cli", "Disabling the Notification feature."); - std::vector enable; - enable.push_back("api"); - FeatureUtility::EnableFeatures(enable); + std::vector disable; + disable.push_back("notification"); + FeatureUtility::DisableFeatures(disable); - String apipath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf"; - NodeUtility::CreateBackupFile(apipath); + Log(LogInformation, "cli", "Enabling the ApiListener feature."); - std::fstream fp; - String tempApiPath = Utility::CreateTempFile(apipath + ".XXXXXX", 0644, fp); + std::vector enable; + enable.push_back("api"); + FeatureUtility::EnableFeatures(enable); - fp << "/**\n" - << " * The API listener is used for distributed monitoring setups.\n" - << " */\n" - << "object ApiListener \"api\" {\n" - << " cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n" - << " key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n" - << " ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n" - << "\n" - << " accept_config = " << accept_config << "\n" - << " accept_commands = " << accept_commands << "\n"; + String apiConfPath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf"; + NodeUtility::CreateBackupFile(apiConfPath); - if (!bind_host.IsEmpty()) - fp << " bind_host = \"" << bind_host << "\"\n"; - if (!bind_port.IsEmpty()) - fp << " bind_port = " << bind_port << "\n"; + std::fstream fp; + String tempApiConfPath = Utility::CreateTempFile(apiConfPath + ".XXXXXX", 0644, fp); - fp << "\n" - << "}\n"; + fp << "/**\n" + << " * The API listener is used for distributed monitoring setups.\n" + << " */\n" + << "object ApiListener \"api\" {\n" + << " cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n" + << " key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n" + << " ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n" + << "\n" + << " accept_config = " << acceptConfig << "\n" + << " accept_commands = " << acceptCommands << "\n"; - fp.close(); + if (!bindHost.IsEmpty()) + fp << " bind_host = \"" << bindHost << "\"\n"; + if (!bindPort.IsEmpty()) + fp << " bind_port = " << bindPort << "\n"; + + fp << "}\n"; + + fp.close(); #ifdef _WIN32 - _unlink(apipath.CStr()); + _unlink(apiConfPath.CStr()); #endif /* _WIN32 */ - if (rename(tempApiPath.CStr(), apipath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiPath)); - } + if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(tempApiConfPath)); + } - /* apilistener config */ - Log(LogInformation, "cli", "Generating local zones.conf."); + /* apilistener config */ + Log(LogInformation, "cli", "Generating local zones.conf."); - NodeUtility::GenerateNodeIcingaConfig(endpoints); + NodeUtility::GenerateNodeIcingaConfig(endpoints); - if (cn != Utility::GetFQDN()) { - Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" - << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; - } + if (cn != Utility::GetFQDN()) { + Log(LogWarning, "cli") + << "CN '" << cn << "' does not match the default FQDN '" + << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; + } - Log(LogInformation, "cli", "Updating constants.conf."); + Log(LogInformation, "cli", "Updating constants.conf."); - String constants_file = Application::GetSysconfDir() + "/icinga2/constants.conf"; + String constants_file = Application::GetSysconfDir() + "/icinga2/constants.conf"; - NodeUtility::CreateBackupFile(constants_file); + NodeUtility::CreateBackupFile(constants_file); - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", cn); + NodeUtility::UpdateConstant("NodeName", cn); + NodeUtility::UpdateConstant("ZoneName", cn); - String ticketPath = ApiListener::GetCertsDir() + "/ticket"; + String ticketPath = ApiListener::GetCertsDir() + "/ticket"; - String tempTicketPath = Utility::CreateTempFile(ticketPath + ".XXXXXX", 0600, fp); + String tempTicketPath = Utility::CreateTempFile(ticketPath + ".XXXXXX", 0600, fp); - if (!Utility::SetFileOwnership(tempTicketPath, user, group)) { - Log(LogWarning, "cli") - << "Cannot set ownership for user '" << user - << "' group '" << group - << "' on file '" << tempTicketPath << "'. Verify it yourself!"; - } + if (!Utility::SetFileOwnership(tempTicketPath, user, group)) { + Log(LogWarning, "cli") + << "Cannot set ownership for user '" << user + << "' group '" << group + << "' on file '" << tempTicketPath << "'. Verify it yourself!"; + } - fp << ticket; + fp << ticket; - fp.close(); + fp.close(); #ifdef _WIN32 - _unlink(ticketPath.CStr()); + _unlink(ticketPath.CStr()); #endif /* _WIN32 */ - if (rename(tempTicketPath.CStr(), ticketPath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempTicketPath)); - } - } else { - /* master setup */ - std::cout << ConsoleColorTag(Console_Bold) << "Starting the Master setup routine...\n"; + if (rename(tempTicketPath.CStr(), ticketPath.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(tempTicketPath)); + } - /* CN */ - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the common name" << ConsoleColorTag(Console_Normal) - << " (CN) [" << Utility::GetFQDN() << "]: "; + return 0; +} - std::getline(std::cin, answer); +int NodeWizardCommand::MasterSetup(void) const +{ + std::string answer; + String choice; - if (answer.empty()) - answer = Utility::GetFQDN(); + std::cout << ConsoleColorTag(Console_Bold) << "Starting the Master setup routine...\n"; - String cn = answer; - cn = cn.Trim(); + /* CN */ + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the common name" << ConsoleColorTag(Console_Normal) + << " (CN) [" << Utility::GetFQDN() << "]: "; - /* check whether the user wants to generate a new certificate or not */ - String existing_path = ApiListener::GetCertsDir() + "/" + cn + ".crt"; + std::getline(std::cin, answer); - std::cout << ConsoleColorTag(Console_Normal) - << "Checking for existing certificates for common name '" << cn << "'...\n"; + if (answer.empty()) + answer = Utility::GetFQDN(); - if (Utility::PathExists(existing_path)) { - std::cout << "Certificate '" << existing_path << "' for CN '" - << cn << "' already existing. Skipping certificate generation.\n"; - } else { - std::cout << "Certificates not yet generated. Running 'api setup' now.\n"; - ApiSetupUtility::SetupMasterCertificates(cn); - } + String cn = answer; + cn = cn.Trim(); - std::cout << ConsoleColorTag(Console_Bold) - << "Generating master configuration for Icinga 2.\n" - << ConsoleColorTag(Console_Normal); - ApiSetupUtility::SetupMasterApiUser(); + /* check whether the user wants to generate a new certificate or not */ + String existing_path = ApiListener::GetCertsDir() + "/" + cn + ".crt"; - if (!FeatureUtility::CheckFeatureEnabled("api")) - ApiSetupUtility::SetupMasterEnableApi(); - else - std::cout << "'api' feature already enabled.\n"; + std::cout << ConsoleColorTag(Console_Normal) + << "Checking for existing certificates for common name '" << cn << "'...\n"; - NodeUtility::GenerateNodeMasterIcingaConfig(); + if (Utility::PathExists(existing_path)) { + std::cout << "Certificate '" << existing_path << "' for CN '" + << cn << "' already existing. Skipping certificate generation.\n"; + } else { + std::cout << "Certificates not yet generated. Running 'api setup' now.\n"; + ApiSetupUtility::SetupMasterCertificates(cn); + } - /* apilistener config */ - std::cout << ConsoleColorTag(Console_Bold) - << "Please specify the API bind host/port (optional):\n"; - std::cout << ConsoleColorTag(Console_Bold) - << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: "; + std::cout << ConsoleColorTag(Console_Bold) + << "Generating master configuration for Icinga 2.\n" + << ConsoleColorTag(Console_Normal); + ApiSetupUtility::SetupMasterApiUser(); - std::getline(std::cin, answer); + if (!FeatureUtility::CheckFeatureEnabled("api")) + ApiSetupUtility::SetupMasterEnableApi(); + else + std::cout << "'api' feature already enabled.\n"; - String bind_host = answer; - bind_host = bind_host.Trim(); + NodeUtility::GenerateNodeMasterIcingaConfig(); - std::cout << ConsoleColorTag(Console_Bold) - << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: "; + /* apilistener config */ + std::cout << ConsoleColorTag(Console_Bold) + << "Please specify the API bind host/port (optional):\n"; + std::cout << ConsoleColorTag(Console_Bold) + << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: "; - std::getline(std::cin, answer); + std::getline(std::cin, answer); - String bind_port = answer; - bind_port = bind_port.Trim(); + String bindHost = answer; + bindHost = bindHost.Trim(); - /* api feature is always enabled, check above */ - String apipath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf"; - NodeUtility::CreateBackupFile(apipath); + std::cout << ConsoleColorTag(Console_Bold) + << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: "; - std::fstream fp; - String tempApiPath = Utility::CreateTempFile(apipath + ".XXXXXX", 0644, fp); + std::getline(std::cin, answer); - fp << "/**\n" - << " * The API listener is used for distributed monitoring setups.\n" - << " */\n" - << "object ApiListener \"api\" {\n" - << " cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n" - << " key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n" - << " ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n"; + String bindPort = answer; + bindPort = bindPort.Trim(); - if (!bind_host.IsEmpty()) - fp << " bind_host = \"" << bind_host << "\"\n"; - if (!bind_port.IsEmpty()) - fp << " bind_port = " << bind_port << "\n"; + /* api feature is always enabled, check above */ + String apiConfPath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf"; + NodeUtility::CreateBackupFile(apiConfPath); - fp << "\n" - << " ticket_salt = TicketSalt\n" - << "}\n"; + std::fstream fp; + String tempApiConfPath = Utility::CreateTempFile(apiConfPath + ".XXXXXX", 0644, fp); - fp.close(); + fp << "/**\n" + << " * The API listener is used for distributed monitoring setups.\n" + << " */\n" + << "object ApiListener \"api\" {\n" + << " cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n" + << " key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n" + << " ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n"; -#ifdef _WIN32 - _unlink(apipath.CStr()); -#endif /* _WIN32 */ + if (!bindHost.IsEmpty()) + fp << " bind_host = \"" << bindHost << "\"\n"; + if (!bindPort.IsEmpty()) + fp << " bind_port = " << bindPort << "\n"; - if (rename(tempApiPath.CStr(), apipath.CStr()) < 0) { - BOOST_THROW_EXCEPTION(posix_error() - << boost::errinfo_api_function("rename") - << boost::errinfo_errno(errno) - << boost::errinfo_file_name(tempApiPath)); - } + fp << "\n" + << " ticket_salt = TicketSalt\n" + << "}\n"; - /* update constants.conf with NodeName = CN + TicketSalt = random value */ - if (cn != Utility::GetFQDN()) { - Log(LogWarning, "cli") - << "CN '" << cn << "' does not match the default FQDN '" - << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!"; - } + fp.close(); + +#ifdef _WIN32 + _unlink(apiConfPath.CStr()); +#endif /* _WIN32 */ - Log(LogInformation, "cli", "Updating constants.conf."); + if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(tempApiConfPath)); + } - String constants_file = Application::GetSysconfDir() + "/icinga2/constants.conf"; + /* update constants.conf with NodeName = CN + TicketSalt = random value */ + if (cn != Utility::GetFQDN()) { + Log(LogWarning, "cli") + << "CN '" << cn << "' does not match the default FQDN '" + << Utility::GetFQDN() << "'. Requires an update for the NodeName constant in constants.conf!"; + } - NodeUtility::CreateBackupFile(constants_file); + Log(LogInformation, "cli", "Updating constants.conf."); - NodeUtility::UpdateConstant("NodeName", cn); - NodeUtility::UpdateConstant("ZoneName", cn); + String constants_file = Application::GetSysconfDir() + "/icinga2/constants.conf"; - String salt = RandomString(16); + NodeUtility::CreateBackupFile(constants_file); - NodeUtility::UpdateConstant("TicketSalt", salt); - } + NodeUtility::UpdateConstant("NodeName", cn); + NodeUtility::UpdateConstant("ZoneName", cn); - std::cout << "Done.\n\n"; + String salt = RandomString(16); - std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n"; + NodeUtility::UpdateConstant("TicketSalt", salt); return 0; } diff --git a/lib/cli/nodewizardcommand.hpp b/lib/cli/nodewizardcommand.hpp index 06872c6c6..fef8c4092 100644 --- a/lib/cli/nodewizardcommand.hpp +++ b/lib/cli/nodewizardcommand.hpp @@ -40,6 +40,10 @@ public: virtual int GetMaxArguments(void) const override; virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const override; virtual ImpersonationLevel GetImpersonationLevel(void) const override; + +private: + int ClientSetup(void) const; + int MasterSetup(void) const; }; }