summaryrefslogtreecommitdiffstats
path: root/lib/cli/apisetuputility.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cli/apisetuputility.cpp')
-rw-r--r--lib/cli/apisetuputility.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/lib/cli/apisetuputility.cpp b/lib/cli/apisetuputility.cpp
new file mode 100644
index 0000000..8bdd767
--- /dev/null
+++ b/lib/cli/apisetuputility.cpp
@@ -0,0 +1,205 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "cli/apisetuputility.hpp"
+#include "cli/nodeutility.hpp"
+#include "cli/featureutility.hpp"
+#include "remote/apilistener.hpp"
+#include "remote/pkiutility.hpp"
+#include "base/atomic-file.hpp"
+#include "base/logger.hpp"
+#include "base/console.hpp"
+#include "base/application.hpp"
+#include "base/tlsutility.hpp"
+#include "base/scriptglobal.hpp"
+#include "base/exception.hpp"
+#include "base/utility.hpp"
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <iostream>
+#include <string>
+#include <fstream>
+#include <vector>
+
+using namespace icinga;
+
+String ApiSetupUtility::GetConfdPath()
+{
+ return Configuration::ConfigDir + "/conf.d";
+}
+
+String ApiSetupUtility::GetApiUsersConfPath()
+{
+ return ApiSetupUtility::GetConfdPath() + "/api-users.conf";
+}
+
+bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
+{
+ if (!SetupMasterCertificates(cn))
+ return false;
+
+ if (!SetupMasterApiUser())
+ return false;
+
+ if (!SetupMasterEnableApi())
+ return false;
+
+ if (!SetupMasterUpdateConstants(cn))
+ return false;
+
+ if (prompt_restart) {
+ std::cout << "Done.\n\n";
+ std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n";
+ }
+
+ return true;
+}
+
+bool ApiSetupUtility::SetupMasterCertificates(const String& cn)
+{
+ Log(LogInformation, "cli", "Generating new CA.");
+
+ if (PkiUtility::NewCa() > 0)
+ Log(LogWarning, "cli", "Found CA, skipping and using the existing one.");
+
+ String pki_path = ApiListener::GetCertsDir();
+ Utility::MkDirP(pki_path, 0700);
+
+ String user = Configuration::RunAsUser;
+ String group = Configuration::RunAsGroup;
+
+ if (!Utility::SetFileOwnership(pki_path, user, group)) {
+ Log(LogWarning, "cli")
+ << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << pki_path << "'.";
+ }
+
+ String key = pki_path + "/" + cn + ".key";
+ String csr = pki_path + "/" + cn + ".csr";
+
+ if (Utility::PathExists(key)) {
+ Log(LogInformation, "cli")
+ << "Private key file '" << key << "' already exists, not generating new certificate.";
+ return true;
+ }
+
+ Log(LogInformation, "cli")
+ << "Generating new CSR in '" << csr << "'.";
+
+ if (Utility::PathExists(key))
+ NodeUtility::CreateBackupFile(key, true);
+ if (Utility::PathExists(csr))
+ NodeUtility::CreateBackupFile(csr);
+
+ if (PkiUtility::NewCert(cn, key, csr, "") > 0) {
+ Log(LogCritical, "cli", "Failed to create certificate signing request.");
+ return false;
+ }
+
+ /* Sign the CSR with the CA key */
+ String cert = pki_path + "/" + cn + ".crt";
+
+ Log(LogInformation, "cli")
+ << "Signing CSR with CA and writing certificate to '" << cert << "'.";
+
+ if (Utility::PathExists(cert))
+ NodeUtility::CreateBackupFile(cert);
+
+ if (PkiUtility::SignCsr(csr, cert) != 0) {
+ Log(LogCritical, "cli", "Could not sign CSR.");
+ return false;
+ }
+
+ /* Copy CA certificate to /etc/icinga2/pki */
+ String ca_path = ApiListener::GetCaDir();
+ String ca = ca_path + "/ca.crt";
+ String ca_key = ca_path + "/ca.key";
+ String target_ca = pki_path + "/ca.crt";
+
+ Log(LogInformation, "cli")
+ << "Copying CA certificate to '" << target_ca << "'.";
+
+ if (Utility::PathExists(target_ca))
+ NodeUtility::CreateBackupFile(target_ca);
+
+ /* does not overwrite existing files! */
+ Utility::CopyFile(ca, target_ca);
+
+ /* fix permissions: root -> icinga daemon user */
+ for (const String& file : { ca_path, ca, ca_key, target_ca, key, csr, cert }) {
+ if (!Utility::SetFileOwnership(file, user, group)) {
+ Log(LogWarning, "cli")
+ << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << file << "'.";
+ }
+ }
+
+ return true;
+}
+
+bool ApiSetupUtility::SetupMasterApiUser()
+{
+ if (!Utility::PathExists(GetConfdPath())) {
+ Log(LogWarning, "cli")
+ << "Path '" << GetConfdPath() << "' do not exist.";
+ Log(LogInformation, "cli")
+ << "Creating path '" << GetConfdPath() << "'.";
+
+ Utility::MkDirP(GetConfdPath(), 0755);
+ }
+
+ String api_username = "root"; // TODO make this available as cli parameter?
+ String api_password = RandomString(8);
+ String apiUsersPath = GetConfdPath() + "/api-users.conf";
+
+ if (Utility::PathExists(apiUsersPath)) {
+ Log(LogInformation, "cli")
+ << "API user config file '" << apiUsersPath << "' already exists, not creating config file.";
+ return true;
+ }
+
+ Log(LogInformation, "cli")
+ << "Adding new ApiUser '" << api_username << "' in '" << apiUsersPath << "'.";
+
+ NodeUtility::CreateBackupFile(apiUsersPath);
+
+ AtomicFile fp (apiUsersPath, 0644);
+
+ fp << "/**\n"
+ << " * The ApiUser objects are used for authentication against the API.\n"
+ << " */\n"
+ << "object ApiUser \"" << api_username << "\" {\n"
+ << " password = \"" << api_password << "\"\n"
+ << " // client_cn = \"\"\n"
+ << "\n"
+ << " permissions = [ \"*\" ]\n"
+ << "}\n";
+
+ fp.Commit();
+
+ return true;
+}
+
+bool ApiSetupUtility::SetupMasterEnableApi()
+{
+ /*
+ * Ensure the api-users.conf file is included, when conf.d inclusion is disabled.
+ */
+ if (!NodeUtility::GetConfigurationIncludeState("\"conf.d\"", true))
+ NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false);
+
+ /*
+ * Enable the API feature
+ */
+ Log(LogInformation, "cli", "Enabling the 'api' feature.");
+
+ FeatureUtility::EnableFeatures({ "api" });
+
+ return true;
+}
+
+bool ApiSetupUtility::SetupMasterUpdateConstants(const String& cn)
+{
+ NodeUtility::UpdateConstant("NodeName", cn);
+ NodeUtility::UpdateConstant("ZoneName", cn);
+
+ return true;
+}