added method to intercept tls handshakes, and to verify certificates
This commit is contained in:
parent
4c2b8ff68e
commit
367de51d77
103
README.md
103
README.md
|
|
@ -64,9 +64,14 @@ Then check out our other commercial and open source solutions:
|
|||
|
||||
INSTALLING
|
||||
==========
|
||||
AMQP-CPP comes with an optional Linux-only TCP module that takes care of the network part required for the AMQP-CPP core library. If you use this module, you are required to link with `pthread`.
|
||||
|
||||
There are two methods to compile AMQP-CPP: CMake and Make. CMake is platform portable, but the Makefile only works on Linux. Building of a shared library is currently not supported on Windows.
|
||||
AMQP-CPP comes with an optional Linux-only TCP module that takes care of the
|
||||
network part required for the AMQP-CPP core library. If you use this module, you
|
||||
are required to link with `pthread` and `dl`.
|
||||
|
||||
There are two methods to compile AMQP-CPP: CMake and Make. CMake is platform portable
|
||||
and works on all systems, while the Makefile only works on Linux. Building of a shared
|
||||
library is currently not supported on Windows.
|
||||
|
||||
After building there are two relevant files to include when using the library.
|
||||
|
||||
|
|
@ -77,9 +82,14 @@ After building there are two relevant files to include when using the library.
|
|||
|
||||
On Windows you are required to define `NOMINMAX` when compiling code that includes public AMQP-CPP header files.
|
||||
|
||||
## CMake
|
||||
The CMake file supports both building and installing. You can choose not to use the install functionality, and instead manually use the build output at `bin/`. Keep in mind that the TCP module is only supported for Linux. An example install method would be:
|
||||
``` bash
|
||||
## Using cmake
|
||||
|
||||
The CMake file supports both building and installing. You can choose not to use
|
||||
the install functionality, and instead manually use the build output at `bin/`. Keep
|
||||
in mind that the TCP module is only supported for Linux. An example install method
|
||||
would be:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. [-DAMQP-CPP_AMQBUILD_SHARED] [-DAMQP-CPP_LINUX_TCP]
|
||||
|
|
@ -91,22 +101,21 @@ cmake --build .. --target install
|
|||
AMQP-CPP_BUILD_SHARED | OFF | Static lib(ON) or shared lib(OFF)? Shared is not supported on Windows.
|
||||
AMQP-CPP_LINUX_TCP | OFF | Should the Linux-only TCP module be built?
|
||||
|
||||
## Make
|
||||
## Using make
|
||||
|
||||
Installing the library is as easy
|
||||
as running `make` and `make install`. This will install the full version of
|
||||
the AMQP-CPP, including the system specific TCP module. If you do not need the
|
||||
additional TCP module (because you take care of handling the network stuff
|
||||
yourself), you can also compile a pure form of the library. Use `make pure`
|
||||
and `make install` for that.
|
||||
Compiling and installing AMQP-CPP with make is as easy as running `make` and
|
||||
then `make install`. This will install the full version of AMQP-CPP, including
|
||||
the system specific TCP module. If you do not need the additional TCP module
|
||||
(because you take care of handling the network stuff yourself), you can also
|
||||
compile a pure form of the library. Use `make pure` and `make install` for that.
|
||||
|
||||
When you compile an application that uses the AMQP-CPP library, do not
|
||||
forget to link with the library. For gcc and clang the linker flag is -lamqpcpp.
|
||||
If you use the fullblown version of AMQP-CPP (with the TCP module), you also
|
||||
need to pass the -lpthread and -ldl linker flags, because the TCP module uses a
|
||||
thread for running an asynchronous and non-blocking DNS hostname lookup, and it
|
||||
optionally dynamically opens the openssl library if a secure connection to
|
||||
RabbitMQ has to be set up.
|
||||
may dynamically look up functions from the openssl library if a secure connection
|
||||
to RabbitMQ has to be set up.
|
||||
|
||||
|
||||
HOW TO USE AMQP-CPP
|
||||
|
|
@ -408,7 +417,7 @@ server using the amqps:// protocol:
|
|||
// init the SSL library (this works for openssl 1.1, for openssl 1.0 use SSL_library_init())
|
||||
OPENSSL_init_ssl(0, NULL);
|
||||
|
||||
// address of the server
|
||||
// address of the server (secure!)
|
||||
AMQP::Address address("amqps://guest:guest@localhost/vhost");
|
||||
|
||||
// create a AMQP connection object
|
||||
|
|
@ -416,11 +425,65 @@ AMQP::TcpConnection connection(&myHandler, address);
|
|||
````
|
||||
|
||||
There are two things to take care of if you want to create a secure connection:
|
||||
(1) you must link your application with the -lssl flag, and (2) you must initialize
|
||||
the openssl library by calling OPENSSL_init_ssl(). This initializating must take
|
||||
place before you let you application connect to RabbitMQ. This is necessary
|
||||
because AMQP-CPP needs access to the openssl library, which it needs for setting up
|
||||
secure connections.
|
||||
(1) you must link your application with the -lssl flag (or use dlopen()), and (2)
|
||||
you must initialize the openssl library by calling OPENSSL_init_ssl(). This
|
||||
initializating must take place before you let you application connect to RabbitMQ.
|
||||
This is necessary because AMQP-CPP needs access to the openssl library to set up
|
||||
secure connections. It can only access this library if you have linked your
|
||||
application with this library, or if you have loaded this library at runtime
|
||||
using dlopen()).
|
||||
|
||||
If you do not want to link your application with openssl, you can also load the
|
||||
openssl library at runtime, and pass in the pointer to the handler to AMQP-CPP:
|
||||
|
||||
````c++
|
||||
// dynamically open the openssl library
|
||||
void *handle = dlopen("/path/to/openssl.so", RTLD_LAZY);
|
||||
|
||||
// tell AMQP-CPP library where the handle to openssl can be found
|
||||
AMQP::openssl(handle);
|
||||
|
||||
// @todo call functions to initialize openssl, and create the AMQP connection
|
||||
// (see exampe above)
|
||||
````
|
||||
|
||||
By itself, AMQP-CPP does not check if the created TLS connection is sufficient
|
||||
secure. Whether the certificate is expired, self-signed, missing or invalid: for
|
||||
AMQP-CPP it all doesn't matter and the connection is simply permitted. If you
|
||||
want to be more strict (for example: if you want to verify the server's certificate),
|
||||
you must do this yourself by implementing the "onSecured()" method in your handler
|
||||
object:
|
||||
|
||||
````c++
|
||||
#include <amqpcpp.h>
|
||||
|
||||
class MyTcpHandler : public AMQP::TcpHandler
|
||||
{
|
||||
/**
|
||||
* Method that is called right after the TLS connection has been created.
|
||||
* In this method you can check the connection properties (like the certificate)
|
||||
* and return false if you find it not secure enough
|
||||
* @param connection the connection that has just completed the tls handshake
|
||||
* @param ssl SSL structure from the openssl library
|
||||
* @return bool true if connection is secure enough to start the AMQP protocol
|
||||
*/
|
||||
virtual bool onSecure(AMQP::TcpConnection *connection, const SSL *ssl) override
|
||||
{
|
||||
// @todo call functions from the openssl library to check the certificate,
|
||||
// like SSL_get_peer_certificate() or SSL_get_verify_result().
|
||||
// For now we always allow the connection to proceed
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* All other methods (like onConnected(), onError(), etc) are left out of this
|
||||
* example, but would be here if this was an actual user space handler class.
|
||||
*/
|
||||
};
|
||||
````
|
||||
|
||||
The SSL pointer that is passed to the onSecured() method refers to the "SSL"
|
||||
structure from the openssl library.
|
||||
|
||||
|
||||
EXISTING EVENT LOOPS
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* class.
|
||||
*
|
||||
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
||||
* @copyright 2015 Copernica BV
|
||||
* @copyright 2015 - 2018 Copernica BV
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -14,6 +14,11 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
/**
|
||||
* Set up namespace
|
||||
*/
|
||||
|
|
@ -35,9 +40,33 @@ public:
|
|||
*/
|
||||
virtual ~TcpHandler() = default;
|
||||
|
||||
/**
|
||||
* Method that is called after a TCP connection has been set up and the initial
|
||||
* TLS handshake is finished too, but right before the AMQP login handshake is
|
||||
* going to take place and the first data is going to be sent over the connection.
|
||||
* This method allows you to inspect the TLS certificate and other connection
|
||||
* properties, and to break up the connection if you find it not secure enough.
|
||||
* The default implementation considers all connections to be secure, even if the
|
||||
* connection has a self-signed or even invalid certificate. To be more strict,
|
||||
* override this method, inspect the certificate and return false if you do not
|
||||
* want to use the connection. The passed in SSL pointer is a pointer to a SSL
|
||||
* structure from the openssl library. This method is only called for secure
|
||||
* connections (connection with an amqps:// address).
|
||||
* @param connection The connection for which TLS was just started
|
||||
* @param ssl Pointer to the SSL structure that can be inspected
|
||||
* @return bool True to proceed / accept the connection, false to break up
|
||||
*/
|
||||
virtual bool onSecured(TcpConnection *connection, const SSL *ssl)
|
||||
{
|
||||
// default implementation: do not inspect anything, just allow the connection
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the heartbeat frequency is negotiated
|
||||
* between the server and the client.
|
||||
* between the server and the client. Applications can override this method
|
||||
* if they want to use a different heartbeat interval (for example: return 0
|
||||
* to disable heartbeats)
|
||||
* @param connection The connection that suggested a heartbeat interval
|
||||
* @param interval The suggested interval from the server
|
||||
* @return uint16_t The interval to use
|
||||
|
|
@ -51,7 +80,9 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Method that is called when the TCP connection ends up in a connected state
|
||||
* Method that is called when the AMQP connection ends up in a connected state
|
||||
* This method is called after the TCP connection has been set up, the (optional)
|
||||
* secure TLS connection, and the AMQP login handshake has been completed.
|
||||
* @param connection The TCP connection
|
||||
*/
|
||||
virtual void onConnected(TcpConnection *connection) {}
|
||||
|
|
|
|||
|
|
@ -53,16 +53,34 @@ private:
|
|||
|
||||
/**
|
||||
* Report a new state
|
||||
* @param state
|
||||
* @param monitor
|
||||
* @return TcpState
|
||||
*/
|
||||
TcpState *nextstate(TcpState *state)
|
||||
TcpState *nextstate(const Monitor &monitor)
|
||||
{
|
||||
// forget the socket to prevent that it is closed by the destructor
|
||||
// check if the handler allows the connection
|
||||
bool allowed = _handler->onSecured(_connection, _ssl);
|
||||
|
||||
// leap out if the user space function destructed the object
|
||||
if (!monitor.valid()) return nullptr;
|
||||
|
||||
// copy the socket because we might forget it
|
||||
auto socket = _socket;
|
||||
|
||||
// forget the socket member to prevent that it is closed by the destructor
|
||||
_socket = -1;
|
||||
|
||||
// done
|
||||
return state;
|
||||
// if connection is allowed, we move to the next state
|
||||
if (allowed) return new SslConnected(_connection, socket, std::move(_ssl), std::move(_out), _handler);
|
||||
|
||||
// report that the connection is broken
|
||||
_handler->onError(_connection, "TLS connection has been blocked by application level checks");
|
||||
|
||||
// the onError method could have destructed this object
|
||||
if (!monitor.valid()) return nullptr;
|
||||
|
||||
// shutdown the connection
|
||||
return new SslShutdown(_connection, socket, std::move(_ssl), true, _handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -170,7 +188,7 @@ public:
|
|||
int result = OpenSSL::SSL_do_handshake(_ssl);
|
||||
|
||||
// if the connection succeeds, we can move to the ssl-connected state
|
||||
if (result == 1) return nextstate(new SslConnected(_connection, _socket, std::move(_ssl), std::move(_out), _handler));
|
||||
if (result == 1) return nextstate(monitor);
|
||||
|
||||
// error was returned, so we must investigate what is going on
|
||||
auto error = OpenSSL::SSL_get_error(_ssl, result);
|
||||
|
|
@ -211,7 +229,7 @@ public:
|
|||
int result = OpenSSL::SSL_do_handshake(_ssl);
|
||||
|
||||
// if the connection succeeds, we can move to the ssl-connected state
|
||||
if (result == 1) return nextstate(new SslConnected(_connection, _socket, std::move(_ssl), std::move(_out), _handler));
|
||||
if (result == 1) return nextstate(monitor);
|
||||
|
||||
// error was returned, so we must investigate what is going on
|
||||
auto error = OpenSSL::SSL_get_error(_ssl, result);
|
||||
|
|
|
|||
Loading…
Reference in New Issue