mirror of
https://github.com/warp-tech/warpgate.git
synced 2025-10-06 05:17:42 +08:00
unattended setup - fixes #409
This commit is contained in:
parent
8831d2006f
commit
f96760982c
2 changed files with 131 additions and 53 deletions
|
@ -1,6 +1,6 @@
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -21,6 +21,7 @@ use warpgate_db_entities::{Role, User, UserRoleAssignment};
|
||||||
|
|
||||||
use crate::commands::common::{assert_interactive_terminal, is_docker};
|
use crate::commands::common::{assert_interactive_terminal, is_docker};
|
||||||
use crate::config::load_config;
|
use crate::config::load_config;
|
||||||
|
use crate::Commands;
|
||||||
|
|
||||||
fn prompt_endpoint(prompt: &str, default: ListenEndpoint) -> ListenEndpoint {
|
fn prompt_endpoint(prompt: &str, default: ListenEndpoint) -> ListenEndpoint {
|
||||||
loop {
|
loop {
|
||||||
|
@ -53,7 +54,9 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Commands::Setup = cli.command {
|
||||||
assert_interactive_terminal();
|
assert_interactive_terminal();
|
||||||
|
}
|
||||||
|
|
||||||
let mut config_dir = cli.config.parent().unwrap_or_else(|| Path::new(&"."));
|
let mut config_dir = cli.config.parent().unwrap_or_else(|| Path::new(&"."));
|
||||||
if config_dir.as_os_str().is_empty() {
|
if config_dir.as_os_str().is_empty() {
|
||||||
|
@ -87,18 +90,22 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
|
let data_path: String = if let Commands::UnattendedSetup { data_path, .. } = &cli.command {
|
||||||
|
data_path.to_owned()
|
||||||
|
} else {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
let default_data_path = "/var/lib/warpgate".to_string();
|
let default_data_path = "/var/lib/warpgate".to_string();
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let default_data_path = "/usr/local/var/lib/warpgate".to_string();
|
let default_data_path = "/usr/local/var/lib/warpgate".to_string();
|
||||||
|
|
||||||
let data_path: String = if is_docker() {
|
if is_docker() {
|
||||||
"/data".to_owned()
|
"/data".to_owned()
|
||||||
} else {
|
} else {
|
||||||
dialoguer::Input::with_theme(&theme)
|
dialoguer::Input::with_theme(&theme)
|
||||||
.default(default_data_path)
|
.default(default_data_path)
|
||||||
.with_prompt("Directory to store app data (up to a few MB) in")
|
.with_prompt("Directory to store app data (up to a few MB) in")
|
||||||
.interact_text()?
|
.interact_text()?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let db_path = PathBuf::from(&data_path).join("db");
|
let db_path = PathBuf::from(&data_path).join("db");
|
||||||
|
@ -115,13 +122,25 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
database_url.push_str(&db_path);
|
database_url.push_str(&db_path);
|
||||||
store.database_url = Secret::new(database_url);
|
store.database_url = Secret::new(database_url);
|
||||||
|
|
||||||
// ---
|
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() {
|
if !is_docker() {
|
||||||
store.http.listen = prompt_endpoint(
|
store.http.listen = prompt_endpoint(
|
||||||
"Endpoint to listen for HTTP connections on",
|
"Endpoint to listen for HTTP connections on",
|
||||||
HTTPConfig::default().listen,
|
HTTPConfig::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() {
|
||||||
info!("You will now choose specific protocol listeners to be enabled.");
|
info!("You will now choose specific protocol listeners to be enabled.");
|
||||||
info!("");
|
info!("");
|
||||||
info!("NB: Nothing will be exposed by default -");
|
info!("NB: Nothing will be exposed by default -");
|
||||||
|
@ -138,9 +157,16 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
SSHConfig::default().listen,
|
SSHConfig::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)
|
store.mysql.enable = dialoguer::Confirm::with_theme(&theme)
|
||||||
.default(true)
|
.default(true)
|
||||||
.with_prompt("Accept MySQL connections?")
|
.with_prompt("Accept MySQL connections?")
|
||||||
|
@ -153,6 +179,7 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
store.http.certificate = PathBuf::from(&data_path)
|
store.http.certificate = PathBuf::from(&data_path)
|
||||||
.join("tls.certificate.pem")
|
.join("tls.certificate.pem")
|
||||||
|
@ -176,10 +203,17 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
|
if let Commands::UnattendedSetup {
|
||||||
|
record_sessions, ..
|
||||||
|
} = &cli.command
|
||||||
|
{
|
||||||
|
store.recordings.enable = *record_sessions;
|
||||||
|
} else {
|
||||||
store.recordings.enable = dialoguer::Confirm::with_theme(&theme)
|
store.recordings.enable = dialoguer::Confirm::with_theme(&theme)
|
||||||
.default(true)
|
.default(true)
|
||||||
.with_prompt("Do you want to record user sessions?")
|
.with_prompt("Do you want to record user sessions?")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
|
}
|
||||||
store.recordings.path = PathBuf::from(&data_path)
|
store.recordings.path = PathBuf::from(&data_path)
|
||||||
.join("recordings")
|
.join("recordings")
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
@ -187,9 +221,25 @@ pub(crate) async fn command(cli: &crate::Cli) -> Result<()> {
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
let admin_password = dialoguer::Password::with_theme(&theme)
|
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")
|
.with_prompt("Set a password for the Warpgate admin user")
|
||||||
.interact()?;
|
.interact()?
|
||||||
|
};
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,35 @@ pub struct Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand)]
|
#[derive(clap::Subcommand)]
|
||||||
enum Commands {
|
pub(crate) enum Commands {
|
||||||
/// Run first-time setup and generate a config file
|
/// Run first-time setup and generate a config file
|
||||||
Setup,
|
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
|
/// Show Warpgate's SSH client keys
|
||||||
ClientKeys,
|
ClientKeys,
|
||||||
/// Run Warpgate
|
/// Run Warpgate
|
||||||
|
@ -62,7 +88,9 @@ async fn _main() -> Result<()> {
|
||||||
Commands::TestTarget { target_name } => {
|
Commands::TestTarget { target_name } => {
|
||||||
crate::commands::test_target::command(&cli, target_name).await
|
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::ClientKeys => crate::commands::client_keys::command(&cli).await,
|
||||||
Commands::RecoverAccess { username } => {
|
Commands::RecoverAccess { username } => {
|
||||||
crate::commands::recover_access::command(&cli, username).await
|
crate::commands::recover_access::command(&cli, username).await
|
||||||
|
|
Loading…
Add table
Reference in a new issue