Skip to content
Open
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 modules/cloud-auth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ set(CLOUD_AUTH_CPP_SOURCES
azure-auth.h
azure-auth.cpp
azure-auth.hpp
oauth2-auth.h
oauth2-auth.cpp
oauth2-auth.hpp
)

set(OTEL_SOURCES
Expand Down
7 changes: 5 additions & 2 deletions modules/cloud-auth/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ modules_cloud_auth_libcloud_auth_cpp_la_SOURCES = \
modules/cloud-auth/google-auth.hpp \
modules/cloud-auth/google-auth.cpp \
modules/cloud-auth/azure-auth.hpp \
modules/cloud-auth/azure-auth.cpp
modules/cloud-auth/azure-auth.cpp \
modules/cloud-auth/oauth2-auth.hpp \
modules/cloud-auth/oauth2-auth.cpp

modules_cloud_auth_libcloud_auth_cpp_la_CXXFLAGS = \
$(AM_CXXFLAGS) \
Expand All @@ -32,7 +34,8 @@ modules_cloud_auth_libcloud_auth_la_SOURCES = \
modules/cloud-auth/cloud-auth.h \
modules/cloud-auth/cloud-auth.c \
modules/cloud-auth/google-auth.h \
modules/cloud-auth/azure-auth.h
modules/cloud-auth/azure-auth.h \
modules/cloud-auth/oauth2-auth.h

modules_cloud_auth_libcloud_auth_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
Expand Down
194 changes: 17 additions & 177 deletions modules/cloud-auth/azure-auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,185 +22,25 @@
*/
#include "azure-auth.hpp"

#include "compat/cpp-start.h"
#include "scratch-buffers.h"
#include "compat/cpp-end.h"

#include <fstream>

using namespace syslogng::cloud_auth::azure;

AzureMonitorAuthenticator::AzureMonitorAuthenticator(const char *tenant_id,
const char *app_id,
const char *app_secret,
const char *scope)
{
auth_url = "https://login.microsoftonline.com/";
auth_url.append(tenant_id);
auth_url.append("/oauth2/v2.0/token");

auth_body = "grant_type=client_credentials&client_id=";
auth_body.append(app_id);
auth_body.append("&client_secret=");
auth_body.append(app_secret);
auth_body.append("&scope=");
auth_body.append(scope);
}

void AzureMonitorAuthenticator::handle_http_header_request(HttpHeaderRequestSignalData *data)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();

lock.lock();

if (now <= refresh_token_after && !cached_token.empty())
{
add_token_to_header(data);
lock.unlock();

data->result = HTTP_SLOT_SUCCESS;
return;
}

cached_token.clear();

std::string response_payload_buffer;
if (!send_token_post_request(response_payload_buffer))
{
lock.unlock();

data->result = HTTP_SLOT_CRITICAL_ERROR;
return;
}

long expiry;
if (!parse_token_and_expiry_from_response(response_payload_buffer, cached_token, &expiry))
{
lock.unlock();

data->result = HTTP_SLOT_CRITICAL_ERROR;
return;
}

refresh_token_after = now + std::chrono::seconds{expiry - 60};
add_token_to_header(data);

lock.unlock();

data->result = HTTP_SLOT_SUCCESS;
}

void
AzureMonitorAuthenticator::add_token_to_header(HttpHeaderRequestSignalData *data)
{
/* Scratch Buffers are marked at this point in http-worker.c */
GString *auth_buffer = scratch_buffers_alloc();
g_string_append(auth_buffer, "Authorization: Bearer ");
g_string_append(auth_buffer, cached_token.c_str());

list_append(data->request_headers, auth_buffer->str);
}

bool
AzureMonitorAuthenticator::send_token_post_request(std::string &response_payload_buffer)
using namespace syslogng::cloud_auth::oauth2;

AzureMonitorAuthenticator::AzureMonitorAuthenticator(const char *tenant_id_,
const char *app_id_,
const char *app_secret_,
const char *scope_)
: OAuth2Authenticator(
app_id_, // client_id
app_secret_, // client_secret
(std::string("https://login.microsoftonline.com/") + tenant_id_ +
"/oauth2/v2.0/token").c_str(), // token_url
scope_, // scope
nullptr, // resource (not used)
nullptr, // authorization_details (not used)
60, // refresh_offset (60s before expiry)
OAUTH2_AUTH_METHOD_POST_BODY // auth_method
)
{
CURLcode ret;
CURL *hnd = curl_easy_init();

if (!hnd)
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"failed to init cURL handle",
evt_tag_str("url", auth_url.c_str()));
goto error;
}

