Add HBA config file support, add hba reload logic. (#442)

Add HBA config file support, add hba reload logic.

Currently supported:

    local/host/hostssl/hostnossl
    IPv4/IPv6 address ranges
    rules reload on receiving SIGHUP
    simple tests

What to improve:

    default db/user
    @<file> support in db/user fields
    ? set auth method for each hba rule
    ? replace rw_lock with coroutine synchronization
    ? make parser kind of configurable


Authored-by: Alexander Belev <belyov97@gmail.com>
Co-authored-by: Dmitry Vasiliev <dmitrivasilyev@ozon.ru>
This commit is contained in:
vadv 2022-07-04 13:01:34 +03:00 committed by GitHub
parent 7a769c50dd
commit f9a1ec21bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 994 additions and 12 deletions

View File

@ -62,6 +62,7 @@ COPY ./docker/prep_stmts/pstmts.conf /etc/odyssey/pstmts.conf
COPY --from=base /ody-integration-test/pkg/ody-integration-test /ody-integration-test
COPY --from=base /prep_stmts/pkg/pstmts-test /pstmts-test
COPY ./docker/scram /scram
COPY ./docker/hba /hba
COPY ./docker/auth_query /auth_query
COPY ./docker/ldap /ldap
COPY ./docker/lagpolling /lagpolling

View File

@ -24,6 +24,8 @@ local all postgres trust
host postgres user1 127.0.0.1/32 trust
host db1 user1 127.0.0.1/32 trust
host all postgres 127.0.0.1/32 trust
host all user_allow 127.0.0.1/32 trust
host all user_reject 127.0.0.1/32 trust
EOF
sed -i 's/max_connections = 100/max_connections = 2000/g' /etc/postgresql/14/main/postgresql.conf
@ -36,7 +38,7 @@ if ! /usr/bin/pg_ctlcluster 14 main start && psql -h localhost -p 5432 -U postgr
fi
# Create databases
for database_name in db scram_db ldap_db auth_query_db db1; do
for database_name in db scram_db ldap_db auth_query_db db1 hba_db; do
sudo -u postgres createdb $database_name >> "$SETUP_LOG" 2>&1 || {
echo "ERROR: 'createdb $database_name' failed, examine the log"
cat "$SETUP_LOG"
@ -102,6 +104,14 @@ psql -h localhost -p 5432 -U user1 -d db1 -c "CREATE SCHEMA sh1" >> $SETUP_LOG 2
exit 1
}
# Create users
psql -h localhost -p 5432 -U postgres -c "create user user_allow password 'correct_password'; create user user_reject password 'correct_password'; create user user_unknown password 'correct_password';" >> $SETUP_LOG 2>&1 || {
echo "ERROR: users creation failed, examine the log"
cat "$SETUP_LOG"
cat "$PG_LOG"
exit 1
}
for i in `seq 0 9`
do
# Create users

View File

@ -20,8 +20,6 @@ then
exit 1
fi
ody-stop
# auth query
/auth_query/test_auth_query.sh
if [ $? -eq 1 ]
@ -29,8 +27,16 @@ then
exit 1
fi
# odyssey hba test
/hba/test.sh
if [ $? -eq 1 ]
then
exit 1
fi
#prepared statements in transaction pooling
/usr/bin/odyssey /etc/odyssey/pstmts.conf
sleep 1
/pstmts-test
ody-stop

56
docker/hba/common.conf Normal file
View File

@ -0,0 +1,56 @@
storage "postgres_server" {
type "remote"
host "127.0.0.1"
port 5432
}
database "hba_db" {
user "user_allow" {
authentication "clear_text"
password "correct_password"
storage "postgres_server"
storage_db "hba_db"
pool "session"
}
user "user_reject" {
authentication "clear_text"
password "correct_password"
storage "postgres_server"
storage_db "hba_db"
pool "session"
}
user "user_unknown" {
authentication "clear_text"
password "correct_password"
storage "postgres_server"
storage_db "hba_db"
pool "session"
}
}
daemonize yes
pid_file "/var/run/odyssey.pid"
unix_socket_dir "/tmp"
unix_socket_mode "0644"
locks_dir "/tmp"
log_format "%p %t %l [%i %s] (%c) %m\n"
log_file "/var/log/odyssey.log"
log_to_stdout no
log_config yes
log_debug yes
log_session yes
log_stats no
log_query yes
hba_file "/hba/odyssey_hba.conf"

View File

@ -0,0 +1,4 @@
local hba_db user_allow allow
local hba_db user_reject deny
host hba_db user_allow 127.0.0.0/24 allow
host hba_db user_reject 127.0.0.0/24 deny

6
docker/hba/tcp.conf Normal file
View File

@ -0,0 +1,6 @@
include "/hba/common.conf"
listen {
host "*"
port 6432
}

91
docker/hba/test.sh Executable file
View File

@ -0,0 +1,91 @@
#!/bin/bash -x
set -ex
#
# TCP
#
/usr/bin/odyssey /hba/tcp.conf
PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_allow -c "SELECT 1" hba_db > /dev/null 2>&1 || {
echo "ERROR: failed auth with hba trust, correct password and plain password in config"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
PGPASSWORD=incorrect_password psql -h localhost -p 6432 -U user_allow -c "SELECT 1" hba_db > /dev/null 2>&1 && {
echo "ERROR: successfully auth with hba trust, but incorrect password"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_reject -c "SELECT 1" hba_db > /dev/null 2>&1 && {
echo "ERROR: successfully auth with hba reject"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
PGPASSWORD=correct_password psql -h localhost -p 6432 -U user_unknown -c "SELECT 1" hba_db > /dev/null 2>&1 && {
echo "ERROR: successfully auth without hba rule"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
ody-stop
#
# Unix
#
/usr/bin/odyssey /hba/unix.conf
PGPASSWORD=correct_password psql -h /tmp -p 6432 -U user_allow -c "SELECT 1" hba_db > /dev/null 2>&1 || {
echo "ERROR: failed auth with hba trust, correct password and plain password in config"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
PGPASSWORD=correct_password psql -h /tmp -p 6432 -U user_reject -c "SELECT 1" hba_db > /dev/null 2>&1 && {
echo "ERROR: successfully auth with hba reject"
cat /var/log/odyssey.log
echo "
"
cat /var/log/postgresql/postgresql-14-main.log
exit 1
}
ody-stop

5
docker/hba/unix.conf Normal file
View File

@ -0,0 +1,5 @@
include "/hba/common.conf"
listen {
port 6432
}

View File

@ -258,6 +258,21 @@ reply with 'too many connections'.
`client_max 100`
#### hba\_file *string*
Path to file containing host based authentication rules.
Omit this option to disable HBA.
`hba_file "path"`
HBA file format follows the format of the PostgreSQL `pg_hba.conf` file.
* Supported record types: `local`, `host`, `hostssl`, `hostnossl`.
* Database field: `all`, `sameuser`, multiple names.
* User field: `all`, multiple names.
* Address field: IPv4 or IPv6 range.
* Auth-method field: `deny` or `reject` (equivalent keywords), which leads to immediate disconnection,
`allow` or `trust` (also equivalent keywords), which means applying auth method specified in matching route.
### Listen
Listen section defines listening servers used for accepting

View File

@ -46,7 +46,10 @@ set(od_src
query.c
storage.c
murmurhash.c
hashmap.c)
hashmap.c
hba.c
hba_reader.c
hba_rule.c)
if (PAM_FOUND)
list(APPEND od_src pam.c)

View File

@ -54,6 +54,7 @@ void od_config_init(od_config_t *config)
config->cache_coroutine = 0;
config->cache_msg_gc_size = 0;
config->coroutine_stack_size = 4;
config->hba_file = NULL;
od_list_init(&config->listen);
}
@ -90,6 +91,8 @@ void od_config_free(od_config_t *config)
free(config->log_syslog_facility);
if (config->locks_dir) {
free(config->locks_dir);
if (config->hba_file)
free(config->hba_file);
}
}

View File

@ -73,6 +73,7 @@ struct od_config {
int cache_coroutine;
int cache_msg_gc_size;
int coroutine_stack_size;
char *hba_file;
od_list_t listen;
};

View File

@ -14,6 +14,7 @@ typedef struct {
od_rules_t *rules;
od_error_t *error;
char *config_file;
od_hba_rules_t *hba_rules;
char *data;
int data_size;
} od_config_reader_t;

View File

@ -134,6 +134,7 @@ typedef enum {
OD_LCATCHUP_TIMEOUT,
OD_LCATCHUP_CHECKS,
OD_LOPTIONS,
OD_LHBA_FILE,
} od_lexeme_t;
static od_keyword_t od_config_keywords[] = {
@ -261,6 +262,7 @@ static od_keyword_t od_config_keywords[] = {
od_keyword("auth_module", OD_LAUTH_MODULE),
od_keyword("password_passthrough", OD_LAUTH_PASSWORD_PASSTHROUGH),
od_keyword("load_module", OD_LMODULE),
od_keyword("hba_file", OD_LHBA_FILE),
/* ldap */
od_keyword("ldap_endpoint", OD_LLDAP_ENDPOINT),
@ -1630,6 +1632,23 @@ error:
return NOT_OK_RESPONSE;
}
static int od_config_reader_hba_import(od_config_reader_t *config_reader)
{
od_config_reader_t reader;
memset(&reader, 0, sizeof(reader));
reader.config = config_reader->config;
reader.error = config_reader->error;
reader.hba_rules = config_reader->hba_rules;
int rc;
rc = od_config_reader_open(&reader, config_reader->config->hba_file);
if (rc == -1)
return -1;
rc = od_hba_reader_parse(&reader);
od_config_reader_close(&reader);
return rc;
}
static int od_config_reader_parse(od_config_reader_t *reader,
od_extention_t *extentions)
{
@ -1665,7 +1684,8 @@ static int od_config_reader_parse(od_config_reader_t *reader,
return NOT_OK_RESPONSE;
rc = od_config_reader_import(
reader->config, reader->rules, reader->error,
extentions, reader->global, config_file);
extentions, reader->global, reader->hba_rules,
config_file);
free(config_file);
if (rc == -1) {
goto error;
@ -2035,6 +2055,15 @@ static int od_config_reader_parse(od_config_reader_t *reader,
}
continue;
}
case OD_LHBA_FILE: {
rc = od_config_reader_string(reader, &config->hba_file);
if (rc == -1)
goto error;
rc = od_config_reader_hba_import(reader);
if (rc == -1)
goto error;
continue;
}
default:
od_config_reader_error(reader, &token,
"unexpected parameter");
@ -2055,13 +2084,15 @@ success:
int od_config_reader_import(od_config_t *config, od_rules_t *rules,
od_error_t *error, od_extention_t *extentions,
od_global_t *global, char *config_file)
od_global_t *global, od_hba_rules_t *hba_rules,
char *config_file)
{
od_config_reader_t reader;
memset(&reader, 0, sizeof(reader));
reader.error = error;
reader.config = config;
reader.rules = rules;
reader.hba_rules = hba_rules;
reader.global = global;
int rc;
rc = od_config_reader_open(&reader, config_file);

View File

@ -8,7 +8,8 @@
*/
extern int od_config_reader_import(od_config_t *, od_rules_t *, od_error_t *,
od_extention_t *, od_global_t *, char *);
od_extention_t *, od_global_t *,
od_hba_rules_t *, char *);
#define OD_READER_ERROR_MAX_LEN 1 << 8

View File

@ -2125,8 +2125,16 @@ void od_frontend(void *arg)
}
}
/* HBA check */
rc = od_hba_process(client);
/* client authentication */
rc = od_auth_frontend(client);
if (rc == OK_RESPONSE) {
rc = od_auth_frontend(client);
} else {
od_frontend_error(client, KIWI_INVALID_PASSWORD,
"host based authentication rejected");
}
if (rc != OK_RESPONSE) {
/* rc == -1

View File

@ -16,11 +16,13 @@ struct od_global {
void *cron;
void *worker_pool;
void *extentions;
void *hba;
};
static inline void od_global_init(od_global_t *global, void *instance,
void *system, void *router, void *cron,
void *worker_pool, void *extentions)
void *worker_pool, void *extentions,
void *hba)
{
global->instance = instance;
global->system = system;
@ -28,6 +30,7 @@ static inline void od_global_init(od_global_t *global, void *instance,
global->cron = cron;
global->worker_pool = worker_pool;
global->extentions = extentions;
global->hba = hba;
}
#endif /* ODYSSEY_GLOBAL_H */

164
sources/hba.c Normal file
View File

@ -0,0 +1,164 @@
/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
#include <odyssey.h>
void od_hba_init(od_hba_t *hba)
{
pthread_mutexattr_init(&hba->attr);
pthread_rwlockattr_setkind_np(
&hba->attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
pthread_mutex_init(&hba->lock, &hba->attr);
od_hba_rules_init(&hba->rules);
}
void od_hba_free(od_hba_t *hba)
{
od_hba_rules_free(&hba->rules);
pthread_mutex_destroy(&hba->lock);
pthread_mutexattr_destroy(&hba->attr);
}
void od_hba_lock(od_hba_t *hba)
{
pthread_mutex_lock(&hba->lock);
}
void od_hba_unlock(od_hba_t *hba)
{
pthread_mutex_unlock(&hba->lock);
}
void od_hba_reload(od_hba_t *hba, od_hba_rules_t *rules)
{
od_hba_lock(hba);
od_list_init(&hba->rules);
memcpy(&hba->rules, &rules, sizeof(hba->rules));
od_hba_unlock(hba);
}
bool od_hba_validate_addr(od_hba_rule_t *rule, struct sockaddr_storage *sa)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
struct sockaddr_in *rule_addr = (struct sockaddr_in *)&rule->addr;
struct sockaddr_in *rule_mask = (struct sockaddr_in *)&rule->mask;
in_addr_t client_addr = sin->sin_addr.s_addr;
in_addr_t client_net = rule_mask->sin_addr.s_addr & client_addr;
return (client_net ^ rule_addr->sin_addr.s_addr) == 0;
}
bool od_hba_validate_addr6(od_hba_rule_t *rule, struct sockaddr_storage *sa)
{
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
struct sockaddr_in6 *rule_addr = (struct sockaddr_in6 *)&rule->addr;
struct sockaddr_in6 *rule_mask = (struct sockaddr_in6 *)&rule->mask;
for (int i = 0; i < 16; ++i) {
uint8_t client_net_byte = rule_mask->sin6_addr.s6_addr[i] &
sin->sin6_addr.s6_addr[i];
if (client_net_byte ^ rule_addr->sin6_addr.s6_addr[i]) {
return false;
}
}
return true;
}
bool od_hba_validate_name(char *client_name, od_hba_rule_name_t *name,
char *client_other_name)
{
if (name->flags & OD_HBA_NAME_ALL) {
return true;
}
if ((name->flags & OD_HBA_NAME_SAMEUSER) &&
strcmp(client_name, client_other_name) == 0) {
return true;
}
od_list_t *i;
od_hba_rule_name_item_t *item;
od_list_foreach(&name->values, i)
{
item = od_container_of(i, od_hba_rule_name_item_t, link);
if (item->value != NULL &&
strcmp(client_name, item->value) == 0) {
return true;
}
}
return false;
}
int od_hba_process(od_client_t *client)
{
od_instance_t *instance = client->global->instance;
od_hba_t *hba = client->global->hba;
od_list_t *i;
od_hba_rule_t *rule;
od_hba_rules_t *rules;
if (instance->config.hba_file == NULL) {
return OK_RESPONSE;
}
struct sockaddr_storage sa;
int salen = sizeof(sa);
struct sockaddr *saddr = (struct sockaddr *)&sa;
int rc = machine_getpeername(client->io.io, saddr, &salen);
if (rc == -1)
return -1;
od_hba_lock(hba);
rules = &hba->rules;
od_hba_unlock(hba);
od_list_foreach(rules, i)
{
rule = od_container_of(i, od_hba_rule_t, link);
if (sa.ss_family == AF_UNIX) {
if (rule->connection_type != OD_CONFIG_HBA_LOCAL)
continue;
} else if (rule->connection_type == OD_CONFIG_HBA_LOCAL) {
continue;
} else if (rule->connection_type == OD_CONFIG_HBA_HOSTSSL &&
!client->startup.is_ssl_request) {
continue;
} else if (rule->connection_type == OD_CONFIG_HBA_HOSTNOSSL &&
client->startup.is_ssl_request) {
continue;
} else if (sa.ss_family == AF_INET) {
if (rule->addr.ss_family != AF_INET ||
!od_hba_validate_addr(rule, &sa)) {
continue;
}
} else if (sa.ss_family == AF_INET6) {
if (rule->addr.ss_family != AF_INET6 ||
!od_hba_validate_addr6(rule, &sa)) {
continue;
}
}
if (!od_hba_validate_name(client->rule->db_name,
&rule->database,
client->rule->user_name)) {
continue;
}
if (!od_hba_validate_name(client->rule->user_name, &rule->user,
client->rule->db_name)) {
continue;
}
rc = rule->auth_method == OD_CONFIG_HBA_ALLOW ? OK_RESPONSE :
NOT_OK_RESPONSE;
return rc;
}
return NOT_OK_RESPONSE;
}

24
sources/hba.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef ODYSSEY_HBA_H
#define ODYSSEY_HBA_H
/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
typedef struct od_hba od_hba_t;
struct od_hba {
pthread_mutex_t lock;
pthread_mutexattr_t attr;
od_hba_rules_t rules;
};
void od_hba_init(od_hba_t *hba);
void od_hba_free(od_hba_t *hba);
void od_hba_reload(od_hba_t *hba, od_hba_rules_t *rules);
int od_hba_process(od_client_t *client);
#endif // ODYSSEY_HBA_H

397
sources/hba_reader.c Normal file
View File

@ -0,0 +1,397 @@
/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
#include <arpa/inet.h>
#include <kiwi.h>
#include <machinarium.h>
#include <odyssey.h>
enum { OD_LLOCAL,
OD_LHOST,
OD_LHOSTSSL,
OD_LHOSTNOSSL,
OD_LALL,
OD_LSAMEUSER,
OD_LALLOW,
OD_LDENY,
};
static od_keyword_t od_hba_keywords[] = {
/* connection types */
od_keyword("local", OD_LLOCAL),
od_keyword("host", OD_LHOST),
od_keyword("hostssl", OD_LHOSTSSL),
od_keyword("hostnossl", OD_LHOSTNOSSL),
/* db/user */
od_keyword("all", OD_LALL),
od_keyword("sameuser", OD_LSAMEUSER),
/* auth type */
od_keyword("allow", OD_LALLOW),
od_keyword("trust", OD_LALLOW),
od_keyword("deny", OD_LDENY),
od_keyword("reject", OD_LDENY),
};
static void od_hba_reader_error(od_config_reader_t *reader, char *msg)
{
od_errorf(reader->error, "%s:%d %s", reader->config_file,
reader->parser.line, msg);
}
static int od_hba_parser_next(od_parser_t *parser, od_token_t *token)
{
/* try to use backlog */
if (parser->backlog_count > 0) {
*token = parser->backlog[parser->backlog_count - 1];
parser->backlog_count--;
return token->type;
}
/* skip white spaces and comments */
for (;;) {
while (parser->pos < parser->end && isspace(*parser->pos)) {
if (*parser->pos == '\n')
parser->line++;
parser->pos++;
}
if (od_unlikely(parser->pos == parser->end)) {
token->type = OD_PARSER_EOF;
return token->type;
}
if (*parser->pos != '#')
break;
while (parser->pos < parser->end && *parser->pos != '\n')
parser->pos++;
if (parser->pos == parser->end) {
token->type = OD_PARSER_EOF;
return token->type;
}
parser->line++;
}
/* symbols */
if (*parser->pos != '\"' && ispunct(*parser->pos)) {
token->type = OD_PARSER_SYMBOL;
token->line = parser->line;
token->value.num = *parser->pos;
parser->pos++;
return token->type;
}
if (isalnum(*parser->pos)) {
token->type = OD_PARSER_KEYWORD;
token->line = parser->line;
token->value.string.pointer = parser->pos;
while (parser->pos < parser->end && *parser->pos != ',' &&
(isalnum(*parser->pos) || ispunct(*parser->pos)))
parser->pos++;
token->value.string.size =
parser->pos - token->value.string.pointer;
return token->type;
}
if (*parser->pos == '\"') {
token->type = OD_PARSER_STRING;
token->line = parser->line;
parser->pos++;
token->value.string.pointer = parser->pos;
while (parser->pos < parser->end && *parser->pos != '\"') {
if (*parser->pos == '\n') {
token->type = OD_PARSER_ERROR;
return token->type;
}
parser->pos++;
}
if (od_unlikely(parser->pos == parser->end)) {
token->type = OD_PARSER_ERROR;
return token->type;
}
token->value.string.size =
parser->pos - token->value.string.pointer;
parser->pos++;
return token->type;
}
/* error */
token->type = OD_PARSER_ERROR;
token->line = parser->line;
return token->type;
}
static int od_hba_reader_match_string(od_token_t token, char **value)
{
char *copy = malloc(token.value.string.size + 1);
if (copy == NULL) {
return NOT_OK_RESPONSE;
}
memcpy(copy, token.value.string.pointer, token.value.string.size);
copy[token.value.string.size] = 0;
if (*value)
free(*value);
*value = copy;
return OK_RESPONSE;
}
static int od_hba_reader_value(od_config_reader_t *reader, void **dest)
{
od_token_t token;
int rc;
char *string_value = NULL;
rc = od_hba_parser_next(&reader->parser, &token);
switch (rc) {
case OD_PARSER_EOF:
return rc;
case OD_PARSER_KEYWORD: {
od_keyword_t *match;
match = od_keyword_match(od_hba_keywords, &token);
if (match) {
*dest = match;
return OD_PARSER_KEYWORD;
}
if (od_hba_reader_match_string(token, &string_value) ==
OK_RESPONSE) {
*dest = string_value;
return OD_PARSER_STRING;
}
od_hba_reader_error(reader, "unable to read string");
return -1;
}
case OD_PARSER_STRING:
if (od_hba_reader_match_string(token, &string_value) ==
OK_RESPONSE) {
*dest = string_value;
return OD_PARSER_STRING;
}
od_hba_reader_error(reader, "unable to read string");
return -1;
default:
od_hba_reader_error(reader, "expected string or keyword");
return -1;
}
}
static int od_hba_reader_address(struct sockaddr_storage *dest,
const char *addr)
{
int rc;
rc = inet_pton(AF_INET, addr, &((struct sockaddr_in *)dest)->sin_addr);
if (rc > 0) {
dest->ss_family = AF_INET;
return 0;
}
if (inet_pton(AF_INET6, addr,
&((struct sockaddr_in6 *)dest)->sin6_addr) > 0) {
dest->ss_family = AF_INET6;
return 0;
}
return -1;
}
static int od_hba_reader_prefix(od_hba_rule_t *hba, char *prefix)
{
char *end = NULL;
unsigned long int len = strtoul(prefix, &end, 10);
if (hba->addr.ss_family == AF_INET) {
if (len > 32)
return -1;
uint32_t mask = 0;
unsigned int i;
struct sockaddr_in *addr = (struct sockaddr_in *)&hba->mask;
for (i = 0; i < len / 8; ++i) {
mask = 0xff | (mask << 8);
}
if (len % 8 != 0)
mask = mask | ((len % 8) << (i * 8));
addr->sin_addr.s_addr = mask;
return 0;
} else if (hba->addr.ss_family == AF_INET6) {
if (len > 128)
return -1;
unsigned int i;
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&hba->mask;
for (i = 0; i < len / 8; ++i) {
addr->sin6_addr.s6_addr[i] = 0xff;
}
if (len % 8 != 0)
addr->sin6_addr.s6_addr[i] = len & 7;
return 0;
}
return -1;
}
static int od_hba_reader_name(od_config_reader_t *reader,
struct od_hba_rule_name *name, bool is_db)
{
od_keyword_t *keyword = NULL;
int rc;
void *value = NULL;
od_token_t token;
while (1) {
rc = od_hba_reader_value(reader, &value);
switch (rc) {
case OD_PARSER_STRING: {
struct od_hba_rule_name_item *item =
od_hba_rule_name_item_add(name);
item->value = (char *)value;
break;
}
case OD_PARSER_KEYWORD:
keyword = (od_keyword_t *)value;
switch (keyword->id) {
case OD_LALL:
name->flags |= OD_HBA_NAME_ALL;
break;
case OD_LSAMEUSER:
if (is_db) {
name->flags |= OD_HBA_NAME_SAMEUSER;
}
}
break;
default:
od_hba_reader_error(reader, "expected name or keyword");
return -1;
}
rc = od_hba_parser_next(&reader->parser, &token);
if (rc == OD_PARSER_SYMBOL && token.value.num == ',') {
continue;
}
od_parser_push(&reader->parser, &token);
return 0;
}
}
int od_hba_reader_parse(od_config_reader_t *reader)
{
od_hba_rule_t *hba = NULL;
for (;;) {
hba = od_hba_rule_create();
if (hba == NULL) {
od_hba_reader_error(reader, "memory allocation error");
return -1;
}
/* connection type */
od_keyword_t *keyword = NULL;
void *connection_type = NULL;
od_hba_rule_conn_type_t conn_type;
int rc;
rc = od_hba_reader_value(reader, &connection_type);
if (rc == OD_PARSER_EOF) {
return 0;
}
if (rc != OD_PARSER_KEYWORD) {
od_hba_reader_error(reader, "invalid connection type");
goto error;
}
keyword = (od_keyword_t *)connection_type;
switch (keyword->id) {
case OD_LLOCAL:
conn_type = OD_CONFIG_HBA_LOCAL;
break;
case OD_LHOST:
conn_type = OD_CONFIG_HBA_HOST;
break;
case OD_LHOSTSSL:
conn_type = OD_CONFIG_HBA_HOSTSSL;
break;
case OD_LHOSTNOSSL:
conn_type = OD_CONFIG_HBA_HOSTNOSSL;
break;
default:
od_hba_reader_error(reader, "invalid connection type");
goto error;
}
hba->connection_type = conn_type;
/* db & user name */
if (od_hba_reader_name(reader, &hba->database, true) != 0) {
goto error;
}
if (od_hba_reader_name(reader, &hba->user, false) != 0) {
goto error;
}
if (conn_type != OD_CONFIG_HBA_LOCAL) {
void *address = NULL;
char *mask = NULL;
/* ip address */
rc = od_hba_reader_value(reader, &address);
if (rc != OD_PARSER_STRING) {
od_hba_reader_error(reader,
"expected IP address");
goto error;
}
mask = strchr(address, '/');
if (mask)
*mask++ = 0;
if (od_hba_reader_address(&hba->addr, address) == -1) {
od_hba_reader_error(reader,
"invalid IP address");
goto error;
}
/* network mask */
if (mask) {
if (od_hba_reader_prefix(hba, mask) == -1) {
od_hba_reader_error(
reader,
"invalid network prefix length");
goto error;
}
} else {
rc = od_hba_reader_value(reader, &address);
if (rc != OD_PARSER_STRING) {
od_hba_reader_error(
reader,
"expected network mask");
goto error;
}
if (od_hba_reader_address(&hba->mask,
address) == -1) {
od_hba_reader_error(
reader, "invalid network mask");
goto error;
}
}
}
/* auth method */
void *auth_method = NULL;
rc = od_hba_reader_value(reader, &auth_method);
if (rc != OD_PARSER_KEYWORD) {
od_hba_reader_error(reader, "expected auth method");
goto error;
}
keyword = (od_keyword_t *)auth_method;
switch (keyword->id) {
case OD_LALLOW:
hba->auth_method = OD_CONFIG_HBA_ALLOW;
break;
case OD_LDENY:
hba->auth_method = OD_CONFIG_HBA_DENY;
break;
default:
od_hba_reader_error(
reader,
"invalid auth method: only allow/deny or trust/reject is now supported");
goto error;
}
od_hba_rules_add(reader->hba_rules, hba);
}
error:
od_hba_rule_free(hba);
return -1;
}

6
sources/hba_reader.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef ODYSSEY_HBA_READER_H
#define ODYSSEY_HBA_READER_H
int od_hba_reader_parse(od_config_reader_t *reader);
#endif // ODYSSEY_HBA_READER_H

75
sources/hba_rule.c Normal file
View File

@ -0,0 +1,75 @@
/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
#include <machinarium.h>
#include <kiwi.h>
#include <odyssey.h>
od_hba_rule_name_item_t *od_hba_rule_name_item_add(od_hba_rule_name_t *name)
{
od_hba_rule_name_item_t *item;
item = (od_hba_rule_name_item_t *)malloc(sizeof(*item));
if (item == NULL)
return NULL;
memset(item, 0, sizeof(*item));
od_list_init(&item->link);
od_list_append(&name->values, &item->link);
return item;
}
od_hba_rule_t *od_hba_rule_create()
{
od_hba_rule_t *hba;
hba = (od_hba_rule_t *)malloc(sizeof(*hba));
if (hba == NULL)
return NULL;
memset(hba, 0, sizeof(*hba));
od_list_init(&hba->database.values);
od_list_init(&hba->user.values);
return hba;
}
void od_hba_rule_free(od_hba_rule_t *hba)
{
od_list_t *i, *n;
od_hba_rule_name_item_t *item;
od_list_foreach_safe(&hba->database.values, i, n)
{
item = od_container_of(i, od_hba_rule_name_item_t, link);
free(item->value);
free(item);
}
od_list_foreach_safe(&hba->user.values, i, n)
{
item = od_container_of(i, od_hba_rule_name_item_t, link);
free(item->value);
free(item);
}
free(hba);
}
void od_hba_rules_init(od_hba_rules_t *rules)
{
od_list_init(rules);
}
void od_hba_rules_free(od_hba_rules_t *rules)
{
od_list_t *i, *n;
od_list_foreach_safe(rules, i, n)
{
od_hba_rule_t *hba;
hba = od_container_of(i, od_hba_rule_t, link);
od_hba_rule_free(hba);
}
}
void od_hba_rules_add(od_hba_rules_t *rules, od_hba_rule_t *rule)
{
od_list_init(&rule->link);
od_list_append(rules, &rule->link);
}

59
sources/hba_rule.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef ODYSSEY_HBA_RULE_H
#define ODYSSEY_HBA_RULE_H
/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
#define OD_HBA_NAME_ALL 1
#define OD_HBA_NAME_SAMEUSER 2
typedef struct od_hba_rule od_hba_rule_t;
typedef enum {
OD_CONFIG_HBA_LOCAL,
OD_CONFIG_HBA_HOST,
OD_CONFIG_HBA_HOSTSSL,
OD_CONFIG_HBA_HOSTNOSSL
} od_hba_rule_conn_type_t;
typedef enum {
OD_CONFIG_HBA_ALLOW,
OD_CONFIG_HBA_DENY,
} od_hba_rule_auth_method_t;
typedef struct od_hba_rule_name_item od_hba_rule_name_item_t;
struct od_hba_rule_name_item {
char *value;
od_list_t link;
};
typedef struct od_hba_rule_name od_hba_rule_name_t;
struct od_hba_rule_name {
unsigned int flags;
od_list_t values;
};
struct od_hba_rule {
od_hba_rule_conn_type_t connection_type;
od_hba_rule_name_t database;
od_hba_rule_name_t user;
struct sockaddr_storage addr;
struct sockaddr_storage mask;
od_hba_rule_auth_method_t auth_method;
od_list_t link;
};
typedef od_list_t od_hba_rules_t;
od_hba_rule_name_item_t *od_hba_rule_name_item_add(od_hba_rule_name_t *name);
od_hba_rule_t *od_hba_rule_create();
void od_hba_rule_free(od_hba_rule_t *hba);
void od_hba_rules_init(od_hba_rules_t *rules);
void od_hba_rules_free(od_hba_rules_t *rules);
void od_hba_rules_add(od_hba_rules_t *rules, od_hba_rule_t *rule);
#endif /* ODYSSEY_HBA_RULE_H */

View File

@ -95,21 +95,23 @@ int od_instance_main(od_instance_t *instance, int argc, char **argv)
od_worker_pool_t worker_pool;
od_extention_t extentions;
od_global_t global;
od_hba_t hba;
od_system_init(&system);
od_router_init(&router, &global);
od_cron_init(&cron);
od_worker_pool_init(&worker_pool);
od_extentions_init(&extentions);
od_hba_init(&hba);
od_global_init(&global, instance, &system, &router, &cron, &worker_pool,
&extentions);
&extentions, &hba);
/* read config file */
od_error_t error;
od_error_init(&error);
int rc;
rc = od_config_reader_import(&instance->config, &router.rules, &error,
&extentions, &global,
&extentions, &global, &hba.rules,
instance->config_file);
if (rc == -1) {
od_error(&instance->logger, "config", NULL, NULL, "%s",

View File

@ -54,6 +54,7 @@
#include "sources/storage.h"
#include "sources/pool.h"
#include "sources/rules.h"
#include "sources/hba_rule.h"
#include "sources/config_common.h"
@ -95,11 +96,13 @@
#include "sources/module.h"
#include "sources/extention.h"
#include "sources/hba_reader.h"
#include "sources/config_reader.h"
#include "sources/auth.h"
#include "sources/query.h"
#include "sources/auth_query.h"
#include "sources/hba.h"
#include "sources/od_dlsym.h"
#include "sources/daemon.h"

View File

@ -349,6 +349,7 @@ void od_system_config_reload(od_system_t *system)
od_instance_t *instance = system->global->instance;
od_router_t *router = system->global->router;
od_extention_t *extentions = system->global->extentions;
od_hba_t *hba = system->global->hba;
od_log(&instance->logger, "config", NULL, NULL,
"importing changes from '%s'", instance->config_file);
@ -366,9 +367,13 @@ void od_system_config_reload(od_system_t *system)
od_rules_t rules;
od_rules_init(&rules);
od_hba_rules_t hba_rules;
od_hba_rules_init(&hba_rules);
int rc;
rc = od_config_reader_import(&config, &rules, &error, extentions,
system->global, instance->config_file);
system->global, &hba_rules,
instance->config_file);
if (rc == -1) {
od_error(&instance->logger, "config", NULL, NULL, "%s",
error.error);
@ -394,6 +399,7 @@ void od_system_config_reload(od_system_t *system)
return;
}
od_config_reload(&instance->config, &config);
od_hba_reload(hba, &hba_rules);
pthread_mutex_unlock(&router->rules.mu);
@ -451,6 +457,7 @@ void od_system_config_reload(od_system_t *system)
}
od_config_free(&config);
od_hba_rules_free(&hba_rules);
if (instance->config.log_config)
od_rules_print(&rules, &instance->logger);