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

mdb iamproxy auth support #561

Merged
merged 30 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
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: 2 additions & 1 deletion sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ set(od_src
hashmap.c
hba.c
hba_reader.c
hba_rule.c)
hba_rule.c
mdb_iamproxy.c)

if (PAM_FOUND)
list(APPEND od_src pam.c)
Expand Down
77 changes: 77 additions & 0 deletions sources/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,78 @@ static inline int od_auth_frontend_cert(od_client_t *client)
return -1;
}

static inline int od_auth_frontend_mdb_iamproxy(od_client_t *client)
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
{
od_instance_t *instance = client->global->instance;
od_route_t *route = client->route;

machine_msg_t *msg;
msg = kiwi_be_write_authentication_clear_text(NULL);
if (msg == NULL)
return -1;
int rc;
rc = od_write(&client->io, msg);
if (rc == -1) {
od_error(&instance->logger, "auth", client, NULL,
"write error: %s", od_io_error(&client->io));
return -1;
}

AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
/* wait for password response */
while (1) {
msg = od_read(&client->io, UINT32_MAX);
if (msg == NULL) {
od_error(&instance->logger, "auth", client, NULL,
"fuck that shit again");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be polite, the Internet remembers all

od_error(&instance->logger, "auth", client, NULL,
"read error: %s", od_io_error(&client->io));
return -1;
}
kiwi_fe_type_t type = *(char *)machine_msg_data(msg);
od_debug(&instance->logger, "auth", client, NULL, "%s",
kiwi_fe_type_to_string(type));
if (type == KIWI_FE_PASSWORD_MESSAGE)
break;
machine_msg_free(msg);
}

/* read password message */
kiwi_password_t client_token;
kiwi_password_init(&client_token);

rc = kiwi_be_read_password(machine_msg_data(msg), machine_msg_size(msg),
&client_token);
if (rc == -1) {
od_error(&instance->logger, "auth", client, NULL,
"password read error");
od_frontend_error(client, KIWI_PROTOCOL_VIOLATION,
"bad password message");
kiwi_password_free(&client_token);
machine_msg_free(msg);
return -1;
}

/* start iam checking */
int authentication_result =
mdb_iamproxy_authenticate_user(client->startup.user.value,
client_token.password, instance,
client);
//int authentication_result = OK_RESPONSE;
kiwi_password_free(&client_token);
machine_msg_free(msg);
if (authentication_result != OK_RESPONSE) {
goto auth_failed; // refence at line 80, 100 and etc
}
return OK_RESPONSE;

auth_failed:
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
od_log(&instance->logger, "auth", client, NULL,
"user '%s.%s' incorrect password",
client->startup.database.value, client->startup.user.value);
od_frontend_error(client, KIWI_INVALID_PASSWORD, "incorrect password");
return NOT_OK_RESPONSE;
}