curl_easy_setopt(hnd, CURLOPT_URL, auth_url.c_str());
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, auth_body.c_str());
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, curl_write_callback);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *) &response_payload_buffer);

ret = curl_easy_perform(hnd);
if (ret != CURLE_OK)
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"error sending HTTP request to metadata server",
evt_tag_str("url", auth_url.c_str()),
evt_tag_str("error", curl_easy_strerror(ret)));
goto error;
}

long http_result_code;
ret = curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &http_result_code);
if (ret != CURLE_OK)
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"failed to get HTTP result code",
evt_tag_str("url", auth_url.c_str()),
evt_tag_str("error", curl_easy_strerror(ret)));
goto error;
}

if (http_result_code != 200)
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"non 200 HTTP result code received",
evt_tag_str("url", auth_url.c_str()),
evt_tag_int("http_result_code", http_result_code));
goto error;
}

curl_easy_cleanup(hnd);
return true;

error:
if (hnd)
{
curl_easy_cleanup(hnd);
}
return false;
}

bool
AzureMonitorAuthenticator::parse_token_and_expiry_from_response(const std::string &response_payload,
std::string &token, long *expiry)
{
picojson::value json;
std::string json_parse_error = picojson::parse(json, response_payload);
if (!json_parse_error.empty())
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"failed to parse response JSON",
evt_tag_str("url", auth_url.c_str()),
evt_tag_str("response_json", response_payload.c_str()));
return false;
}

if (!json.is<picojson::object>() || !json.contains("access_token") || !json.contains("expires_in"))
{
msg_error("cloud_auth::azure::AzureMonitorAuthenticator: "
"unexpected response JSON",
evt_tag_str("url", auth_url.c_str()),
evt_tag_str("response_json", response_payload.c_str()));
return false;
}

token.assign(json.get("access_token").get<std::string>());
*expiry = lround(json.get("expires_in").get<double>()); /* getting a long from picojson is not always available */
return true;
}

size_t
AzureMonitorAuthenticator::curl_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
const char *data = (const char *) contents;
std::string *response_payload_buffer = (std::string *) userp;

size_t real_size = size * nmemb;
response_payload_buffer->append(data, real_size);

return real_size;
}

/* C Wrappers */
Expand Down
25 changes: 3 additions & 22 deletions modules/cloud-auth/azure-auth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,18 @@
#define AZURE_AUTH_HPP

#include "azure-auth.h"
#include "cloud-auth.hpp"

#include <mutex>
#include <jwt-cpp/jwt.h>
#include "oauth2-auth.hpp"

namespace syslogng {
namespace cloud_auth {
namespace azure {

class AzureMonitorAuthenticator: public syslogng::cloud_auth::Authenticator
class AzureMonitorAuthenticator: public syslogng::cloud_auth::oauth2::OAuth2Authenticator
{
public:
AzureMonitorAuthenticator(const char *tenant_id, const char *app_id,
const char *app_secret, const char *scope);
const char *app_secret, const char *scope_);
~AzureMonitorAuthenticator() {};

void handle_http_header_request(HttpHeaderRequestSignalData *data);

private:
std::string auth_url;
std::string auth_body;

std::mutex lock;
std::string cached_token;
std::chrono::system_clock::time_point refresh_token_after;

void add_token_to_header(HttpHeaderRequestSignalData *data);
bool parse_token_and_expiry_from_response(const std::string &response_payload,
std::string &token, long *expiry);
static size_t curl_write_callback(void *contents, size_t size, size_t nmemb, void *userp);
bool send_token_post_request(std::string &response_payload_buffer);
};

}
Expand Down
40 changes: 40 additions & 0 deletions modules/cloud-auth/cloud-auth-grammar.ym
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "cloud-auth.h"
#include "google-auth.h"
#include "azure-auth.h"
#include "oauth2-auth.h"

CloudAuthenticator *last_authenticator;
}
Expand Down Expand Up @@ -62,6 +63,16 @@ CloudAuthenticator *last_authenticator;
%token KW_APP_ID
%token KW_APP_SECRET
%token KW_SCOPE
%token KW_CLIENT_ID
%token KW_CLIENT_SECRET
%token KW_TOKEN_URL
%token KW_RESOURCE
%token KW_AUTHORIZATION_DETAILS
%token KW_REFRESH_OFFSET
%token KW_OAUTH2
%token KW_AUTH_METHOD
%token KW_BASIC
%token KW_POST_BODY

