A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
7.4 KiB

6 years ago
7 years ago
6 years ago
7 years ago
7 years ago
6 years ago
7 years ago
6 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
6 years ago
7 years ago
7 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
  1. use std::ops::Deref;
  2. use std::path::Path;
  3. use command_runner::{CommandRunner, SetuidCommandRunner};
  4. use storage::{SimpleStorage, Storage};
  5. use symbols::{Action, Symbol, SymbolRunner};
  6. use symbols::acme::{AcmeCert, AcmeCertChain};
  7. use symbols::file::File;
  8. use symbols::git::checkout::GitCheckout;
  9. use symbols::hook::Hook;
  10. use symbols::list::ListAction;
  11. use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser};
  12. use symbols::nginx::server::{NginxServer, server_config};
  13. use symbols::owner::Owner;
  14. use symbols::stored_directory::{StoredDirectory, StorageDirection};
  15. use symbols::systemd::reload::ReloadService;
  16. use symbols::tls::SelfSignedTlsCert;
  17. pub trait Policy {
  18. fn user_name_for_host(&self, host_name: &'static str) -> String;
  19. }
  20. pub struct DefaultPolicy;
  21. impl Policy for DefaultPolicy {
  22. fn user_name_for_host(&self, host_name: &'static str) -> String {
  23. host_name.split('.').rev().fold(String::new(), |result, part| if result.len() > 0 { result + "_" } else { result } + part)
  24. }
  25. }
  26. pub struct SymbolFactory<'a, C: 'a + CommandRunner, R: 'a + SymbolRunner, P: 'a + Policy>{
  27. command_runner: &'a C,
  28. acme_command_runner: SetuidCommandRunner<'a, C>,
  29. symbol_runner: &'a R,
  30. policy: &'a P
  31. }
  32. impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFactory<'b, C, R, P> {
  33. pub fn new(command_runner: &'b C, symbol_runner: &'b R, policy: &'b P) -> Self {
  34. let acme_user = "acme"; // FIXME: CONFIG
  35. let acme_command_runner = SetuidCommandRunner::new(acme_user, command_runner);
  36. SymbolFactory {
  37. command_runner: command_runner,
  38. acme_command_runner: acme_command_runner,
  39. symbol_runner: symbol_runner,
  40. policy: policy
  41. }
  42. }
  43. pub fn get_nginx_acme_server<'a, 'c: 'a, S: 'a + Symbol>(&'c self, host: &'static str, nginx_server_symbol: S) -> Box<Action + 'a> {
  44. Box::new(ListAction::new(vec![
  45. Box::new(SelfSignedTlsCert::new(
  46. host.into(),
  47. self.command_runner
  48. )).into_action(self.symbol_runner),
  49. Box::new(Hook::new(
  50. nginx_server_symbol,
  51. ReloadService::new("nginx", self.command_runner)
  52. )).into_action(self.symbol_runner),
  53. Box::new(AcmeCert::new(
  54. host.into(),
  55. &self.acme_command_runner
  56. )).into_action(self.symbol_runner),
  57. Box::new(Hook::new(
  58. AcmeCertChain::new(
  59. host.into(),
  60. &self.acme_command_runner
  61. ),
  62. ReloadService::new("nginx", self.command_runner)
  63. )).into_action(self.symbol_runner)
  64. ]))
  65. }
  66. pub fn get_nginx_acme_challenge_config<'a>(&'a self) -> Box<Action + 'a> {
  67. Box::new(File::new(
  68. "/etc/nginx/snippets/acme-challenge.conf", "location ^~ /.well-known/acme-challenge/ {
  69. alias /home/acme/challenges/;
  70. try_files $uri =404;
  71. }"
  72. )).into_action(self.symbol_runner)
  73. }
  74. fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String {
  75. format!("/run/php/{}.sock", user_name)
  76. }
  77. fn get_php_fpm_pool<'a>(&'a self, user_name: &str) -> Box<Action + 'a> {
  78. let socket = self.get_php_fpm_pool_socket_path(user_name);
  79. Box::new(Hook::new(
  80. File::new(
  81. format!("/etc/php/7.0/fpm/pool.d/{}.conf", user_name),
  82. format!(
  83. "[{0}]
  84. user = {0}
  85. group = www-data
  86. listen = {1}
  87. listen.owner = www-data
  88. pm = ondemand
  89. pm.max_children = 10"
  90. , user_name, socket)),
  91. ReloadService::new("php7.0-fpm", self.command_runner)
  92. )).into_action(self.symbol_runner)
  93. }
  94. pub fn serve_php<'a>(&'a self, host_name: &'static str, root_dir: &'static str) -> Box<Action + 'a> {
  95. let user_name = self.policy.user_name_for_host(host_name);
  96. let socket = self.get_php_fpm_pool_socket_path(&user_name);
  97. Box::new(ListAction::new(vec![
  98. self.get_php_fpm_pool(&user_name),
  99. self.get_nginx_acme_server(host_name,
  100. NginxServer::new_php(
  101. host_name,
  102. socket.into(),
  103. root_dir,
  104. self.command_runner
  105. )
  106. )
  107. ]))
  108. }
  109. pub fn serve_dokuwiki<'a>(&'a self, host_name: &'static str, root_dir: &'static str) -> Box<Action + 'a> {
  110. let user_name = self.policy.user_name_for_host(host_name);
  111. let socket = self.get_php_fpm_pool_socket_path(&user_name);
  112. Box::new(ListAction::new(vec![
  113. self.get_php_fpm_pool(&user_name),
  114. self.get_nginx_acme_server(host_name,
  115. NginxServer::new(
  116. host_name,
  117. server_config("hostname", &format!("
  118. root {};
  119. index doku.php;
  120. location ~ [^/]\\.php(/|$) {{
  121. fastcgi_pass unix:{};
  122. include \"snippets/fastcgi-php.conf\";
  123. }}
  124. location ~ /(data/|conf/|bin/|inc/|install.php) {{ deny all; }}
  125. location / {{ try_files $uri $uri/ @dokuwiki; }}
  126. location @dokuwiki {{
  127. # rewrites \"doku.php/\" out of the URLs if you set the userewrite setting to .htaccess in dokuwiki config page
  128. rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
  129. rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
  130. rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
  131. rewrite ^/(.*) /doku.php?id=$1&$args last;
  132. }}
  133. ",
  134. root_dir,
  135. socket)),
  136. self.command_runner
  137. ))
  138. ]))
  139. }
  140. pub fn serve_redir<'a>(&'a self, host_name: &'static str, target: &'static str) -> Box<Action + 'a> {
  141. self.get_nginx_acme_server(host_name, NginxServer::new_redir(host_name, target, self.command_runner))
  142. }
  143. pub fn serve_static<'a>(&'a self, host_name: &'static str, dir: &'a str) -> Box<Action + 'a> {
  144. self.get_nginx_acme_server(host_name, NginxServer::new_static(host_name, dir, self.command_runner))
  145. }
  146. pub fn get_stored_directory<'a, T: Into<String>>(&'a self, storage_name: &'static str, target: T) -> (Box<Action + 'a>, Box<Action + 'a>) {
  147. let data = SimpleStorage::new("/root/data".to_string(), storage_name.to_string());
  148. let string_target = target.into();
  149. (
  150. Box::new(StoredDirectory::new(string_target.clone().into(), data.clone(), StorageDirection::Save, self.command_runner)).into_action(self.symbol_runner),
  151. Box::new(StoredDirectory::new(string_target.into(), data.clone(), StorageDirection::Load, self.command_runner)).into_action(self.symbol_runner)
  152. )
  153. }
  154. pub fn get_mariadb_database<'a>(&'a self, name: &'static str) -> Box<Action + 'a> {
  155. let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name));
  156. Box::new(ListAction::new(vec![
  157. Box::new(MariaDBDatabase::new(name.into(), db_dump.read_filename().unwrap().into(), self.command_runner)).into_action(self.symbol_runner),
  158. Box::new(DatabaseDump::new(name, db_dump, self.command_runner)).into_action(self.symbol_runner)
  159. ]))
  160. }
  161. pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> Box<Action + 'a> {
  162. Box::new(MariaDBUser::new(user_name.into(), self.command_runner)).into_action(self.symbol_runner)
  163. }
  164. pub fn get_git_checkout<'a, T: 'a + AsRef<str>>(&'a self, target: T, source: &'a str, branch: &'a str) -> Box<Action + 'a> {
  165. Box::new(GitCheckout::new(target, source, branch, self.command_runner)).into_action(self.symbol_runner)
  166. }
  167. pub fn get_owner<'a, F: 'a + AsRef<str>>(&'a self, file: F, user: &'a str) -> Box<Action + 'a> {
  168. Box::new(Owner::new(file, user.into(), self.command_runner)).into_action(self.symbol_runner)
  169. }
  170. pub fn get_file<'a, F: 'a + Deref<Target=str>, Q: 'a + AsRef<Path>>(&'a self, path: Q, content: F) -> Box<Action + 'a> {
  171. Box::new(File::new(path, content)).into_action(self.symbol_runner)
  172. }
  173. }