diff --git a/sources/CMakeLists.txt b/sources/CMakeLists.txt index f1c6729e..14137272 100644 --- a/sources/CMakeLists.txt +++ b/sources/CMakeLists.txt @@ -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) diff --git a/sources/auth.c b/sources/auth.c index d627cd27..c4675a99 100644 --- a/sources/auth.c +++ b/sources/auth.c @@ -67,6 +67,19 @@ static inline int od_auth_frontend_cleartext(od_client_t *client) od_extention_t *extentions = client->global->extentions; + /* support mdb_iamproxy authentication */ + if (client->rule->enable_mdb_iamproxy_auth) { + int authentication_result = mdb_iamproxy_authenticate_user( + client->startup.user.value, client_token.password, + instance, client); + 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; + } + #ifdef LDAP_FOUND if (client->rule->ldap_endpoint_name) { od_debug(&instance->logger, "auth", client, NULL, diff --git a/sources/client.h b/sources/client.h index 19ec4ed1..68e49b2b 100644 --- a/sources/client.h +++ b/sources/client.h @@ -72,6 +72,9 @@ struct od_client { int ldap_storage_password_len; char *ldap_auth_dn; #endif + + /* external_id for logging additional ifno about client */ + char *external_id; }; static const size_t OD_CLIENT_DEFAULT_HASHMAP_SZ = 420; @@ -108,6 +111,7 @@ static inline void od_client_init(od_client_t *client) client->ldap_storage_password_len = 0; client->ldap_auth_dn = NULL; #endif + client->external_id = NULL; kiwi_be_startup_init(&client->startup); kiwi_vars_init(&client->vars); @@ -146,6 +150,9 @@ static inline void od_client_free(od_client_t *client) if (client->prep_stmt_ids) { od_hashmap_free(client->prep_stmt_ids); } + if (client->external_id) { + free(client->external_id); + } free(client); } diff --git a/sources/config_reader.c b/sources/config_reader.c index 997d5e22..f5066ac1 100644 --- a/sources/config_reader.c +++ b/sources/config_reader.c @@ -115,6 +115,8 @@ typedef enum { OD_LAUTH_QUERY_USER, OD_LAUTH_LDAP_SERVICE, OD_LAUTH_PASSWORD_PASSTHROUGH, + OD_LAUTH_MDB_IAMPROXY_ENABLE, + OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH, OD_LQUANTILES, OD_LMODULE, OD_LLDAP_ENDPOINT, @@ -275,6 +277,9 @@ static od_keyword_t od_config_keywords[] = { od_keyword("password_passthrough", OD_LAUTH_PASSWORD_PASSTHROUGH), od_keyword("load_module", OD_LMODULE), od_keyword("hba_file", OD_LHBA_FILE), + od_keyword("enable_mdb_iamproxy_auth", OD_LAUTH_MDB_IAMPROXY_ENABLE), + od_keyword("mdb_iamproxy_socket_path", + OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH), /* ldap */ od_keyword("ldap_endpoint", OD_LLDAP_ENDPOINT), @@ -1205,6 +1210,7 @@ static int od_config_reader_rule_settings(od_config_reader_t *reader, od_extention_t *extentions, od_storage_watchdog_t *watchdog) { + rule->mdb_iamproxy_socket_path = NULL; for (;;) { od_token_t token; int rc; @@ -1293,6 +1299,24 @@ static int od_config_reader_rule_settings(od_config_reader_t *reader, &rule->auth_module)) return NOT_OK_RESPONSE; break; + /* mdb_iamproxy authentication */ + case OD_LAUTH_MDB_IAMPROXY_ENABLE: { + if (!od_config_reader_yes_no( + reader, &rule->enable_mdb_iamproxy_auth)) + return NOT_OK_RESPONSE; + if (rule->mdb_iamproxy_socket_path == NULL) + rule->mdb_iamproxy_socket_path = + "/var/run/iam-auth-proxy/iam-auth-proxy.sock"; + break; + } + case OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH: { + if (rule->mdb_iamproxy_socket_path != NULL) + free(rule->mdb_iamproxy_socket_path); + if (!od_config_reader_string( + reader, &rule->mdb_iamproxy_socket_path)) + return NOT_OK_RESPONSE; + break; + } #ifdef PAM_FOUND /* auth_pam_service */ case OD_LAUTH_PAM_SERVICE: diff --git a/sources/frontend.c b/sources/frontend.c index 1de3bbf9..a1f9432c 100644 --- a/sources/frontend.c +++ b/sources/frontend.c @@ -2340,4 +2340,4 @@ cleanup: od_router_unroute(router, client); /* close frontend connection */ od_frontend_close(client); -} \ No newline at end of file +} diff --git a/sources/logger.c b/sources/logger.c index 1b5cbda9..f4a1e2b5 100644 --- a/sources/logger.c +++ b/sources/logger.c @@ -215,6 +215,17 @@ od_logger_format(od_logger_t *logger, od_logger_level_t level, char *context, if (od_unlikely(format_pos == format_end)) break; switch (*format_pos) { + /* external_id */ + case 'x': { + if (client && client->external_id != NULL) { + len = od_snprintf(dst_pos, + dst_end - dst_pos, + "%s", + client->external_id); + dst_pos += len; + break; + } + } /* unixtime */ case 'n': { time_t tm = time(NULL); diff --git a/sources/mdb_iamproxy.c b/sources/mdb_iamproxy.c new file mode 100644 index 00000000..acbb1b7b --- /dev/null +++ b/sources/mdb_iamproxy.c @@ -0,0 +1,250 @@ + +/* + * Odyssey. + * + * Scalable PostgreSQL connection pooler. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*CONNECTION CALLBACK TYPES*/ +#define MDB_IAMPROXY_CONN_ERROR -1 +#define MDB_IAMPROXY_CONN_TIMEOUT -1 +#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) +{ + 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; + } + 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; + } + + /*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; + 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); + + /*SETUP IO*/ + machine_io_t *io; + io = machine_io_create(); + if (io == NULL) { + authentication_result = MDB_IAMPROXY_CONN_ERROR; + goto free_end; + } + + /*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; + } + + client->external_id = malloc(machine_msg_size(external_user)); + memcpy(client->external_id, (char *)machine_msg_data(external_user), + machine_msg_size(external_user)); + + od_log(&instance->logger, "auth", client, NULL, + "user '%s.%s', with client_id: %s was authenticated by iam with subject_id: %s", + client->startup.database.value, client->startup.user.value, + client->id.id, client->external_id); + + /*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; +} diff --git a/sources/mdb_iamproxy.h b/sources/mdb_iamproxy.h new file mode 100644 index 00000000..00ca3f89 --- /dev/null +++ b/sources/mdb_iamproxy.h @@ -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 */ diff --git a/sources/rules.c b/sources/rules.c index ad89a692..45090137 100644 --- a/sources/rules.c +++ b/sources/rules.c @@ -200,6 +200,8 @@ void od_rules_rule_free(od_rule_t *rule) free(rule->storage_password); if (rule->pool) od_rule_pool_free(rule->pool); + if (rule->mdb_iamproxy_socket_path) + free(rule->mdb_iamproxy_socket_path); od_list_t *i, *n; od_list_foreach_safe(&rule->auth_common_names, i, n) diff --git a/sources/rules.h b/sources/rules.h index db8190f8..7095c0c1 100644 --- a/sources/rules.h +++ b/sources/rules.h @@ -79,6 +79,9 @@ struct od_rule { od_list_t auth_common_names; int auth_common_names_count; + int enable_mdb_iamproxy_auth; + char *mdb_iamproxy_socket_path; + #ifdef PAM_FOUND /* PAM parametrs */ char *auth_pam_service;