1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "cli/apisetuputility.hpp"
4 #include "cli/nodeutility.hpp"
5 #include "cli/featureutility.hpp"
6 #include "remote/apilistener.hpp"
7 #include "remote/pkiutility.hpp"
8 #include "base/logger.hpp"
9 #include "base/console.hpp"
10 #include "base/application.hpp"
11 #include "base/tlsutility.hpp"
12 #include "base/scriptglobal.hpp"
13 #include "base/exception.hpp"
14 #include <boost/algorithm/string/join.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/algorithm/string/case_conv.hpp>
22 using namespace icinga;
24 String ApiSetupUtility::GetConfdPath()
26 return Configuration::ConfigDir + "/conf.d";
29 String ApiSetupUtility::GetApiUsersConfPath()
31 return ApiSetupUtility::GetConfdPath() + "/api-users.conf";
34 bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
36 if (!SetupMasterCertificates(cn))
39 if (!SetupMasterApiUser())
42 if (!SetupMasterEnableApi())
45 if (!SetupMasterUpdateConstants(cn))
49 std::cout << "Done.\n\n";
50 std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n";
56 bool ApiSetupUtility::SetupMasterCertificates(const String& cn)
58 Log(LogInformation, "cli", "Generating new CA.");
60 if (PkiUtility::NewCa() > 0)
61 Log(LogWarning, "cli", "Found CA, skipping and using the existing one.");
63 String pki_path = ApiListener::GetCertsDir();
64 Utility::MkDirP(pki_path, 0700);
66 String user = Configuration::RunAsUser;
67 String group = Configuration::RunAsGroup;
69 if (!Utility::SetFileOwnership(pki_path, user, group)) {
70 Log(LogWarning, "cli")
71 << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << pki_path << "'.";
74 String key = pki_path + "/" + cn + ".key";
75 String csr = pki_path + "/" + cn + ".csr";
77 if (Utility::PathExists(key)) {
78 Log(LogInformation, "cli")
79 << "Private key file '" << key << "' already exists, not generating new certificate.";
83 Log(LogInformation, "cli")
84 << "Generating new CSR in '" << csr << "'.";
86 if (Utility::PathExists(key))
87 NodeUtility::CreateBackupFile(key, true);
88 if (Utility::PathExists(csr))
89 NodeUtility::CreateBackupFile(csr);
91 if (PkiUtility::NewCert(cn, key, csr, "") > 0) {
92 Log(LogCritical, "cli", "Failed to create certificate signing request.");
96 /* Sign the CSR with the CA key */
97 String cert = pki_path + "/" + cn + ".crt";
99 Log(LogInformation, "cli")
100 << "Signing CSR with CA and writing certificate to '" << cert << "'.";
102 if (Utility::PathExists(cert))
103 NodeUtility::CreateBackupFile(cert);
105 if (PkiUtility::SignCsr(csr, cert) != 0) {
106 Log(LogCritical, "cli", "Could not sign CSR.");
110 /* Copy CA certificate to /etc/icinga2/pki */
111 String ca_path = ApiListener::GetCaDir();
112 String ca = ca_path + "/ca.crt";
113 String ca_key = ca_path + "/ca.key";
114 String target_ca = pki_path + "/ca.crt";
116 Log(LogInformation, "cli")
117 << "Copying CA certificate to '" << target_ca << "'.";
119 if (Utility::PathExists(target_ca))
120 NodeUtility::CreateBackupFile(target_ca);
122 /* does not overwrite existing files! */
123 Utility::CopyFile(ca, target_ca);
125 /* fix permissions: root -> icinga daemon user */
126 for (const String& file : { ca_path, ca, ca_key, target_ca, key, csr, cert }) {
127 if (!Utility::SetFileOwnership(file, user, group)) {
128 Log(LogWarning, "cli")
129 << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << file << "'.";
136 bool ApiSetupUtility::SetupMasterApiUser()
138 if (!Utility::PathExists(GetConfdPath())) {
139 Log(LogWarning, "cli")
140 << "Path '" << GetConfdPath() << "' do not exist.";
141 Log(LogInformation, "cli")
142 << "Creating path '" << GetConfdPath() << "'.";
144 Utility::MkDirP(GetConfdPath(), 0755);
147 String api_username = "root"; // TODO make this available as cli parameter?
148 String api_password = RandomString(8);
149 String apiUsersPath = GetConfdPath() + "/api-users.conf";
151 if (Utility::PathExists(apiUsersPath)) {
152 Log(LogInformation, "cli")
153 << "API user config file '" << apiUsersPath << "' already exists, not creating config file.";
157 Log(LogInformation, "cli")
158 << "Adding new ApiUser '" << api_username << "' in '" << apiUsersPath << "'.";
160 NodeUtility::CreateBackupFile(apiUsersPath);
163 String tempFilename = Utility::CreateTempFile(apiUsersPath + ".XXXXXX", 0644, fp);
166 << " * The ApiUser objects are used for authentication against the API.\n"
168 << "object ApiUser \"" << api_username << "\" {\n"
169 << " password = \"" << api_password << "\"\n"
170 << " // client_cn = \"\"\n"
172 << " permissions = [ \"*\" ]\n"
178 _unlink(apiUsersPath.CStr());
181 if (rename(tempFilename.CStr(), apiUsersPath.CStr()) < 0) {
182 BOOST_THROW_EXCEPTION(posix_error()
183 << boost::errinfo_api_function("rename")
184 << boost::errinfo_errno(errno)
185 << boost::errinfo_file_name(tempFilename));
191 bool ApiSetupUtility::SetupMasterEnableApi()
194 * Ensure the api-users.conf file is included, when conf.d inclusion is disabled.
196 if (!NodeUtility::GetConfigurationIncludeState("\"conf.d\"", true))
197 NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false);
200 * Enable the API feature
202 Log(LogInformation, "cli", "Enabling the 'api' feature.");
204 FeatureUtility::EnableFeatures({ "api" });
209 bool ApiSetupUtility::SetupMasterUpdateConstants(const String& cn)
211 NodeUtility::UpdateConstant("NodeName", cn);
212 NodeUtility::UpdateConstant("ZoneName", cn);