%%

Expand Down Expand Up @@ -95,6 +106,14 @@ inner_destination_cloud_auth_option
{
cloud_auth_dest_plugin_set_authenticator(last_driver_plugin, last_authenticator);
}
| KW_OAUTH2
{
last_authenticator = oauth2_authenticator_new();
}
'(' oauth2_dest_auth_options ')'
{
cloud_auth_dest_plugin_set_authenticator(last_driver_plugin, last_authenticator);
}
;

google_dest_auth_options
Expand Down Expand Up @@ -162,6 +181,27 @@ azure_dest_monitor_option
| KW_SCOPE '(' string ')' {azure_authenticator_set_scope(last_authenticator, $3); free($3);}
;

oauth2_dest_auth_options
: oauth2_dest_auth_option oauth2_dest_auth_options
|
;

oauth2_dest_auth_option
: KW_CLIENT_ID '(' string ')' {oauth2_authenticator_set_client_id(last_authenticator, $3); free($3);}
| KW_CLIENT_SECRET '(' string ')' {oauth2_authenticator_set_client_secret(last_authenticator, $3); free($3);}
| KW_TOKEN_URL '(' string ')' {oauth2_authenticator_set_token_url(last_authenticator, $3); free($3);}
| KW_SCOPE '(' string ')' {oauth2_authenticator_set_scope(last_authenticator, $3); free($3);}
| KW_RESOURCE '(' string ')' {oauth2_authenticator_set_resource(last_authenticator, $3); free($3);}
| KW_AUTHORIZATION_DETAILS '(' string ')' {oauth2_authenticator_set_authorization_details(last_authenticator, $3); free($3);}
| KW_REFRESH_OFFSET '(' nonnegative_integer64 ')' {oauth2_authenticator_set_refresh_offset(last_authenticator, $3);}
| KW_AUTH_METHOD '(' oauth2_auth_method_value ')'
;

oauth2_auth_method_value
: KW_BASIC { oauth2_authenticator_set_auth_method(last_authenticator, OAUTH2_AUTH_METHOD_BASIC); }
| KW_POST_BODY { oauth2_authenticator_set_auth_method(last_authenticator, OAUTH2_AUTH_METHOD_POST_BODY); }
;

/* INCLUDE_RULES */

%%
10 changes: 10 additions & 0 deletions modules/cloud-auth/cloud-auth-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ static CfgLexerKeyword cloud_auth_keywords[] =
{ "app_id", KW_APP_ID },
{ "app_secret", KW_APP_SECRET },
{ "scope", KW_SCOPE },
{ "client_id", KW_CLIENT_ID },
{ "client_secret", KW_CLIENT_SECRET },
{ "token_url", KW_TOKEN_URL },
{ "resource", KW_RESOURCE },
{ "authorization_details", KW_AUTHORIZATION_DETAILS },
{ "refresh_offset", KW_REFRESH_OFFSET },
{ "oauth2", KW_OAUTH2 },
{ "auth_method", KW_AUTH_METHOD },
{ "basic", KW_BASIC },
{ "post_body", KW_POST_BODY },
{ NULL }
};

Expand Down
3 changes: 3 additions & 0 deletions modules/cloud-auth/cloud-auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ _attach(LogDriverPlugin *s, LogDriver *d)
SignalSlotConnector *ssc = driver->super.signal_slot_connector;
CONNECT(ssc, signal_http_header_request, cloud_authenticator_handle_http_header_request, self->authenticator);

CONNECT(ssc, signal_grpc_metadata_request, cloud_authenticator_handle_grpc_metadata_request, self->authenticator);

return TRUE;
}

Expand All @@ -56,6 +58,7 @@ _detach(LogDriverPlugin *s, LogDriver *d)

SignalSlotConnector *ssc = driver->super.signal_slot_connector;
DISCONNECT(ssc, signal_http_header_request, cloud_authenticator_handle_http_header_request, self->authenticator);
DISCONNECT(ssc, signal_grpc_metadata_request, cloud_authenticator_handle_grpc_metadata_request, self->authenticator);
}

void
Expand Down
6 changes: 6 additions & 0 deletions modules/cloud-auth/cloud-auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ cloud_authenticator_handle_http_header_request(CloudAuthenticator *s, HttpHeader
{
s->cpp->handle_http_header_request(data);
}

void
cloud_authenticator_handle_grpc_metadata_request(CloudAuthenticator *s, GrpcMetadataRequestSignalData *data)
{
s->cpp->handle_grpc_metadata_request(data);
}
Loading