Skip to content

Commit

Permalink
Update node wizard
Browse files Browse the repository at this point in the history
The wizard now displays basic information on the to-be-imported master
certificate and asks the user for confirmation.

fixes #10632
  • Loading branch information
Crunsher committed Nov 25, 2015
1 parent 9705e08 commit 7c522da
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 60 deletions.
2 changes: 1 addition & 1 deletion lib/cli/nodesetupcommand.cpp
Expand Up @@ -291,7 +291,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm,
return 1;
}

String trustedcert = vm["trustedcert"].as<std::string>();
boost::shared_ptr<X509> trustedcert = GetX509Certificate(vm["trustedcert"].as<std::string>());

Log(LogInformation, "cli")
<< "Verifying trusted certificate from file '" << trustedcert << "'.";
Expand Down
25 changes: 17 additions & 8 deletions lib/cli/nodewizardcommand.cpp
Expand Up @@ -281,19 +281,28 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, cons
<< "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";
<< "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"
Expand All @@ -318,7 +327,7 @@ int NodeWizardCommand::Run(const boost::program_options::variables_map& vm, cons
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 << ", "
<< master_port <<"'. Please try again.";
Expand Down
3 changes: 2 additions & 1 deletion lib/cli/pkirequestcommand.cpp
Expand Up @@ -20,6 +20,7 @@
#include "cli/pkirequestcommand.hpp"
#include "cli/pkiutility.hpp"
#include "base/logger.hpp"
#include "base/tlsutility.hpp"
#include <iostream>

using namespace icinga;
Expand Down Expand Up @@ -105,6 +106,6 @@ int PKIRequestCommand::Run(const boost::program_options::variables_map& vm, cons
port = vm["port"].as<std::string>();

return PkiUtility::RequestCertificate(vm["host"].as<std::string>(), port, vm["key"].as<std::string>(),
vm["cert"].as<std::string>(), vm["ca"].as<std::string>(), vm["trustedcert"].as<std::string>(),
vm["cert"].as<std::string>(), vm["ca"].as<std::string>(), GetX509Certificate(vm["trustedcert"].as<std::string>()),
vm["ticket"].as<std::string>());
}
28 changes: 11 additions & 17 deletions lib/cli/pkisavecertcommand.cpp
Expand Up @@ -20,6 +20,7 @@
#include "cli/pkisavecertcommand.hpp"
#include "cli/pkiutility.hpp"
#include "base/logger.hpp"
#include "base/tlsutility.hpp"

using namespace icinga;
namespace po = boost::program_options;
Expand All @@ -40,11 +41,11 @@ void PKISaveCertCommand::InitParameters(boost::program_options::options_descript
boost::program_options::options_description& hiddenDesc) const
{
visibleDesc.add_options()
("key", po::value<std::string>(), "Key file path (input)")
("cert", po::value<std::string>(), "Certificate file path (input)")
("key", po::value<std::string>(), "Key file path (input), obsolete")
("cert", po::value<std::string>(), "Certificate file path (input), obsolete")
("trustedcert", po::value<std::string>(), "Trusted certificate file path (output)")
("host", po::value<std::string>(), "Icinga 2 host")
("port", po::value<std::string>(), "Icinga 2 port");
("port", po::value<std::string>()->default_value("5665"), "Icinga 2 port");
}

std::vector<String> PKISaveCertCommand::GetArgumentSuggestions(const String& argument, const String& word) const
Expand All @@ -71,25 +72,18 @@ int PKISaveCertCommand::Run(const boost::program_options::variables_map& vm, con
return 1;
}

if (!vm.count("key")) {
Log(LogCritical, "cli", "Key input file path (--key) must be specified.");
return 1;
}

if (!vm.count("cert")) {
Log(LogCritical, "cli", "Certificate input file path (--cert) must be specified.");
return 1;
}

if (!vm.count("trustedcert")) {
Log(LogCritical, "cli", "Trusted certificate output file path (--trustedcert) must be specified.");
return 1;
}

String port = "5665";
boost::shared_ptr<X509> cert =
PkiUtility::FetchCert(vm["host"].as<std::string>(), vm["port"].as<std::string>());

if (vm.count("port"))
port = vm["port"].as<std::string>();
if (!cert) {
Log(LogCritical, "cli", "Failed to fetch certificate from host");
return 1;
}

return PkiUtility::SaveCert(vm["host"].as<std::string>(), port, vm["key"].as<std::string>(), vm["cert"].as<std::string>(), vm["trustedcert"].as<std::string>());
return PkiUtility::WriteCert(cert, vm["trustedcert"].as<std::string>());
}
89 changes: 59 additions & 30 deletions lib/cli/pkiutility.cpp
Expand Up @@ -22,6 +22,7 @@
#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"
Expand Down Expand Up @@ -110,30 +111,30 @@ int PkiUtility::SignCsr(const String& csrfile, const String& certfile)
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);
Expand All @@ -144,25 +145,23 @@ int PkiUtility::SaveCert(const String& host, const String& port, const String& k

}

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;
Expand All @@ -176,7 +175,7 @@ int PkiUtility::GenTicket(const String& cn, const String& salt, std::ostream& ti
}

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();

Expand Down Expand Up @@ -213,17 +212,7 @@ int PkiUtility::RequestCertificate(const String& host, const String& port, const

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;
}
Expand Down Expand Up @@ -312,3 +301,43 @@ int PkiUtility::RequestCertificate(const String& host, const String& port, const

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();
}
9 changes: 6 additions & 3 deletions lib/cli/pkiutility.hpp
Expand Up @@ -24,6 +24,7 @@
#include "cli/i2-cli.hpp"
#include "base/dictionary.hpp"
#include "base/string.hpp"
#include <openssl/x509v3.h>

namespace icinga
{
Expand All @@ -40,15 +41,17 @@ class I2_CLI_API PkiUtility
static int NewCa(void);
static int NewCert(const String& cn, const String& keyfile, const String& csrfile, const String& certfile);
static int SignCsr(const String& csrfile, const String& certfile);
static int SaveCert(const String& host, const String& port, const String& keyfile, const String& certfile, const String& trustedfile);
static boost::shared_ptr<X509> FetchCert(const String& host, const String& port);
static int WriteCert(const boost::shared_ptr<X509>& cert, const String& trustedfile);
static int GenTicket(const String& cn, const String& salt, std::ostream& ticketfp);
static int 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);
static String GetCertificateInformation(const boost::shared_ptr<X509>& certificate);

private:
PkiUtility(void);


};

}
Expand Down

0 comments on commit 7c522da

Please sign in to comment.