Skip to content

tls http proxy #232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@
#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/xio.h"
#include "azure_c_shared_utility/socketio.h"
#include "azure_c_shared_utility/tlsio.h"
#include "azure_c_shared_utility/platform.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/http_proxy_io.h"
#include "azure_c_shared_utility/azure_base64.h"
#include "azure_c_shared_utility/shared_util_options.h"

static const char* const OPTION_UNDERLYING_IO_OPTIONS = "underlying_io_options";
static const char* const OPTION_USE_TLS_HTTP_PROXY = "use_tls_http_proxy";
static const char* const OPTION_TLS_HTTP_PROXY_TRUSTED_CERT = "tls_http_proxy_TrustedCerts";
static const char* const OPTION_TLS_HTTP_PROXY_X509_CERT = "tls_http_proxy_x509certificate";
static const char* const OPTION_TLS_HTTP_PROXY_X509_PRIVATE_KEY = "tls_http_proxy_x509privatekey";

typedef enum HTTP_PROXY_IO_STATE_TAG
{
Expand Down Expand Up @@ -45,6 +52,7 @@ typedef struct HTTP_PROXY_IO_INSTANCE_TAG
XIO_HANDLE underlying_io;
unsigned char* receive_buffer;
size_t receive_buffer_size;
bool use_tls_http_proxy;
} HTTP_PROXY_IO_INSTANCE;

