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.

132 lines
2.8 KiB

7 years ago
7 years ago
7 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 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
7 years ago
5 years ago
7 years ago
5 years ago
  1. use crate::command_runner::CommandRunner;
  2. use crate::symbols::Symbol;
  3. use async_trait::async_trait;
  4. use std::borrow::Borrow;
  5. use std::error::Error;
  6. use std::fs::File as FsFile;
  7. use std::io::Write;
  8. use std::marker::PhantomData;
  9. use std::path::Path;
  10. #[derive(Debug)]
  11. pub struct Cert<_C, C, D, P> {
  12. domain: D,
  13. command_runner: C,
  14. root_cert_path: P,
  15. account_key_path: P,
  16. challenges_path: P,
  17. csr_path: P,
  18. cert_path: P,
  19. phantom: PhantomData<_C>,
  20. }
  21. impl<_C, C, D, P> Cert<_C, C, D, P> {
  22. pub fn new(
  23. domain: D,
  24. command_runner: C,
  25. root_cert_path: P,
  26. account_key_path: P,
  27. challenges_path: P,
  28. csr_path: P,
  29. cert_path: P,
  30. ) -> Self {
  31. Self {
  32. domain,
  33. command_runner,
  34. root_cert_path,
  35. account_key_path,
  36. challenges_path,
  37. csr_path,
  38. cert_path,
  39. phantom: PhantomData::default(),
  40. }
  41. }
  42. }
  43. const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
  44. #[async_trait(?Send)]
  45. impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for Cert<_C, C, D, P> {
  46. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  47. if !self.cert_path.as_ref().exists() {
  48. return Ok(false);
  49. }
  50. let output = self
  51. .command_runner
  52. .borrow()
  53. .run_with_args(
  54. "openssl",
  55. args![
  56. "x509",
  57. "-in",
  58. self.cert_path.as_ref(),
  59. "-noout",
  60. "-subject",
  61. "-checkend",
  62. (30 * DAYS_IN_SECONDS).to_string(),
  63. ],
  64. )
  65. .await?;
  66. if output.status.success()
  67. && output.stdout
  68. == format!(
  69. "subject=CN = {}\nCertificate will not expire\n",
  70. self.domain.as_ref()
  71. )
  72. .as_bytes()
  73. {
  74. Ok(
  75. self
  76. .command_runner
  77. .borrow()
  78. .run_successfully(
  79. "openssl",
  80. args![
  81. "verify",
  82. "--untrusted",
  83. self.root_cert_path.as_ref(),
  84. self.cert_path.as_ref(),
  85. ],
  86. )
  87. .await
  88. .is_ok(),
  89. )
  90. } else if output.status.code() == Some(1)
  91. && output.stdout
  92. == format!(
  93. "subject=CN = {}\nCertificate will expire\n",
  94. self.domain.as_ref()
  95. )
  96. .as_bytes()
  97. {
  98. Ok(false)
  99. } else {
  100. Err(String::from_utf8(output.stderr)?.into())
  101. }
  102. }
  103. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  104. let output = self
  105. .command_runner
  106. .borrow()
  107. .get_output(
  108. "acme-tiny",
  109. args![
  110. "--account-key",
  111. self.account_key_path.as_ref(),
  112. "--csr",
  113. self.csr_path.as_ref(),
  114. "--acme-dir",
  115. self.challenges_path.as_ref(),
  116. ],
  117. )
  118. .await?;
  119. let mut file = FsFile::create(self.cert_path.as_ref())?;
  120. file.write_all(&output)?;
  121. Ok(())
  122. }
  123. }
  124. #[cfg(test)]
  125. mod test {}