*
* @returns An exit status.
*/
-int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
+int NodeWizardCommand::Run(const boost::program_options::variables_map& vm,
+ const std::vector<std::string>& ap) const
{
/*
* The wizard will get all information from the user,
* and then call all required functions.
*/
- std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundBlue) << "Welcome to the Icinga 2 Setup Wizard!\n"
+ std::cout << ConsoleColorTag(Console_Bold | Console_ForegroundBlue)
+ << "Welcome to the Icinga 2 Setup Wizard!\n"
<< "\n"
<< "We'll guide you through all required configuration details.\n"
<< "\n"
bool is_node_setup = true;
/* master or node setup */
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify if this is a satellite setup "
- << "('n' installs a master setup)" << ConsoleColorTag(Console_Normal) << " [Y/n]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify if this is a satellite setup "
+ << "('n' installs a master setup)" << ConsoleColorTag(Console_Normal)
+ << " [Y/n]: ";
std::getline (std::cin, answer);
boost::algorithm::to_lower(answer);
std::cout << "Starting the Node setup routine...\n";
/* CN */
- std::cout << ConsoleColorTag(Console_Bold) << "Please specifiy the common name (CN)" << ConsoleColorTag(Console_Normal) << " [" << Utility::GetFQDN() << "]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specifiy the common name (CN)"
+ << ConsoleColorTag(Console_Normal)
+ << " [" << Utility::GetFQDN() << "]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
String cn = answer;
cn = cn.Trim();
- std::cout << ConsoleColorTag(Console_Bold) << "Please specifiy the local zone name" << ConsoleColorTag(Console_Normal) << " [" << cn << "]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specifiy the local zone name"
+ << ConsoleColorTag(Console_Normal) << " [" << cn << "]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
String endpoint_buffer;
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify the master endpoint(s) this node should connect to:" << ConsoleColorTag(Console_Normal) << "\n";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify the master endpoint(s) this node should connect to:"
+ << ConsoleColorTag(Console_Normal) << "\n";
String master_endpoint_name;
wizard_endpoint_loop_start:
- std::cout << ConsoleColorTag(Console_Bold) << "Master Common Name" << ConsoleColorTag(Console_Normal) << " (CN from your master setup): ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Master Common Name" << ConsoleColorTag(Console_Normal)
+ << " (CN from your master setup): ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
endpoint_buffer = answer;
endpoint_buffer = endpoint_buffer.Trim();
- std::cout << "Do you want to establish a connection to the master " << ConsoleColorTag(Console_Bold) << "from this node?" << ConsoleColorTag(Console_Normal) << " [Y/n]: ";
+ std::cout << "Do you want to establish a connection to the master "
+ << ConsoleColorTag(Console_Bold) << "from this node?"
+ << ConsoleColorTag(Console_Normal) << " [Y/n]: ";
std::getline (std::cin, answer);
boost::algorithm::to_lower(answer);
Log(LogWarning, "cli", "Node to master connection setup skipped");
std::cout << "Connection setup skipped. Please configure your master to connect to this node.\n";
} else {
- std::cout << ConsoleColorTag(Console_Bold) << "Please fill out the master connection information:" << ConsoleColorTag(Console_Normal) << "\n";
- std::cout << ConsoleColorTag(Console_Bold) << "Master endpoint host" << ConsoleColorTag(Console_Normal) << " (Your master's IP address or FQDN): ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please fill out the master connection information:"
+ << ConsoleColorTag(Console_Normal) << "\n"
+ << ConsoleColorTag(Console_Bold) << "Master endpoint host"
+ << ConsoleColorTag(Console_Normal) << " (Your master's IP address or FQDN): ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
endpoint_buffer += "," + tmp;
master_endpoint_name = tmp; //store the endpoint name for later
- std::cout << ConsoleColorTag(Console_Bold) << "Master endpoint port" << ConsoleColorTag(Console_Normal) << " [5665]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Master endpoint port" << ConsoleColorTag(Console_Normal)
+ << " [5665]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
endpoints.push_back(endpoint_buffer);
- std::cout << ConsoleColorTag(Console_Bold) << "Add more master endpoints?" << ConsoleColorTag(Console_Normal) << " [y/N]: ";
+ std::cout << ConsoleColorTag(Console_Bold) << "Add more master endpoints?"
+ << ConsoleColorTag(Console_Normal) << " [y/N]: ";
std::getline (std::cin, answer);
boost::algorithm::to_lower(answer);
if (choice.Contains("y"))
goto wizard_endpoint_loop_start;
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify the master connection for CSR auto-signing" << ConsoleColorTag(Console_Normal) << " (defaults to master endpoint host):\n";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify the master connection for CSR auto-signing"
+ << ConsoleColorTag(Console_Normal) << " (defaults to master endpoint host):\n";
wizard_master_host:
- std::cout << ConsoleColorTag(Console_Bold) << "Host" << ConsoleColorTag(Console_Normal) << " [" << master_endpoint_name << "]: ";
+ std::cout << ConsoleColorTag(Console_Bold) << "Host"
+ << ConsoleColorTag(Console_Normal) << " [" << master_endpoint_name << "]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
String master_host = answer;
master_host = master_host.Trim();
- std::cout << ConsoleColorTag(Console_Bold) << "Port" << ConsoleColorTag(Console_Normal) << " [5665]: ";
+ std::cout << ConsoleColorTag(Console_Bold) << "Port"
+ << ConsoleColorTag(Console_Normal) << " [5665]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
if (!Utility::SetFileOwnership(pki_path, user, group)) {
Log(LogWarning, "cli")
- << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << pki_path << "'. Verify it yourself!";
+ << "Cannot set ownership for user '" << user
+ << "' group '" << group
+ << "' on file '" << pki_path << "'. Verify it yourself!";
}
String node_cert = pki_path + "/" + cn + ".crt";
if (PkiUtility::NewCert(cn, node_key, Empty, node_cert) > 0) {
Log(LogCritical, "cli")
- << "Failed to create new self-signed certificate for CN '" << cn << "'. Please try again.";
+ << "Failed to create new self-signed certificate for CN '"
+ << cn << "'. Please try again.";
return 1;
}
/* fix permissions: root -> icinga daemon user */
if (!Utility::SetFileOwnership(node_key, user, group)) {
Log(LogWarning, "cli")
- << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << node_key << "'. Verify it yourself!";
+ << "Cannot set ownership for user '" << user
+ << "' group '" << group
+ << "' on file '" << node_key << "'. Verify it yourself!";
}
//save-cert and store the master certificate somewhere
-
- Log(LogInformation, "cli", "Generating self-signed certificate:");
-
Log(LogInformation, "cli")
<< "Fetching public certificate from master ("
<< master_host << ", " << master_port << "):\n";
- String trusted_cert = PkiUtility::GetPkiPath() + "/trusted-master.crt";
+ boost::shared_ptr<X509> trustedcert = PkiUtility::FetchCert(master_host, master_port);
+ if (!trustedcert) {
+ Log(LogCritical, "cli", "Peer did not present a valid certificate.");
+ return 1;
+ }
- if (Utility::PathExists(trusted_cert))
- NodeUtility::CreateBackupFile(trusted_cert);
+ std::cout << ConsoleColorTag(Console_Bold) << "Certificate information:\n"
+ << ConsoleColorTag(Console_Normal) << PkiUtility::GetCertificateInformation(trustedcert)
+ << ConsoleColorTag(Console_Bold) << "\nIs this information correct?"
+ << ConsoleColorTag(Console_Normal) << " [y/N]: ";
- if (PkiUtility::SaveCert(master_host, master_port, node_key, node_cert, trusted_cert) > 0) {
- Log(LogCritical, "cli")
- << "Failed to fetch trusted master certificate. Please try again.";
+ std::getline (std::cin, answer);
+ boost::algorithm::to_lower(answer);
+ if (answer != "y") {
+ Log(LogWarning, "cli", "Process aborted.");
return 1;
}
- Log(LogInformation, "cli")
- << "Stored trusted master certificate in '" << trusted_cert << "'.\n";
+ Log(LogInformation, "cli", "Received trusted master certificate.\n");
wizard_ticket:
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify the request ticket generated on your Icinga 2 master." << ConsoleColorTag(Console_Normal) << "\n"
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify the request ticket generated on your Icinga 2 master."
+ << ConsoleColorTag(Console_Normal) << "\n"
<< " (Hint: # icinga2 pki ticket --cn '" << cn << "'): ";
std::getline(std::cin, answer);
ticket = ticket.Trim();
Log(LogInformation, "cli")
- << "Processing self-signed certificate request. Ticket '" << ticket << "'.\n";
+ << "Requesting certificate with ticket '" << ticket << "'.\n";
String target_ca = pki_path + "/ca.crt";
if (Utility::PathExists(node_cert))
NodeUtility::CreateBackupFile(node_cert);
- if (PkiUtility::RequestCertificate(master_host, master_port, node_key, node_cert, target_ca, trusted_cert, ticket) > 0) {
+ if (PkiUtility::RequestCertificate(master_host, master_port, node_key,
+ node_cert, target_ca, trustedcert, ticket) > 0) {
Log(LogCritical, "cli")
- << "Failed to fetch signed certificate from master '" << master_host << ", "
+ << "Failed to fetch signed certificate from master '"
+ << master_host << ", "
<< master_port <<"'. Please try again.";
goto wizard_ticket;
}
/* fix permissions (again) when updating the signed certificate */
if (!Utility::SetFileOwnership(node_cert, user, group)) {
Log(LogWarning, "cli")
- << "Cannot set ownership for user '" << user << "' group '" << group << "' on file '" << node_cert << "'. Verify it yourself!";
+ << "Cannot set ownership for user '" << user
+ << "' group '" << group << "' on file '"
+ << node_cert << "'. Verify it yourself!";
}
/* apilistener config */
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify the API bind host/port" << ConsoleColorTag(Console_Normal) << " (optional):\n";
- std::cout << ConsoleColorTag(Console_Bold) << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify the API bind host/port"
+ << ConsoleColorTag(Console_Normal) << " (optional):\n"
+ << ConsoleColorTag(Console_Bold) << "Bind Host"
+ << ConsoleColorTag(Console_Normal) << " []: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
String bind_port = answer;
bind_port = bind_port.Trim();
- std::cout << ConsoleColorTag(Console_Bold) << "Accept config from master?" << ConsoleColorTag(Console_Normal) << " [y/N]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Accept config from master?" << ConsoleColorTag(Console_Normal)
+ << " [y/N]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
choice = answer;
String accept_config = choice.Contains("y") ? "true" : "false";
- std::cout << ConsoleColorTag(Console_Bold) << "Accept commands from master?" << ConsoleColorTag(Console_Normal) << " [y/N]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Accept commands from master?" << ConsoleColorTag(Console_Normal)
+ << " [y/N]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
choice = answer;
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!";
+ << "CN '" << cn << "' does not match the default FQDN '"
+ << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!";
}
Log(LogInformation, "cli", "Updating constants.conf.");
std::cout << ConsoleColorTag(Console_Bold) << "Starting the Master setup routine...\n";
/* CN */
- std::cout << ConsoleColorTag(Console_Bold) << "Please specifiy the common name" << ConsoleColorTag(Console_Normal) << " (CN) [" << Utility::GetFQDN() << "]: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specifiy the common name" << ConsoleColorTag(Console_Normal)
+ << " (CN) [" << Utility::GetFQDN() << "]: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
/* check whether the user wants to generate a new certificate or not */
String existing_path = PkiUtility::GetPkiPath() + "/" + cn + ".crt";
- std::cout << ConsoleColorTag(Console_Normal) << "Checking for existing certificates for common name '" << cn << "'...\n";
+ std::cout << ConsoleColorTag(Console_Normal)
+ << "Checking for existing certificates for common name '" << cn << "'...\n";
if (Utility::PathExists(existing_path)) {
- std::cout << "Certificate '" << existing_path << "' for CN '" << cn << "' already existing. Skipping certificate generation.\n";
+ std::cout << "Certificate '" << existing_path << "' for CN '"
+ << cn << "' already existing. Skipping certificate generation.\n";
} else {
std::cout << "Certificates not yet generated. Running 'api setup' now.\n";
ApiSetupUtility::SetupMasterCertificates(cn);
}
- std::cout << ConsoleColorTag(Console_Bold) << "Generating master configuration for Icinga 2.\n" << ConsoleColorTag(Console_Normal);
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Generating master configuration for Icinga 2.\n"
+ << ConsoleColorTag(Console_Normal);
ApiSetupUtility::SetupMasterApiUser();
if (!FeatureUtility::CheckFeatureEnabled("api"))
NodeUtility::GenerateNodeMasterIcingaConfig();
/* apilistener config */
- std::cout << ConsoleColorTag(Console_Bold) << "Please specify the API bind host/port (optional):\n";
- std::cout << ConsoleColorTag(Console_Bold) << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Please specify the API bind host/port (optional):\n";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Bind Host" << ConsoleColorTag(Console_Normal) << " []: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
String bind_host = answer;
bind_host = bind_host.Trim();
- std::cout << ConsoleColorTag(Console_Bold) << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: ";
+ std::cout << ConsoleColorTag(Console_Bold)
+ << "Bind Port" << ConsoleColorTag(Console_Normal) << " []: ";
std::getline(std::cin, answer);
boost::algorithm::to_lower(answer);
/* update constants.conf with NodeName = CN + TicketSalt = random value */
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!";
+ << "CN '" << cn << "' does not match the default FQDN '"
+ << Utility::GetFQDN() << "'. Requires update for NodeName constant in constants.conf!";
}
Log(LogInformation, "cli", "Updating constants.conf.");
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/tlsutility.hpp"
+#include "base/console.hpp"
#include "base/tlsstream.hpp"
#include "base/tcpsocket.hpp"
#include "base/json.hpp"
return 0;
}
-int PkiUtility::SaveCert(const String& host, const String& port, const String& keyfile, const String& certfile, const String& trustedfile)
+boost::shared_ptr<X509> PkiUtility::FetchCert(const String& host, const String& port)
{
TcpSocket::Ptr client = new TcpSocket();
try {
client->Connect(host, port);
} catch (const std::exception& ex) {
- Log(LogCritical, "cli")
+ Log(LogCritical, "pki")
<< "Cannot connect to host '" << host << "' on port '" << port << "'";
- Log(LogDebug, "cli")
+ Log(LogDebug, "pki")
<< "Cannot connect to host '" << host << "' on port '" << port << "':\n" << DiagnosticInformation(ex);
- return 1;
+ return NULL;
}
boost::shared_ptr<SSL_CTX> sslContext;
try {
- sslContext = MakeSSLContext(certfile, keyfile);
+ sslContext = MakeSSLContext();
} catch (const std::exception& ex) {
- Log(LogCritical, "cli")
- << "Cannot make SSL context for cert path: '" << certfile << "' key path: '" << keyfile << "'.";
- Log(LogDebug, "cli")
- << "Cannot make SSL context for cert path: '" << certfile << "' key path: '" << keyfile << "':\n" << DiagnosticInformation(ex);
- return 1;
+ Log(LogCritical, "pki")
+ << "Cannot make SSL context.";
+ Log(LogDebug, "pki")
+ << "Cannot make SSL context:\n" << DiagnosticInformation(ex);
+ return NULL;
}
TlsStream::Ptr stream = new TlsStream(client, String(), RoleClient, sslContext);
}
- boost::shared_ptr<X509> cert = stream->GetPeerCertificate();
-
- if (!cert) {
- Log(LogCritical, "cli", "Peer did not present a valid certificate.");
- return 1;
- }
+ return stream->GetPeerCertificate();
+}
+int PkiUtility::WriteCert(const boost::shared_ptr<X509>& cert, const String& trustedfile)
+{
std::ofstream fpcert;
fpcert.open(trustedfile.CStr());
fpcert << CertificateToString(cert);
fpcert.close();
if (fpcert.fail()) {
- Log(LogCritical, "cli")
+ Log(LogCritical, "pki")
<< "Could not write certificate to file '" << trustedfile << "'.";
return 1;
}
- Log(LogInformation, "cli")
+ Log(LogInformation, "pki")
<< "Writing trusted certificate to file '" << trustedfile << "'.";
return 0;
}
int PkiUtility::RequestCertificate(const String& host, const String& port, const String& keyfile,
- const String& certfile, const String& cafile, const String& trustedfile, const String& ticket)
+ const String& certfile, const String& cafile, const boost::shared_ptr<X509>& trustedCert, const String& ticket)
{
TcpSocket::Ptr client = new TcpSocket();
boost::shared_ptr<X509> peerCert = stream->GetPeerCertificate();
- boost::shared_ptr<X509> trustedCert;
-
- try {
- trustedCert = GetX509Certificate(trustedfile);
- } catch (const std::exception&) {
- Log(LogCritical, "cli")
- << "Cannot get trusted from cert path: '" << trustedfile << "'.";
- return 1;
- }
-
- if (CertificateToString(peerCert) != CertificateToString(trustedCert)) {
+ if (X509_cmp(peerCert.get(), trustedCert.get())) {
Log(LogCritical, "cli", "Peer certificate does not match trusted certificate.");
return 1;
}
return 0;
}
+
+String PkiUtility::GetCertificateInformation(const boost::shared_ptr<X509>& cert) {
+ BIO *out = BIO_new(BIO_s_mem());
+ String pre;
+
+ pre = "\n Subject: ";
+ BIO_write(out, pre.CStr(), pre.GetLength());
+ X509_NAME_print_ex(out, X509_get_subject_name(cert.get()), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
+
+ pre = "\n Issuer: ";
+ BIO_write(out, pre.CStr(), pre.GetLength());
+ X509_NAME_print_ex(out, X509_get_issuer_name(cert.get()), 0, XN_FLAG_ONELINE & ~ASN1_STRFLGS_ESC_MSB);
+
+ pre = "\n Valid From: ";
+ BIO_write(out, pre.CStr(), pre.GetLength());
+ ASN1_TIME_print(out, X509_get_notBefore(cert));
+
+ pre = "\n Valid Until: ";
+ BIO_write(out, pre.CStr(), pre.GetLength());
+ ASN1_TIME_print(out, X509_get_notAfter(cert));
+
+ pre = "\n Fingerprint: ";
+ BIO_write(out, pre.CStr(), pre.GetLength());
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned int diglen;
+ X509_digest(cert.get(), EVP_sha1(), md, &diglen);
+
+ char *data;
+ long length = BIO_get_mem_data(out, &data);
+
+ std::stringstream info;
+ info << String(data, data + length);
+ for (unsigned int i = 0; i < diglen; i++) {
+ info << std::setfill('0') << std::setw(2) << std::uppercase
+ << std::hex << static_cast<int>(md[i]) << ' ';
+ }
+ info << '\n';
+
+ return info.str();
+}