Skip to content
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

Implement Authintercept interface to communicate with configured http service #53

Merged
merged 1 commit into from
Aug 23, 2021
Merged
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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ include("${BUILD_FLAVOUR_DIR}/main.pre.cmake")
# Find threads
find_package(Threads)

# Proto files to serialize/deserialize auth request/response data
add_subdirectory(authproto)

# Main library
add_subdirectory(libamqpprox)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Usage: amqpprox_ctl <control_socket> ARGS

```
$ amqpprox_ctl /tmp/amqpprox HELP
AUTH (SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - Change authentication mechanism for connecting clients
BACKEND (ADD name datacenter host port [SEND-PROXY] [TLS] | ADD_DNS name datacenter address port [SEND-PROXY] [TLS] | DELETE name | PRINT) - Change backend servers
CONN Print the connected sessions
DATACENTER SET name | PRINT
Expand Down
5 changes: 3 additions & 2 deletions amqpprox/amqpprox.m.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <amqpprox_affinitypartitionpolicy.h>

// Control commands
#include <amqpprox_authcontrolcommand.h>
#include <amqpprox_backendcontrolcommand.h>
#include <amqpprox_connectionscontrolcommand.h>
#include <amqpprox_datacentercontrolcommand.h>
Expand Down Expand Up @@ -77,7 +78,6 @@ Although most configuration is injected by the amqpprox_ctl program, the logging
)helptext";
}


int main(int argc, char *argv[])
{
using namespace std::string_literals;
Expand Down Expand Up @@ -234,7 +234,8 @@ int main(int argc, char *argv[])
CommandPtr(new LoggingControlCommand),
CommandPtr(new StatControlCommand(&eventSource)),
CommandPtr(new MapHostnameControlCommand()),
CommandPtr(new TlsControlCommand)};
CommandPtr(new TlsControlCommand),
CommandPtr(new AuthControlCommand)};

for (auto &&command : commands) {
control.addControlCommand(std::move(command));
Expand Down
8 changes: 8 additions & 0 deletions authproto/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
include(FindProtobuf)
find_package(Protobuf REQUIRED)

include_directories(${Protobuf_INCLUDE_DIRS})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS sasl.proto authrequest.proto authresponse.proto)
add_library(authproto ${PROTO_SRCS} ${PROTO_HDRS})
set(PROTO_HDR_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
target_link_libraries(authproto PUBLIC ${Protobuf_LIBRARIES})
24 changes: 24 additions & 0 deletions authproto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## amqpprox HTTP Auth service

### Introduction
Out of the box, amqpprox will perform no authentication of incoming connections - they only need to present a valid vhost. Using the `AUTH SERVICE ...` ctl command amqpprox can be configured to query an http service to auth inbound connections - a bit like [RabbitMQ's http auth backend](https://github.com/rabbitmq/rabbitmq-server/tree/master/deps/rabbitmq_auth_backend_http).
This document describes what information is sent to the service, and how the service should respond.

### Request
The service will receive POST request from amqpprox proxy with following fields in [protobuf format](./authrequest.proto)
- Vhost - Vhost name for connecting clients
- SASL auth data
- Auth mechanism - Authentication mechanism field extracted from START-OK AMQP connection method, sent by client during handshake. E.g. PLAIN
- Credentials - Response field extracted from START-OK AMQP connection method, sent by client during handshake. E.g. '\0user\0password'

### Response
The service should respond to amqpprox proxy with following fields in [protobuf format](./authresponse.proto)
- Auth Result - This will decide, whether to allow or deny clients. It is a enum type, takes ALLOW or DENY value only.
- Reason - Reason for the returned auth result. It is an optional field, but good to specify.
- SASL auth data - It is an optional field. In case of absence, the START-OK AMQP connection method, received from clients will be sent to the broker without any modification during handshake.
- Broker mechanism - Authentication mechanism field for START-OK AMQP connection method. This will be injected into START-OK AMQP connection method to send to the broker during handshake. E.g. PLAIN
- Credentials - Response field for START-OK AMQP connection method. This will also be injectd into START-OK AMQP connection method to send to the broker during handshake. E.g.'\0user\0password'

The external HTTP auth service is used to authenticate clients during the handshake before connecting to the destination RabbitMQ broker. The service receives SASL fields (mechanism and credentials) to authenticate and replies with Allow/Deny, and (optionally) with overridden SASL fields for the outbound connection to the broker.

The service is called once for each client connection, with a 30 seconds default timeout for each service request.
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,13 @@
** limitations under the License.
*/

Chinmay1412 marked this conversation as resolved.
Show resolved Hide resolved
#include <amqpprox_authrequestdata.h>
syntax = "proto3";

namespace Bloomberg {
namespace amqpprox {
import "sasl.proto";

AuthRequestData::AuthRequestData(std::string_view vhostName,
std::string_view authMechanism,
std::string_view credentials)
: d_vhostName(vhostName)
, d_authMechanism(authMechanism)
, d_credentials(credentials)
{
}

AuthRequestData::AuthRequestData()
: d_vhostName()
, d_authMechanism()
, d_credentials()
{
}
package Bloomberg.amqpprox.authproto;

}
message AuthRequest {
string vhostName = 1;
SASL authData = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,18 @@
** limitations under the License.
*/

#include <amqpprox_authresponsedata.h>
syntax = "proto3";

#include <string_view>
import "sasl.proto";

namespace Bloomberg {
namespace amqpprox {
package Bloomberg.amqpprox.authproto;

AuthResponseData::AuthResponseData(const AuthResult &authResult,
std::string_view reason,
std::string_view authMechanism,
std::string_view credentials)
: d_authResult(authResult)
, d_reason(reason)
, d_authMechanism(authMechanism)
, d_credentials(credentials)
{
}

}
message AuthResponse {
enum AuthResult {
ALLOW = 0;
DENY = 1;
}
AuthResult result = 1;
string reason = 2;
SASL authData = 3;
}
24 changes: 24 additions & 0 deletions authproto/sasl.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
** Copyright 2021 Bloomberg Finance L.P.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

syntax = "proto3";

package Bloomberg.amqpprox.authproto;

message SASL {
string authMechanism = 1;
bytes credentials = 2;
}
1 change: 1 addition & 0 deletions buildfiles/conan/conanfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
boost/1.76.0
openssl/1.1.1k
gtest/1.10.0
protobuf/3.17.1

[generators]
cmake
19 changes: 19 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

```
$ amqpprox_ctl /tmp/amqpprox HELP
AUTH (SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - Change authentication mechanism for connecting clients
BACKEND (ADD name datacenter host port [SEND-PROXY] [TLS] | ADD_DNS name datacenter address port [SEND-PROXY] [TLS] | DELETE name | PRINT) - Change backend servers
CONN Print the connected sessions
DATACENTER SET name | PRINT
Expand All @@ -30,6 +31,24 @@ TLS (INGRESS | EGRESS) (KEY_FILE file | CERT_CHAIN_FILE file | RSA_KEY_FILE file
```


## AUTH commands

The command represents authentication mechanism for connecting clients. By default all the clients will be allowed to connect to broker without any authentication. With the help of this command, one can set up an external HTTP auth service to authenticate clients, before allowing them to start communicating with broker. The schema structure for the HTTP auth service is defined [here](../authproto)

#### AUTH SERVICE hostname port target

Configures the external HTTP auth service to authenticate connecting clients.
Chinmay1412 marked this conversation as resolved.
Show resolved Hide resolved

For example: `AUTH SERVICE localhost 1234 /auth?tier=dev` will trigger queries to http://localhost:1234/auth?tier=dev when a client connects.

#### AUTH ALWAYS_ALLOW

Stops authentication for connecting clients. So all clients will be allowed to connect to broker.

#### AUTH PRINT

Prints information about current configured auth mechanism.

## BACKEND commands

A backend represents an instance of an AMQP broker.
Expand Down
9 changes: 5 additions & 4 deletions libamqpprox/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ add_library(libamqpprox STATIC
amqpprox_methods_start.cpp
amqpprox_methods_startok.cpp
amqpprox_authinterceptinterface.cpp
amqpprox_authrequestdata.cpp
amqpprox_authresponsedata.cpp
amqpprox_defaultauthintercept.cpp)
amqpprox_defaultauthintercept.cpp
amqpprox_httpauthintercept.cpp
amqpprox_authcontrolcommand.cpp)

target_link_libraries(libamqpprox LINK_PUBLIC ${LIBAMQPPROX_LIBS})
target_include_directories(libamqpprox PRIVATE ${PROTO_HDR_PATH})
target_link_libraries(libamqpprox LINK_PUBLIC authproto ${LIBAMQPPROX_LIBS})

include("${BUILD_FLAVOUR_DIR}/libamqpprox.post.cmake" OPTIONAL)
106 changes: 106 additions & 0 deletions libamqpprox/amqpprox_authcontrolcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
** Copyright 2021 Bloomberg Finance L.P.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <amqpprox_authcontrolcommand.h>

#include <amqpprox_defaultauthintercept.h>
#include <amqpprox_httpauthintercept.h>
#include <amqpprox_server.h>

#include <memory>
#include <sstream>
#include <string>

#include <boost/algorithm/string.hpp>

namespace Bloomberg {
namespace amqpprox {

AuthControlCommand::AuthControlCommand()
{
}

std::string AuthControlCommand::commandVerb() const
{
return "AUTH";
}

std::string AuthControlCommand::helpText() const
{
return "(SERVICE hostname port target | ALWAYS_ALLOW | PRINT) - "
"Change authentication mechanism for connecting clients";
}

void AuthControlCommand::handleCommand(const std::string & /* command */,
const std::string & restOfCommand,
const OutputFunctor &outputFunctor,
Server * serverHandle,
Control * /* controlHandle */)
{
ControlCommandOutput<OutputFunctor> output(outputFunctor);

std::istringstream iss(restOfCommand);
std::string subcommand;
if (iss >> subcommand) {
boost::to_upper(subcommand);

if (subcommand == "SERVICE") {
std::string hostname;
int port = -1;
std::string target;
if (!(iss >> hostname)) {
output << "No hostname specified.\n";
return;
}
if (!(iss >> port && port > 0 && port <= 65535)) {
output << "Invalid port provided.\n";
return;
}
if (!(iss >> target)) {
output << "No http target specified.\n";
return;
}

serverHandle->setAuthIntercept(std::make_shared<HttpAuthIntercept>(
serverHandle->ioService(),
hostname,
std::to_string(port),
target,
serverHandle->getDNSResolverPtr()));

serverHandle->getAuthIntercept()->print(output);
}
else if (subcommand == "ALWAYS_ALLOW") {
serverHandle->setAuthIntercept(
std::make_shared<DefaultAuthIntercept>(
serverHandle->ioService()));

serverHandle->getAuthIntercept()->print(output);
}
else if (subcommand == "PRINT") {
std::ostringstream oss;
serverHandle->getAuthIntercept()->print(output);
}
else {
output << "Unknown subcommand.\n";
}
}
else {
output << "No subcommand provided.\n";
}
}

}
}
63 changes: 63 additions & 0 deletions libamqpprox/amqpprox_authcontrolcommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
** Copyright 2021 Bloomberg Finance L.P.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef BLOOMBERG_AMQPPROX_AUTHCONTROLCOMMAND
#define BLOOMBERG_AMQPPROX_AUTHCONTROLCOMMAND

#include <amqpprox_controlcommand.h>
#include <amqpprox_server.h>

#include <string>

namespace Bloomberg {
namespace amqpprox {

class AuthControlCommand : public ControlCommand {
public:
// CREATORS
AuthControlCommand();

virtual ~AuthControlCommand() override = default;

// MANIPULATORS
/**
* \return the command verb this handle
*/
virtual std::string commandVerb() const override;

/**
* \return a string of the help text for this command
*/
virtual std::string helpText() const override;

/**
* \brief Execute a command, providing any output to the provided functor
* \param command to execute
* \param restOfCommand parameters for the command
* \param outputFunctor is called back with the output
* \param serverHandle access to the Server object
* \param controlHandle access to the Control object
*/
virtual void handleCommand(const std::string & command,
const std::string & restOfCommand,
const OutputFunctor &outputFunctor,
Server * serverHandle,
Control * controlHandle) override;
};

}
}

#endif
Loading