Separate server and client passwords optionally (#407)

* Separate server and user passwords

* config
This commit is contained in:
Lev Kokotov
2023-04-18 09:57:17 -07:00
committed by GitHub
parent a18eb42df5
commit 3dae3d0777
7 changed files with 77 additions and 75 deletions

View File

@@ -108,7 +108,7 @@ If we should log client disconnections
### autoreload ### autoreload
``` ```
path: general.autoreload path: general.autoreload
default: false default: 15000
``` ```
When set to true, PgCat reloads configs if it detects a change in the config file. When set to true, PgCat reloads configs if it detects a change in the config file.
@@ -127,7 +127,7 @@ path: general.tcp_keepalives_idle
default: 5 default: 5
``` ```
Number of seconds of connection idleness to wait before sending a keepalive packet to the server and client. Number of seconds of connection idleness to wait before sending a keepalive packet to the server.
### tcp_keepalives_count ### tcp_keepalives_count
``` ```
@@ -175,41 +175,11 @@ Connecting to that database allows running commands like `SHOW POOLS`, `SHOW DAT
### admin_password ### admin_password
``` ```
path: general.admin_password path: general.admin_password
default: <UNSET> default: "admin_pass"
``` ```
Password to access the virtual administrative database Password to access the virtual administrative database
### auth_query (experimental)
```
path: general.auth_query
default: <UNSET>
```
Query to be sent to servers to obtain the hash used for md5 authentication. The connection will be
established using the database configured in the pool. This parameter is inherited by every pool
and can be redefined in pool configuration.
### auth_query_user (experimental)
```
path: general.auth_query_user
default: <UNSET>
```
User to be used for connecting to servers to obtain the hash used for md5 authentication by sending the query
specified in `auth_query_user`. The connection will be established using the database configured in the pool.
This parameter is inherited by every pool and can be redefined in pool configuration.
### auth_query_password (experimental)
```
path: general.auth_query_password
default: <UNSET>
```
Password to be used for connecting to servers to obtain the hash used for md5 authentication by sending the query
specified in `auth_query_user`. The connection will be established using the database configured in the pool.
This parameter is inherited by every pool and can be redefined in pool configuration.
## `pools.<pool_name>` Section ## `pools.<pool_name>` Section
### pool_mode ### pool_mode
@@ -243,7 +213,7 @@ If the client doesn't specify, PgCat routes traffic to this role by default.
`replica` round-robin between replicas only without touching the primary, `replica` round-robin between replicas only without touching the primary,
`primary` all queries go to the primary unless otherwise specified. `primary` all queries go to the primary unless otherwise specified.
### query_parser_enabled (experimental) ### query_parser_enabled
``` ```
path: pools.<pool_name>.query_parser_enabled path: pools.<pool_name>.query_parser_enabled
default: true default: true
@@ -264,7 +234,7 @@ If the query parser is enabled and this setting is enabled, the primary will be
load balancing of read queries. Otherwise, the primary will only be used for write load balancing of read queries. Otherwise, the primary will only be used for write
queries. The primary can always be explicitly selected with our custom protocol. queries. The primary can always be explicitly selected with our custom protocol.
### sharding_key_regex (experimental) ### sharding_key_regex
``` ```
path: pools.<pool_name>.sharding_key_regex path: pools.<pool_name>.sharding_key_regex
default: <UNSET> default: <UNSET>
@@ -286,7 +256,7 @@ Current options:
`pg_bigint_hash`: PARTITION BY HASH (Postgres hashing function) `pg_bigint_hash`: PARTITION BY HASH (Postgres hashing function)
`sha1`: A hashing function based on SHA1 `sha1`: A hashing function based on SHA1
### automatic_sharding_key (experimental) ### automatic_sharding_key
``` ```
path: pools.<pool_name>.automatic_sharding_key path: pools.<pool_name>.automatic_sharding_key
default: <UNSET> default: <UNSET>
@@ -311,30 +281,6 @@ default: 3000
Connect timeout can be overwritten in the pool Connect timeout can be overwritten in the pool
### auth_query (experimental)
```
path: general.auth_query
default: <UNSET>
```
Auth query can be overwritten in the pool
### auth_query_user (experimental)
```
path: general.auth_query_user
default: <UNSET>
```
Auth query user can be overwritten in the pool
### auth_query_password (experimental)
```
path: general.auth_query_password
default: <UNSET>
```
Auth query password can be overwritten in the pool
## `pools.<pool_name>.users.<user_index>` Section ## `pools.<pool_name>.users.<user_index>` Section
### username ### username
@@ -343,7 +289,8 @@ path: pools.<pool_name>.users.<user_index>.username
default: "sharding_user" default: "sharding_user"
``` ```
Postgresql username PostgreSQL username used to authenticate the user and connect to the server
if `server_username` is not set.
### password ### password
``` ```
@@ -351,7 +298,26 @@ path: pools.<pool_name>.users.<user_index>.password
default: "sharding_user" default: "sharding_user"
``` ```
Postgresql password PostgreSQL password used to authenticate the user and connect to the server
if `server_password` is not set.
### server_username
```
path: pools.<pool_name>.users.<user_index>.server_username
default: <UNSET>
example: "another_user"
```
PostgreSQL username used to connect to the server.
### server_password
```
path: pools.<pool_name>.users.<user_index>.server_password
default: <UNSET>
example: "another_password"
```
PostgreSQL password used to connect to the server.
### pool_size ### pool_size
``` ```
@@ -382,7 +348,7 @@ default: [["127.0.0.1", 5432, "primary"], ["localhost", 5432, "replica"]]
Array of servers in the shard, each server entry is an array of `[host, port, role]` Array of servers in the shard, each server entry is an array of `[host, port, role]`
### mirrors (experimental) ### mirrors
``` ```
path: pools.<pool_name>.shards.<shard_index>.mirrors path: pools.<pool_name>.shards.<shard_index>.mirrors
default: <UNSET> default: <UNSET>

View File

@@ -125,10 +125,22 @@ connect_timeout = 3000
# User configs are structured as pool.<pool_name>.users.<user_index> # User configs are structured as pool.<pool_name>.users.<user_index>
# This section holds the credentials for users that may connect to this cluster # This section holds the credentials for users that may connect to this cluster
[pools.sharded_db.users.0] [pools.sharded_db.users.0]
# Postgresql username # PostgreSQL username used to authenticate the user and connect to the server
# if `server_username` is not set.
username = "sharding_user" username = "sharding_user"
# Postgresql password
# PostgreSQL password used to authenticate the user and connect to the server
# if `server_password` is not set.
password = "sharding_user" password = "sharding_user"
pool_mode = "session"
# PostgreSQL username used to connect to the server.
# server_username = "another_user"
# PostgreSQL password used to connect to the server.
# server_password = "another_password"
# Maximum number of server connections that can be established for this user # Maximum number of server connections that can be established for this user
# The maximum number of connection from a single Pgcat process to any database in the cluster # The maximum number of connection from a single Pgcat process to any database in the cluster
# is the sum of pool_size across all users. # is the sum of pool_size across all users.

View File

@@ -72,6 +72,8 @@ impl AuthPassthrough {
let auth_user = crate::config::User { let auth_user = crate::config::User {
username: self.user.clone(), username: self.user.clone(),
password: Some(self.password.clone()), password: Some(self.password.clone()),
server_username: None,
server_password: None,
pool_size: 1, pool_size: 1,
statement_timeout: 0, statement_timeout: 0,
pool_mode: None, pool_mode: None,

View File

@@ -1128,6 +1128,11 @@ where
self.buffer.put(&message[..]); self.buffer.put(&message[..]);
} }
// Close the prepared statement.
'C' => {
self.buffer.put(&message[..]);
}
// Execute // Execute
// Execute a prepared statement prepared in `P` and bound in `B`. // Execute a prepared statement prepared in `P` and bound in `B`.
'E' => { 'E' => {

View File

@@ -178,6 +178,8 @@ impl Address {
pub struct User { pub struct User {
pub username: String, pub username: String,
pub password: Option<String>, pub password: Option<String>,
pub server_username: Option<String>,
pub server_password: Option<String>,
pub pool_size: u32, pub pool_size: u32,
pub pool_mode: Option<PoolMode>, pub pool_mode: Option<PoolMode>,
#[serde(default)] // 0 #[serde(default)] // 0
@@ -189,6 +191,8 @@ impl Default for User {
User { User {
username: String::from("postgres"), username: String::from("postgres"),
password: None, password: None,
server_username: None,
server_password: None,
pool_size: 15, pool_size: 15,
statement_timeout: 0, statement_timeout: 0,
pool_mode: None, pool_mode: None,

View File

@@ -103,19 +103,32 @@ impl Server {
trace!("Sending StartupMessage"); trace!("Sending StartupMessage");
// StartupMessage // StartupMessage
startup(&mut stream, &user.username, database).await?; let username = match user.server_username {
Some(ref server_username) => server_username,
None => &user.username,
};
let password = match user.server_password {
Some(ref server_password) => Some(server_password),
None => match user.password {
Some(ref password) => Some(password),
None => None,
},
};
startup(&mut stream, username, database).await?;
let mut server_info = BytesMut::new(); let mut server_info = BytesMut::new();
let mut process_id: i32 = 0; let mut process_id: i32 = 0;
let mut secret_key: i32 = 0; let mut secret_key: i32 = 0;
let server_identifier = ServerIdentifier::new(&user.username, &database); let server_identifier = ServerIdentifier::new(username, &database);
// We'll be handling multiple packets, but they will all be structured the same. // We'll be handling multiple packets, but they will all be structured the same.
// We'll loop here until this exchange is complete. // We'll loop here until this exchange is complete.
let mut scram: Option<ScramSha256> = None; let mut scram: Option<ScramSha256> = match password {
if let Some(password) = &user.password.clone() { Some(password) => Some(ScramSha256::new(password)),
scram = Some(ScramSha256::new(password)); None => None,
} };
loop { loop {
let code = match stream.read_u8().await { let code = match stream.read_u8().await {
@@ -172,11 +185,10 @@ impl Server {
} }
}; };
match &user.password { match password {
// Using plaintext password // Using plaintext password
Some(password) => { Some(password) => {
md5_password(&mut stream, &user.username, password, &salt[..]) md5_password(&mut stream, username, password, &salt[..]).await?
.await?
} }
// Using auth passthrough, in this case we should already have a // Using auth passthrough, in this case we should already have a

View File

@@ -0,0 +1 @@
tomli