mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-26 15:14:07 +02:00
parent
cdff792c11
commit
0ec07bce51
@ -727,4 +727,24 @@ String RandomString(int length)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VerifyCertificate(const boost::shared_ptr<X509>& caCertificate, const boost::shared_ptr<X509>& certificate)
|
||||||
|
{
|
||||||
|
X509_STORE *store = X509_STORE_new();
|
||||||
|
|
||||||
|
if (!store)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
X509_STORE_add_cert(store, caCertificate.get());
|
||||||
|
|
||||||
|
X509_STORE_CTX *csc = X509_STORE_CTX_new();
|
||||||
|
X509_STORE_CTX_init(csc, store, certificate.get(), NULL);
|
||||||
|
|
||||||
|
int rc = X509_verify_cert(csc);
|
||||||
|
|
||||||
|
X509_STORE_CTX_free(csc);
|
||||||
|
X509_STORE_free(store);
|
||||||
|
|
||||||
|
return rc == 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ String I2_BASE_API PBKDF2_SHA1(const String& password, const String& salt, int i
|
|||||||
String I2_BASE_API SHA1(const String& s, bool binary = false);
|
String I2_BASE_API SHA1(const String& s, bool binary = false);
|
||||||
String I2_BASE_API SHA256(const String& s);
|
String I2_BASE_API SHA256(const String& s);
|
||||||
String I2_BASE_API RandomString(int length);
|
String I2_BASE_API RandomString(int length);
|
||||||
|
bool I2_BASE_API VerifyCertificate(const boost::shared_ptr<X509>& caCertificate, const boost::shared_ptr<X509>& certificate);
|
||||||
|
|
||||||
class I2_BASE_API openssl_error : virtual public std::exception, virtual public boost::exception { };
|
class I2_BASE_API openssl_error : virtual public std::exception, virtual public boost::exception { };
|
||||||
|
|
||||||
|
@ -395,7 +395,6 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri
|
|||||||
Log(LogWarning, "ApiListener")
|
Log(LogWarning, "ApiListener")
|
||||||
<< "Certificate validation failed for endpoint '" << hostname
|
<< "Certificate validation failed for endpoint '" << hostname
|
||||||
<< "': " << tlsStream->GetVerifyError();
|
<< "': " << tlsStream->GetVerifyError();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +483,7 @@ void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoi
|
|||||||
Log(LogInformation, "ApiListener")
|
Log(LogInformation, "ApiListener")
|
||||||
<< "Requesting new certificate for this Icinga instance from endpoint '" << endpoint->GetName() << "'.";
|
<< "Requesting new certificate for this Icinga instance from endpoint '" << endpoint->GetName() << "'.";
|
||||||
|
|
||||||
SendCertificateRequest(aclient);
|
aclient->SendCertificateRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure that the config updates are synced
|
/* Make sure that the config updates are synced
|
||||||
@ -539,19 +538,6 @@ void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoi
|
|||||||
<< "Finished syncing endpoint '" << endpoint->GetName() << "' in zone '" << eZone->GetName() << "'.";
|
<< "Finished syncing endpoint '" << endpoint->GetName() << "' in zone '" << eZone->GetName() << "'.";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiListener::SendCertificateRequest(const JsonRpcConnection::Ptr& aclient)
|
|
||||||
{
|
|
||||||
Dictionary::Ptr message = new Dictionary();
|
|
||||||
message->Set("jsonrpc", "2.0");
|
|
||||||
message->Set("method", "pki::RequestCertificate");
|
|
||||||
|
|
||||||
Dictionary::Ptr params = new Dictionary();
|
|
||||||
|
|
||||||
message->Set("params", params);
|
|
||||||
|
|
||||||
JsonRpc::SendMessage(aclient->GetStream(), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApiListener::ApiTimerHandler(void)
|
void ApiListener::ApiTimerHandler(void)
|
||||||
{
|
{
|
||||||
double now = Utility::GetTime();
|
double now = Utility::GetTime();
|
||||||
|
@ -158,7 +158,6 @@ private:
|
|||||||
|
|
||||||
static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file);
|
static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file);
|
||||||
void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);
|
void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);
|
||||||
void SendCertificateRequest(const JsonRpcConnection::Ptr& aclient);
|
|
||||||
|
|
||||||
/* configsync */
|
/* configsync */
|
||||||
void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
|
void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "remote/jsonrpcconnection.hpp"
|
#include "remote/jsonrpcconnection.hpp"
|
||||||
#include "remote/apilistener.hpp"
|
#include "remote/apilistener.hpp"
|
||||||
#include "remote/apifunction.hpp"
|
#include "remote/apifunction.hpp"
|
||||||
|
#include "remote/jsonrpc.hpp"
|
||||||
#include "base/configtype.hpp"
|
#include "base/configtype.hpp"
|
||||||
#include "base/objectlock.hpp"
|
#include "base/objectlock.hpp"
|
||||||
#include "base/utility.hpp"
|
#include "base/utility.hpp"
|
||||||
@ -27,6 +28,7 @@
|
|||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
#include "base/convert.hpp"
|
#include "base/convert.hpp"
|
||||||
#include <boost/thread/once.hpp>
|
#include <boost/thread/once.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
@ -67,8 +69,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
|||||||
|
|
||||||
ApiListener::Ptr listener = ApiListener::GetInstance();
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
|
|
||||||
String cacertfile = listener->GetCaPath();
|
boost::shared_ptr<X509> cacert = GetX509Certificate(listener->GetCaPath());
|
||||||
boost::shared_ptr<X509> cacert = GetX509Certificate(cacertfile);
|
|
||||||
result->Set("ca", CertificateToString(cacert));
|
result->Set("ca", CertificateToString(cacert));
|
||||||
|
|
||||||
if (Utility::PathExists(requestPath)) {
|
if (Utility::PathExists(requestPath)) {
|
||||||
@ -112,6 +113,18 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
|||||||
subject = X509_get_subject_name(cert.get());
|
subject = X509_get_subject_name(cert.get());
|
||||||
|
|
||||||
newcert = CreateCertIcingaCA(pubkey, subject);
|
newcert = CreateCertIcingaCA(pubkey, subject);
|
||||||
|
|
||||||
|
/* verify that the new cert matches the CA we're using for the ApiListener;
|
||||||
|
* this ensures that the CA we have in /var/lib/icinga2/ca matches the one
|
||||||
|
* we're using for cluster connections (there's no point in sending a client
|
||||||
|
* a certificate it wouldn't be able to use to connect to us anyway) */
|
||||||
|
if (!VerifyCertificate(cacert, newcert)) {
|
||||||
|
Log(LogWarning, "JsonRpcConnection")
|
||||||
|
<< "The CA in '" << listener->GetCaPath() << "' does not match the CA which Icinga uses "
|
||||||
|
<< "for its own cluster connections. This is most likely a configuration problem.";
|
||||||
|
goto delayed_request;
|
||||||
|
}
|
||||||
|
|
||||||
result->Set("cert", CertificateToString(newcert));
|
result->Set("cert", CertificateToString(newcert));
|
||||||
|
|
||||||
result->Set("status_code", 0);
|
result->Set("status_code", 0);
|
||||||
@ -132,3 +145,82 @@ delayed_request:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonRpcConnection::SendCertificateRequest(void)
|
||||||
|
{
|
||||||
|
Dictionary::Ptr message = new Dictionary();
|
||||||
|
message->Set("jsonrpc", "2.0");
|
||||||
|
message->Set("method", "pki::RequestCertificate");
|
||||||
|
|
||||||
|
String id = Utility::NewUniqueID();
|
||||||
|
message->Set("id", id);
|
||||||
|
|
||||||
|
Dictionary::Ptr params = new Dictionary();
|
||||||
|
|
||||||
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
|
|
||||||
|
if (listener)
|
||||||
|
params->Set("ticket", listener->GetClientTicket());
|
||||||
|
|
||||||
|
message->Set("params", params);
|
||||||
|
|
||||||
|
RegisterCallback(id, boost::bind(&JsonRpcConnection::CertificateRequestResponseHandler, this, _1));
|
||||||
|
|
||||||
|
JsonRpc::SendMessage(GetStream(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonRpcConnection::CertificateRequestResponseHandler(const Dictionary::Ptr& message)
|
||||||
|
{
|
||||||
|
Log(LogWarning, "JsonRpcConnection")
|
||||||
|
<< message->ToString();
|
||||||
|
|
||||||
|
Dictionary::Ptr result = message->Get("result");
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String ca = result->Get("ca");
|
||||||
|
String cert = result->Get("cert");
|
||||||
|
int status = result->Get("status_code");
|
||||||
|
|
||||||
|
/* TODO: make sure the cert's public key matches ours */
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
/* TODO: log error */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiListener::Ptr listener = ApiListener::GetInstance();
|
||||||
|
|
||||||
|
if (!listener)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String caPath = listener->GetCaPath();
|
||||||
|
|
||||||
|
std::fstream cafp;
|
||||||
|
String tempCaPath = Utility::CreateTempFile(caPath + ".XXXXXX", 0644, cafp);
|
||||||
|
cafp << ca;
|
||||||
|
cafp.close();
|
||||||
|
|
||||||
|
if (rename(tempCaPath.CStr(), caPath.CStr()) < 0) {
|
||||||
|
BOOST_THROW_EXCEPTION(posix_error()
|
||||||
|
<< boost::errinfo_api_function("rename")
|
||||||
|
<< boost::errinfo_errno(errno)
|
||||||
|
<< boost::errinfo_file_name(tempCaPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
String certPath = listener->GetCertPath();
|
||||||
|
|
||||||
|
std::fstream certfp;
|
||||||
|
String tempCertPath = Utility::CreateTempFile(certPath + ".XXXXXX", 0644, certfp);
|
||||||
|
certfp << cert;
|
||||||
|
certfp.close();
|
||||||
|
|
||||||
|
if (rename(tempCertPath.CStr(), certPath.CStr()) < 0) {
|
||||||
|
BOOST_THROW_EXCEPTION(posix_error()
|
||||||
|
<< boost::errinfo_api_function("rename")
|
||||||
|
<< boost::errinfo_errno(errno)
|
||||||
|
<< boost::errinfo_file_name(tempCertPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update ApiListener's SSL_CTX */
|
||||||
|
}
|
||||||
|
@ -184,7 +184,30 @@ void JsonRpcConnection::MessageHandler(const String& jsonString)
|
|||||||
origin->FromZone = Zone::GetByName(message->Get("originZone"));
|
origin->FromZone = Zone::GetByName(message->Get("originZone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String method = message->Get("method");
|
Value vmethod;
|
||||||
|
|
||||||
|
if (!message->Get("method", &vmethod)) {
|
||||||
|
Value vid;
|
||||||
|
|
||||||
|
if (!message->Get("id", &vid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
String id = vid;
|
||||||
|
|
||||||
|
auto it = m_ApiCallbacks.find(id);
|
||||||
|
|
||||||
|
if (it == m_ApiCallbacks.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ApiCallbackInfo aci = it->second;
|
||||||
|
m_ApiCallbacks.erase(it);
|
||||||
|
|
||||||
|
aci.Callback(message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String method = vmethod;
|
||||||
|
|
||||||
Log(LogNotice, "JsonRpcConnection")
|
Log(LogNotice, "JsonRpcConnection")
|
||||||
<< "Received '" << method << "' message from '" << m_Identity << "'";
|
<< "Received '" << method << "' message from '" << m_Identity << "'";
|
||||||
@ -330,3 +353,11 @@ double JsonRpcConnection::GetWorkQueueRate(void)
|
|||||||
return rate / count;
|
return rate / count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonRpcConnection::RegisterCallback(const String& id, const boost::function<void (const Dictionary::Ptr&)>& callback)
|
||||||
|
{
|
||||||
|
ApiCallbackInfo aci;
|
||||||
|
aci.Timestamp = Utility::GetTime();
|
||||||
|
aci.Callback = callback;
|
||||||
|
|
||||||
|
m_ApiCallbacks[id] = aci;
|
||||||
|
}
|
||||||
|
@ -43,6 +43,12 @@ enum ClientType
|
|||||||
|
|
||||||
class MessageOrigin;
|
class MessageOrigin;
|
||||||
|
|
||||||
|
struct ApiCallbackInfo
|
||||||
|
{
|
||||||
|
double Timestamp;
|
||||||
|
boost::function<void (const Dictionary::Ptr&)> Callback;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An API client connection.
|
* An API client connection.
|
||||||
*
|
*
|
||||||
@ -75,6 +81,8 @@ public:
|
|||||||
static int GetWorkQueueLength(void);
|
static int GetWorkQueueLength(void);
|
||||||
static double GetWorkQueueRate(void);
|
static double GetWorkQueueRate(void);
|
||||||
|
|
||||||
|
void SendCertificateRequest(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_ID;
|
int m_ID;
|
||||||
String m_Identity;
|
String m_Identity;
|
||||||
@ -87,6 +95,7 @@ private:
|
|||||||
double m_NextHeartbeat;
|
double m_NextHeartbeat;
|
||||||
double m_HeartbeatTimeout;
|
double m_HeartbeatTimeout;
|
||||||
boost::mutex m_DataHandlerMutex;
|
boost::mutex m_DataHandlerMutex;
|
||||||
|
std::map<String, ApiCallbackInfo> m_ApiCallbacks;
|
||||||
|
|
||||||
StreamReadContext m_Context;
|
StreamReadContext m_Context;
|
||||||
|
|
||||||
@ -98,6 +107,10 @@ private:
|
|||||||
static void StaticInitialize(void);
|
static void StaticInitialize(void);
|
||||||
static void TimeoutTimerHandler(void);
|
static void TimeoutTimerHandler(void);
|
||||||
void CheckLiveness(void);
|
void CheckLiveness(void);
|
||||||
|
|
||||||
|
void RegisterCallback(const String& id, const boost::function<void (const Dictionary::Ptr&)>& callback);
|
||||||
|
|
||||||
|
void CertificateRequestResponseHandler(const Dictionary::Ptr& message);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user