diff --git a/.github/docker-images/base-images/amazonlinux/Dockerfile b/.github/docker-images/base-images/amazonlinux/Dockerfile index 149c0bf..419391e 100644 --- a/.github/docker-images/base-images/amazonlinux/Dockerfile +++ b/.github/docker-images/base-images/amazonlinux/Dockerfile @@ -1,10 +1,10 @@ -FROM amazonlinux:latest as base +FROM amazonlinux:2023 as base ARG OPENSSL_CONFIG # Install Prerequisites RUN yum check-update; yum upgrade -y && \ - yum install -y git boost-devel autoconf automake \ + yum install -y git boost-devel autoconf automake libatomic \ wget libtool make gcc-c++ unzip cmake3 python-devel openssl-devel which # Install Dependencies @@ -12,7 +12,7 @@ RUN yum check-update; yum upgrade -y && \ RUN mkdir /home/dependencies WORKDIR /home/dependencies -RUN wget https://www.zlib.net/zlib-1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ +RUN wget https://github.com/madler/zlib/archive/v1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ tar xzvf /tmp/zlib-1.2.13.tar.gz && \ cd zlib-1.2.13 && \ ./configure && \ diff --git a/.github/docker-images/base-images/debian-ubuntu/Dockerfile b/.github/docker-images/base-images/debian-ubuntu/Dockerfile index 12b9357..84dbc7f 100644 --- a/.github/docker-images/base-images/debian-ubuntu/Dockerfile +++ b/.github/docker-images/base-images/debian-ubuntu/Dockerfile @@ -12,7 +12,7 @@ RUN apt update && apt upgrade -y && \ RUN mkdir /home/dependencies WORKDIR /home/dependencies -RUN wget https://www.zlib.net/zlib-1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ +RUN wget https://github.com/madler/zlib/archive/v1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ tar xzvf /tmp/zlib-1.2.13.tar.gz && \ cd zlib-1.2.13 && \ ./configure && \ diff --git a/.github/docker-images/base-images/fedora/Dockerfile b/.github/docker-images/base-images/fedora/Dockerfile index ec81ab4..ed6c64a 100644 --- a/.github/docker-images/base-images/fedora/Dockerfile +++ b/.github/docker-images/base-images/fedora/Dockerfile @@ -10,7 +10,7 @@ RUN dnf -y update \ RUN mkdir /home/dependencies WORKDIR /home/dependencies -RUN wget https://www.zlib.net/zlib-1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ +RUN wget https://github.com/madler/zlib/archive/v1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ tar xzvf /tmp/zlib-1.2.13.tar.gz && \ cd zlib-1.2.13 && \ ./configure && \ diff --git a/.github/docker-images/base-images/ubi8/Dockerfile b/.github/docker-images/base-images/ubi8/Dockerfile index 88c0ed2..d4e3f4d 100644 --- a/.github/docker-images/base-images/ubi8/Dockerfile +++ b/.github/docker-images/base-images/ubi8/Dockerfile @@ -9,7 +9,7 @@ RUN yum -y update \ RUN mkdir /home/dependencies WORKDIR /home/dependencies -RUN wget https://www.zlib.net/zlib-1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ +RUN wget https://github.com/madler/zlib/archive/v1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ tar xzvf /tmp/zlib-1.2.13.tar.gz && \ cd zlib-1.2.13 && \ ./configure && \ diff --git a/.github/workflows/base-images.yml b/.github/workflows/base-images.yml index d1da7ee..36bd02a 100644 --- a/.github/workflows/base-images.yml +++ b/.github/workflows/base-images.yml @@ -7,9 +7,9 @@ name: Base Image Builds on: push: - branches: ['base-images', 'docker-builds'] + branches: ['main', 'base-images', 'docker-builds'] pull_request: - branches: ['base-images', 'docker-builds'] + branches: ['main', 'base-images', 'docker-builds'] types: [opened, closed] env: diff --git a/Dockerfile b/Dockerfile index d639404..28bc37a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN yum check-update; yum upgrade -y && \ RUN mkdir /home/dependencies WORKDIR /home/dependencies -RUN wget https://www.zlib.net/zlib-1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ +RUN wget https://github.com/madler/zlib/archive/v1.2.13.tar.gz -O /tmp/zlib-1.2.13.tar.gz && \ tar xzvf /tmp/zlib-1.2.13.tar.gz && \ cd zlib-1.2.13 && \ ./configure && \ diff --git a/README.md b/README.md index cb0ce06..7f0f696 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ and you can add ` -p ` to expose a port from the docker container. Note: This step may be simpler to complete via a native software application manager. Ubuntu example: -`sudo apt install zlibc` +`sudo apt install zlib1g` Fedora example: `dnf install zlib` diff --git a/src/LocalproxyConfig.h b/src/LocalproxyConfig.h index f553fbe..0f12ead 100644 --- a/src/LocalproxyConfig.h +++ b/src/LocalproxyConfig.h @@ -100,6 +100,7 @@ namespace aws { * The end point will store either source listening or destination service depends on the mode of local proxy. */ std::unordered_map serviceId_to_endpoint_map; + /** * A flag to judge if v2 local proxy needs to fallback to communicate using v1 local proxy message format. * v1 local proxy format fallback will be enabled when a tunnel is opened with no or 1 service id. diff --git a/src/TcpAdapterProxy.cpp b/src/TcpAdapterProxy.cpp index 435dc72..599f432 100644 --- a/src/TcpAdapterProxy.cpp +++ b/src/TcpAdapterProxy.cpp @@ -253,8 +253,12 @@ namespace aws { namespace iot { namespace securedtunneling { { if (tac.serviceId_to_tcp_server_map.find(service_id) == tac.serviceId_to_tcp_server_map.end()) { - BOOST_LOG_SEV(log, debug) << "No serviceId_to_tcp_server mapping for service_id: " << service_id; - return connection_ptr; + if (tac.serviceId_to_tcp_server_map.find(tac.adapter_config.serviceId_to_endpoint_map.cbegin()->first) == tac.serviceId_to_tcp_server_map.end()) + { + BOOST_LOG_SEV(log, debug) << "No serviceId_to_tcp_server mapping for service_id: " << service_id; + return connection_ptr; + } + service_id = tac.adapter_config.serviceId_to_endpoint_map.cbegin()->first;; } tcp_server::pointer server = tac.serviceId_to_tcp_server_map[service_id]; BOOST_LOG_SEV(log, trace) << "num active connections for service id " << service_id << ": " << server->connectionId_to_tcp_connection_map.size(); @@ -376,6 +380,7 @@ namespace aws { namespace iot { namespace securedtunneling { BOOST_LOG_SEV(this->log, trace) << "Post-reset TCP drain complete. Closing TCP socket for service id " << service_id << " connection id " << connection_id; BOOST_LOG_SEV(this->log, info) << "Disconnected from: " << connection_to_reset->socket().remote_endpoint(); connection_to_reset->socket_.close(); + delete_tcp_socket(tac, service_id, connection_id); *tcp_write_buffer_drain_complete = true; if (*web_socket_write_buffer_drain_complete) { @@ -515,8 +520,9 @@ namespace aws { namespace iot { namespace securedtunneling { tcp_connection::pointer socket_connection = get_tcp_connection(tac, service_id, connection_id); // if simultaneous connections are not enabled, then send a stream reset - if (tac.adapter_config.is_v2_message_format) + if (tac.adapter_config.is_v2_message_format || tac.adapter_config.is_v1_message_format) { + BOOST_LOG_SEV(log, info) << "simultaneous connections are not enabled, sending stream reset"; socket_connection->after_send_message = std::bind(&tcp_adapter_proxy::setup_tcp_socket, this, std::ref(tac), service_id); tac.serviceId_to_control_message_handler_map[service_id] = std::bind(&tcp_adapter_proxy::ignore_message_and_stop, this, std::ref(tac), std::placeholders::_1); async_send_stream_reset(tac, service_id, connection_id); @@ -600,8 +606,16 @@ namespace aws { namespace iot { namespace securedtunneling { BOOST_LOG_SEV(log, debug) << "Sending stream start, setting new stream ID to: " << new_stream_id << ", service id: " << service_id; + if (tac.adapter_config.is_v1_message_format) + { + outgoing_message.set_serviceid(""); + } + else + { + outgoing_message.set_serviceid(service_id); + } + outgoing_message.set_type(Message_Type_STREAM_START); - outgoing_message.set_serviceid(service_id); outgoing_message.set_streamid(new_stream_id); outgoing_message.set_connectionid(connection_id); outgoing_message.set_ignorable(false); @@ -638,8 +652,16 @@ namespace aws { namespace iot { namespace securedtunneling { } std::int32_t stream_id = tac.serviceId_to_streamId_map[service_id]; + if (tac.adapter_config.is_v1_message_format) + { + outgoing_message.set_serviceid(""); + } + else + { + outgoing_message.set_serviceid(service_id); + } + outgoing_message.set_type(Message_Type_CONNECTION_START); - outgoing_message.set_serviceid(service_id); outgoing_message.set_streamid(stream_id); outgoing_message.set_connectionid(connection_id); outgoing_message.set_ignorable(false); @@ -657,12 +679,20 @@ namespace aws { namespace iot { namespace securedtunneling { return; } + if (tac.adapter_config.is_v1_message_format) + { + outgoing_message.set_serviceid(""); + } + else + { + outgoing_message.set_serviceid(service_id); + } + // NOTE: serviceIds -> streamId mapping will be updated when send/receive stream start, no action needed now. std::int32_t stream_id = tac.serviceId_to_streamId_map[service_id]; outgoing_message.set_type(Message_Type_STREAM_RESET); - outgoing_message.set_serviceid(service_id); outgoing_message.set_streamid(stream_id); - outgoing_message.set_connectionid(0); + outgoing_message.set_connectionid(connection_id); outgoing_message.set_ignorable(false); outgoing_message.clear_payload(); async_send_message(tac, outgoing_message, service_id, connection_id); @@ -677,10 +707,19 @@ namespace aws { namespace iot { namespace securedtunneling { BOOST_LOG_SEV(log, warning) << "No stream id mapping found for service id " << service_id << " . Skip connection reset."; return; } + + if (tac.adapter_config.is_v1_message_format) + { + outgoing_message.set_serviceid(""); + } + else + { + outgoing_message.set_serviceid(service_id); + } + // NOTE: serviceIds -> streamId mapping will be updated when send/receive stream start, no action needed now. std::int32_t stream_id = tac.serviceId_to_streamId_map[service_id]; outgoing_message.set_type(Message_Type_CONNECTION_RESET); - outgoing_message.set_serviceid(service_id); outgoing_message.set_streamid(stream_id); outgoing_message.set_connectionid(connection_id); outgoing_message.set_ignorable(false); @@ -885,8 +924,8 @@ namespace aws { namespace iot { namespace securedtunneling { { BOOST_LOG_SEV(log, debug) << "SSL host verification is off"; } - //next ssl handshake - tac.wss->async_ssl_handshake(boost::asio::ssl::stream_base::client, [=, &tac](boost::system::error_code const &ec) + //next ssl handshake and providing host string + tac.wss->async_ssl_handshake(boost::asio::ssl::stream_base::client, tac.adapter_config.proxy_host.c_str(), [=, &tac](boost::system::error_code const &ec) { if (ec) { @@ -1073,7 +1112,7 @@ namespace aws { namespace iot { namespace securedtunneling { // backward compatibility: set connection id to 1 if first received a message with no connection id (id value will be 0) if (!connection_id) { - connection_id = 1; + BOOST_LOG_SEV(log, info) << "reverting to v2 message format"; tac.adapter_config.is_v2_message_format = true; } string service_id = message.serviceid(); @@ -1305,6 +1344,7 @@ namespace aws { namespace iot { namespace securedtunneling { // Remove empty string map and put new mapping tac.adapter_config.serviceId_to_endpoint_map.erase(""); tac.adapter_config.serviceId_to_endpoint_map[service_id] = endpoint; + BOOST_LOG_SEV(log, info) << "Updated port mapping for v1 format: "; for (auto m : tac.adapter_config.serviceId_to_endpoint_map) { @@ -1328,7 +1368,7 @@ namespace aws { namespace iot { namespace securedtunneling { // backward compatibility: set connection id to 1 if first received a message with no connection id (id value will be 0) if (!connection_id) { - connection_id = 1; + BOOST_LOG_SEV(log, info) << "reverting to v2 message format"; tac.adapter_config.is_v2_message_format = true; } string service_id = message.serviceid(); @@ -1431,7 +1471,7 @@ namespace aws { namespace iot { namespace securedtunneling { // backward compatibility: set connection id to 1 if first received a message with no connection id (id value will be 0) if (!connection_id) { - connection_id = 1; + BOOST_LOG_SEV(log, info) << "reverting to v2 message format"; tac.adapter_config.is_v2_message_format = true; } /** @@ -1562,7 +1602,7 @@ namespace aws { namespace iot { namespace securedtunneling { // backward compatibility: set connection id to 1 if first received a message with no connection id (id value will be 0) if (!connection_id) { - connection_id = 1; + BOOST_LOG_SEV(log, info) << "reverting to v2 message format"; tac.adapter_config.is_v2_message_format = true; } tcp_connection::pointer connection = get_tcp_connection(tac, service_id, connection_id); @@ -1762,8 +1802,17 @@ namespace aws { namespace iot { namespace securedtunneling { throw proxy_exception((boost::format("No streamId exists for the service Id %1%") % service_id).str()); } BOOST_LOG_SEV(log, debug) << "Prepare to send data message: service id: " << service_id << " stream id: " << tac.serviceId_to_streamId_map[service_id] << " connection id: " << connection_id; + + if (tac.adapter_config.is_v1_message_format) + { + outgoing_message.set_serviceid(""); + } + else + { + outgoing_message.set_serviceid(service_id); + } + // Construct outgoing message - outgoing_message.set_serviceid(service_id); outgoing_message.set_streamid(tac.serviceId_to_streamId_map[service_id]); outgoing_message.set_connectionid(connection_id); size_t const send_size = std::min(GET_SETTING(settings, MESSAGE_MAX_PAYLOAD_SIZE), @@ -1988,9 +2037,10 @@ namespace aws { namespace iot { namespace securedtunneling { uint32_t new_connection_id = ++server->highest_connection_id; // backward compatibility: set connection id to 1 if simultaneous connections is not enabled - if (tac.adapter_config.is_v2_message_format) + if (tac.adapter_config.is_v2_message_format || tac.adapter_config.is_v1_message_format) { - new_connection_id = 1; + BOOST_LOG_SEV(log, info) << "Falling back to older protocol, setting new connection id to 0"; + new_connection_id = 0; } BOOST_LOG_SEV(log, info) << "creating tcp connection id " << new_connection_id; @@ -2009,7 +2059,7 @@ namespace aws { namespace iot { namespace securedtunneling { server->connectionId_to_tcp_connection_map[new_connection_id]->socket() = std::move(new_socket); BOOST_LOG_SEV(log, info) << "Accepted tcp connection on port " << server->connectionId_to_tcp_connection_map[new_connection_id]->socket().local_endpoint().port() << " from " << server->connectionId_to_tcp_connection_map[new_connection_id]->socket().remote_endpoint(); - if (is_first_connection) + if (is_first_connection || tac.adapter_config.is_v1_message_format || tac.adapter_config.is_v2_message_format) { async_send_stream_start(tac, service_id, new_connection_id); } @@ -2266,4 +2316,4 @@ namespace aws { namespace iot { namespace securedtunneling { return false; } } -}}} +}}} \ No newline at end of file diff --git a/src/WebSocketStream.cpp b/src/WebSocketStream.cpp index fdd11c8..7790cce 100644 --- a/src/WebSocketStream.cpp +++ b/src/WebSocketStream.cpp @@ -173,15 +173,35 @@ namespace aws { } } - void WebSocketStream::async_ssl_handshake(const ssl::stream_base::handshake_type &type, + void WebSocketStream::async_ssl_handshake(const ssl::stream_base::handshake_type &type, const std::string &host, const BoostCallbackFunc &handler) { if (localproxyConfig.is_web_proxy_using_tls) { BOOST_LOG_SEV(*log, trace) << "Calling next_layer().async_handshake with type: " << WEB_PROXY_WITH_TLS_TYPE_NAME; + // Set SNI Hostname (many hosts need this to handshake successfully) + if(!SSL_set_tlsext_host_name(boost::get>(wss)->next_layer().native_handle(), host.c_str())) + { + BOOST_LOG_SEV(*log, trace) << "SSL next_layer() failed to set SNI"; + } + else + { + BOOST_LOG_SEV(*log, trace) << "SSL next_layer() SNI is set : " + << host; + } return boost::get>(wss)->next_layer().async_handshake(type, handler); } else { BOOST_LOG_SEV(*log, trace) << "Calling next_layer().async_handshake with type: " << WEB_PROXY_NO_TLS_TYPE_NAME; + // Set SNI Hostname (many hosts need this to handshake successfully) + if(!SSL_set_tlsext_host_name(boost::get>(wss)->next_layer().native_handle(), host.c_str())) + { + BOOST_LOG_SEV(*log, trace) << "SSL next_layer() failed to set SNI"; + } + else + { + BOOST_LOG_SEV(*log, trace) << "SSL next_layer() SNI is set : " + << host; + } return boost::get>(wss)->next_layer().async_handshake(type, handler); } } diff --git a/src/WebSocketStream.h b/src/WebSocketStream.h index 981e0d0..1d661e2 100644 --- a/src/WebSocketStream.h +++ b/src/WebSocketStream.h @@ -147,10 +147,11 @@ namespace aws { /** * Performs the SSL handshake between the localproxy and the proxy server asynchronously. * @param type The handshake type + * @param host the host subdoman and domain * @param handler the callback handler when the async operation is complete. */ void - async_ssl_handshake(const ssl::stream_base::handshake_type &type, const BoostCallbackFunc &handler); + async_ssl_handshake(const ssl::stream_base::handshake_type &type, const std::string &host, const BoostCallbackFunc &handler); #endif /**