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.

246 lines
5.8 KiB

7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
7 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io;
  4. use std::path::{Path, PathBuf};
  5. use crate::command_runner::CommandRunner;
  6. use crate::resources::Resource;
  7. use crate::symbols::file::File as FileSymbol;
  8. use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
  9. #[derive(Debug)]
  10. pub enum NginxServerError<E: Error> {
  11. ExecError(E),
  12. GenericError,
  13. }
  14. impl From<io::Error> for NginxServerError<io::Error> {
  15. fn from(err: io::Error) -> Self {
  16. Self::ExecError(err)
  17. }
  18. }
  19. impl<E: Error> Error for NginxServerError<E> {
  20. fn description(&self) -> &str {
  21. match self {
  22. Self::ExecError(ref e) => e.description(),
  23. Self::GenericError => "Generic error",
  24. }
  25. }
  26. fn cause(&self) -> Option<&dyn Error> {
  27. match self {
  28. Self::ExecError(ref e) => Some(e),
  29. _ => None,
  30. }
  31. }
  32. }
  33. impl<E: Error> fmt::Display for NginxServerError<E> {
  34. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  35. write!(f, "{}", self.description())
  36. }
  37. }
  38. pub struct NginxServer<'a, C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> {
  39. command_runner: &'a C,
  40. file: FileSymbol<T, P>,
  41. }
  42. pub fn server_config(domain: &str, content: &str) -> String {
  43. format!(
  44. "server {{
  45. listen 443 ssl http2;
  46. server_name {0};
  47. include \"snippets/acme-challenge.conf\";
  48. ssl_certificate /etc/ssl/local_certs/{0}.chained.crt;
  49. ssl_certificate_key /etc/ssl/private/{0}.key;
  50. add_header Strict-Transport-Security \"max-age=31536000\";
  51. {1}
  52. }}
  53. # Redirect all HTTP links to the matching HTTPS page
  54. server {{
  55. listen 80;
  56. server_name {0};
  57. include \"snippets/acme-challenge.conf\";
  58. location / {{
  59. return 301 https://$host$request_uri;
  60. }}
  61. }}
  62. ",
  63. domain, content
  64. )
  65. }
  66. pub fn php_server_config_snippet<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
  67. socket_path: SOCKET,
  68. static_path: STATIC,
  69. ) -> String {
  70. format!(
  71. "
  72. root {};
  73. index index.html index.php;
  74. location ~ [^/]\\.php(/|$) {{
  75. fastcgi_pass unix:{};
  76. include \"snippets/fastcgi-php.conf\";
  77. }}",
  78. static_path.as_ref().to_str().unwrap(),
  79. socket_path.as_ref().to_str().unwrap()
  80. )
  81. }
  82. pub trait SocketSpec {
  83. fn to_nginx(&self) -> String;
  84. }
  85. impl<T> SocketSpec for T
  86. where
  87. T: AsRef<Path>,
  88. {
  89. fn to_nginx(&self) -> String {
  90. format!("unix:{}:", self.as_ref().to_str().unwrap())
  91. }
  92. }
  93. pub struct LocalTcpSocket(usize);
  94. impl LocalTcpSocket {
  95. pub const fn new(x: usize) -> Self {
  96. Self(x)
  97. }
  98. }
  99. impl SocketSpec for LocalTcpSocket {
  100. fn to_nginx(&self) -> String {
  101. format!("localhost:{}", self.0)
  102. }
  103. }
  104. impl<'a, C: CommandRunner> NginxServer<'a, C, String, PathBuf> {
  105. pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
  106. let content = server_config(
  107. domain,
  108. &format!(
  109. "location / {{
  110. return 301 $scheme://{}$request_uri;
  111. }}",
  112. target
  113. ),
  114. );
  115. Self::new(domain, content, command_runner)
  116. }
  117. pub fn new_proxy<S: SocketSpec, STATIC: AsRef<Path>>(
  118. domain: &'a str,
  119. socket_path: &S,
  120. static_path: STATIC,
  121. command_runner: &'a C,
  122. ) -> Self {
  123. let proxy_content = format!(
  124. "location / {{
  125. try_files $uri @proxy;
  126. }}
  127. location @proxy {{
  128. include fastcgi_params;
  129. proxy_pass http://{};
  130. proxy_redirect off;
  131. }}",
  132. socket_path.to_nginx()
  133. );
  134. let content = server_config(
  135. domain,
  136. &format!(
  137. "
  138. root {};
  139. {}
  140. ",
  141. static_path.as_ref().to_str().unwrap(),
  142. proxy_content
  143. ),
  144. );
  145. Self::new(domain, content, command_runner)
  146. }
  147. pub fn new_php<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
  148. domain: &'a str,
  149. socket_path: SOCKET,
  150. static_path: STATIC,
  151. command_runner: &'a C,
  152. additional_config: &'a str,
  153. ) -> Self {
  154. let content = server_config(
  155. domain,
  156. &(php_server_config_snippet(socket_path, static_path) + additional_config),
  157. );
  158. Self::new(domain, content, command_runner)
  159. }
  160. pub fn new_static<S: AsRef<Path>>(
  161. domain: &'a str,
  162. static_path: S,
  163. command_runner: &'a C,
  164. ) -> Self {
  165. let content = server_config(
  166. domain,
  167. &format!(
  168. "
  169. root {};
  170. try_files $uri $uri/ $uri.html =404;
  171. ",
  172. static_path.as_ref().to_str().unwrap()
  173. ),
  174. );
  175. Self::new(domain, content, command_runner)
  176. }
  177. pub fn new(domain: &'a str, content: String, command_runner: &'a C) -> Self {
  178. let file_path: PathBuf = ["/etc/nginx/sites-enabled/", domain].iter().collect();
  179. NginxServer {
  180. command_runner,
  181. file: FileSymbol::new(file_path, content),
  182. }
  183. }
  184. }
  185. impl<C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> Symbol for NginxServer<'_, C, T, P> {
  186. fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  187. if !self.file.target_reached()? {
  188. return Ok(false);
  189. }
  190. // TODO: Could try to find out if the server is in the live config
  191. Ok(true)
  192. }
  193. fn execute(&self) -> Result<(), Box<dyn Error>> {
  194. self.file.execute()?;
  195. self
  196. .command_runner
  197. .run_successfully("systemctl", args!["reload-or-restart", "nginx"])
  198. }
  199. fn get_prerequisites(&self) -> Vec<Resource> {
  200. self.file.get_prerequisites()
  201. }
  202. fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
  203. Box::new(SymbolAction::new(runner, self))
  204. }
  205. fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
  206. where
  207. Self: 'b,
  208. {
  209. Box::new(OwnedSymbolAction::new(runner, *self))
  210. }
  211. }
  212. impl<C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> fmt::Display for NginxServer<'_, C, T, P> {
  213. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
  214. write!(f, "Nginx server config")
  215. }
  216. }