]> granicus.if.org Git - icinga2/blob - lib/cli/apisetuputility.cpp
add some object locking to the Dump method (which could theoreticylly suffer from...
[icinga2] / lib / cli / apisetuputility.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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>
17 #include <iostream>
18 #include <string>
19 #include <fstream>
20 #include <vector>
21
22 using namespace icinga;
23
24 String ApiSetupUtility::GetConfdPath()
25 {
26         return Configuration::ConfigDir + "/conf.d";
27 }
28
29 String ApiSetupUtility::GetApiUsersConfPath()
30 {
31         return ApiSetupUtility::GetConfdPath() + "/api-users.conf";
32 }
33
34 bool ApiSetupUtility::SetupMaster(const String& cn, bool prompt_restart)
35 {
36         if (!SetupMasterCertificates(cn))
37                 return false;
38
39         if (!SetupMasterApiUser())
40                 return false;
41
42         if (!SetupMasterEnableApi())
43                 return false;
44
45         if (!SetupMasterUpdateConstants(cn))
46                 return false;
47
48         if (prompt_restart) {
49                 std::cout << "Done.\n\n";
50                 std::cout << "Now restart your Icinga 2 daemon to finish the installation!\n\n";
51         }
52
53         return true;
54 }
55
56 bool ApiSetupUtility::SetupMasterCertificates(const String& cn)
57 {
58         Log(LogInformation, "cli", "Generating new CA.");
59
60         if (PkiUtility::NewCa() > 0)
61                 Log(LogWarning, "cli", "Found CA, skipping and using the existing one.");
62
63         String pki_path = ApiListener::GetCertsDir();
64         Utility::MkDirP(pki_path, 0700);
65
66         String user = Configuration::RunAsUser;
67         String group = Configuration::RunAsGroup;
68
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 << "'.";
72         }
73
74         String key = pki_path + "/" + cn + ".key";
75         String csr = pki_path + "/" + cn + ".csr";
76
77         if (Utility::PathExists(key)) {
78                 Log(LogInformation, "cli")
79                         << "Private key file '" << key << "' already exists, not generating new certificate.";
80                 return true;
81         }
82
83         Log(LogInformation, "cli")
84                 << "Generating new CSR in '" << csr << "'.";
85
86         if (Utility::PathExists(key))
87                 NodeUtility::CreateBackupFile(key, true);
88         if (Utility::PathExists(csr))
89                 NodeUtility::CreateBackupFile(csr);
90
91         if (PkiUtility::NewCert(cn, key, csr, "") > 0) {
92                 Log(LogCritical, "cli", "Failed to create certificate signing request.");
93                 return false;
94         }
95
96         /* Sign the CSR with the CA key */
97         String cert = pki_path + "/" + cn + ".crt";
98
99         Log(LogInformation, "cli")
100                 << "Signing CSR with CA and writing certificate to '" << cert << "'.";
101
102         if (Utility::PathExists(cert))
103                 NodeUtility::CreateBackupFile(cert);
104
105         if (PkiUtility::SignCsr(csr, cert) != 0) {
106                 Log(LogCritical, "cli", "Could not sign CSR.");
107                 return false;
108         }
109
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";
115
116         Log(LogInformation, "cli")
117                 << "Copying CA certificate to '" << target_ca << "'.";
118
119         if (Utility::PathExists(target_ca))
120                 NodeUtility::CreateBackupFile(target_ca);
121
122         /* does not overwrite existing files! */
123         Utility::CopyFile(ca, target_ca);
124
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 << "'.";
130                 }
131         }
132
133         return true;
134 }
135
136 bool ApiSetupUtility::SetupMasterApiUser()
137 {
138         if (!Utility::PathExists(GetConfdPath())) {
139                 Log(LogWarning, "cli")
140                         << "Path '" << GetConfdPath() << "' do not exist.";
141                 Log(LogInformation, "cli")
142                         << "Creating path '" << GetConfdPath() << "'.";
143
144                 Utility::MkDirP(GetConfdPath(), 0755);
145         }
146
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";
150
151         if (Utility::PathExists(apiUsersPath)) {
152                 Log(LogInformation, "cli")
153                         << "API user config file '" << apiUsersPath << "' already exists, not creating config file.";
154                 return true;
155         }
156
157         Log(LogInformation, "cli")
158                 << "Adding new ApiUser '" << api_username << "' in '" << apiUsersPath << "'.";
159
160         NodeUtility::CreateBackupFile(apiUsersPath);
161
162         std::fstream fp;
163         String tempFilename = Utility::CreateTempFile(apiUsersPath + ".XXXXXX", 0644, fp);
164
165         fp << "/**\n"
166                 << " * The ApiUser objects are used for authentication against the API.\n"
167                 << " */\n"
168                 << "object ApiUser \"" << api_username << "\" {\n"
169                 << "  password = \"" << api_password << "\"\n"
170                 << "  // client_cn = \"\"\n"
171                 << "\n"
172                 << "  permissions = [ \"*\" ]\n"
173                 << "}\n";
174
175         fp.close();
176
177 #ifdef _WIN32
178         _unlink(apiUsersPath.CStr());
179 #endif /* _WIN32 */
180
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));
186         }
187
188         return true;
189 }
190
191 bool ApiSetupUtility::SetupMasterEnableApi()
192 {
193         /*
194         * Ensure the api-users.conf file is included, when conf.d inclusion is disabled.
195         */
196         if (!NodeUtility::GetConfigurationIncludeState("\"conf.d\"", true))
197                 NodeUtility::UpdateConfiguration("\"conf.d/api-users.conf\"", true, false);
198
199         /*
200         * Enable the API feature
201         */
202         Log(LogInformation, "cli", "Enabling the 'api' feature.");
203
204         FeatureUtility::EnableFeatures({ "api" });
205
206         return true;
207 }
208
209 bool ApiSetupUtility::SetupMasterUpdateConstants(const String& cn)
210 {
211         NodeUtility::UpdateConstant("NodeName", cn);
212         NodeUtility::UpdateConstant("ZoneName", cn);
213
214         return true;
215 }