Skip to content

Commit

Permalink
Allow running postgres on root + fix clippy lints
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszkj committed Apr 26, 2023
1 parent a9dd685 commit f974077
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 104 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/.idea
Cargo.lock
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ maintenance = { status = "experimental" }

[dependencies]
glob = "0.3"
lazy_static = "1.4.0"
nix = "0.22"
nix = "0.26"
tempdir = "0.3"
thiserror = "1.0"
tokio = { version = "1.8", features = ["parking_lot", "rt", "sync", "io-util", "process", "macros", "fs"], default-features = false, optional = true }
tracing = "0.1"
which = "4.0"
once_cell = "1"

[dev-dependencies]
test-env-log = { version = "0.2", default-features = false, features = ["trace"] }
test-log = { version = "0.2", default-features = false, features = ["trace"] }
tokio = { version = "1.8", features = ["parking_lot", "rt", "rt-multi-thread", "sync", "io-util", "process", "macros", "fs"], default-features = false }
tokio-postgres = "0.7"
tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "fmt"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] }

[features]
default = []
Expand Down
104 changes: 64 additions & 40 deletions src/asynchronous.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use nix::unistd::Uid;
use std::path::Path;
use std::process::Stdio;
use std::sync::Arc;
Expand All @@ -16,6 +17,7 @@ use tracing::{debug, instrument};

use crate::errors::{ProcessCapture, TmpPostgrustError, TmpPostgrustResult};
use crate::search::find_postgresql_command;
use crate::POSTGRES_UID_GID;

/// Limit the total processes that can be running at any one time.
pub(crate) static MAX_CONCURRENT_PROCESSES: Semaphore = Semaphore::const_new(8);
Expand All @@ -32,7 +34,7 @@ async fn exec_process(
.await
.map_err(|err| TmpPostgrustError::ExecSubprocessFailed {
source: err,
command: format!("{:?}", command),
command: format!("{command:?}"),
})?;

if output.status.success() {
Expand All @@ -56,12 +58,15 @@ pub(crate) fn start_postgres_subprocess(
let postgres_path =
find_postgresql_command("bin", "postgres").expect("failed to find postgres");

Command::new(postgres_path)
let mut command = Command::new(postgres_path);
command
.env("PGDATA", data_directory.to_str().unwrap())
.arg("-p")
.arg(port.to_string())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stderr(Stdio::piped());
cmd_as_non_root(&mut command);
command
.spawn()
.map_err(TmpPostgrustError::SpawnSubprocessFailed)
}
Expand All @@ -71,13 +76,12 @@ pub(crate) async fn exec_init_db(data_directory: &'_ Path) -> TmpPostgrustResult
let initdb_path = find_postgresql_command("bin", "initdb").expect("failed to find initdb");

debug!("Initializing database in: {:?}", data_directory);
exec_process(
&mut Command::new(initdb_path)
.env("PGDATA", data_directory.to_str().unwrap())
.arg("--username=postgres"),
TmpPostgrustError::InitDBFailed,
)
.await
let mut command = Command::new(initdb_path);
command
.env("PGDATA", data_directory.to_str().unwrap())
.arg("--username=postgres");
cmd_as_non_root(&mut command);
exec_process(&mut command, TmpPostgrustError::InitDBFailed).await
}

#[instrument]
Expand Down Expand Up @@ -117,21 +121,20 @@ pub(crate) async fn exec_create_db(
owner: &'_ str,
dbname: &'_ str,
) -> TmpPostgrustResult<()> {
exec_process(
&mut Command::new("createdb")
.arg("-h")
.arg(socket)
.arg("-p")
.arg(port.to_string())
.arg("-U")
.arg("postgres")
.arg("-O")
.arg(owner)
.arg("--echo")
.arg(dbname),
TmpPostgrustError::CreateDBFailed,
)
.await
let mut command = Command::new("createdb");
command
.arg("-h")
.arg(socket)
.arg("-p")
.arg(port.to_string())
.arg("-U")
.arg("postgres")
.arg("-O")
.arg(owner)
.arg("--echo")
.arg(dbname);
cmd_as_non_root(&mut command);
exec_process(&mut command, TmpPostgrustError::CreateDBFailed).await
}

#[instrument]
Expand All @@ -140,23 +143,35 @@ pub(crate) async fn exec_create_user(
port: u32,
username: &'_ str,
) -> TmpPostgrustResult<()> {
exec_process(
&mut Command::new("createuser")
.arg("-h")
.arg(socket)
.arg("-p")
.arg(port.to_string())
.arg("-U")
.arg("postgres")
.arg("--superuser")
.arg("--echo")
.arg(username),
TmpPostgrustError::CreateDBFailed,
)
.await
let mut command = Command::new("createuser");
command
.arg("-h")
.arg(socket)
.arg("-p")
.arg(port.to_string())
.arg("-U")
.arg("postgres")
.arg("--superuser")
.arg("--echo")
.arg(username);
cmd_as_non_root(&mut command);
exec_process(&mut command, TmpPostgrustError::CreateDBFailed).await
}

/// ProcessGuard represents a postgresql process that is running in the background.
#[instrument]
pub(crate) async fn chown_to_non_root(dir: &Path) -> TmpPostgrustResult<()> {
let current_uid = Uid::effective();
if !current_uid.is_root() {
return Ok(());
}

let (uid, gid) = &*POSTGRES_UID_GID;
let mut cmd = Command::new("chown");
cmd.arg("-R").arg(format!("{uid}:{gid}")).arg(dir);
exec_process(&mut cmd, TmpPostgrustError::UpdatingPermissionsFailed).await
}

/// `ProcessGuard` represents a postgresql process that is running in the background.
/// once the guard is dropped the process will be killed.
pub struct ProcessGuard {
/// Allows users to read stdout by line for debugging.
Expand Down Expand Up @@ -188,3 +203,12 @@ impl Drop for ProcessGuard {
}
}
}

fn cmd_as_non_root(command: &mut Command) {
let current_uid = Uid::effective();
if current_uid.is_root() {
// PostgreSQL cannot be run as root, so change to default user
let (user_id, group_id) = &*POSTGRES_UID_GID;
command.uid(user_id.as_raw()).gid(group_id.as_raw());
}
}
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub enum TmpPostgrustError {
/// Error when the cache directory cannot be created.
#[error("failed to create cache directory")]
CreateCacheDirFailed(#[source] std::io::Error),
/// Error when `cp` fails for the initialized database.
#[error("updating directory permission to non-root failed")]
UpdatingPermissionsFailed(ProcessCapture),
}

/// Result type for `TmpPostgrustError`, used by functions in this crate.
Expand Down
Loading

0 comments on commit f974077

Please sign in to comment.