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.

193 lines
6.6 KiB

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