diff --git a/lib/cli/agentsetupcommand.cpp b/lib/cli/agentsetupcommand.cpp index 511c78dbe..cae712c5a 100644 --- a/lib/cli/agentsetupcommand.cpp +++ b/lib/cli/agentsetupcommand.cpp @@ -238,13 +238,11 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v if (tokens.size() == 2) master_port = tokens[1]; - Log(LogInformation, "cli") << "Verifying master host connection information: host '" << master_host << "', port '" << master_port << "'."; - /* - * 2. trusted cert must be passed (retrieved by the user with 'pki save-cert' before) + * 3. trusted cert must be passed (retrieved by the user with 'pki save-cert' before) */ if (!vm.count("trustedcert")) { @@ -259,50 +257,50 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v Log(LogInformation, "cli") << "Verifying trusted certificate from file '" << trustedcert << "'."; + /* + * 4. retrieve CN and pass it (defaults to FQDN) + */ + String cn = Utility::GetFQDN(); if (vm.count("cn")) cn = vm["cn"].as(); - String NodeName = cn; - String ZoneName = cn; - - /* - * 3. retrieve CN and pass it (defaults to FQDN) - */ - Log(LogInformation, "cli") << "Using the following CN (defaults to FQDN): '" << cn << "'."; /* - * 4. new ca, new cert and pki request a signed certificate from the master + * 4. pki request a signed certificate from the master + * Requires local ca & key/crt */ String local_pki_path = PkiUtility::GetLocalPkiPath(); - Log(LogInformation, "cli") - << "Generating new CA."; - - if (PkiUtility::NewCa() > 0) { - Log(LogWarning, "cli") - << "Found CA, skipping and using the existing one."; - } - - Log(LogInformation, "cli") - << "Generating a self-signed certificate."; - String keyfile = local_pki_path + "/" + cn + ".key"; String certfile = local_pki_path + "/" + cn + ".crt"; String cafile = PkiUtility::GetLocalCaPath() + "/ca.crt"; - if (PkiUtility::NewCert(cn, keyfile, Empty, certfile) > 0) { + //TODO: local CA or any other one? + if (!Utility::PathExists(cafile)) { Log(LogCritical, "cli") - << "Failed to create self-signed certificate"; + << "CA file '" << cafile << "' does not exist. Please generate a new CA first.\n" + << "Hist: 'icinga2 pki new-ca'"; + return 1; } - /* - * 5. Copy certificates to /etc/icinga2/pki - */ + if (!Utility::PathExists(keyfile)) { + Log(LogCritical, "cli") + << "Private key file '" << keyfile << "' does not exist. Please generate a new certificate first.\n" + << "Hist: 'icinga2 pki new-cert'"; + return 1; + } + + if (!Utility::PathExists(certfile)) { + Log(LogCritical, "cli") + << "Cert file '" << certfile << "' does not exist. Please generate a new certificate first.\n" + << "Hist: 'icinga2 pki new-cert'"; + return 1; + } String pki_path = PkiUtility::GetPkiPath(); @@ -310,11 +308,10 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v String port = "5665"; - PkiUtility::RequestCertificate(master_host, master_port, keyfile, - certfile, cafile, trustedcert, ticket); + PkiUtility::RequestCertificate(master_host, master_port, keyfile, certfile, cafile, trustedcert, ticket); /* - * 6. get public key signed by the master, private key and ca.crt and copy it to /etc/icinga2/pki + * 5. get public key signed by the master, private key and ca.crt and copy it to /etc/icinga2/pki */ //Log(LogInformation, "cli") @@ -323,7 +320,7 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v //std::cout << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "PLACEHOLDER" << ConsoleColorTag(Console_Normal) << std::endl; /* - * 7. enable the ApiListener config (verifiy its data) + * 6. enable the ApiListener config (verifiy its data) */ Log(LogInformation, "cli") @@ -349,8 +346,7 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v /* - * 8. generate local zones.conf with zone+endpoint - * TODO: Move that into a function + * 7. generate local zones.conf with zone+endpoint */ Log(LogInformation, "cli", "Generating zone and object configuration."); @@ -362,78 +358,22 @@ int AgentSetupCommand::SetupAgent(const boost::program_options::variables_map& v endpoints.push_back("master-noconnect"); //no endpoint means no connection attempt. fake name required for master endpoint name } - String zones_path = Application::GetSysconfDir() + "/icinga2/zones.conf.TESTBYMICHI"; //FIXME - - Array::Ptr my_config = make_shared(); - - Dictionary::Ptr my_master_zone = make_shared(); - Array::Ptr my_master_zone_members = make_shared(); - - BOOST_FOREACH(const std::string& endpoint, endpoints) { - - /* extract all --endpoint arguments and store host,port info */ - std::vector tokens; - boost::algorithm::split(tokens, endpoint, boost::is_any_of(",")); - - Dictionary::Ptr my_master_endpoint = make_shared(); - - if (tokens.size() == 1 || tokens.size() == 2) - my_master_endpoint->Set("host", tokens[0]); - - if (tokens.size() == 2) - my_master_endpoint->Set("port", tokens[1]); - - my_master_endpoint->Set("__name", String(endpoint)); - my_master_endpoint->Set("__type", "Endpoint"); - - /* save endpoint in master zone */ - my_master_zone_members->Add(String(endpoint)); //find a better name - - my_config->Add(my_master_endpoint); - } - - - /* add the master zone to the config */ - my_master_zone->Set("__name", "master"); //hardcoded name - my_master_zone->Set("__type", "Zone"); - my_master_zone->Set("endpoints", my_master_zone_members); - - my_config->Add(my_master_zone); - - /* store the local generated agent configuration */ - Dictionary::Ptr my_endpoint = make_shared(); - Dictionary::Ptr my_zone = make_shared(); - - my_endpoint->Set("__name", NodeName); - my_endpoint->Set("__type", "Endpoint"); - - Array::Ptr my_zone_members = make_shared(); - my_zone_members->Add(NodeName); - - my_zone->Set("__name", NodeName); - my_zone->Set("__type", "Zone"); - my_zone->Set("//this is the local agent", NodeName); - my_zone->Set("endpoints", my_zone_members); - - /* store the local config */ - my_config->Add(my_endpoint); - my_config->Add(my_zone); - - /* write the newly generated configuration */ - AgentUtility::WriteAgentConfigObjects(zones_path, my_config); + AgentUtility::GenerateAgentIcingaConfig(endpoints, cn); /* - * 9. update constants.conf with NodeName = CN + * 8. 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!"; + } //Log(LogInformation, "cli") // << "Updating configuration with NodeName constant."; //TODO requires parsing of constants.conf, editing the entry and dumping it again? - //std::cout << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "PLACEHOLDER" << ConsoleColorTag(Console_Normal) << std::endl; - /* - * 10. tell the user to reload icinga2 + * 9. tell the user to reload icinga2 */ Log(LogInformation, "cli", "Make sure to restart Icinga 2."); diff --git a/lib/cli/agentutility.cpp b/lib/cli/agentutility.cpp index b4b36f8be..7306281f2 100644 --- a/lib/cli/agentutility.cpp +++ b/lib/cli/agentutility.cpp @@ -30,8 +30,10 @@ #include "base/objectlock.hpp" #include "base/console.hpp" #include +#include #include #include +#include #include #include @@ -254,13 +256,85 @@ void AgentUtility::CollectAgents(const String& agent_file, std::vector& agents.push_back(agent); } +/* + * Agent Setup helpers + */ + +int AgentUtility::GenerateAgentIcingaConfig(const std::vector& endpoints, const String& nodename) +{ + Array::Ptr my_config = make_shared(); + + Dictionary::Ptr my_master_zone = make_shared(); + Array::Ptr my_master_zone_members = make_shared(); + + BOOST_FOREACH(const std::string& endpoint, endpoints) { + + /* extract all --endpoint arguments and store host,port info */ + std::vector tokens; + boost::algorithm::split(tokens, endpoint, boost::is_any_of(",")); + + Dictionary::Ptr my_master_endpoint = make_shared(); + + if (tokens.size() == 1 || tokens.size() == 2) + my_master_endpoint->Set("host", tokens[0]); + + if (tokens.size() == 2) + my_master_endpoint->Set("port", tokens[1]); + + my_master_endpoint->Set("__name", String(endpoint)); + my_master_endpoint->Set("__type", "Endpoint"); + + /* save endpoint in master zone */ + my_master_zone_members->Add(String(endpoint)); //find a better name + + my_config->Add(my_master_endpoint); + } + + + /* add the master zone to the config */ + my_master_zone->Set("__name", "master"); //hardcoded name + my_master_zone->Set("__type", "Zone"); + my_master_zone->Set("endpoints", my_master_zone_members); + + my_config->Add(my_master_zone); + + /* store the local generated agent configuration */ + Dictionary::Ptr my_endpoint = make_shared(); + Dictionary::Ptr my_zone = make_shared(); + + my_endpoint->Set("__name", nodename); + my_endpoint->Set("__type", "Endpoint"); + + Array::Ptr my_zone_members = make_shared(); + my_zone_members->Add(nodename); + + my_zone->Set("__name", nodename); + my_zone->Set("__type", "Zone"); + my_zone->Set("//this is the local agent", nodename); + my_zone->Set("endpoints", my_zone_members); + + /* store the local config */ + my_config->Add(my_endpoint); + my_config->Add(my_zone); + + /* write the newly generated configuration */ + String zones_path = Application::GetSysconfDir() + "/icinga2/zones.conf"; + + AgentUtility::WriteAgentConfigObjects(zones_path, my_config); + + return 0; +} + /* * This is ugly and requires refactoring into a generic config writer class. * TODO. */ bool AgentUtility::WriteAgentConfigObjects(const String& filename, const Array::Ptr& objects) { - Log(LogInformation, "cli", "Dumping config items to file '" + filename + "'"); + Log(LogInformation, "cli", "Dumping config items to file '" + filename + "'."); + + /* create a backup first */ + CreateBackupFile(filename); Utility::MkDirP(Utility::DirName(filename), 0755); @@ -268,6 +342,11 @@ bool AgentUtility::WriteAgentConfigObjects(const String& filename, const Array:: std::ofstream fp(tempPath.CStr(), std::ofstream::out | std::ostream::trunc); + fp << "/*\n"; + fp << " * Generated by Icinga 2 agent setup commands\n"; + fp << " * on " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", Utility::GetTime()) << "\n"; + fp << " */\n\n"; + ObjectLock olock(objects); BOOST_FOREACH(const Dictionary::Ptr& object, objects) { @@ -294,6 +373,31 @@ bool AgentUtility::WriteAgentConfigObjects(const String& filename, const Array:: return true; } +/* + * We generally don't overwrite files without backup before + */ +bool AgentUtility::CreateBackupFile(const String& target) +{ + if (Utility::PathExists(target)) { + String backup = target + ".orig"; + +#ifdef _WIN32 + _unlink(backup.CStr()); +#endif /* _WIN32 */ + + if (rename(target.CStr(), backup.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(target)); + } + + Log(LogInformation, "cli", "Created backup file '" + backup + "'."); + } + + return true; +} + void AgentUtility::SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object) { fp << "object " << type << " \"" << name << "\" {\n"; @@ -305,7 +409,7 @@ void AgentUtility::SerializeObject(std::ostream& fp, const String& name, const S FormatValue(fp, kv.second); fp << "\n"; } - fp << "}\n"; + fp << "}\n\n"; } void AgentUtility::FormatValue(std::ostream& fp, const Value& val) diff --git a/lib/cli/agentutility.hpp b/lib/cli/agentutility.hpp index 599f1ed0a..7823a2a10 100644 --- a/lib/cli/agentutility.hpp +++ b/lib/cli/agentutility.hpp @@ -54,8 +54,13 @@ public: static bool GetAgents(std::vector& agents); + static bool CreateBackupFile(const String& target); + static bool WriteAgentConfigObjects(const String& filename, const Array::Ptr& objects); + /* agent setup helpers */ + static int GenerateAgentIcingaConfig(const std::vector& endpoints, const String& nodename); + private: AgentUtility(void); static bool RemoveAgentFile(const String& path);