@ -4,18 +4,18 @@ use std::path::Path;
use command_runner ::{ CommandRunner , SetuidCommandRunner } ;
use command_runner ::{ CommandRunner , SetuidCommandRunner } ;
use storage ::{ SimpleStorage , Storage } ;
use storage ::{ SimpleStorage , Storage } ;
use symbols ::{ Action , Symbol , SymbolRunner } ;
use symbols ::acme ::{ AcmeCert , AcmeCertChain } ;
use symbols ::acme ::{ AcmeCert , AcmeCertChain } ;
use symbols ::file ::File ;
use symbols ::file ::File ;
use symbols ::git ::checkout ::GitCheckout ;
use symbols ::git ::checkout ::GitCheckout ;
use symbols ::hook ::Hook ;
use symbols ::hook ::Hook ;
use symbols ::list ::ListAction ;
use symbols ::list ::ListAction ;
use symbols ::mariadb ::{ DatabaseDump , MariaDBDatabase , MariaDBUser } ;
use symbols ::mariadb ::{ DatabaseDump , MariaDBDatabase , MariaDBUser } ;
use symbols ::nginx ::server ::{ NginxServer , server_config , php_server_config_snippet } ;
use symbols ::nginx ::server ::{ php_server_config_snippet , server_config , NginxServer } ;
use symbols ::owner ::Owner ;
use symbols ::owner ::Owner ;
use symbols ::stored_directory ::{ StoredDirectory , StorageDirection } ;
use symbols ::stored_directory ::{ StorageDirection , StoredDirectory } ;
use symbols ::systemd ::reload ::ReloadService ;
use symbols ::systemd ::reload ::ReloadService ;
use symbols ::tls ::SelfSignedTlsCert ;
use symbols ::tls ::SelfSignedTlsCert ;
use symbols ::{ Action , Symbol , SymbolRunner } ;
pub trait Policy {
pub trait Policy {
fn user_name_for_host ( & self , host_name : & 'static str ) -> String ;
fn user_name_for_host ( & self , host_name : & 'static str ) -> String ;
@ -32,11 +32,11 @@ impl Policy for DefaultPolicy {
}
}
}
}
pub struct SymbolFactory < 'a , C : 'a + CommandRunner , R : 'a + SymbolRunner , P : 'a + Policy > {
pub struct SymbolFactory < 'a , C : 'a + CommandRunner , R : 'a + SymbolRunner , P : 'a + Policy > {
command_runner : & 'a C ,
command_runner : & 'a C ,
acme_command_runner : SetuidCommandRunner < 'a , C > ,
acme_command_runner : SetuidCommandRunner < 'a , C > ,
symbol_runner : & 'a R ,
symbol_runner : & 'a R ,
policy : & 'a P
policy : & 'a P ,
}
}
impl < 'b , C : 'b + CommandRunner , R : 'b + SymbolRunner , P : 'b + Policy > SymbolFactory < 'b , C , R , P > {
impl < 'b , C : 'b + CommandRunner , R : 'b + SymbolRunner , P : 'b + Policy > SymbolFactory < 'b , C , R , P > {
@ -44,39 +44,45 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact
let acme_user = "acme" ; // FIXME: CONFIG
let acme_user = "acme" ; // FIXME: CONFIG
let acme_command_runner = SetuidCommandRunner ::new ( acme_user , command_runner ) ;
let acme_command_runner = SetuidCommandRunner ::new ( acme_user , command_runner ) ;
SymbolFactory { command_runner , acme_command_runner , symbol_runner , policy }
SymbolFactory {
command_runner ,
acme_command_runner ,
symbol_runner ,
policy ,
}
}
}
pub fn get_nginx_acme_server < 'a , 'c : 'a , S : 'a + Symbol > ( & 'c self , host : & 'static str , nginx_server_symbol : S ) -> Box < dyn Action + 'a > {
pub fn get_nginx_acme_server < 'a , 'c : 'a , S : 'a + Symbol > (
& 'c self ,
host : & 'static str ,
nginx_server_symbol : S ,
) -> Box < dyn Action + 'a > {
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( SelfSignedTlsCert ::new (
host . into ( ) ,
self . command_runner
) ) . into_action ( self . symbol_runner ) ,
Box ::new ( SelfSignedTlsCert ::new ( host . into ( ) , self . command_runner ) )
. into_action ( self . symbol_runner ) ,
Box ::new ( Hook ::new (
Box ::new ( Hook ::new (
nginx_server_symbol ,
nginx_server_symbol ,
ReloadService ::new ( "nginx" , self . command_runner )
) ) . into_action ( self . symbol_runner ) ,
Box ::new ( AcmeCert ::new (
host . into ( ) ,
& self . acme_command_runner
) ) . into_action ( self . symbol_runner ) ,
ReloadService ::new ( "nginx" , self . command_runner ) ,
) )
. into_action ( self . symbol_runner ) ,
Box ::new ( AcmeCert ::new ( host . into ( ) , & self . acme_command_runner ) )
. into_action ( self . symbol_runner ) ,
Box ::new ( Hook ::new (
Box ::new ( Hook ::new (
AcmeCertChain ::new (
host . into ( ) ,
& self . acme_command_runner
) ,
ReloadService ::new ( "nginx" , self . command_runner )
) ) . into_action ( self . symbol_runner )
AcmeCertChain ::new ( host . into ( ) , & self . acme_command_runner ) ,
ReloadService ::new ( "nginx" , self . command_runner ) ,
) )
. into_action ( self . symbol_runner ) ,
] ) )
] ) )
}
}
pub fn get_nginx_acme_challenge_config < 'a > ( & 'a self ) -> Box < dyn Action + 'a > {
pub fn get_nginx_acme_challenge_config < 'a > ( & 'a self ) -> Box < dyn Action + 'a > {
Box ::new ( File ::new (
Box ::new ( File ::new (
"/etc/nginx/snippets/acme-challenge.conf" , " location ^ ~ / . well - known / acme - challenge / {
"/etc/nginx/snippets/acme-challenge.conf" ,
" location ^ ~ / . well - known / acme - challenge / {
alias / home / acme / challenges / ;
alias / home / acme / challenges / ;
try_files $uri = 404 ;
try_files $uri = 404 ;
} "
) ) . into_action ( self . symbol_runner )
} " ,
) )
. into_action ( self . symbol_runner )
}
}
fn get_php_fpm_pool_socket_path < 'a > ( & 'a self , user_name : & str ) -> String {
fn get_php_fpm_pool_socket_path < 'a > ( & 'a self , user_name : & str ) -> String {
@ -99,47 +105,66 @@ pm = ondemand
pm . max_children = 10
pm . max_children = 10
catch_workers_output = yes
catch_workers_output = yes
env [ PATH ] = / usr / local / bin :/ usr / bin :/ bin
env [ PATH ] = / usr / local / bin :/ usr / bin :/ bin
"
, user_name , socket ) ) ,
ReloadService ::new ( "php7.0-fpm" , self . command_runner )
) ) . into_action ( self . symbol_runner )
" ,
user_name , socket
) ,
) ,
ReloadService ::new ( "php7.0-fpm" , self . command_runner ) ,
) )
. into_action ( self . symbol_runner )
}
}
pub fn serve_php < 'a > ( & 'a self , host_name : & 'static str , root_dir : Cow < 'a , str > ) -> Box < dyn Action + 'a > {
pub fn serve_php < 'a > (
& 'a self ,
host_name : & 'static str ,
root_dir : Cow < 'a , str > ,
) -> Box < dyn Action + 'a > {
let user_name = self . policy . user_name_for_host ( host_name ) ;
let user_name = self . policy . user_name_for_host ( host_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
self . get_php_fpm_pool ( & user_name ) ,
self . get_php_fpm_pool ( & user_name ) ,
self . get_nginx_acme_server ( host_name ,
NginxServer ::new_php (
self . get_nginx_acme_server (
host_name ,
host_name ,
socket . into ( ) ,
root_dir ,
self . command_runner
)
)
NginxServer ::new_php ( host_name , socket . into ( ) , root_dir , self . command_runner ) ,
) ,
] ) )
] ) )
}
}
pub fn serve_wordpress < 'a > ( & 'a self , host_name : & 'static str , root_dir : Cow < 'a , str > ) -> Box < dyn Action + 'a > {
pub fn serve_wordpress < 'a > (
& 'a self ,
host_name : & 'static str ,
root_dir : Cow < 'a , str > ,
) -> Box < dyn Action + 'a > {
let user_name = self . policy . user_name_for_host ( host_name ) ;
let user_name = self . policy . user_name_for_host ( host_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
self . get_php_fpm_pool ( & user_name ) ,
self . get_php_fpm_pool ( & user_name ) ,
self . get_nginx_acme_server ( host_name ,
self . get_nginx_acme_server (
host_name ,
NginxServer ::new (
NginxServer ::new (
host_name ,
host_name ,
server_config ( host_name , & format ! ( " { }
server_config (
host_name ,
& format ! (
" { }
location / { {
location / { {
try_files $uri $uri / / index . php ? $args ;
try_files $uri $uri / / index . php ? $args ;
} }
} }
" , php_server_config_snippet ( socket . into ( ) , root_dir ) ) ) ,
self . command_runner
) )
" ,
php_server_config_snippet ( socket . into ( ) , root_dir )
) ,
) ,
self . command_runner ,
) ,
) ,
] ) )
] ) )
}
}
pub fn serve_dokuwiki < 'a > ( & 'a self , host_name : & 'static str , root_dir : & 'static str ) -> Box < dyn Action + 'a > {
pub fn serve_dokuwiki < 'a > (
& 'a self ,
host_name : & 'static str ,
root_dir : & 'static str ,
) -> Box < dyn Action + 'a > {
let user_name = self . policy . user_name_for_host ( host_name ) ;
let user_name = self . policy . user_name_for_host ( host_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
@ -174,15 +199,23 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
] ) )
] ) )
}
}
pub fn serve_nextcloud < 'a > ( & 'a self , host_name : & 'static str , root_dir : Cow < 'a , str > ) -> Box < dyn Action + 'a > {
pub fn serve_nextcloud < 'a > (
& 'a self ,
host_name : & 'static str ,
root_dir : Cow < 'a , str > ,
) -> Box < dyn Action + 'a > {
let user_name = self . policy . user_name_for_host ( host_name ) ;
let user_name = self . policy . user_name_for_host ( host_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
let socket = self . get_php_fpm_pool_socket_path ( & user_name ) ;
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
self . get_php_fpm_pool ( & user_name ) ,
self . get_php_fpm_pool ( & user_name ) ,
self . get_nginx_acme_server ( host_name ,
self . get_nginx_acme_server (
host_name ,
NginxServer ::new (
NginxServer ::new (
host_name ,
host_name ,
server_config ( host_name , & format ! ( " { }
server_config (
host_name ,
& format ! (
" { }
client_max_body_size 500 M ;
client_max_body_size 500 M ;
# Disable gzip to avoid the removal of the ETag header
# Disable gzip to avoid the removal of the ETag header
@ -230,50 +263,106 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
location ~ * \ \ . ( ? :jpg | jpeg | gif | bmp | ico | png | swf ) $ { {
location ~ * \ \ . ( ? :jpg | jpeg | gif | bmp | ico | png | swf ) $ { {
access_log off ;
access_log off ;
} }
} }
" , php_server_config_snippet ( socket . into ( ) , root_dir ) ) ) ,
self . command_runner
) )
" ,
php_server_config_snippet ( socket . into ( ) , root_dir )
) ,
) ,
self . command_runner ,
) ,
) ,
] ) )
] ) )
}
}
pub fn serve_redir < 'a > ( & 'a self , host_name : & 'static str , target : & 'static str ) -> Box < dyn Action + 'a > {
self . get_nginx_acme_server ( host_name , NginxServer ::new_redir ( host_name , target , self . command_runner ) )
pub fn serve_redir < 'a > (
& 'a self ,
host_name : & 'static str ,
target : & 'static str ,
) -> Box < dyn Action + 'a > {
self . get_nginx_acme_server (
host_name ,
NginxServer ::new_redir ( host_name , target , self . command_runner ) ,
)
}
}
pub fn serve_static < 'a > ( & 'a self , host_name : & 'static str , dir : & 'a str ) -> Box < dyn Action + 'a > {
pub fn serve_static < 'a > ( & 'a self , host_name : & 'static str , dir : & 'a str ) -> Box < dyn Action + 'a > {
self . get_nginx_acme_server ( host_name , NginxServer ::new_static ( host_name , dir , self . command_runner ) )
self . get_nginx_acme_server (
host_name ,
NginxServer ::new_static ( host_name , dir , self . command_runner ) ,
)
}
}
pub fn get_stored_directory < 'a , T : Into < String > > ( & 'a self , storage_name : & 'static str , target : T ) -> ( Box < dyn Action + 'a > , Box < dyn Action + 'a > ) {
pub fn get_stored_directory < 'a , T : Into < String > > (
& 'a self ,
storage_name : & 'static str ,
target : T ,
) -> ( Box < dyn Action + 'a > , Box < dyn Action + 'a > ) {
let data = SimpleStorage ::new ( "/root/data" . to_string ( ) , storage_name . to_string ( ) ) ;
let data = SimpleStorage ::new ( "/root/data" . to_string ( ) , storage_name . to_string ( ) ) ;
let string_target = target . into ( ) ;
let string_target = target . into ( ) ;
(
(
Box ::new ( StoredDirectory ::new ( string_target . clone ( ) . into ( ) , data . clone ( ) , StorageDirection ::Save , self . command_runner ) ) . into_action ( self . symbol_runner ) ,
Box ::new ( StoredDirectory ::new ( string_target . into ( ) , data . clone ( ) , StorageDirection ::Load , self . command_runner ) ) . into_action ( self . symbol_runner )
Box ::new ( StoredDirectory ::new (
string_target . clone ( ) . into ( ) ,
data . clone ( ) ,
StorageDirection ::Save ,
self . command_runner ,
) )
. into_action ( self . symbol_runner ) ,
Box ::new ( StoredDirectory ::new (
string_target . into ( ) ,
data . clone ( ) ,
StorageDirection ::Load ,
self . command_runner ,
) )
. into_action ( self . symbol_runner ) ,
)
)
}
}
pub fn get_mariadb_database < 'a > ( & 'a self , name : & 'static str ) -> Box < dyn Action + 'a > {
pub fn get_mariadb_database < 'a > ( & 'a self , name : & 'static str ) -> Box < dyn Action + 'a > {
let db_dump = SimpleStorage ::new ( "/root/data" . to_string ( ) , format ! ( "{}.sql" , name ) ) ;
let db_dump = SimpleStorage ::new ( "/root/data" . to_string ( ) , format ! ( "{}.sql" , name ) ) ;
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( ListAction ::new ( vec ! [
Box ::new ( MariaDBDatabase ::new ( name . into ( ) , db_dump . read_filename ( ) . unwrap ( ) . into ( ) , self . command_runner ) ) . into_action ( self . symbol_runner ) ,
Box ::new ( DatabaseDump ::new ( name , db_dump , self . command_runner ) ) . into_action ( self . symbol_runner )
Box ::new ( MariaDBDatabase ::new (
name . into ( ) ,
db_dump . read_filename ( ) . unwrap ( ) . into ( ) ,
self . command_runner ,
) )
. into_action ( self . symbol_runner ) ,
Box ::new ( DatabaseDump ::new ( name , db_dump , self . command_runner ) )
. into_action ( self . symbol_runner ) ,
] ) )
] ) )
}
}
pub fn get_mariadb_user < 'a > ( & 'a self , user_name : & 'static str ) -> Box < dyn Action + 'a > {
pub fn get_mariadb_user < 'a > ( & 'a self , user_name : & 'static str ) -> Box < dyn Action + 'a > {
Box ::new ( MariaDBUser ::new ( user_name . into ( ) , self . command_runner ) ) . into_action ( self . symbol_runner )
Box ::new ( MariaDBUser ::new ( user_name . into ( ) , self . command_runner ) )
. into_action ( self . symbol_runner )
}
}
pub fn get_git_checkout < 'a , T : 'a + AsRef < str > > ( & 'a self , target : T , source : & 'a str , branch : & 'a str ) -> Box < dyn Action + 'a > {
Box ::new ( GitCheckout ::new ( target , source , branch , self . command_runner ) ) . into_action ( self . symbol_runner )
pub fn get_git_checkout < 'a , T : 'a + AsRef < str > > (
& 'a self ,
target : T ,
source : & 'a str ,
branch : & 'a str ,
) -> Box < dyn Action + 'a > {
Box ::new ( GitCheckout ::new (
target ,
source ,
branch ,
self . command_runner ,
) )
. into_action ( self . symbol_runner )
}
}
pub fn get_owner < 'a , F : 'a + AsRef < str > > ( & 'a self , file : F , user : & 'a str ) -> Box < dyn Action + 'a > {
pub fn get_owner < 'a , F : 'a + AsRef < str > > (
& 'a self ,
file : F ,
user : & 'a str ,
) -> Box < dyn Action + 'a > {
Box ::new ( Owner ::new ( file , user . into ( ) , self . command_runner ) ) . into_action ( self . symbol_runner )
Box ::new ( Owner ::new ( file , user . into ( ) , self . command_runner ) ) . into_action ( self . symbol_runner )
}
}
pub fn get_file < 'a , F : 'a + Deref < Target = str > , Q : 'a + AsRef < Path > > ( & 'a self , path : Q , content : F ) -> Box < dyn Action + 'a > {
pub fn get_file < 'a , F : 'a + Deref < Target = str > , Q : 'a + AsRef < Path > > (
& 'a self ,
path : Q ,
content : F ,
) -> Box < dyn Action + 'a > {
Box ::new ( File ::new ( path , content ) ) . into_action ( self . symbol_runner )
Box ::new ( File ::new ( path , content ) ) . into_action ( self . symbol_runner )
}
}
}
}