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.

154 lines
3.6 KiB

7 years ago
7 years ago
5 years ago
7 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
7 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 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
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
7 years ago
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
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::fs::File as FsFile;
  4. use std::io::Write;
  5. use std::path::{Path, PathBuf};
  6. use crate::command_runner::CommandRunner;
  7. use crate::resources::Resource;
  8. use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
  9. pub struct AcmeCert<
  10. 'a,
  11. D: AsRef<str>,
  12. R: AsRef<Path>,
  13. C: CommandRunner,
  14. K: AsRef<Path>,
  15. CH: AsRef<Path>,
  16. > {
  17. domain: D,
  18. command_runner: &'a C,
  19. root_cert_path: R,
  20. account_key_path: K,
  21. challenges_path: CH,
  22. }
  23. impl<'a, D: AsRef<str>, R: AsRef<Path>, C: CommandRunner, K: AsRef<Path>, CH: AsRef<Path>>
  24. AcmeCert<'a, D, R, C, K, CH>
  25. {
  26. pub fn new(
  27. domain: D,
  28. command_runner: &'a C,
  29. root_cert_path: R,
  30. account_key_path: K,
  31. challenges_path: CH,
  32. ) -> Self {
  33. AcmeCert {
  34. domain,
  35. command_runner,
  36. root_cert_path,
  37. account_key_path,
  38. challenges_path,
  39. }
  40. }
  41. fn get_csr_path(&self) -> PathBuf {
  42. format!("/etc/ssl/local_certs/{}.csr", self.domain.as_ref()).into()
  43. }
  44. fn get_cert_path(&self) -> PathBuf {
  45. format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into()
  46. }
  47. }
  48. impl<'a, D: AsRef<str>, R: AsRef<Path>, C: CommandRunner, K: AsRef<Path>, CH: AsRef<Path>>
  49. fmt::Display for AcmeCert<'a, D, R, C, K, CH>
  50. {
  51. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  52. write!(f, "AcmeCert {}", self.domain.as_ref())
  53. }
  54. }
  55. const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
  56. impl<'a, D: AsRef<str>, R: AsRef<Path>, C: CommandRunner, K: AsRef<Path>, CH: AsRef<Path>> Symbol
  57. for AcmeCert<'a, D, R, C, K, CH>
  58. {
  59. fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  60. if !self.get_cert_path().exists() {
  61. return Ok(false);
  62. }
  63. let output = self.command_runner.run_with_args(
  64. "openssl",
  65. args![
  66. "x509",
  67. "-in",
  68. self.get_cert_path(),
  69. "-noout",
  70. "-subject",
  71. "-checkend",
  72. (30 * DAYS_IN_SECONDS).to_string(),
  73. ],
  74. )?;
  75. if output.status.success()
  76. && output.stdout
  77. == format!(
  78. "subject=CN = {}\nCertificate will not expire\n",
  79. self.domain.as_ref()
  80. )
  81. .as_bytes()
  82. {
  83. Ok(
  84. self
  85. .command_runner
  86. .run_successfully(
  87. "openssl",
  88. args![
  89. "verify",
  90. "--untrusted",
  91. self.root_cert_path.as_ref(),
  92. self.get_cert_path(),
  93. ],
  94. )
  95. .is_ok(),
  96. )
  97. } else if output.status.code() == Some(1)
  98. && output.stdout
  99. == format!(
  100. "subject=CN = {}\nCertificate will expire\n",
  101. self.domain.as_ref()
  102. )
  103. .as_bytes()
  104. {
  105. Ok(false)
  106. } else {
  107. Err(String::from_utf8(output.stderr)?.into())
  108. }
  109. }
  110. fn execute(&self) -> Result<(), Box<dyn Error>> {
  111. let output = self.command_runner.get_output(
  112. "acme-tiny",
  113. args![
  114. "--account-key",
  115. self.account_key_path.as_ref(),
  116. "--csr",
  117. self.get_csr_path(),
  118. "--acme-dir",
  119. self.challenges_path.as_ref(),
  120. ],
  121. )?;
  122. let mut file = FsFile::create(self.get_cert_path())?;
  123. file.write_all(&output)?;
  124. Ok(())
  125. }
  126. fn get_prerequisites(&self) -> Vec<Resource> {
  127. vec![Resource::new("file", self.get_csr_path().to_str().unwrap())]
  128. }
  129. fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
  130. Box::new(SymbolAction::new(runner, self))
  131. }
  132. fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
  133. where
  134. Self: 'b,
  135. {
  136. Box::new(OwnedSymbolAction::new(runner, *self))
  137. }
  138. }
  139. #[cfg(test)]
  140. mod test {}