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.

221 lines
7.2 KiB

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