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.

203 lines
6.9 KiB

  1. #[cfg(feature = "crypto_openssl")]
  2. mod openssl_server;
  3. #[cfg(feature = "crypto_openssl")]
  4. use crate::openssl_server::start as server_start;
  5. use acme_common::crypto::{get_lib_name, get_lib_version, HashFunction, KeyType, X509Certificate};
  6. use acme_common::error::Error;
  7. use acme_common::logs::{set_log_system, DEFAULT_LOG_LEVEL};
  8. use acme_common::{clean_pid_file, to_idna};
  9. use clap::{Arg, ArgMatches, Command};
  10. use log::{debug, error, info};
  11. use std::fs::File;
  12. use std::io::{self, Read};
  13. const APP_NAME: &str = env!("CARGO_PKG_NAME");
  14. const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
  15. const DEFAULT_PID_FILE: &str = env!("TACD_DEFAULT_PID_FILE");
  16. const DEFAULT_LISTEN_ADDR: &str = "127.0.0.1:5001";
  17. const DEFAULT_CRT_KEY_TYPE: KeyType = KeyType::EcdsaP256;
  18. const DEFAULT_CRT_DIGEST: HashFunction = HashFunction::Sha256;
  19. const ALPN_ACME_PROTO_NAME: &[u8] = b"\x0aacme-tls/1";
  20. fn read_line(path: Option<&str>) -> Result<String, Error> {
  21. let mut input = String::new();
  22. match path {
  23. Some(p) => File::open(p)?.read_to_string(&mut input)?,
  24. None => io::stdin().read_line(&mut input)?,
  25. };
  26. let line = input.trim().to_string();
  27. Ok(line)
  28. }
  29. fn get_acme_value(cnf: &ArgMatches, opt: &str, opt_file: &str) -> Result<String, Error> {
  30. match cnf.value_of(opt) {
  31. Some(v) => Ok(v.to_string()),
  32. None => {
  33. debug!(
  34. "reading {} from {}",
  35. opt,
  36. cnf.value_of(opt_file).unwrap_or("stdin")
  37. );
  38. read_line(cnf.value_of(opt_file))
  39. }
  40. }
  41. }
  42. fn init(cnf: &ArgMatches) -> Result<(), Error> {
  43. acme_common::init_server(
  44. cnf.is_present("foreground"),
  45. cnf.value_of("pid-file"),
  46. DEFAULT_PID_FILE,
  47. );
  48. let domain = get_acme_value(cnf, "domain", "domain-file")?;
  49. let domain = to_idna(&domain)?;
  50. let ext = get_acme_value(cnf, "acme-ext", "acme-ext-file")?;
  51. let listen_addr = cnf.value_of("listen").unwrap_or(DEFAULT_LISTEN_ADDR);
  52. let crt_signature_alg = match cnf.value_of("crt-signature-alg") {
  53. Some(alg) => alg.parse()?,
  54. None => DEFAULT_CRT_KEY_TYPE,
  55. };
  56. let crt_digest = match cnf.value_of("crt-digest") {
  57. Some(alg) => alg.parse()?,
  58. None => DEFAULT_CRT_DIGEST,
  59. };
  60. let (pk, cert) = X509Certificate::from_acme_ext(&domain, &ext, crt_signature_alg, crt_digest)?;
  61. info!("starting {} on {} for {}", APP_NAME, listen_addr, domain);
  62. server_start(listen_addr, &cert, &pk)?;
  63. Ok(())
  64. }
  65. fn main() {
  66. let full_version = format!(
  67. "{} built for {}\n\nCryptographic library:\n - {} {}",
  68. APP_VERSION,
  69. env!("TACD_TARGET"),
  70. get_lib_name(),
  71. get_lib_version(),
  72. );
  73. let default_crt_key_type = DEFAULT_CRT_KEY_TYPE.to_string();
  74. let default_crt_digest = DEFAULT_CRT_DIGEST.to_string();
  75. let default_log_level = DEFAULT_LOG_LEVEL.to_string().to_lowercase();
  76. let matches = Command::new(APP_NAME)
  77. .version(APP_VERSION)
  78. .long_version(full_version.as_str())
  79. .arg(
  80. Arg::new("listen")
  81. .long("listen")
  82. .short('l')
  83. .help("Host and port to listen on")
  84. .takes_value(true)
  85. .value_name("host:port|unix:path")
  86. .default_value(DEFAULT_LISTEN_ADDR),
  87. )
  88. .arg(
  89. Arg::new("domain")
  90. .long("domain")
  91. .short('d')
  92. .help("The domain that is being validated")
  93. .takes_value(true)
  94. .value_name("STRING")
  95. .conflicts_with("domain-file"),
  96. )
  97. .arg(
  98. Arg::new("domain-file")
  99. .long("domain-file")
  100. .help("File from which is read the domain that is being validated")
  101. .takes_value(true)
  102. .value_name("FILE")
  103. .conflicts_with("domain"),
  104. )
  105. .arg(
  106. Arg::new("acme-ext")
  107. .long("acme-ext")
  108. .short('e')
  109. .help("The acmeIdentifier extension to set in the self-signed certificate")
  110. .takes_value(true)
  111. .value_name("STRING")
  112. .conflicts_with("acme-ext-file"),
  113. )
  114. .arg(
  115. Arg::new("acme-ext-file")
  116. .long("acme-ext-file")
  117. .help("File from which is read the acmeIdentifier extension to set in the self-signed certificate")
  118. .takes_value(true)
  119. .value_name("FILE")
  120. .conflicts_with("acme-ext"),
  121. )
  122. .arg(
  123. Arg::new("crt-signature-alg")
  124. .long("crt-signature-alg")
  125. .help("The certificate's signature algorithm")
  126. .takes_value(true)
  127. .value_name("STRING")
  128. .possible_values(&KeyType::list_possible_values())
  129. .default_value(&default_crt_key_type),
  130. )
  131. .arg(
  132. Arg::new("crt-digest")
  133. .long("crt-digest")
  134. .help("The certificate's digest algorithm")
  135. .takes_value(true)
  136. .value_name("STRING")
  137. .possible_values(&HashFunction::list_possible_values())
  138. .default_value(&default_crt_digest),
  139. )
  140. .arg(
  141. Arg::new("log-level")
  142. .long("log-level")
  143. .help("Specify the log level")
  144. .takes_value(true)
  145. .value_name("LEVEL")
  146. .possible_values(&["error", "warn", "info", "debug", "trace"])
  147. .default_value(&default_log_level),
  148. )
  149. .arg(
  150. Arg::new("to-syslog")
  151. .long("log-syslog")
  152. .help("Sends log messages via syslog")
  153. .conflicts_with("to-stderr"),
  154. )
  155. .arg(
  156. Arg::new("to-stderr")
  157. .long("log-stderr")
  158. .help("Prints log messages to the standard error output")
  159. .conflicts_with("to-syslog"),
  160. )
  161. .arg(
  162. Arg::new("foreground")
  163. .long("foreground")
  164. .short('f')
  165. .help("Runs in the foreground"),
  166. )
  167. .arg(
  168. Arg::new("pid-file")
  169. .long("pid-file")
  170. .help("Path to the PID file")
  171. .takes_value(true)
  172. .value_name("FILE")
  173. .default_value(DEFAULT_PID_FILE),
  174. )
  175. .get_matches();
  176. match set_log_system(
  177. matches.value_of("log-level"),
  178. matches.is_present("to-syslog"),
  179. matches.is_present("to-stderr"),
  180. ) {
  181. Ok(_) => {}
  182. Err(e) => {
  183. eprintln!("Error: {}", e);
  184. std::process::exit(2);
  185. }
  186. };
  187. match init(&matches) {
  188. Ok(_) => {}
  189. Err(e) => {
  190. error!("{}", e);
  191. let _ = clean_pid_file(matches.value_of("pid-file"));
  192. std::process::exit(1);
  193. }
  194. };
  195. }