Skip to content

Commit

Permalink
unattended setup - fixes #409
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugeny committed Oct 17, 2022
1 parent 8831d20 commit f967609
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 54 deletions.
154 changes: 102 additions & 52 deletions warpgate/src/commands/setup.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::net::ToSocketAddrs;
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::{Path, PathBuf};

use anyhow::Result;
Expand All @@ -21,6 +21,7 @@ use warpgate_db_entities::{Role, User, UserRoleAssignment};

use crate::commands::common::{assert_interactive_terminal, is_docker};
use crate::config::load_config;
use crate::Commands;

fn prompt_endpoint(prompt: &str, default: ListenEndpoint) -> ListenEndpoint {
loop {
Expand Down Expand Up @@ -53,7 +54,9 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
std::process::exit(1);
}

assert_interactive_terminal();
if let Commands::Setup = cli.command {
assert_interactive_terminal();
}

let mut config_dir = cli.config.parent().unwrap_or_else(|| Path::new(&"."));
if config_dir.as_os_str().is_empty() {
Expand Down Expand Up @@ -87,18 +90,22 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {

// ---

#[cfg(target_os = "linux")]
let default_data_path = "/var/lib/warpgate".to_string();
#[cfg(target_os = "macos")]
let default_data_path = "/usr/local/var/lib/warpgate".to_string();

let data_path: String = if is_docker() {
"/data".to_owned()
let data_path: String = if let Commands::UnattendedSetup { data_path, .. } = &cli.command {
data_path.to_owned()
} else {
dialoguer::Input::with_theme(&theme)
.default(default_data_path)
.with_prompt("Directory to store app data (up to a few MB) in")
.interact_text()?
#[cfg(target_os = "linux")]
let default_data_path = "/var/lib/warpgate".to_string();
#[cfg(target_os = "macos")]
let default_data_path = "/usr/local/var/lib/warpgate".to_string();

if is_docker() {
"/data".to_owned()
} else {
dialoguer::Input::with_theme(&theme)
.default(default_data_path)
.with_prompt("Directory to store app data (up to a few MB) in")
.interact_text()?
}
};

let db_path = PathBuf::from(&data_path).join("db");
Expand All @@ -115,42 +122,62 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
database_url.push_str(&db_path);
store.database_url = Secret::new(database_url);

// ---
if !is_docker() {
store.http.listen = prompt_endpoint(
"Endpoint to listen for HTTP connections on",
HTTPConfig::default().listen,
);

info!("You will now choose specific protocol listeners to be enabled.");
info!("");
info!("NB: Nothing will be exposed by default -");
info!(" you'll set target hosts in the config file later.");

store.ssh.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Accept SSH connections?")
.interact()?;

if store.ssh.enable {
store.ssh.listen = prompt_endpoint(
"Endpoint to listen for SSH connections on",
SSHConfig::default().listen,
if let Commands::UnattendedSetup { http_port, .. } = &cli.command {
store.http.enable = true;
store.http.listen = ListenEndpoint(SocketAddr::from(([0, 0, 0, 0], *http_port)));
} else {
if !is_docker() {
store.http.listen = prompt_endpoint(
"Endpoint to listen for HTTP connections on",
HTTPConfig::default().listen,
);
}
}

// ---

store.mysql.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Accept MySQL connections?")
.interact()?;
if let Commands::UnattendedSetup { ssh_port, .. } = &cli.command {
if let Some(ssh_port) = ssh_port {
store.ssh.enable = true;
store.ssh.listen = ListenEndpoint(SocketAddr::from(([0, 0, 0, 0], *ssh_port)));
}
} else {
if !is_docker() {
info!("You will now choose specific protocol listeners to be enabled.");
info!("");
info!("NB: Nothing will be exposed by default -");
info!(" you'll set target hosts in the config file later.");

store.ssh.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Accept SSH connections?")
.interact()?;

if store.ssh.enable {
store.ssh.listen = prompt_endpoint(
"Endpoint to listen for SSH connections on",
SSHConfig::default().listen,
);
}
}
}

if store.mysql.enable {
store.mysql.listen = prompt_endpoint(
"Endpoint to listen for MySQL connections on",
MySQLConfig::default().listen,
);
if let Commands::UnattendedSetup { ssh_port, .. } = &cli.command {
if let Some(ssh_port) = ssh_port {
store.ssh.enable = true;
store.ssh.listen = ListenEndpoint(SocketAddr::from(([0, 0, 0, 0], *ssh_port)));
}
} else {
if !is_docker() {
store.mysql.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Accept MySQL connections?")
.interact()?;

if store.mysql.enable {
store.mysql.listen = prompt_endpoint(
"Endpoint to listen for MySQL connections on",
MySQLConfig::default().listen,
);
}
}
}

Expand All @@ -176,20 +203,43 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {

// ---

store.recordings.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Do you want to record user sessions?")
.interact()?;
if let Commands::UnattendedSetup {
record_sessions, ..
} = &cli.command
{
store.recordings.enable = *record_sessions;
} else {
store.recordings.enable = dialoguer::Confirm::with_theme(&theme)
.default(true)
.with_prompt("Do you want to record user sessions?")
.interact()?;
}
store.recordings.path = PathBuf::from(&data_path)
.join("recordings")
.to_string_lossy()
.to_string();

// ---

let admin_password = dialoguer::Password::with_theme(&theme)
.with_prompt("Set a password for the Warpgate admin user")
.interact()?;
let admin_password = if let Commands::UnattendedSetup { admin_password, .. } = &cli.command {
if let Some(admin_password) = admin_password {
admin_password.to_owned()
} else {
if let Ok(admin_password) = std::env::var("WARPGATE_ADMIN_PASSWORD") {
admin_password
} else {
error!(
"You must supply the admin password either through the --admin-password option"
);
error!("or the WARPGATE_ADMIN_PASSWORD environment variable.");
std::process::exit(1);
}
}
} else {
dialoguer::Password::with_theme(&theme)
.with_prompt("Set a password for the Warpgate admin user")
.interact()?
};

// ---

Expand Down
32 changes: 30 additions & 2 deletions warpgate/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,35 @@ pub struct Cli {
}

#[derive(clap::Subcommand)]
enum Commands {
pub(crate) enum Commands {
/// Run first-time setup and generate a config file
Setup,
/// Run first-time setup non-interactively
UnattendedSetup {
/// Directory to store data in
#[clap(long)]
data_path: String,

/// HTTP port
#[clap(long)]
http_port: u16,

/// Enable SSH and set port
#[clap(long)]
ssh_port: Option<u16>,

/// Enable MySQL and set port
#[clap(long)]
mysql_port: Option<u16>,

/// Enable session recording
#[clap(long)]
record_sessions: bool,

/// Password for the initial user (required if WARPGATE_ADMIN_PASSWORD env var is not set)
#[clap(long)]
admin_password: Option<String>,
},
/// Show Warpgate's SSH client keys
ClientKeys,
/// Run Warpgate
Expand Down Expand Up @@ -62,7 +88,9 @@ async fn _main() -> Result<()> {
Commands::TestTarget { target_name } => {
crate::commands::test_target::command(&cli, target_name).await
}
Commands::Setup => crate::commands::setup::command(&cli).await,
Commands::Setup | Commands::UnattendedSetup { .. } => {
crate::commands::setup::command(&cli).await
}
Commands::ClientKeys => crate::commands::client_keys::command(&cli).await,
Commands::RecoverAccess { username } => {
crate::commands::recover_access::command(&cli, username).await
Expand Down

0 comments on commit f967609

Please sign in to comment.