Compare commits
4 Commits
2fb5bb3d84
...
win-servic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64a07e0ed6 | ||
|
|
f245a6575d | ||
|
|
e3050bc5ff | ||
|
|
d593eedf01 |
@@ -31,6 +31,7 @@ pub struct ClientMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub static BOOTSTRAP_PATH: &str = "bootstrap_token";
|
pub static BOOTSTRAP_PATH: &str = "bootstrap_token";
|
||||||
|
pub const DEFAULT_SERVER_PORT: u16 = 50051;
|
||||||
static HOME_OVERRIDE: LazyLock<RwLock<Option<PathBuf>>> = LazyLock::new(|| RwLock::new(None));
|
static HOME_OVERRIDE: LazyLock<RwLock<Option<PathBuf>>> = LazyLock::new(|| RwLock::new(None));
|
||||||
|
|
||||||
pub fn set_home_path_override(path: Option<PathBuf>) -> Result<(), std::io::Error> {
|
pub fn set_home_path_override(path: Option<PathBuf>) -> Result<(), std::io::Error> {
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
use std::{net::SocketAddr, path::PathBuf};
|
use std::{
|
||||||
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
const DEFAULT_LISTEN_ADDR: &str = "127.0.0.1:50051";
|
const DEFAULT_LISTEN_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(
|
||||||
|
Ipv4Addr::LOCALHOST,
|
||||||
|
arbiter_proto::DEFAULT_SERVER_PORT,
|
||||||
|
));
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "arbiter-server")]
|
#[command(name = "arbiter-server")]
|
||||||
@@ -25,7 +31,7 @@ pub enum Command {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Args)]
|
#[derive(Debug, Clone, Args)]
|
||||||
pub struct RunArgs {
|
pub struct RunArgs {
|
||||||
#[arg(long, default_value = DEFAULT_LISTEN_ADDR)]
|
#[arg(long, default_value_t = DEFAULT_LISTEN_ADDR)]
|
||||||
pub listen_addr: SocketAddr,
|
pub listen_addr: SocketAddr,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub data_dir: Option<PathBuf>,
|
pub data_dir: Option<PathBuf>,
|
||||||
@@ -34,9 +40,7 @@ pub struct RunArgs {
|
|||||||
impl Default for RunArgs {
|
impl Default for RunArgs {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
listen_addr: DEFAULT_LISTEN_ADDR
|
listen_addr: DEFAULT_LISTEN_ADDR,
|
||||||
.parse()
|
|
||||||
.expect("listen address literal must be valid"),
|
|
||||||
data_dir: None,
|
data_dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +65,7 @@ pub struct ServiceInstallArgs {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Args)]
|
#[derive(Debug, Clone, Args)]
|
||||||
pub struct ServiceRunArgs {
|
pub struct ServiceRunArgs {
|
||||||
#[arg(long, default_value = DEFAULT_LISTEN_ADDR)]
|
#[arg(long, default_value_t = DEFAULT_LISTEN_ADDR)]
|
||||||
pub listen_addr: SocketAddr,
|
pub listen_addr: SocketAddr,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub data_dir: Option<PathBuf>,
|
pub data_dir: Option<PathBuf>,
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
use crate::context::ServerContext;
|
|
||||||
|
use std::{net::SocketAddr, path::PathBuf};
|
||||||
|
|
||||||
|
use arbiter_proto::{proto::arbiter_service_server::ArbiterServiceServer, url::ArbiterUrl};
|
||||||
|
use miette::miette;
|
||||||
|
use tonic::transport::{Identity, ServerTlsConfig};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::{actors::bootstrap::GetToken, context::ServerContext};
|
||||||
|
|
||||||
pub mod actors;
|
pub mod actors;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod evm;
|
pub mod evm;
|
||||||
pub mod grpc;
|
pub mod grpc;
|
||||||
pub mod runtime;
|
|
||||||
pub mod safe_cell;
|
pub mod safe_cell;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
@@ -19,3 +26,64 @@ impl Server {
|
|||||||
Self { context }
|
Self { context }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RunConfig {
|
||||||
|
pub addr: SocketAddr,
|
||||||
|
pub data_dir: Option<PathBuf>,
|
||||||
|
pub log_arbiter_url: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunConfig {
|
||||||
|
pub fn new(addr: SocketAddr, data_dir: Option<PathBuf>) -> Self {
|
||||||
|
Self {
|
||||||
|
addr,
|
||||||
|
data_dir,
|
||||||
|
log_arbiter_url: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_server_until_shutdown<F>(config: RunConfig, shutdown: F) -> miette::Result<()>
|
||||||
|
where
|
||||||
|
F: Future<Output = ()> + Send + 'static,
|
||||||
|
{
|
||||||
|
arbiter_proto::set_home_path_override(config.data_dir.clone())
|
||||||
|
.map_err(|err| miette!("failed to set home path override: {err}"))?;
|
||||||
|
|
||||||
|
let db = db::create_pool(None).await?;
|
||||||
|
info!(addr = %config.addr, "Database ready");
|
||||||
|
|
||||||
|
let context = ServerContext::new(db).await?;
|
||||||
|
info!(addr = %config.addr, "Server context ready");
|
||||||
|
|
||||||
|
if config.log_arbiter_url {
|
||||||
|
let url = ArbiterUrl {
|
||||||
|
host: config.addr.ip().to_string(),
|
||||||
|
port: config.addr.port(),
|
||||||
|
ca_cert: context.tls.ca_cert().clone().into_owned(),
|
||||||
|
bootstrap_token: context
|
||||||
|
.actors
|
||||||
|
.bootstrapper
|
||||||
|
.ask(GetToken)
|
||||||
|
.await
|
||||||
|
.map_err(|err| miette!("failed to get bootstrap token from actor: {err}"))?,
|
||||||
|
};
|
||||||
|
info!(%url, "Server URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
let tls = ServerTlsConfig::new().identity(Identity::from_pem(
|
||||||
|
context.tls.cert_pem(),
|
||||||
|
context.tls.key_pem(),
|
||||||
|
));
|
||||||
|
|
||||||
|
tonic::transport::Server::builder()
|
||||||
|
.tls_config(tls)
|
||||||
|
.map_err(|err| miette!("Failed to setup TLS: {err}"))?
|
||||||
|
.add_service(ArbiterServiceServer::new(Server::new(context)))
|
||||||
|
.serve_with_shutdown(config.addr, shutdown)
|
||||||
|
.await
|
||||||
|
.map_err(|e| miette!("gRPC server error: {e}"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ async fn main() -> miette::Result<()> {
|
|||||||
|
|
||||||
async fn run_foreground(args: RunArgs) -> miette::Result<()> {
|
async fn run_foreground(args: RunArgs) -> miette::Result<()> {
|
||||||
info!(addr = %args.listen_addr, "Starting arbiter server");
|
info!(addr = %args.listen_addr, "Starting arbiter server");
|
||||||
arbiter_server::runtime::run_server_until_shutdown(
|
arbiter_server::run_server_until_shutdown(
|
||||||
arbiter_server::runtime::RunConfig::new(args.listen_addr, args.data_dir),
|
arbiter_server::RunConfig::new(args.listen_addr, args.data_dir),
|
||||||
std::future::pending::<()>(),
|
std::future::pending::<()>(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
use std::{future::Future, net::SocketAddr, path::PathBuf};
|
|
||||||
|
|
||||||
use arbiter_proto::{proto::arbiter_service_server::ArbiterServiceServer, url::ArbiterUrl};
|
|
||||||
use kameo::actor::ActorRef;
|
|
||||||
use miette::miette;
|
|
||||||
use tonic::transport::{Identity, ServerTlsConfig};
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::{Server, actors::bootstrap::GetToken, context::ServerContext, db};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RunConfig {
|
|
||||||
pub addr: SocketAddr,
|
|
||||||
pub data_dir: Option<PathBuf>,
|
|
||||||
pub log_arbiter_url: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunConfig {
|
|
||||||
pub fn new(addr: SocketAddr, data_dir: Option<PathBuf>) -> Self {
|
|
||||||
Self {
|
|
||||||
addr,
|
|
||||||
data_dir,
|
|
||||||
log_arbiter_url: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_server_until_shutdown<F>(config: RunConfig, shutdown: F) -> miette::Result<()>
|
|
||||||
where
|
|
||||||
F: Future<Output = ()> + Send + 'static,
|
|
||||||
{
|
|
||||||
arbiter_proto::set_home_path_override(config.data_dir.clone())
|
|
||||||
.map_err(|err| miette!("failed to set home path override: {err}"))?;
|
|
||||||
|
|
||||||
let db = db::create_pool(None).await?;
|
|
||||||
info!(addr = %config.addr, "Database ready");
|
|
||||||
|
|
||||||
let context = ServerContext::new(db).await?;
|
|
||||||
info!(addr = %config.addr, "Server context ready");
|
|
||||||
|
|
||||||
if config.log_arbiter_url {
|
|
||||||
let url =
|
|
||||||
build_arbiter_url(config.addr, &context.actors.bootstrapper, &context.tls).await?;
|
|
||||||
info!(%url, "Server URL");
|
|
||||||
}
|
|
||||||
|
|
||||||
let tls = ServerTlsConfig::new().identity(Identity::from_pem(
|
|
||||||
context.tls.cert_pem(),
|
|
||||||
context.tls.key_pem(),
|
|
||||||
));
|
|
||||||
|
|
||||||
tonic::transport::Server::builder()
|
|
||||||
.tls_config(tls)
|
|
||||||
.map_err(|err| miette!("Failed to setup TLS: {err}"))?
|
|
||||||
.add_service(ArbiterServiceServer::new(Server::new(context)))
|
|
||||||
.serve_with_shutdown(config.addr, shutdown)
|
|
||||||
.await
|
|
||||||
.map_err(|e| miette!("gRPC server error: {e}"))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn build_arbiter_url(
|
|
||||||
addr: SocketAddr,
|
|
||||||
bootstrapper: &ActorRef<crate::actors::bootstrap::Bootstrapper>,
|
|
||||||
tls: &crate::context::tls::TlsManager,
|
|
||||||
) -> miette::Result<ArbiterUrl> {
|
|
||||||
Ok(ArbiterUrl {
|
|
||||||
host: addr.ip().to_string(),
|
|
||||||
port: addr.port(),
|
|
||||||
ca_cert: tls.ca_cert().clone().into_owned(),
|
|
||||||
bootstrap_token: bootstrapper
|
|
||||||
.ask(GetToken)
|
|
||||||
.await
|
|
||||||
.map_err(|err| miette!("failed to get bootstrap token from actor: {err}"))?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ use windows_service::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::cli::{ServiceInstallArgs, ServiceRunArgs};
|
use crate::cli::{ServiceInstallArgs, ServiceRunArgs};
|
||||||
use arbiter_server::runtime::{RunConfig, run_server_until_shutdown};
|
use arbiter_server::{RunConfig, run_server_until_shutdown};
|
||||||
|
|
||||||
const SERVICE_NAME: &str = "ArbiterServer";
|
const SERVICE_NAME: &str = "ArbiterServer";
|
||||||
const SERVICE_DISPLAY_NAME: &str = "Arbiter Server";
|
const SERVICE_DISPLAY_NAME: &str = "Arbiter Server";
|
||||||
@@ -58,7 +58,7 @@ pub fn install_service(args: ServiceInstallArgs) -> miette::Result<()> {
|
|||||||
name: OsString::from(SERVICE_NAME),
|
name: OsString::from(SERVICE_NAME),
|
||||||
display_name: OsString::from(SERVICE_DISPLAY_NAME),
|
display_name: OsString::from(SERVICE_DISPLAY_NAME),
|
||||||
service_type: ServiceType::OWN_PROCESS,
|
service_type: ServiceType::OWN_PROCESS,
|
||||||
start_type: ServiceStartType::OnDemand,
|
start_type: ServiceStartType::AutoStart,
|
||||||
error_control: ServiceErrorControl::Normal,
|
error_control: ServiceErrorControl::Normal,
|
||||||
executable_path: executable,
|
executable_path: executable,
|
||||||
launch_arguments,
|
launch_arguments,
|
||||||
@@ -203,8 +203,9 @@ fn ensure_admin_rights() -> miette::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_token_acl_contract(data_dir: &Path) -> miette::Result<()> {
|
fn ensure_token_acl_contract(data_dir: &Path) -> miette::Result<()> {
|
||||||
// IMPORTANT: This ACL setup is intentionally explicit and should not be simplified away,
|
// IMPORTANT: Keep this ACL setup explicit.
|
||||||
// because service-account and interactive-user access requirements are different in production.
|
// The service account needs write access, while the interactive user only needs read access
|
||||||
|
// to the bootstrap token and service data directory.
|
||||||
let target = data_dir.as_os_str();
|
let target = data_dir.as_os_str();
|
||||||
|
|
||||||
let status = Command::new("icacls")
|
let status = Command::new("icacls")
|
||||||
|
|||||||
Reference in New Issue
Block a user