1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "cli/nodewizardcommand.hpp"
4 #include "cli/nodeutility.hpp"
5 #include "cli/featureutility.hpp"
6 #include "cli/apisetuputility.hpp"
7 #include "remote/apilistener.hpp"
8 #include "remote/pkiutility.hpp"
9 #include "base/logger.hpp"
10 #include "base/console.hpp"
11 #include "base/application.hpp"
12 #include "base/tlsutility.hpp"
13 #include "base/scriptglobal.hpp"
14 #include "base/exception.hpp"
15 #include <boost/algorithm/string/join.hpp>
16 #include <boost/algorithm/string/replace.hpp>
17 #include <boost/algorithm/string/case_conv.hpp>
23 using namespace icinga;
24 namespace po = boost::program_options;
26 REGISTER_CLICOMMAND("node/wizard", NodeWizardCommand);
28 String NodeWizardCommand::GetDescription() const
30 return "Wizard for Icinga 2 node setup.";
33 String NodeWizardCommand::GetShortDescription() const
35 return "wizard for node setup";
38 ImpersonationLevel NodeWizardCommand::GetImpersonationLevel() const
40 return ImpersonateIcinga;
43 int NodeWizardCommand::GetMaxArguments() const
48 void NodeWizardCommand::InitParameters(boost::program_options::options_description& visibleDesc,
49 boost::program_options::options_description& hiddenDesc) const
51 visibleDesc.add_options()
52 ("verbose", "increase log level");
56 * The entry point for the "node wizard" CLI command.
58 * @returns An exit status.
60 int NodeWizardCommand::Run(const boost::program_options::variables_map& vm,
61 const std::vector<std::string>& ap) const
63 if (!vm.count("verbose"))
64 Logger::SetConsoleLogSeverity(LogCritical);
67 * The wizard will get all information from the user,
68 * and then call all required functions.
71 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundBlue)
72 << "Welcome to the Icinga 2 Setup Wizard!\n"
74 << "We will guide you through all required configuration details.\n"
76 << ConsoleColorTag(Console_Normal);
78 /* 0. master or node setup?
80 * 2. Master information for autosigning
81 * 3. Trusted cert location
82 * 4. CN to use (defaults to FQDN)
84 * 6. New self signed certificate
85 * 7. Request signed certificate from master
86 * 8. copy key information to /var/lib/icinga2/certs
87 * 9. enable ApiListener feature
88 * 10. generate zones.conf with endpoints and zone objects
89 * 11. set NodeName = cn in constants.conf
90 * 12. disable conf.d directory?
91 * 13. reload icinga2, or tell the user to
95 /* master or satellite/client setup */
96 std::cout << ConsoleColorTag(Console_Bold)
97 << "Please specify if this is a satellite/client setup "
98 << "('n' installs a master setup)" << ConsoleColorTag(Console_Normal)
100 std::getline (std::cin, answer);
102 boost::algorithm::to_lower(answer);
104 String choice = answer;
110 if (choice.Contains("n"))
119 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
121 << ConsoleColorTag(Console_Normal);
123 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundRed)
124 << "Now restart your Icinga 2 daemon to finish the installation!\n"
125 << ConsoleColorTag(Console_Normal);
130 int NodeWizardCommand::ClientSetup() const
134 bool connectToParent = false;
136 std::cout << "Starting the Client/Satellite setup routine...\n\n";
139 std::cout << ConsoleColorTag(Console_Bold)
140 << "Please specify the common name (CN)"
141 << ConsoleColorTag(Console_Normal)
142 << " [" << Utility::GetFQDN() << "]: ";
144 std::getline(std::cin, answer);
147 answer = Utility::GetFQDN();
152 std::vector<std::string> endpoints;
154 String endpointBuffer;
156 std::cout << ConsoleColorTag(Console_Bold)
157 << "\nPlease specify the parent endpoint(s) (master or satellite) where this node should connect to:"
158 << ConsoleColorTag(Console_Normal) << "\n";
159 String parentEndpointName;
161 wizard_endpoint_loop_start:
163 std::cout << ConsoleColorTag(Console_Bold)
164 << "Master/Satellite Common Name" << ConsoleColorTag(Console_Normal)
165 << " (CN from your master/satellite node): ";
167 std::getline(std::cin, answer);
169 if (answer.empty()) {
170 Log(LogWarning, "cli", "Master/Satellite CN is required! Please retry.");
171 goto wizard_endpoint_loop_start;
174 endpointBuffer = answer;
175 endpointBuffer = endpointBuffer.Trim();
177 std::cout << "\nDo you want to establish a connection to the parent node "
178 << ConsoleColorTag(Console_Bold) << "from this node?"
179 << ConsoleColorTag(Console_Normal) << " [Y/n]: ";
181 std::getline (std::cin, answer);
182 boost::algorithm::to_lower(answer);
185 String parentEndpointPort = "5665";
187 if (choice.Contains("n")) {
188 connectToParent = false;
190 Log(LogWarning, "cli", "Node to master/satellite connection setup skipped");
191 std::cout << "Connection setup skipped. Please configure your parent node to\n"
192 << "connect to this node by setting the 'host' attribute for the node Endpoint object.\n";
195 connectToParent = true;
197 std::cout << ConsoleColorTag(Console_Bold)
198 << "Please specify the master/satellite connection information:"
199 << ConsoleColorTag(Console_Normal) << "\n"
200 << ConsoleColorTag(Console_Bold) << "Master/Satellite endpoint host"
201 << ConsoleColorTag(Console_Normal) << " (IP address or FQDN): ";
203 std::getline(std::cin, answer);
205 if (answer.empty()) {
206 Log(LogWarning, "cli", "Please enter the parent endpoint (master/satellite) connection information.");
207 goto wizard_endpoint_loop_start;
213 endpointBuffer += "," + tmp;
214 parentEndpointName = tmp;
216 std::cout << ConsoleColorTag(Console_Bold)
217 << "Master/Satellite endpoint port" << ConsoleColorTag(Console_Normal)
218 << " [" << parentEndpointPort << "]: ";
220 std::getline(std::cin, answer);
223 parentEndpointPort = answer;
225 endpointBuffer += "," + parentEndpointPort.Trim();
228 endpoints.push_back(endpointBuffer);
230 std::cout << ConsoleColorTag(Console_Bold) << "\nAdd more master/satellite endpoints?"
231 << ConsoleColorTag(Console_Normal) << " [y/N]: ";
232 std::getline (std::cin, answer);
234 boost::algorithm::to_lower(answer);
238 if (choice.Contains("y"))
239 goto wizard_endpoint_loop_start;
241 /* Extract parent node information. */
242 String parentHost, parentPort;
244 for (const String& endpoint : endpoints) {
245 std::vector<String> tokens = endpoint.Split(",");
247 if (tokens.size() > 1)
248 parentHost = tokens[1];
250 if (tokens.size() > 2)
251 parentPort = tokens[2];
254 /* workaround for fetching the master cert */
255 String certsDir = ApiListener::GetCertsDir();
256 Utility::MkDirP(certsDir, 0700);
258 String user = Configuration::RunAsUser;
259 String group = Configuration::RunAsGroup;
261 if (!Utility::SetFileOwnership(certsDir, user, group)) {
262 Log(LogWarning, "cli")
263 << "Cannot set ownership for user '" << user
264 << "' group '" << group
265 << "' on file '" << certsDir << "'. Verify it yourself!";
268 String nodeCert = certsDir + "/" + cn + ".crt";
269 String nodeKey = certsDir + "/" + cn + ".key";
271 if (Utility::PathExists(nodeKey))
272 NodeUtility::CreateBackupFile(nodeKey, true);
273 if (Utility::PathExists(nodeCert))
274 NodeUtility::CreateBackupFile(nodeCert);
276 if (PkiUtility::NewCert(cn, nodeKey, Empty, nodeCert) > 0) {
277 Log(LogCritical, "cli")
278 << "Failed to create new self-signed certificate for CN '"
279 << cn << "'. Please try again.";
283 /* fix permissions: root -> icinga daemon user */
284 if (!Utility::SetFileOwnership(nodeKey, user, group)) {
285 Log(LogWarning, "cli")
286 << "Cannot set ownership for user '" << user
287 << "' group '" << group
288 << "' on file '" << nodeKey << "'. Verify it yourself!";
291 std::shared_ptr<X509> trustedParentCert;
293 /* Check whether we should connect to the parent node and present its trusted certificate. */
294 if (connectToParent) {
295 //save-cert and store the master certificate somewhere
296 Log(LogInformation, "cli")
297 << "Fetching public certificate from master ("
298 << parentHost << ", " << parentPort << "):\n";
300 trustedParentCert = PkiUtility::FetchCert(parentHost, parentPort);
301 if (!trustedParentCert) {
302 Log(LogCritical, "cli", "Peer did not present a valid certificate.");
306 std::cout << ConsoleColorTag(Console_Bold) << "Parent certificate information:\n"
307 << ConsoleColorTag(Console_Normal) << PkiUtility::GetCertificateInformation(trustedParentCert)
308 << ConsoleColorTag(Console_Bold) << "\nIs this information correct?"
309 << ConsoleColorTag(Console_Normal) << " [y/N]: ";
311 std::getline (std::cin, answer);
312 boost::algorithm::to_lower(answer);
314 Log(LogWarning, "cli", "Process aborted.");
318 Log(LogInformation, "cli", "Received trusted parent certificate.\n");
322 String nodeCA = certsDir + "/ca.crt";
325 /* Check whether we can connect to the parent node and fetch the client and CA certificate. */
326 if (connectToParent) {
327 std::cout << ConsoleColorTag(Console_Bold)
328 << "\nPlease specify the request ticket generated on your Icinga 2 master "
329 << ConsoleColorTag(Console_Normal) << "(optional)"
330 << ConsoleColorTag(Console_Bold) << "."
331 << ConsoleColorTag(Console_Normal) << "\n"
332 << " (Hint: # icinga2 pki ticket --cn '" << cn << "'): ";
334 std::getline(std::cin, answer);
336 if (answer.empty()) {
337 std::cout << ConsoleColorTag(Console_Bold) << "\n"
338 << "No ticket was specified. Please approve the certificate signing request manually\n"
339 << "on the master (see 'icinga2 ca list' and 'icinga2 ca sign --help' for details)."
340 << ConsoleColorTag(Console_Normal) << "\n";
344 ticket = ticket.Trim();
346 if (ticket.IsEmpty()) {
347 Log(LogInformation, "cli")
348 << "Requesting certificate without a ticket.";
350 Log(LogInformation, "cli")
351 << "Requesting certificate with ticket '" << ticket << "'.";
354 if (Utility::PathExists(nodeCA))
355 NodeUtility::CreateBackupFile(nodeCA);
356 if (Utility::PathExists(nodeCert))
357 NodeUtility::CreateBackupFile(nodeCert);
359 if (PkiUtility::RequestCertificate(parentHost, parentPort, nodeKey,
360 nodeCert, nodeCA, trustedParentCert, ticket) > 0) {
361 Log(LogCritical, "cli")
362 << "Failed to fetch signed certificate from master '"
363 << parentHost << ", "
364 << parentPort << "'. Please try again.";
368 /* fix permissions (again) when updating the signed certificate */
369 if (!Utility::SetFileOwnership(nodeCert, user, group)) {
370 Log(LogWarning, "cli")
371 << "Cannot set ownership for user '" << user
372 << "' group '" << group << "' on file '"
373 << nodeCert << "'. Verify it yourself!";
376 /* We cannot retrieve the parent certificate.
377 * Tell the user to manually copy the ca.crt file
378 * into DataDir + "/certs"
381 std::cout << ConsoleColorTag(Console_Bold)
382 << "\nNo connection to the parent node was specified.\n\n"
383 << "Please copy the public CA certificate from your master/satellite\n"
384 << "into '" << nodeCA << "' before starting Icinga 2.\n"
385 << ConsoleColorTag(Console_Normal);
387 if (Utility::PathExists(nodeCA)) {
388 std::cout << ConsoleColorTag(Console_Bold)
389 << "\nFound public CA certificate in '" << nodeCA << "'.\n"
390 << "Please verify that it is the same as on your master/satellite.\n"
391 << ConsoleColorTag(Console_Normal);
396 /* apilistener config */
397 std::cout << ConsoleColorTag(Console_Bold)
398 << "Please specify the API bind host/port "
399 << ConsoleColorTag(Console_Normal) << "(optional)"
400 << ConsoleColorTag(Console_Bold) << ":\n";
402 std::cout << ConsoleColorTag(Console_Bold)
403 << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: ";
405 std::getline(std::cin, answer);
407 String bindHost = answer;
408 bindHost = bindHost.Trim();
410 std::cout << ConsoleColorTag(Console_Bold)
411 << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: ";
413 std::getline(std::cin, answer);
415 String bindPort = answer;
416 bindPort = bindPort.Trim();
418 std::cout << ConsoleColorTag(Console_Bold) << "\n"
419 << "Accept config from parent node?" << ConsoleColorTag(Console_Normal)
421 std::getline(std::cin, answer);
422 boost::algorithm::to_lower(answer);
425 String acceptConfig = choice.Contains("y") ? "true" : "false";
427 std::cout << ConsoleColorTag(Console_Bold)
428 << "Accept commands from parent node?" << ConsoleColorTag(Console_Normal)
430 std::getline(std::cin, answer);
431 boost::algorithm::to_lower(answer);
434 String acceptCommands = choice.Contains("y") ? "true" : "false";
438 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
439 << "Reconfiguring Icinga...\n"
440 << ConsoleColorTag(Console_Normal);
442 /* disable the notifications feature on client nodes */
443 Log(LogInformation, "cli", "Disabling the Notification feature.");
445 FeatureUtility::DisableFeatures({ "notification" });
447 Log(LogInformation, "cli", "Enabling the ApiListener feature.");
449 FeatureUtility::EnableFeatures({ "api" });
451 String apiConfPath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf";
452 NodeUtility::CreateBackupFile(apiConfPath);
455 String tempApiConfPath = Utility::CreateTempFile(apiConfPath + ".XXXXXX", 0644, fp);
458 << " * The API listener is used for distributed monitoring setups.\n"
460 << "object ApiListener \"api\" {\n"
461 << " accept_config = " << acceptConfig << "\n"
462 << " accept_commands = " << acceptCommands << "\n";
464 if (!bindHost.IsEmpty())
465 fp << " bind_host = \"" << bindHost << "\"\n";
466 if (!bindPort.IsEmpty())
467 fp << " bind_port = " << bindPort << "\n";
474 _unlink(apiConfPath.CStr());
477 if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) {
478 BOOST_THROW_EXCEPTION(posix_error()
479 << boost::errinfo_api_function("rename")
480 << boost::errinfo_errno(errno)
481 << boost::errinfo_file_name(tempApiConfPath));
484 /* Zones configuration. */
485 Log(LogInformation, "cli", "Generating local zones.conf.");
487 /* Setup command hardcodes this as FQDN */
488 String endpointName = cn;
490 /* Different local zone name. */
491 std::cout << "\nLocal zone name [" + endpointName + "]: ";
492 std::getline(std::cin, answer);
495 answer = endpointName;
497 String zoneName = answer;
498 zoneName = zoneName.Trim();
500 /* Different parent zone name. */
501 std::cout << "Parent zone name [master]: ";
502 std::getline(std::cin, answer);
507 String parentZoneName = answer;
508 parentZoneName = parentZoneName.Trim();
511 std::vector<String> globalZones { "global-templates", "director-global" };
513 std::cout << "\nDefault global zones: " << boost::algorithm::join(globalZones, " ");
514 std::cout << "\nDo you want to specify additional global zones? [y/N]: ";
516 std::getline(std::cin, answer);
517 boost::algorithm::to_lower(answer);
520 wizard_global_zone_loop_start:
521 if (choice.Contains("y")) {
522 std::cout << "\nPlease specify the name of the global Zone: ";
524 std::getline(std::cin, answer);
526 if (answer.empty()) {
527 std::cout << "\nName of the global Zone is required! Please retry.";
528 goto wizard_global_zone_loop_start;
531 String globalZoneName = answer;
532 globalZoneName = globalZoneName.Trim();
534 if (std::find(globalZones.begin(), globalZones.end(), globalZoneName) != globalZones.end()) {
535 std::cout << "The global zone '" << globalZoneName << "' is already specified."
537 goto wizard_global_zone_loop_start;
540 globalZones.push_back(globalZoneName);
542 std::cout << "\nDo you want to specify another global zone? [y/N]: ";
544 std::getline(std::cin, answer);
545 boost::algorithm::to_lower(answer);
548 if (choice.Contains("y"))
549 goto wizard_global_zone_loop_start;
551 Log(LogInformation, "cli", "No additional global Zones have been specified");
553 /* Generate node configuration. */
554 NodeUtility::GenerateNodeIcingaConfig(endpointName, zoneName, parentZoneName, endpoints, globalZones);
556 if (cn != Utility::GetFQDN()) {
557 Log(LogWarning, "cli")
558 << "CN '" << cn << "' does not match the default FQDN '"
559 << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!";
562 NodeUtility::UpdateConstant("NodeName", cn);
563 NodeUtility::UpdateConstant("ZoneName", cn);
565 if (!ticket.IsEmpty()) {
566 String ticketPath = ApiListener::GetCertsDir() + "/ticket";
568 String tempTicketPath = Utility::CreateTempFile(ticketPath + ".XXXXXX", 0600, fp);
570 if (!Utility::SetFileOwnership(tempTicketPath, user, group)) {
571 Log(LogWarning, "cli")
572 << "Cannot set ownership for user '" << user
573 << "' group '" << group
574 << "' on file '" << tempTicketPath << "'. Verify it yourself!";
582 _unlink(ticketPath.CStr());
585 if (rename(tempTicketPath.CStr(), ticketPath.CStr()) < 0) {
586 BOOST_THROW_EXCEPTION(posix_error()
587 << boost::errinfo_api_function("rename")
588 << boost::errinfo_errno(errno)
589 << boost::errinfo_file_name(tempTicketPath));
593 /* If no parent connection was made, the user must supply the ca.crt before restarting Icinga 2.*/
594 if (!connectToParent) {
595 Log(LogWarning, "cli")
596 << "No connection to the parent node was specified.\n\n"
597 << "Please copy the public CA certificate from your master/satellite\n"
598 << "into '" << nodeCA << "' before starting Icinga 2.\n";
600 Log(LogInformation, "cli", "Make sure to restart Icinga 2.");
603 /* Disable conf.d inclusion */
604 std::cout << "\nDo you want to disable the inclusion of the conf.d directory [Y/n]: ";
606 std::getline(std::cin, answer);
607 boost::algorithm::to_lower(answer);
610 if (choice.Contains("n"))
611 Log(LogInformation, "cli")
612 << "conf.d directory has not been disabled.";
614 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
615 << "Disabling the inclusion of the conf.d directory...\n"
616 << ConsoleColorTag(Console_Normal);
618 if (!NodeUtility::UpdateConfiguration("\"conf.d\"", false, true)) {
619 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundRed)
620 << "Failed to disable the conf.d inclusion, it may already have been disabled.\n"
621 << ConsoleColorTag(Console_Normal);
624 /* Satellite/Clients should not include the api-users.conf file.
625 * The configuration should instead be managed via config sync or automation tools.
632 int NodeWizardCommand::MasterSetup() const
637 std::cout << ConsoleColorTag(Console_Bold) << "Starting the Master setup routine...\n\n";
640 std::cout << ConsoleColorTag(Console_Bold)
641 << "Please specify the common name" << ConsoleColorTag(Console_Normal)
642 << " (CN) [" << Utility::GetFQDN() << "]: ";
644 std::getline(std::cin, answer);
647 answer = Utility::GetFQDN();
652 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
653 << "Reconfiguring Icinga...\n"
654 << ConsoleColorTag(Console_Normal);
656 /* check whether the user wants to generate a new certificate or not */
657 String existing_path = ApiListener::GetCertsDir() + "/" + cn + ".crt";
659 std::cout << ConsoleColorTag(Console_Normal)
660 << "Checking for existing certificates for common name '" << cn << "'...\n";
662 if (Utility::PathExists(existing_path)) {
663 std::cout << "Certificate '" << existing_path << "' for CN '"
664 << cn << "' already existing. Skipping certificate generation.\n";
666 std::cout << "Certificates not yet generated. Running 'api setup' now.\n";
667 ApiSetupUtility::SetupMasterCertificates(cn);
670 std::cout << ConsoleColorTag(Console_Bold)
671 << "Generating master configuration for Icinga 2.\n"
672 << ConsoleColorTag(Console_Normal);
674 ApiSetupUtility::SetupMasterApiUser();
676 if (!FeatureUtility::CheckFeatureEnabled("api"))
677 ApiSetupUtility::SetupMasterEnableApi();
679 std::cout << "'api' feature already enabled.\n";
681 /* Setup command hardcodes this as FQDN */
682 String endpointName = cn;
684 /* Different zone name. */
685 std::cout << "\nMaster zone name [master]: ";
686 std::getline(std::cin, answer);
691 String zoneName = answer;
692 zoneName = zoneName.Trim();
695 std::vector<String> globalZones { "global-templates", "director-global" };
697 std::cout << "\nDefault global zones: " << boost::algorithm::join(globalZones, " ");
698 std::cout << "\nDo you want to specify additional global zones? [y/N]: ";
700 std::getline(std::cin, answer);
701 boost::algorithm::to_lower(answer);
704 wizard_global_zone_loop_start:
705 if (choice.Contains("y")) {
706 std::cout << "\nPlease specify the name of the global Zone: ";
708 std::getline(std::cin, answer);
710 if (answer.empty()) {
711 std::cout << "\nName of the global Zone is required! Please retry.";
712 goto wizard_global_zone_loop_start;
715 String globalZoneName = answer;
716 globalZoneName = globalZoneName.Trim();
718 if (std::find(globalZones.begin(), globalZones.end(), globalZoneName) != globalZones.end()) {
719 std::cout << "The global zone '" << globalZoneName << "' is already specified."
721 goto wizard_global_zone_loop_start;
724 globalZones.push_back(globalZoneName);
726 std::cout << "\nDo you want to specify another global zone? [y/N]: ";
728 std::getline(std::cin, answer);
729 boost::algorithm::to_lower(answer);
732 if (choice.Contains("y"))
733 goto wizard_global_zone_loop_start;
735 Log(LogInformation, "cli", "No additional global Zones have been specified");
737 /* Generate master configuration. */
738 NodeUtility::GenerateNodeMasterIcingaConfig(endpointName, zoneName, globalZones);
740 /* apilistener config */
741 std::cout << ConsoleColorTag(Console_Bold)
742 << "Please specify the API bind host/port "
743 << ConsoleColorTag(Console_Normal) << "(optional)"
744 << ConsoleColorTag(Console_Bold) << ":\n";
746 std::cout << ConsoleColorTag(Console_Bold)
747 << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: ";
749 std::getline(std::cin, answer);
751 String bindHost = answer;
752 bindHost = bindHost.Trim();
754 std::cout << ConsoleColorTag(Console_Bold)
755 << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: ";
757 std::getline(std::cin, answer);
759 String bindPort = answer;
760 bindPort = bindPort.Trim();
762 /* api feature is always enabled, check above */
763 String apiConfPath = FeatureUtility::GetFeaturesAvailablePath() + "/api.conf";
764 NodeUtility::CreateBackupFile(apiConfPath);
767 String tempApiConfPath = Utility::CreateTempFile(apiConfPath + ".XXXXXX", 0644, fp);
770 << " * The API listener is used for distributed monitoring setups.\n"
772 << "object ApiListener \"api\" {\n";
774 if (!bindHost.IsEmpty())
775 fp << " bind_host = \"" << bindHost << "\"\n";
776 if (!bindPort.IsEmpty())
777 fp << " bind_port = " << bindPort << "\n";
780 << " ticket_salt = TicketSalt\n"
786 _unlink(apiConfPath.CStr());
789 if (rename(tempApiConfPath.CStr(), apiConfPath.CStr()) < 0) {
790 BOOST_THROW_EXCEPTION(posix_error()
791 << boost::errinfo_api_function("rename")
792 << boost::errinfo_errno(errno)
793 << boost::errinfo_file_name(tempApiConfPath));
796 /* update constants.conf with NodeName = CN + TicketSalt = random value */
797 if (cn != Utility::GetFQDN()) {
798 Log(LogWarning, "cli")
799 << "CN '" << cn << "' does not match the default FQDN '"
800 << Utility::GetFQDN() << "'. Requires an update for the NodeName constant in constants.conf!";
803 Log(LogInformation, "cli", "Updating constants.conf.");
805 NodeUtility::CreateBackupFile(NodeUtility::GetConstantsConfPath());
807 NodeUtility::UpdateConstant("NodeName", cn);
808 NodeUtility::UpdateConstant("ZoneName", cn);
810 String salt = RandomString(16);
812 NodeUtility::UpdateConstant("TicketSalt", salt);
814 /* Disable conf.d inclusion */
815 std::cout << "\nDo you want to disable the inclusion of the conf.d directory [Y/n]: ";
817 std::getline(std::cin, answer);
818 boost::algorithm::to_lower(answer);
821 if (choice.Contains("n"))
822 Log(LogInformation, "cli")
823 << "conf.d directory has not been disabled.";
825 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
826 << "Disabling the inclusion of the conf.d directory...\n"
827 << ConsoleColorTag(Console_Normal);
829 if (!NodeUtility::UpdateConfiguration("\"conf.d\"", false, true)) {
830 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundRed)
831 << "Failed to disable the conf.d inclusion, it may already have been disabled.\n"
832 << ConsoleColorTag(Console_Normal);
835 /* Include api-users.conf */
836 String apiUsersFilePath = Configuration::ConfigDir + "/conf.d/api-users.conf";
838 std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundGreen)
839 << "Checking if the api-users.conf file exists...\n"
840 << ConsoleColorTag(Console_Normal);
842 if (Utility::PathExists(apiUsersFilePath)) {
843 NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false);
845 Log(LogWarning, "cli")
846 << "Included file '" << apiUsersFilePath << "' does not exist.";