static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters)
Expand Down Expand Up @@ -188,6 +196,7 @@ static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters)
result->receive_buffer = NULL;
result->receive_buffer_size = 0;
result->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED;
result->use_tls_http_proxy = false;
}
}
}
Expand Down Expand Up @@ -918,6 +927,110 @@ static int http_proxy_io_set_option(CONCRETE_IO_HANDLE http_proxy_io, const char
result = 0;
}
}
else if (strcmp(option_name, OPTION_USE_TLS_HTTP_PROXY) == 0)
{
bool use_tls_http_proxy = *((bool*)value);
if(http_proxy_io_instance->use_tls_http_proxy)
{
LogError("use tls http proxy has already been specified");
result = MU_FAILURE;
}
else
{
if(use_tls_http_proxy)
{
// close and desotry the original socket io
(void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL);
xio_destroy(http_proxy_io_instance->underlying_io);
http_proxy_io_instance->use_tls_http_proxy = true;
const IO_INTERFACE_DESCRIPTION* underlying_io_interface;
underlying_io_interface = platform_get_default_tlsio();
// create a new tls io
TLSIO_CONFIG tls_io_config;
XIO_HANDLE tls_io;
tls_io_config.hostname = http_proxy_io_instance->proxy_hostname;
tls_io_config.port = http_proxy_io_instance->proxy_port;
tls_io_config.underlying_io_interface = NULL;
tls_io_config.underlying_io_parameters = NULL;
tls_io = xio_create(underlying_io_interface, &tls_io_config);
if (tls_io == NULL)
{
LogError("xio_create failed");
result = MU_FAILURE;
}
else
{
http_proxy_io_instance->underlying_io = tls_io;
result = 0;
}
}
else
{
// setting false to false, do nothing
result = 0;
}
}
}
else if (strcmp(option_name, OPTION_TLS_HTTP_PROXY_TRUSTED_CERT) == 0)
{
if (!http_proxy_io_instance->use_tls_http_proxy)
{
LogError("Invalid option %s", option_name);
result = MU_FAILURE;
}
else
{
if (xio_setoption(http_proxy_io_instance->underlying_io, OPTION_TRUSTED_CERT, value) != 0)
{
LogError("Setting option %s failed", option_name);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
else if (strcmp(option_name, OPTION_TLS_HTTP_PROXY_X509_CERT) == 0)
{
if (!http_proxy_io_instance->use_tls_http_proxy)
{
LogError("Invalid option %s", option_name);
result = MU_FAILURE;
}
else
{
if (xio_setoption(http_proxy_io_instance->underlying_io, SU_OPTION_X509_CERT, value) != 0)
{
LogError("Setting option %s failed", option_name);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
else if (strcmp(option_name, OPTION_TLS_HTTP_PROXY_X509_PRIVATE_KEY) == 0)
{
if (!http_proxy_io_instance->use_tls_http_proxy)
{
LogError("Invalid option %s", option_name);
result = MU_FAILURE;
}
else
{
if (xio_setoption(http_proxy_io_instance->underlying_io, SU_OPTION_X509_PRIVATE_KEY, value) != 0)
{
LogError("Setting option %s failed", option_name);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
/* Codes_SRS_HTTP_PROXY_IO_01_043: [ If the option_name argument indicates an option that is not handled by http_proxy_io_set_option, then xio_setoption shall be called on the underlying IO created in http_proxy_io_create, passing the option name and value to it. ]*/
/* Codes_SRS_HTTP_PROXY_IO_01_056: [ The value argument shall be allowed to be NULL. ]*/
else if (xio_setoption(http_proxy_io_instance->underlying_io, option_name, value) != 0)
Expand Down Expand Up @@ -949,7 +1062,7 @@ static void* http_proxy_io_clone_option(const char* name, const void* value)
}
else
{
if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0)
if (strcmp(name, OPTION_UNDERLYING_IO_OPTIONS) == 0 || (strcmp(name, OPTION_USE_TLS_HTTP_PROXY) == 0))
{
result = (void*)value;
}
Expand Down
12 changes: 12 additions & 0 deletions src/xio.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ cdef class XIO(StructBase):
if c_xio.xio_setoption(self._c_value, option_name, option_value) != 0:
raise self._value_error("Failed to set option {}".format(option_name))

cpdef set_bool_value_option(self, bytes name, bint value):
cdef char *option_name = name
cdef bint option_value = value
if c_xio.xio_setoption(self._c_value, option_name, <void*>(&option_value)) != 0:
raise self._value_error("Failed to set option {}".format(name))

cpdef set_bytes_value_option(self, bytes name, bytes value):
cdef char *option_name = name
cdef char *option_value = value
if c_xio.xio_setoption(self._c_value, option_name, <void*>option_value) != 0:
raise self._value_error("Failed to set option {}".format(name))

cpdef set_certificates(self, bytes value):
cdef char *certificate = value
if c_xio.xio_setoption(self._c_value, b'TrustedCerts', <void*>certificate) != 0:
Expand Down
2 changes: 1 addition & 1 deletion uamqp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
pass # Async not supported.


__version__ = "1.3.0"
__version__ = "1.4.0b1"


_logger = logging.getLogger(__name__)
Expand Down
64 changes: 64 additions & 0 deletions uamqp/authentication/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,46 @@ def _build_proxy_config(self, hostname, port, proxy_settings):
def _encode(self, value):
return value.encode(self._encoding) if isinstance(value, six.text_type) else value

@staticmethod
def _configure_tls_http_proxy(
underlying_xio,
proxy_server_cert=None,
proxy_client_cert=None,
proxy_client_private_key=None
):
if any((proxy_client_cert, proxy_client_private_key)) and\
(not all((proxy_client_cert, proxy_client_private_key))):
raise ValueError("Client cert and key must both present.")

underlying_xio.set_bool_value_option(b"use_tls_http_proxy", True)

proxy_server_cert = proxy_server_cert or certifi.where()
with open(proxy_server_cert, 'rb') as proxy_server_cert_handle:
proxy_server_cert_data = proxy_server_cert_handle.read()
try:
underlying_xio.set_bytes_value_option(b"tls_http_proxy_TrustedCerts", proxy_server_cert_data)
except ValueError:
_logger.warning('Unable to set external proxy certificates.')

if proxy_client_cert:
with open(proxy_client_cert, 'rb') as proxy_client_cert_handle:
proxy_client_cert_data = proxy_client_cert_handle.read()
try:
underlying_xio.set_bytes_value_option(b"tls_http_proxy_x509certificate", proxy_client_cert_data)
except ValueError:
_logger.warning('Unable to set external proxy x509certificates.')

if proxy_client_private_key:
with open(proxy_client_private_key, 'rb') as proxy_client_private_key_handle:
proxy_client_private_key_data = proxy_client_private_key_handle.read()
try:
underlying_xio.set_bytes_value_option(
b"tls_http_proxy_x509privatekey",
proxy_client_private_key_data
)
except ValueError:
_logger.warning('Unable to set external x509privatekey.')

def set_io(self, hostname, port, http_proxy, transport_type):
if transport_type == TransportType.AmqpOverWebsocket or http_proxy is not None:
self.set_wsio(hostname, port or constants.DEFAULT_AMQP_WSS_PORT, http_proxy)
Expand All @@ -89,14 +129,38 @@ def set_wsio(self, hostname, port, http_proxy):
_tlsio_config.hostname = hostname
_tlsio_config.port = port

proxy_server_cert = None
proxy_client_cert = None
proxy_client_private_key = None
use_tls_http_proxy = False

if http_proxy:
proxy_config = self._build_proxy_config(hostname, port, http_proxy)
proxy_server_cert = http_proxy.get("proxy_verify")
proxy_cert = http_proxy.get("proxy_cert")
if proxy_cert is not None:
if not (isinstance(proxy_cert, tuple) and len(proxy_cert) == 2):
raise ValueError(
"proxy_cert must be a tuple containing both of certificate and private key file path",
", and certificate file path must be put in front of the private key file path. ",
"E.g. proxy_cert=(<cert_file_path>, <private_key_path>)"
)
proxy_client_cert, proxy_client_private_key = proxy_cert
use_tls_http_proxy = any((proxy_server_cert, proxy_client_cert, proxy_client_private_key))
_tlsio_config.set_proxy_config(proxy_config)

_wsio_config.set_tlsio_config(_default_tlsio, _tlsio_config)

_underlying_xio = c_uamqp.xio_from_wsioconfig(_wsio_config) # pylint: disable=attribute-defined-outside-init

if http_proxy and use_tls_http_proxy:
self._configure_tls_http_proxy(
_underlying_xio,
proxy_server_cert,
proxy_client_cert,
proxy_client_private_key
)

cert = self.cert_file or certifi.where()
with open(cert, 'rb') as cert_handle:
cert_data = cert_handle.read()
Expand Down