static inline int od_auth_frontend_block(od_client_t *client)
{
od_instance_t *instance = client->global->instance;
Expand Down Expand Up @@ -696,6 +768,11 @@ int od_auth_frontend(od_client_t *client)
return -1;
case OD_RULE_AUTH_NONE:
break;
case OD_RULE_AUTH_MDB_IAMPROXY:
rc = od_auth_frontend_mdb_iamproxy(client);
if (rc == -1)
return -1;
break;
default:
assert(0);
break;
Expand Down
254 changes: 254 additions & 0 deletions sources/mdb_iamproxy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@

/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/

#include <odyssey.h>
#include <machinarium.h>
#include <limits.h>
#include <stdint.h>
#include <malloc.h>
#include <sys/poll.h>
#include <sys/un.h>
#include <sys/socket.h>

/*CONNECTION CALLBACK TYPES*/
#define MDB_IAMPROXY_CONN_ERROR -1
#define MDB_IAMPROXY_CONN_TIMEOUT -1
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
#define MDB_IAMPROXY_CONN_ACCEPTED 0
#define MDB_IAMPROXY_CONN_DENIED -1

#define MDB_IAMPROXY_RES_ERROR -1
#define MDB_IAMPROXY_RES_OK 0

/*AUTHENTICATION TIMEOUT LIMIT*/
#define MDB_IAMPROXY_DEFAULT_HEADER_SIZE 8
#define MDB_IAMPROXY_DEFAULT_CNT_CONNECTIONS 1
#define MDB_IAMPROXY_MAX_MSG_BODY_SIZE 1048576 // 1 Mb

#define MDB_IAMPROXY_DEFAULT_CONNECTION_TIMEOUT 1000
#define MDB_IAMPROXY_DEFAULT_RECEIVING_HEADER_TIMEOUT 4000
#define MDB_IAMPROXY_DEFAULT_RECEIVING_BODY_TIMEOUT 1000
#define MDB_IAMPROXY_DEFAULT_SENDING_TIMEOUT 1000

/*PAM SOCKET FILE*/
#define MDB_IAMPROXY_DEFAULT_SOCKET_FILE \
"/var/run/iam-auth-proxy/iam-auth-proxy.sock" // PAM SOCKET FILE place

void put_header(char dst[], uint64_t src)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's mark these as static.
Generaly we do all the formatting stuff in KiWi... it's kind of byte-formatting lib.

{
for (int i = 0; i < MDB_IAMPROXY_DEFAULT_HEADER_SIZE; ++i) {
dst[i] = (src & 0xFF);
src >>= CHAR_BIT;
}
}

void fetch_header(uint64_t *dst, char src[])
{
for (int i = 0; i < MDB_IAMPROXY_DEFAULT_HEADER_SIZE; ++i) {
(*dst) |= (((uint64_t)src[i]) << (i * CHAR_BIT));
}
}

machine_msg_t *mdb_iamproxy_io_read(machine_io_t *io)
{
machine_msg_t *header;
machine_msg_t *msg;

uint64_t body_size = 0;
uint64_t received = 0;

/* RECEIVE HEADER */
header = machine_read(io, MDB_IAMPROXY_DEFAULT_HEADER_SIZE,
MDB_IAMPROXY_DEFAULT_RECEIVING_HEADER_TIMEOUT);
if (header == NULL) {
return NULL;
}
fetch_header(&body_size, (char *)machine_msg_data(header));
machine_msg_free(header);

if (body_size > MDB_IAMPROXY_MAX_MSG_BODY_SIZE) {
return NULL;
}
msg = machine_read(io, body_size,
MDB_IAMPROXY_DEFAULT_RECEIVING_BODY_TIMEOUT);
if (msg == NULL) {
return NULL;
}

return msg;
}

int mdb_iamproxy_io_write(machine_io_t *io, machine_msg_t *msg)
{
/*GET COMMON MSG INFO AND ALLOCATE BUFFER*/
int32_t send_result = MDB_IAMPROXY_RES_OK;
uint64_t body_size = machine_msg_size(
msg); // stores size of message (add one byte for 'c\0')

/* PREPARE HEADER BUFFER */
machine_msg_t *header =
machine_msg_create(MDB_IAMPROXY_DEFAULT_HEADER_SIZE);
if (header == NULL) {
send_result = MDB_IAMPROXY_RES_ERROR;
goto free_end;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
}
put_header((char *)machine_msg_data(header), body_size);

/*SEND HEADER TO SOCKET*/
if (machine_write(io, header, MDB_IAMPROXY_DEFAULT_SENDING_TIMEOUT) <
0) {
send_result = MDB_IAMPROXY_RES_ERROR;
goto free_end;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
}

/*SEND MSG TO SOCKET*/
if (machine_write(io, msg, MDB_IAMPROXY_DEFAULT_SENDING_TIMEOUT) < 0) {
send_result = MDB_IAMPROXY_RES_ERROR;
goto free_end;
}

free_end:
return send_result;
}

int mdb_iamproxy_authenticate_user(const char *username, const char *token,
od_instance_t *instance, od_client_t *client)
{
int32_t authentication_result =
MDB_IAMPROXY_CONN_DENIED; // stores authenticate status for user (default value: CONN_DENIED)
int32_t correct_sending =
MDB_IAMPROXY_CONN_ACCEPTED; // stores stutus of sending data to iam-auth-proxy
char *auth_status_char;
machine_msg_t *msg_username = NULL, *msg_token = NULL,
*auth_status = NULL, *external_user = NULL;

/*SOCKET SETUP*/
struct sockaddr *saddr;
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
struct sockaddr_un
exchange_socket; // socket for interprocceses connection
memset(&exchange_socket, 0, sizeof(exchange_socket));
exchange_socket.sun_family = AF_UNIX;
saddr = (struct sockaddr *)&exchange_socket;
od_snprintf(exchange_socket.sun_path, sizeof(exchange_socket.sun_path),
"%s", MDB_IAMPROXY_DEFAULT_SOCKET_FILE);
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved

/*SETUP IO*/
machine_io_t *io;
io = machine_io_create();
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
if (io == NULL) {
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_end;
}

machine_set_nodelay(io, instance->config.nodelay);
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved
if (instance->config.keepalive > 0) {
machine_set_keepalive(io, 1, instance->config.keepalive,
instance->config.keepalive_keep_interval,
instance->config.keepalive_probes,
instance->config.keepalive_usr_timeout);
}

/*CONNECT TO SOCKET*/
int rc = machine_connect(io, saddr,
MDB_IAMPROXY_DEFAULT_CONNECTION_TIMEOUT);
if (rc == NOT_OK_RESPONSE) {
od_error(&instance->logger, "auth", client, NULL,
"failed to connect to %s", exchange_socket.sun_path);
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_end;
}

/*COMMUNICATE WITH SOCKET*/
msg_username = machine_msg_create(0);
if (msg_username == NULL) {
od_error(&instance->logger, "auth", client, NULL,
"failed to allocate msg_username");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}
if (machine_msg_write(msg_username, username, strlen(username) + 1) <
0) {
od_error(&instance->logger, "auth", client, NULL,
"failed to send username to msg_username");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}

msg_token = machine_msg_create(0);
if (msg_token == NULL) {
od_error(&instance->logger, "auth", client, NULL,
"failed to allocate msg_token");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}
if (machine_msg_write(msg_token, token, strlen(token) + 1) < 0) {
od_error(&instance->logger, "auth", client, NULL,
"failed to write token to msg_token");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}

correct_sending = mdb_iamproxy_io_write(
io, msg_username); // send USERNAME to socket
if (correct_sending !=
MDB_IAMPROXY_RES_OK) { // error during sending data to socket
od_error(&instance->logger, "auth", client, NULL,
"failed to send username to iam-auth-proxy");
authentication_result = correct_sending;
goto free_io;
}
correct_sending =
mdb_iamproxy_io_write(io, msg_token); // send TOKEN to socket
if (correct_sending !=
MDB_IAMPROXY_RES_OK) { // error during sending data to socket
od_error(&instance->logger, "auth", client, NULL,
"failed to send token to iam-auth-proxy");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}

/*COMMUNUCATE WITH SOCKET*/
auth_status =
mdb_iamproxy_io_read(io); // recieve auth_status from socket
if (auth_status == NULL) { // recieving is not completed successfully
od_error(&instance->logger, "auth", client, NULL,
"failed to receive auth_status from iam-auth-proxy");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_io;
}

auth_status_char = (char *)machine_msg_data(auth_status);
if ((unsigned)auth_status_char[0]) {
authentication_result = MDB_IAMPROXY_CONN_ACCEPTED;
} else {
authentication_result = MDB_IAMPROXY_CONN_DENIED;
}

external_user =
mdb_iamproxy_io_read(io); // recieve subject_id from socket
if (external_user == NULL) {
od_error(&instance->logger, "auth", client, NULL,
"failed to receive external_user from iam-auth-proxy");
authentication_result = MDB_IAMPROXY_CONN_ERROR;
goto free_auth_status;
}

od_log(&instance->logger, "auth", client, NULL,
"user '%s.%s' was authenticated with subject_id: %s",
client->startup.database.value, client->startup.user.value,
(char *)machine_msg_data(external_user));
AndrewOvvv marked this conversation as resolved.
Show resolved Hide resolved

/*FREE RESOURCES*/
free_external_user:
machine_msg_free(external_user);
free_auth_status:
machine_msg_free(auth_status);
free_io:
machine_io_free(io);
free_end:
/*RETURN RESULT*/
return authentication_result;
}
14 changes: 14 additions & 0 deletions sources/mdb_iamproxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef ODYSSEY_IAMPROXY_H
#define ODYSSEY_IAMPROXY_H

/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/

int mdb_iamproxy_authenticate_user(const char *username, const char *token,
od_instance_t *instance,
od_client_t *client);

#endif /* ODYSSEY_IAMPROXy_H */
2 changes: 2 additions & 0 deletions sources/rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,8 @@ int od_rules_validate(od_rules_t *rules, od_config_t *config,
}
} else if (strcmp(rule->auth, "cert") == 0) {
rule->auth_mode = OD_RULE_AUTH_CERT;
} else if (strcmp(rule->auth, "mdb-iamproxy") == 0) {
rule->auth_mode = OD_RULE_AUTH_MDB_IAMPROXY;
} else {
od_error(
logger, "rules", NULL, NULL,
Expand Down
3 changes: 2 additions & 1 deletion sources/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ typedef enum {
OD_RULE_AUTH_CLEAR_TEXT,
OD_RULE_AUTH_MD5,
OD_RULE_AUTH_SCRAM_SHA_256,
OD_RULE_AUTH_CERT
OD_RULE_AUTH_CERT,
OD_RULE_AUTH_MDB_IAMPROXY
} od_rule_auth_type_t;

struct od_rule_auth {
Expand Down
Loading