1 //! Hyper based HTTP server for Krill.
2 //!
3 use std::{
4 collections::HashMap,
5 convert::Infallible,
6 env,
7 fs::File,
8 io::Read,
9 path::{Path, PathBuf},
10 process,
11 str::FromStr,
12 sync::Arc,
13 };
14
15 use bytes::Bytes;
16 use rpki::repository::resources::AsId;
17 use serde::Serialize;
18
19 use futures::TryFutureExt;
20 use hyper::{
21 header::HeaderName,
22 http::HeaderValue,
23 server::conn::AddrIncoming,
24 service::{make_service_fn, service_fn},
25 Method,
26 };
27
28 use crate::{
29 commons::{
30 api::{
31 AspaDefinitionUpdates, BgpStats, ChildHandle, CommandHistoryCriteria, Handle, ParentCaContact, ParentCaReq,
32 ParentHandle, PublisherList, RepositoryContact, RoaDefinitionUpdates, RtaName, Token,
33 },
34 bgp::BgpAnalysisAdvice,
35 error::Error,
36 eventsourcing::AggregateStoreError,
37 remote::rfc8183,
38 util::file,
39 KrillResult,
40 },
41 constants::{
42 KRILL_ENV_HTTP_LOG_INFO, KRILL_ENV_UPGRADE_ONLY, KRILL_VERSION_MAJOR, KRILL_VERSION_MINOR, KRILL_VERSION_PATCH,
43 NO_RESOURCE,
44 },
45 daemon::{
46 auth::common::permissions::Permission,
47 auth::Auth,
48 ca::{CaStatus, RouteAuthorizationUpdates, TA_NAME},
49 config::Config,
50 http::{
51 auth::auth, statics::statics, testbed::testbed, tls, tls_keys, HttpResponse, Request, RequestPath,
52 RoutingResult,
53 },
54 krillserver::KrillServer,
55 },
56 upgrades::{post_start_upgrade, pre_start_upgrade, update_storage_version},
57 };
58
59 //------------ State -----------------------------------------------------
60
61 pub type State = Arc<KrillServer>;
62
parse_config() -> KrillResult<Config>63 pub fn parse_config() -> KrillResult<Config> {
64 Config::create().map_err(|e| Error::Custom(format!("Could not parse config: {}", e)))
65 }
66
print_write_error_hint_and_die(error_msg: String)67 fn print_write_error_hint_and_die(error_msg: String) {
68 eprintln!("{}", error_msg);
69 eprintln!();
70 eprintln!("Hint: if you use systemd you may need to override the allowed ReadWritePaths,");
71 eprintln!("the easiest way may be by doing 'systemctl edit krill' and add a section like:");
72 eprintln!();
73 eprintln!("[Service]");
74 eprintln!("ReadWritePaths=/local/path1 /local/path2 ...");
75 }
76
write_pid_file_or_die(config: &Config)77 fn write_pid_file_or_die(config: &Config) {
78 let pid_file = config.pid_file();
79 if let Err(e) = file::save(process::id().to_string().as_bytes(), &pid_file) {
80 print_write_error_hint_and_die(format!("Could not write PID file: {}", e));
81 }
82 }
83
test_data_dir_or_die(config_item: &str, dir: &Path)84 fn test_data_dir_or_die(config_item: &str, dir: &Path) {
85 let test_file = dir.join("test");
86
87 if let Err(e) = file::save(b"test", &test_file) {
88 print_write_error_hint_and_die(format!(
89 "Cannot write to dir '{}' for configuration setting '{}', Error: {}",
90 dir.to_string_lossy(),
91 config_item,
92 e
93 ));
94 } else if let Err(e) = file::delete_file(&test_file) {
95 print_write_error_hint_and_die(format!(
96 "Cannot delete test file '{}' in dir for configuration setting '{}', Error: {}",
97 test_file.to_string_lossy(),
98 config_item,
99 e
100 ));
101 }
102 }
103
test_data_dirs_or_die(config: &Config)104 fn test_data_dirs_or_die(config: &Config) {
105 test_data_dir_or_die("data_dir", &config.data_dir);
106 if let Some(rfc8181_log_dir) = &config.rfc8181_log_dir {
107 test_data_dir_or_die("rfc8181_log_dir", rfc8181_log_dir);
108 }
109 if let Some(rfc6492_log_dir) = &config.rfc6492_log_dir {
110 test_data_dir_or_die("rfc6492_log_dir", rfc6492_log_dir);
111 }
112 }
113
start_krill_daemon(config: Arc<Config>) -> Result<(), Error>114 pub async fn start_krill_daemon(config: Arc<Config>) -> Result<(), Error> {
115 write_pid_file_or_die(&config);
116 test_data_dirs_or_die(&config);
117
118 // Call upgrade, this will only do actual work if needed.
119 pre_start_upgrade(config.clone())?;
120
121 // Create the server, this will create the necessary data sub-directories if needed
122 let krill = KrillServer::build(config.clone()).await?;
123
124 // Call post-start upgrades to trigger any upgrade related runtime actions, such as
125 // re-issuing ROAs because subject name strategy has changed.
126 post_start_upgrade(&config, &krill).await?;
127
128 // Update the version identifiers for the storage dirs
129 update_storage_version(&config.data_dir).await?;
130
131 // If the operator wanted to do the upgrade only, now is a good time to report success and stop
132 if env::var(KRILL_ENV_UPGRADE_ONLY).is_ok() {
133 println!("Krill upgrade successful");
134 }
135
136 // Reset the RRDP session after a restart.
137 krill.repository_session_reset()?;
138
139 let state = Arc::new(krill);
140
141 let service = make_service_fn(move |_| {
142 let state = state.clone();
143 async move {
144 Ok::<_, Infallible>(service_fn(move |req: hyper::Request<hyper::Body>| {
145 let state = state.clone();
146 map_requests(req, state)
147 }))
148 }
149 });
150
151 tls_keys::create_key_cert_if_needed(&config.data_dir).map_err(|e| Error::HttpsSetup(format!("{}", e)))?;
152
153 let server_config_builder = tls::TlsConfigBuilder::new()
154 .cert_path(tls_keys::cert_file_path(&config.data_dir))
155 .key_path(tls_keys::key_file_path(&config.data_dir));
156 let server_config = server_config_builder.build().unwrap();
157
158 let incoming = AddrIncoming::bind(&config.socket_addr()).map_err(|e| {
159 Error::Custom(format!(
160 "Could not bind to address and port: {}, Error: {}",
161 &config.socket_addr(),
162 e
163 ))
164 })?;
165
166 let acceptor = tls::TlsAcceptor::new(server_config, incoming);
167
168 let server = hyper::Server::builder(acceptor)
169 .serve(service)
170 .map_err(|e| eprintln!("Server error: {}", e));
171
172 if server.await.is_err() {
173 eprintln!("Krill failed to start");
174 }
175
176 Ok(())
177 }
178
179 struct RequestLogger {
180 req_method: hyper::Method,
181 req_path: String,
182 }
183
184 impl RequestLogger {
begin(req: &hyper::Request<hyper::Body>) -> Self185 fn begin(req: &hyper::Request<hyper::Body>) -> Self {
186 let req_method = req.method().clone();
187 let req_path = RequestPath::from_request(req).full().to_string();
188
189 if log_enabled!(log::Level::Trace) {
190 trace!(
191 "Request: method={} path={} headers={:?}",
192 &req_method,
193 &req_path,
194 &req.headers()
195 );
196 }
197
198 RequestLogger { req_method, req_path }
199 }
200
end(&self, res: Result<&HttpResponse, &Error>)201 fn end(&self, res: Result<&HttpResponse, &Error>) {
202 match res {
203 Ok(response) => {
204 match (response.status(), response.benign(), response.cause()) {
205 (s, false, Some(cause)) if s.is_client_error() => warn!("HTTP {}: {}", s.as_u16(), cause),
206 (s, false, Some(cause)) if s.is_server_error() => error!("HTTP {}: {}", s.as_u16(), cause),
207 _ => {}
208 }
209
210 if env::var(KRILL_ENV_HTTP_LOG_INFO).is_ok() {
211 info!("{} {} {}", self.req_method, self.req_path, response.status());
212 } else {
213 debug!("{} {} {}", self.req_method, self.req_path, response.status());
214 }
215 if response.loggable() && log_enabled!(log::Level::Trace) {
216 trace!("Response: headers={:?} body={:?}", response.headers(), response.body());
217 }
218 }
219 Err(err) => {
220 error!("{} {} Error: {}", self.req_method, self.req_path, err);
221 }
222 }
223 }
224 }
225
map_requests(req: hyper::Request<hyper::Body>, state: State) -> Result<hyper::Response<hyper::Body>, Error>226 async fn map_requests(req: hyper::Request<hyper::Body>, state: State) -> Result<hyper::Response<hyper::Body>, Error> {
227 let logger = RequestLogger::begin(&req);
228
229 let req = Request::new(req, state).await;
230
231 // Save any updated auth details, e.g. if an OpenID Connect token needed
232 // refreshing.
233 let new_auth = req.actor().new_auth();
234
235 // We used to use .or_else() here but that causes a large recursive call
236 // tree due to these calls being to async functions, large enough with the
237 // given Request object passed each time that it eventually resulted in
238 // stack overflow. By doing it by hand like this we avoid the use of the
239 // macros that cause the recursion. We could also look at putting less data
240 // on the stack.
241 let mut res = api(req).await;
242 if let Err(req) = res {
243 res = auth(req).await;
244 }
245 if let Err(req) = res {
246 res = health(req).await;
247 }
248 if let Err(req) = res {
249 res = metrics(req).await;
250 }
251 if let Err(req) = res {
252 res = stats(req).await;
253 }
254 if let Err(req) = res {
255 res = rfc8181(req).await;
256 }
257 if let Err(req) = res {
258 res = rfc6492(req).await;
259 }
260 if let Err(req) = res {
261 res = statics(req).await;
262 }
263 if let Err(req) = res {
264 res = ta(req).await;
265 }
266 if let Err(req) = res {
267 res = rrdp(req).await;
268 }
269 if let Err(req) = res {
270 res = testbed(req).await;
271 }
272 if let Err(req) = res {
273 res = render_not_found(req).await;
274 }
275
276 let res = res.map_err(|_| Error::custom("should have received not found response"));
277
278 // Augment the response with any updated auth details that were determined
279 // above.
280 let res = add_new_auth_to_response(res, new_auth);
281
282 // Log the request and the response.
283 logger.end(res.as_ref());
284
285 res.map(|res| res.response())
286 }
287
288 //------------ Support Functions ---------------------------------------------
289
290 /// HTTP redirects cannot have a response body and so we cannot render the error
291 /// to be displayed in Lagosta as a JSON body, instead we must package the JSON
292 /// as a query parameter.
render_error_redirect(err: Error) -> RoutingResult293 pub fn render_error_redirect(err: Error) -> RoutingResult {
294 let response = err.to_error_response();
295 let json = serde_json::to_string(&response).or_else(|err| {
296 Ok(format!(
297 "JSON serialization error while processing internal error: {}",
298 err
299 ))
300 })?;
301 let b64 = base64::encode(json);
302 let location = format!("/index.html#/login?error={}", b64);
303 Ok(HttpResponse::found(&location))
304 }
305
render_empty_res(res: Result<(), Error>) -> RoutingResult306 pub fn render_empty_res(res: Result<(), Error>) -> RoutingResult {
307 match res {
308 Ok(()) => render_ok(),
309 Err(e) => render_error(e),
310 }
311 }
312
313 #[allow(clippy::unnecessary_wraps)]
render_error(e: Error) -> RoutingResult314 fn render_error(e: Error) -> RoutingResult {
315 debug!("Server Error: {}", e);
316 Ok(HttpResponse::response_from_error(e))
317 }
318
319 #[allow(clippy::unnecessary_wraps)]
render_json<O: Serialize>(obj: O) -> RoutingResult320 fn render_json<O: Serialize>(obj: O) -> RoutingResult {
321 Ok(HttpResponse::json(&obj))
322 }
323
render_json_res<O: Serialize>(res: Result<O, Error>) -> RoutingResult324 fn render_json_res<O: Serialize>(res: Result<O, Error>) -> RoutingResult {
325 match res {
326 Ok(o) => render_json(o),
327 Err(e) => render_error(e),
328 }
329 }
330
331 /// A clean 404 result for the API (no content, not for humans)
332 #[allow(clippy::unnecessary_wraps)]
render_unknown_resource() -> RoutingResult333 fn render_unknown_resource() -> RoutingResult {
334 Ok(HttpResponse::response_from_error(Error::ApiUnknownResource))
335 }
336
337 /// A clean 200 result for the API (no content, not for humans)
338 #[allow(clippy::unnecessary_wraps)]
render_ok() -> RoutingResult339 pub fn render_ok() -> RoutingResult {
340 Ok(HttpResponse::ok())
341 }
342
343 #[allow(clippy::unnecessary_wraps)]
render_unknown_method() -> RoutingResult344 pub fn render_unknown_method() -> RoutingResult {
345 Ok(HttpResponse::response_from_error(Error::ApiUnknownMethod))
346 }
347
348 /// A clean 404 response
349 #[allow(clippy::unnecessary_wraps)]
render_not_found(_req: Request) -> RoutingResult350 pub async fn render_not_found(_req: Request) -> RoutingResult {
351 Ok(HttpResponse::not_found())
352 }
353
354 /// Returns the server health.
health(req: Request) -> RoutingResult355 pub async fn health(req: Request) -> RoutingResult {
356 if req.is_get() && req.path().segment() == "health" {
357 render_ok()
358 } else {
359 Err(req)
360 }
361 }
362
363 /// Produce prometheus style metrics
metrics(req: Request) -> RoutingResult364 pub async fn metrics(req: Request) -> RoutingResult {
365 if req.is_get() && req.path().segment().starts_with("metrics") {
366 let server = req.state();
367
368 struct AllBgpStats {
369 announcements_valid: HashMap<Handle, usize>,
370 announcements_invalid_asn: HashMap<Handle, usize>,
371 announcements_invalid_length: HashMap<Handle, usize>,
372 announcements_not_found: HashMap<Handle, usize>,
373 roas_too_permissive: HashMap<Handle, usize>,
374 roas_redundant: HashMap<Handle, usize>,
375 roas_stale: HashMap<Handle, usize>,
376 roas_total: HashMap<Handle, usize>,
377 }
378
379 impl AllBgpStats {
380 fn add_ca(&mut self, ca: &Handle, stats: &BgpStats) {
381 self.announcements_valid.insert(ca.clone(), stats.announcements_valid);
382 self.announcements_invalid_asn
383 .insert(ca.clone(), stats.announcements_invalid_asn);
384 self.announcements_invalid_length
385 .insert(ca.clone(), stats.announcements_invalid_length);
386 self.announcements_not_found
387 .insert(ca.clone(), stats.announcements_not_found);
388 self.roas_too_permissive.insert(ca.clone(), stats.roas_too_permissive);
389 self.roas_redundant.insert(ca.clone(), stats.roas_redundant);
390 self.roas_stale.insert(ca.clone(), stats.roas_stale);
391 self.roas_total.insert(ca.clone(), stats.roas_total);
392 }
393 }
394
395 let mut res = String::new();
396
397 let info = server.server_info();
398 res.push_str("# HELP krill_server_start unix timestamp in seconds of last krill server start\n");
399 res.push_str("# TYPE krill_server_start gauge\n");
400 res.push_str(&format!("krill_server_start {}\n", info.started()));
401 res.push('\n');
402
403 res.push_str("# HELP krill_version_major krill server major version number\n");
404 res.push_str("# TYPE krill_version_major gauge\n");
405 res.push_str(&format!("krill_version_major {}\n", KRILL_VERSION_MAJOR));
406 res.push('\n');
407
408 res.push_str("# HELP krill_version_minor krill server minor version number\n");
409 res.push_str("# TYPE krill_version_minor gauge\n");
410 res.push_str(&format!("krill_version_minor {}\n", KRILL_VERSION_MINOR));
411 res.push('\n');
412
413 res.push_str("# HELP krill_version_patch krill server patch version number\n");
414 res.push_str("# TYPE krill_version_patch gauge\n");
415 res.push_str(&format!("krill_version_patch {}\n", KRILL_VERSION_PATCH));
416
417 #[cfg(feature = "multi-user")]
418 {
419 res.push('\n');
420 res.push_str("# HELP krill_auth_session_cache_size total number of cached login session tokens\n");
421 res.push_str("# TYPE krill_auth_session_cache_size gauge\n");
422 res.push_str(&format!(
423 "krill_auth_session_cache_size {}\n",
424 server.login_session_cache_size()
425 ));
426 }
427
428 if let Ok(cas_stats) = server.cas_stats().await {
429 let number_cas = cas_stats.len();
430
431 res.push('\n');
432 res.push_str("# HELP krill_cas number of cas in krill\n");
433 res.push_str("# TYPE krill_cas gauge\n");
434 res.push_str(&format!("krill_cas {}\n", number_cas));
435
436 if !server.config.metrics.metrics_hide_ca_details {
437 // Show per CA details
438
439 let mut ca_status_map: HashMap<Handle, Arc<CaStatus>> = HashMap::new();
440
441 for ca in cas_stats.keys() {
442 if let Ok(ca_status) = server.ca_status(ca).await {
443 ca_status_map.insert(ca.clone(), ca_status);
444 }
445 }
446
447 {
448 // CA -> Parent metrics
449
450 // krill_ca_parent_success{{ca="ca", parent="parent"}} 1
451 // krill_ca_parent_last_success_time{{ca="ca", parent="parent"}} 1630921599 // timestamp
452
453 res.push('\n');
454 res.push_str(
455 "# HELP krill_ca_parent_success status of last CA to parent connection (0=issue, 1=success)\n",
456 );
457 res.push_str("# TYPE krill_ca_parent_success gauge\n");
458 for (ca, status) in ca_status_map.iter() {
459 if ca.as_str() != TA_NAME {
460 for (parent, status) in status.parents().iter() {
461 // skip the ones for which we have no status yet, i.e it was really only just added
462 // and no attempt to connect has yet been made.
463 if let Some(exchange) = status.last_exchange() {
464 let value = if exchange.was_success() { 1 } else { 0 };
465 res.push_str(&format!(
466 "krill_ca_parent_success{{ca=\"{}\", parent=\"{}\"}} {}\n",
467 ca, parent, value
468 ));
469 }
470 }
471 }
472 }
473
474 res.push('\n');
475 res.push_str(
476 "# HELP krill_ca_parent_last_success_time unix timestamp in seconds of last successful CA to parent connection\n",
477 );
478 res.push_str("# TYPE krill_ca_parent_last_success_time gauge\n");
479
480 for (ca, status) in ca_status_map.iter() {
481 if ca.as_str() != TA_NAME {
482 for (parent, status) in status.parents().iter() {
483 // skip the ones for which we have no successful connection at all. Most likely
484 // they were just added (in which case it will come) - or were never successful
485 // in which case the metric above will say that the status is 0
486 if let Some(last_success) = status.last_success() {
487 res.push_str(&format!(
488 "krill_ca_parent_last_success_time{{ca=\"{}\", parent=\"{}\"}} {}\n",
489 ca, parent, last_success
490 ));
491 }
492 }
493 }
494 }
495 }
496
497 {
498 // CA -> Publication Server status
499
500 // krill_ca_repo_success{{ca="ca"}} 1
501 // krill_ca_repo_last_success_time{{ca="ca"}} 1630921599
502 // krill_ca_repo_next_before_time{{ca="ca"}} 1630921599
503
504 res.push('\n');
505 res.push_str("# HELP krill_ca_ps_success status of last CA to Publication Server connection (0=issue, 1=success)\n");
506 res.push_str("# TYPE krill_ca_ps_success gauge\n");
507 for (ca, status) in ca_status_map.iter() {
508 // skip the ones for which we have no status yet, i.e it was really only just added
509 // and no attempt to connect has yet been made.
510 if let Some(exchange) = status.repo().last_exchange() {
511 let value = if exchange.was_success() { 1 } else { 0 };
512 res.push_str(&format!("krill_ca_ps_success{{ca=\"{}\"}} {}\n", ca, value));
513 }
514 }
515
516 res.push('\n');
517 res.push_str("# HELP krill_ca_ps_last_success_time unix timestamp in seconds of last successful CA to Publication Server connection\n");
518 res.push_str("# TYPE krill_ca_ps_last_success_time gauge\n");
519 for (ca, status) in ca_status_map.iter() {
520 // skip the ones for which we have no status yet, i.e it was really only just added
521 // and no attempt to connect has yet been made.
522 if let Some(last_success) = status.repo().last_success() {
523 res.push_str(&format!(
524 "krill_ca_ps_last_success_time{{ca=\"{}\"}} {}\n",
525 ca, last_success
526 ));
527 }
528 }
529
530 res.push('\n');
531 res.push_str("# HELP krill_ca_ps_next_planned_time unix timestamp in seconds of next planned CA to Publication Server connection (unless e.g. ROAs are changed)\n");
532 res.push_str("# TYPE krill_ca_ps_next_planned_time gauge\n");
533 for (ca, status) in ca_status_map.iter() {
534 // skip the ones for which we have no status yet, i.e it was really only just added
535 // and no attempt to connect has yet been made.
536 let timestamp = status.repo().next_exchange_before();
537 res.push_str(&format!(
538 "krill_ca_ps_next_planned_time{{ca=\"{}\"}} {}\n",
539 ca, timestamp
540 ));
541 }
542 }
543
544 // Do not show child metrics if none of the CAs has any children..
545 // Many users do not delegate so, showing these metrics would just be confusing.
546 let any_children = cas_stats.values().any(|ca| ca.child_count() > 0);
547
548 if any_children && !server.config.metrics.metrics_hide_child_details {
549 // CA -> Children
550
551 // krill_cas_children{ca="parent"} 11 // nr of children
552 // krill_ca_child_success{ca="parent", child="child"} 1
553 // krill_ca_child_state{ca="parent", child="child"} 1
554 // krill_ca_child_last_connection{ca="parent", child="child"} 1630921599
555 // krill_ca_child_last_success{ca="parent", child="child"} 1630921599
556 // krill_ca_child_agent_total{ca="parent", ua="krill/0.9.2"} 11
557
558 res.push('\n');
559 res.push_str("# HELP krill_cas_children number of children for CA\n");
560 res.push_str("# TYPE krill_cas_children gauge\n");
561 for (ca, status) in cas_stats.iter() {
562 res.push_str(&format!(
563 "krill_cas_children{{ca=\"{}\"}} {}\n",
564 ca,
565 status.child_count()
566 ));
567 }
568
569 res.push('\n');
570 res.push_str(
571 "# HELP krill_ca_child_success status of last child to CA connection (0=issue, 1=success)\n",
572 );
573 res.push_str("# TYPE krill_ca_child_success gauge\n");
574 for (ca, status) in ca_status_map.iter() {
575 // skip the ones for which we have no status yet, i.e it was really only just added
576 // and no attempt to connect has yet been made.
577 for (child, status) in status.children().iter() {
578 if let Some(exchange) = status.last_exchange() {
579 let value = if exchange.was_success() { 1 } else { 0 };
580 res.push_str(&format!(
581 "krill_ca_child_success{{ca=\"{}\", child=\"{}\"}} {}\n",
582 ca, child, value
583 ));
584 }
585 }
586 }
587
588 res.push('\n');
589 res.push_str(
590 "# HELP krill_ca_child_state child state (see 'suspend_child_after_inactive_hours' config) (0=suspended, 1=active)\n",
591 );
592 res.push_str("# TYPE krill_ca_child_state gauge\n");
593 for (ca, status) in ca_status_map.iter() {
594 for (child, status) in status.children().iter() {
595 let value = if status.suspended().is_none() { 1 } else { 0 };
596
597 res.push_str(&format!(
598 "krill_ca_child_state{{ca=\"{}\", child=\"{}\"}} {}\n",
599 ca, child, value
600 ));
601 }
602 }
603
604 res.push('\n');
605 res.push_str("# HELP krill_ca_child_last_connection unix timestamp in seconds of last child to CA connection\n");
606 res.push_str("# TYPE krill_ca_child_last_connection gauge\n");
607 for (ca, status) in ca_status_map.iter() {
608 // skip the ones for which we have no status yet, i.e it was really only just added
609 // and no attempt to connect has yet been made.
610 for (child, status) in status.children().iter() {
611 if let Some(exchange) = status.last_exchange() {
612 let timestamp = exchange.timestamp();
613 res.push_str(&format!(
614 "krill_ca_child_last_connection{{ca=\"{}\", child=\"{}\"}} {}\n",
615 ca, child, timestamp
616 ));
617 }
618 }
619 }
620
621 res.push('\n');
622 res.push_str(
623 "# HELP krill_ca_child_last_success unix timestamp in seconds of last successful child to CA connection\n",
624 );
625 res.push_str("# TYPE krill_ca_child_last_success gauge\n");
626 for (ca, status) in ca_status_map.iter() {
627 // skip the ones for which we have no status yet, i.e it was really only just added
628 // and no attempt to connect has yet been made.
629 for (child, status) in status.children().iter() {
630 if let Some(time) = status.last_success() {
631 res.push_str(&format!(
632 "krill_ca_child_last_success{{ca=\"{}\", child=\"{}\"}} {}\n",
633 ca, child, time
634 ));
635 }
636 }
637 }
638
639 res.push('\n');
640 res.push_str(
641 "# HELP krill_ca_child_agent_total total children per user agent based on their last connection\n",
642 );
643 res.push_str("# TYPE krill_ca_child_agent_total gauge\n");
644 for (ca, status) in ca_status_map.iter() {
645 // skip the ones for which we have no status yet, i.e it was really only just added
646 // and no attempt to connect has yet been made.
647
648 let mut user_agent_totals: HashMap<String, usize> = HashMap::new();
649 for status in status.children().values() {
650 if let Some(exchange) = status.last_exchange() {
651 let agent = exchange.user_agent().cloned().unwrap_or_else(|| "<none>".to_string());
652 *user_agent_totals.entry(agent).or_insert(0) += 1;
653 }
654 }
655
656 for (ua, total) in user_agent_totals.iter() {
657 res.push_str(&format!(
658 "krill_ca_child_agent_total{{ca=\"{}\", user_agent=\"{}\"}} {}\n",
659 ca, ua, total
660 ));
661 }
662 }
663 }
664
665 if !server.config.metrics.metrics_hide_roa_details {
666 // BGP Announcement metrics
667
668 // Aggregate ROA vs BGP stats per status
669 let mut all_bgp_stats = AllBgpStats {
670 announcements_valid: HashMap::new(),
671 announcements_invalid_asn: HashMap::new(),
672 announcements_invalid_length: HashMap::new(),
673 announcements_not_found: HashMap::new(),
674 roas_too_permissive: HashMap::new(),
675 roas_redundant: HashMap::new(),
676 roas_stale: HashMap::new(),
677 roas_total: HashMap::new(),
678 };
679
680 for (ca, ca_stats) in cas_stats.iter() {
681 all_bgp_stats.add_ca(ca, ca_stats.bgp_stats());
682 }
683
684 res.push('\n');
685 res.push_str("# HELP krill_cas_bgp_announcements_valid number of announcements seen for CA resources with RPKI state VALID\n");
686 res.push_str("# TYPE krill_cas_bgp_announcements_valid gauge\n");
687 for (ca, nr) in all_bgp_stats.announcements_valid.iter() {
688 res.push_str(&format!("krill_cas_bgp_announcements_valid{{ca=\"{}\"}} {}\n", ca, nr));
689 }
690
691 res.push('\n');
692 res.push_str("# HELP krill_cas_bgp_announcements_invalid_asn number of announcements seen for CA resources with RPKI state INVALID (ASN mismatch)\n");
693 res.push_str("# TYPE krill_cas_bgp_announcements_invalid_asn gauge\n");
694 for (ca, nr) in all_bgp_stats.announcements_invalid_asn.iter() {
695 res.push_str(&format!(
696 "krill_cas_bgp_announcements_invalid_asn{{ca=\"{}\"}} {}\n",
697 ca, nr
698 ));
699 }
700
701 res.push('\n');
702 res.push_str("# HELP krill_cas_bgp_announcements_invalid_length number of announcements seen for CA resources with RPKI state INVALID (prefix exceeds max length)\n");
703 res.push_str("# TYPE krill_cas_bgp_announcements_invalid_length gauge\n");
704 for (ca, nr) in all_bgp_stats.announcements_invalid_length.iter() {
705 res.push_str(&format!(
706 "krill_cas_bgp_announcements_invalid_length{{ca=\"{}\"}} {}\n",
707 ca, nr
708 ));
709 }
710
711 res.push('\n');
712 res.push_str("# HELP krill_cas_bgp_announcements_not_found number of announcements seen for CA resources with RPKI state NOT FOUND (none of the CA's ROAs cover this)\n");
713 res.push_str("# TYPE krill_cas_bgp_announcements_not_found gauge\n");
714 for (ca, nr) in all_bgp_stats.announcements_not_found.iter() {
715 res.push_str(&format!(
716 "krill_cas_bgp_announcements_not_found{{ca=\"{}\"}} {}\n",
717 ca, nr
718 ));
719 }
720
721 res.push('\n');
722 res.push_str("# HELP krill_cas_bgp_roas_too_permissive number of ROAs for this CA which allow excess announcements (0 may also indicate that no BGP info is available)\n");
723 res.push_str("# TYPE krill_cas_bgp_roas_too_permissive gauge\n");
724 for (ca, nr) in all_bgp_stats.roas_too_permissive.iter() {
725 res.push_str(&format!("krill_cas_bgp_roas_too_permissive{{ca=\"{}\"}} {}\n", ca, nr));
726 }
727
728 res.push('\n');
729 res.push_str("# HELP krill_cas_bgp_roas_redundant number of ROAs for this CA which are redundant (0 may also indicate that no BGP info is available)\n");
730 res.push_str("# TYPE krill_cas_bgp_roas_redundant gauge\n");
731 for (ca, nr) in all_bgp_stats.roas_redundant.iter() {
732 res.push_str(&format!("krill_cas_bgp_roas_redundant{{ca=\"{}\"}} {}\n", ca, nr));
733 }
734
735 res.push('\n');
736 res.push_str("# HELP krill_cas_bgp_roas_stale number of ROAs for this CA for which no announcements are seen (0 may also indicate that no BGP info is available)\n");
737 res.push_str("# TYPE krill_cas_bgp_roas_stale gauge\n");
738 for (ca, nr) in all_bgp_stats.roas_stale.iter() {
739 res.push_str(&format!("krill_cas_bgp_roas_stale{{ca=\"{}\"}} {}\n", ca, nr));
740 }
741
742 res.push('\n');
743 res.push_str("# HELP krill_cas_bgp_roas_total total number of ROAs for this CA\n");
744 res.push_str("# TYPE krill_cas_bgp_roas_stale gauge\n");
745 for (ca, nr) in all_bgp_stats.roas_total.iter() {
746 res.push_str(&format!("krill_cas_bgp_roas_total{{ca=\"{}\"}} {}\n", ca, nr));
747 }
748 }
749 }
750 }
751
752 if let Ok(stats) = server.repo_stats() {
753 let publishers = stats.get_publishers();
754
755 res.push('\n');
756 res.push_str("# HELP krill_repo_publisher number of publishers in repository\n");
757 res.push_str("# TYPE krill_repo_publisher gauge\n");
758 res.push_str(&format!("krill_repo_publisher {}\n", publishers.len()));
759
760 if let Some(last_update) = stats.last_update() {
761 res.push('\n');
762 res.push_str(
763 "# HELP krill_repo_rrdp_last_update unix timestamp in seconds of last update by any publisher\n",
764 );
765 res.push_str("# TYPE krill_repo_rrdp_last_update gauge\n");
766 res.push_str(&format!("krill_repo_rrdp_last_update {}\n", last_update.timestamp()));
767 }
768
769 res.push('\n');
770 res.push_str("# HELP krill_repo_rrdp_serial RRDP serial\n");
771 res.push_str("# TYPE krill_repo_rrdp_serial counter\n");
772 res.push_str(&format!("krill_repo_rrdp_serial {}\n", stats.serial()));
773
774 if !server.config.metrics.metrics_hide_publisher_details {
775 res.push('\n');
776 res.push_str("# HELP krill_repo_objects number of objects in repository for publisher\n");
777 res.push_str("# TYPE krill_repo_objects gauge\n");
778 for (publisher, stats) in publishers {
779 res.push_str(&format!(
780 "krill_repo_objects{{publisher=\"{}\"}} {}\n",
781 publisher,
782 stats.objects()
783 ));
784 }
785
786 res.push('\n');
787 res.push_str("# HELP krill_repo_size size of objects in bytes in repository for publisher\n");
788 res.push_str("# TYPE krill_repo_size gauge\n");
789 for (publisher, stats) in publishers {
790 res.push_str(&format!(
791 "krill_repo_size{{publisher=\"{}\"}} {}\n",
792 publisher,
793 stats.size()
794 ));
795 }
796
797 res.push('\n');
798 res.push_str("# HELP krill_repo_last_update unix timestamp in seconds of last update for publisher\n");
799 res.push_str("# TYPE krill_repo_last_update gauge\n");
800 for (publisher, stats) in publishers {
801 if let Some(last_update) = stats.last_update() {
802 res.push_str(&format!(
803 "krill_repo_last_update{{publisher=\"{}\"}} {}\n",
804 publisher,
805 last_update.timestamp()
806 ));
807 }
808 }
809 }
810 }
811
812 Ok(HttpResponse::text(res.into_bytes()))
813 } else {
814 Err(req)
815 }
816 }
817
818 //------------ Publication ---------------------------------------------------
819
820 /// Handle RFC8181 queries and return the appropriate response.
rfc8181(req: Request) -> RoutingResult821 pub async fn rfc8181(req: Request) -> RoutingResult {
822 if req.path().segment() == "rfc8181" {
823 let mut path = req.path().clone();
824 let publisher = match path.path_arg() {
825 Some(publisher) => publisher,
826 None => return render_error(Error::ApiInvalidHandle),
827 };
828
829 let state = req.state().clone();
830
831 let bytes = match req.rfc8181_bytes().await {
832 Ok(bytes) => bytes,
833 Err(e) => return render_error(e),
834 };
835
836 match state.rfc8181(publisher, bytes) {
837 Ok(bytes) => Ok(HttpResponse::rfc8181(bytes.to_vec())),
838 Err(e) => render_error(e),
839 }
840 } else {
841 Err(req)
842 }
843 }
844
845 //------------ Embedded TA --------------------------------------------------
ta(req: Request) -> RoutingResult846 async fn ta(req: Request) -> RoutingResult {
847 match *req.method() {
848 Method::GET => match req.path.full() {
849 "/ta/ta.tal" => tal(req).await,
850 "/testbed.tal" => tal(req).await,
851 "/ta/ta.cer" => ta_cer(req).await,
852 _ => Err(req),
853 },
854 _ => Err(req),
855 }
856 }
857
tal(req: Request) -> RoutingResult858 pub async fn tal(req: Request) -> RoutingResult {
859 match req.state().ta().await {
860 Ok(ta) => Ok(HttpResponse::text(format!("{}", ta.tal()).into_bytes())),
861 Err(_) => render_unknown_resource(),
862 }
863 }
864
ta_cer(req: Request) -> RoutingResult865 pub async fn ta_cer(req: Request) -> RoutingResult {
866 match req.state().trust_anchor_cert().await {
867 Some(cert) => Ok(HttpResponse::cert(cert.to_captured().to_vec())),
868 None => render_unknown_resource(),
869 }
870 }
871
872 //------------ Provisioning (RFC6492) ----------------------------------------
873
874 /// Process an RFC 6492 request
875 ///
rfc6492(req: Request) -> RoutingResult876 pub async fn rfc6492(req: Request) -> RoutingResult {
877 if req.path().segment() == "rfc6492" {
878 let mut path = req.path().clone();
879 let ca = match path.path_arg() {
880 Some(ca) => ca,
881 None => return render_error(Error::ApiInvalidHandle),
882 };
883
884 let actor = req.actor();
885 let state = req.state().clone();
886 let user_agent = req.user_agent();
887
888 let bytes = match req.rfc6492_bytes().await {
889 Ok(bytes) => bytes,
890 Err(e) => return render_error(e),
891 };
892 let krill_server = state;
893 match krill_server.rfc6492(ca, bytes, user_agent, &actor).await {
894 Ok(bytes) => Ok(HttpResponse::rfc6492(bytes.to_vec())),
895 Err(e) => render_error(e),
896 }
897 } else {
898 Err(req)
899 }
900 }
901
902 /// Return various stats as json
stats(req: Request) -> RoutingResult903 async fn stats(req: Request) -> RoutingResult {
904 match *req.method() {
905 Method::GET => match req.path().full() {
906 "/stats/info" => render_json(req.state().server_info()),
907 "/stats/repo" => render_json_res(req.state().repo_stats()),
908 "/stats/cas" => render_json_res(req.state().cas_stats().await),
909 _ => Err(req),
910 },
911 _ => Err(req),
912 }
913 }
914
915 // Suppress any error in the unlikely event that we fail to inject the
916 // Authorization header into the HTTP response as this is an internal error that
917 // we should shield the user from, but log a warning as this is very unexpected.
add_authorization_headers_to_response(org_response: HttpResponse, token: Token) -> HttpResponse918 fn add_authorization_headers_to_response(org_response: HttpResponse, token: Token) -> HttpResponse {
919 let mut new_header_names = Vec::new();
920 let mut new_header_values = Vec::new();
921
922 new_header_names.push(HeaderName::from_str("Authorization"));
923 new_header_values.push(HeaderValue::from_str(&format!("Bearer {}", &token)));
924
925 let okay = !new_header_names
926 .iter()
927 .zip(new_header_values.iter())
928 .any(|(n, v)| n.is_err() | v.is_err());
929
930 if okay {
931 let (parts, body) = org_response.response().into_parts();
932 let mut augmented_response = hyper::Response::from_parts(parts, body);
933 let headers = augmented_response.headers_mut();
934 for (name, value) in new_header_names.into_iter().zip(new_header_values.into_iter()) {
935 headers.insert(name.unwrap(), value.unwrap());
936 }
937 HttpResponse::new(augmented_response)
938 } else {
939 let mut conversion_errors = Vec::new();
940 conversion_errors.extend(
941 new_header_names
942 .into_iter()
943 .filter(|result| result.is_err())
944 .map(|i| i.unwrap_err().to_string()),
945 );
946 conversion_errors.extend(
947 new_header_values
948 .into_iter()
949 .filter(|result| result.is_err())
950 .map(|i| i.unwrap_err().to_string()),
951 );
952 warn!(
953 "Internal error: unable to add refreshed auth token to the response: {:?}",
954 conversion_errors.join(", ")
955 );
956 org_response
957 }
958 }
959
add_new_auth_to_response(res: Result<HttpResponse, Error>, opt_auth: Option<Auth>) -> Result<HttpResponse, Error>960 fn add_new_auth_to_response(res: Result<HttpResponse, Error>, opt_auth: Option<Auth>) -> Result<HttpResponse, Error> {
961 if let Some(Auth::Bearer(token)) = opt_auth {
962 res.map(|ok_res| add_authorization_headers_to_response(ok_res, token))
963 } else {
964 res
965 }
966 }
967
968 // aa! macro aka if-authorized-then-run-the-given-code-else-return-http-403
969 // ------------------------------------------------------------------------
970 // This macro handles returning from API handler functions if the request is not
971 // Authenticated or lacks sufficient Authorization. We don't use a normal fn for
972 // this as then each API handler function would have to also test for success or
973 // failure and also return the forbidden response to the caller, That would be
974 // both verbose and repetitive. We also can't use the ? operator to return Err
975 // as Err is used to propagate the request to the next handler in the chain. If
976 // we had a child crate we could use a proc macro instead so that we could
977 // "annotate" each API handler function with something like:
978 // #[require_permission(CA_CREATE)]
979 // Which would insert the generated code at the start of the function body,
980 // similar to how this macro is used in each function.
981 macro_rules! aa {
982 (no_warn $req:ident, $perm:expr, $action:expr) => {{
983 aa!($req, $perm, NO_RESOURCE, $action, true)
984 }};
985 ($req:ident, $perm:expr, $action:expr) => {{
986 aa!($req, $perm, NO_RESOURCE, $action, false)
987 }};
988 (no_warn $req:ident, $perm:expr, $resource:expr, $action:expr) => {{
989 aa!($req, $perm, $resource, $action, true)
990 }};
991 ($req:ident, $perm:expr, $resource:expr, $action:expr) => {{
992 aa!($req, $perm, $resource, $action, false)
993 }};
994 ($req:ident, $perm:expr, $resource:expr, $action:expr, $benign:expr) => {{
995 match $req.actor().is_allowed($perm, $resource) {
996 Ok(true) => $action,
997 Ok(false) => {
998 let msg = format!(
999 "User '{}' does not have permission '{}' on resource '{}'",
1000 $req.actor().name(),
1001 $perm,
1002 $resource
1003 );
1004 Ok(HttpResponse::forbidden(msg).with_benign($benign))
1005 }
1006 Err(err) => {
1007 // Avoid an extra round of error -> string -> error conversion
1008 // which causes the error message to nest, e.g.
1009 // "Invalid credentials: Invalid credentials: Session expired"
1010 match err {
1011 Error::ApiInvalidCredentials(_)
1012 | Error::ApiInsufficientRights(_)
1013 | Error::ApiAuthPermanentError(_)
1014 | Error::ApiAuthTransientError(_)
1015 | Error::ApiAuthSessionExpired(_)
1016 | Error::ApiLoginError(_) => Ok(HttpResponse::response_from_error(err).with_benign($benign)),
1017 _ => Ok(HttpResponse::forbidden(format!("{}", err)).with_benign($benign)),
1018 }
1019 }
1020 }
1021 }};
1022 }
1023
1024 /// Maps the API methods
api(req: Request) -> RoutingResult1025 async fn api(req: Request) -> RoutingResult {
1026 if !req.path().full().starts_with("/api/v1") {
1027 Err(req) // Not for us
1028 } else {
1029 // Eat the first two segments of the path "api/v1"
1030 let mut path = req.path().clone();
1031 path.next(); // gets 'v1' and drops it.
1032
1033 match path.next() {
1034 Some("authorized") => api_authorized(req).await,
1035 restricted_endpoint => {
1036 // Make sure access is allowed
1037 aa!(req, Permission::LOGIN, {
1038 match restricted_endpoint {
1039 Some("bulk") => api_bulk(req, &mut path).await,
1040 Some("cas") => api_cas(req, &mut path).await,
1041 Some("pubd") => aa!(req, Permission::PUB_ADMIN, api_publication_server(req, &mut path).await),
1042 _ => render_unknown_method(),
1043 }
1044 })
1045 }
1046 }
1047 }
1048 }
1049
api_authorized(req: Request) -> RoutingResult1050 async fn api_authorized(req: Request) -> RoutingResult {
1051 // Use 'no_warn' to prevent the log being filled with warnings about
1052 // insufficient user rights as this API endpoint is invoked by Lagosta on
1053 // every view transition, and not being authorized is a valid state that
1054 // triggers Lagosta to show a login form, not something to warn about!
1055 aa!(no_warn
1056 req,
1057 Permission::LOGIN,
1058 match *req.method() {
1059 Method::GET => render_ok(),
1060 _ => render_unknown_method(),
1061 }
1062 )
1063 }
1064
api_bulk(req: Request, path: &mut RequestPath) -> RoutingResult1065 async fn api_bulk(req: Request, path: &mut RequestPath) -> RoutingResult {
1066 match path.full() {
1067 "/api/v1/bulk/cas/issues" => api_all_ca_issues(req).await,
1068 "/api/v1/bulk/cas/sync/parent" => api_refresh_all(req).await,
1069 "/api/v1/bulk/cas/sync/repo" => api_resync_all(req).await,
1070 "/api/v1/bulk/cas/publish" => api_republish_all(req).await,
1071 _ => render_unknown_method(),
1072 }
1073 }
1074
api_cas(req: Request, path: &mut RequestPath) -> RoutingResult1075 async fn api_cas(req: Request, path: &mut RequestPath) -> RoutingResult {
1076 match path.path_arg::<Handle>() {
1077 Some(ca) => aa!(req, Permission::CA_READ, ca.clone(), {
1078 match path.next() {
1079 None => match *req.method() {
1080 Method::GET => api_ca_info(req, ca).await,
1081 Method::DELETE => api_ca_delete(req, ca).await,
1082 _ => render_unknown_method(),
1083 },
1084 Some("aspas") => api_ca_aspas(req, path, ca).await,
1085 Some("children") => api_ca_children(req, path, ca).await,
1086 Some("history") => api_ca_history(req, path, ca).await,
1087
1088 Some("id") => api_ca_id(req, path, ca).await,
1089 Some("issues") => api_ca_issues(req, ca).await,
1090 Some("keys") => api_ca_keys(req, path, ca).await,
1091 Some("parents") => api_ca_parents(req, path, ca).await,
1092 Some("repo") => api_ca_repo(req, path, ca).await,
1093 Some("routes") => api_ca_routes(req, path, ca).await,
1094 Some("stats") => api_ca_stats(req, path, ca).await,
1095 Some("sync") => api_ca_sync(req, path, ca).await,
1096
1097 Some("rta") => api_ca_rta(req, path, ca).await,
1098
1099 _ => render_unknown_method(),
1100 }
1101 }),
1102 None => match *req.method() {
1103 Method::GET => api_cas_list(req).await,
1104 Method::POST => api_ca_init(req).await,
1105 _ => render_unknown_method(),
1106 },
1107 }
1108 }
1109
api_ca_keys(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1110 async fn api_ca_keys(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1111 match *req.method() {
1112 Method::POST => match path.next() {
1113 Some("roll_init") => api_ca_kr_init(req, ca).await,
1114 Some("roll_activate") => api_ca_kr_activate(req, ca).await,
1115 _ => render_unknown_method(),
1116 },
1117 _ => render_unknown_method(),
1118 }
1119 }
1120
api_ca_parents(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1121 async fn api_ca_parents(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1122 if let Some(parent) = path.path_arg() {
1123 match *req.method() {
1124 Method::GET => api_ca_my_parent_contact(req, ca, parent).await,
1125 Method::POST => api_ca_parent_add_or_update(req, ca, Some(parent)).await,
1126 Method::DELETE => api_ca_remove_parent(req, ca, parent).await,
1127 _ => render_unknown_method(),
1128 }
1129 } else {
1130 match *req.method() {
1131 Method::GET => api_ca_my_parent_statuses(req, ca).await,
1132 Method::POST => api_ca_parent_add_or_update(req, ca, None).await,
1133 _ => render_unknown_method(),
1134 }
1135 }
1136 }
1137
api_ca_repo(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1138 async fn api_ca_repo(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1139 match path.next() {
1140 None => match *req.method() {
1141 Method::GET => api_ca_repo_details(req, ca).await,
1142 Method::POST => api_ca_repo_update(req, ca).await,
1143 _ => render_unknown_method(),
1144 },
1145 Some("status") => api_ca_repo_status(req, ca).await,
1146 _ => render_unknown_method(),
1147 }
1148 }
1149
api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1150 async fn api_ca_routes(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1151 match path.next() {
1152 None => match *req.method() {
1153 Method::GET => api_ca_routes_show(req, ca).await,
1154 Method::POST => api_ca_routes_update(req, ca).await,
1155 _ => render_unknown_method(),
1156 },
1157 Some("try") => match *req.method() {
1158 Method::POST => api_ca_routes_try_update(req, ca).await,
1159 _ => render_unknown_method(),
1160 },
1161 Some("analysis") => api_ca_routes_analysis(req, path, ca).await,
1162 _ => render_unknown_method(),
1163 }
1164 }
1165
api_ca_stats(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1166 async fn api_ca_stats(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1167 match path.next() {
1168 Some("children") => match path.next() {
1169 Some("connections") => api_ca_stats_child_connections(req, ca).await,
1170 _ => render_unknown_method(),
1171 },
1172 _ => render_unknown_method(),
1173 }
1174 }
1175
api_ca_sync(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1176 async fn api_ca_sync(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1177 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1178 if req.is_post() {
1179 let actor = req.actor();
1180 match path.next() {
1181 Some("parents") => render_empty_res(req.state().cas_refresh_single(ca, &actor).await),
1182 Some("repo") => render_empty_res(req.state().cas_repo_sync_single(&ca).await),
1183 _ => render_unknown_method(),
1184 }
1185 } else {
1186 render_unknown_method()
1187 }
1188 })
1189 }
1190
api_publication_server(req: Request, path: &mut RequestPath) -> RoutingResult1191 async fn api_publication_server(req: Request, path: &mut RequestPath) -> RoutingResult {
1192 match path.next() {
1193 Some("publishers") => api_publishers(req, path).await,
1194 Some("stale") => api_stale_publishers(req, path.next()).await,
1195 Some("init") => match *req.method() {
1196 Method::POST => {
1197 let state = req.state.clone();
1198 match req.json().await {
1199 Ok(uris) => render_empty_res(state.repository_init(uris)),
1200 Err(e) => render_error(e),
1201 }
1202 }
1203 Method::DELETE => render_empty_res(req.state.repository_clear()),
1204 _ => render_unknown_method(),
1205 },
1206 _ => render_unknown_method(),
1207 }
1208 }
1209
api_publishers(req: Request, path: &mut RequestPath) -> RoutingResult1210 async fn api_publishers(req: Request, path: &mut RequestPath) -> RoutingResult {
1211 match *req.method() {
1212 Method::GET => match path.path_arg() {
1213 Some(publisher) => match path.next() {
1214 None => api_show_pbl(req, publisher).await,
1215 Some("response.xml") => api_repository_response_xml(req, publisher).await,
1216 Some("response.json") => api_repository_response_json(req, publisher).await,
1217
1218 _ => render_unknown_method(),
1219 },
1220 None => api_list_pbl(req).await,
1221 },
1222 Method::POST => match path.next() {
1223 None => api_add_pbl(req).await,
1224 _ => render_unknown_method(),
1225 },
1226 Method::DELETE => match path.path_arg() {
1227 Some(publisher) => api_remove_pbl(req, publisher).await,
1228 None => render_error(Error::ApiInvalidHandle),
1229 },
1230 _ => render_unknown_method(),
1231 }
1232 }
1233
1234 //------------ Admin: Publishers ---------------------------------------------
1235
1236 /// Returns a list of publisher which have not updated for more
1237 /// than the given number of seconds.
api_stale_publishers(req: Request, seconds: Option<&str>) -> RoutingResult1238 pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> RoutingResult {
1239 aa!(req, Permission::PUB_LIST, {
1240 let seconds = seconds.unwrap_or("");
1241 match i64::from_str(seconds) {
1242 Ok(seconds) => render_json_res(
1243 req.state()
1244 .repo_stats()
1245 .map(|stats| PublisherList::build(&stats.stale_publishers(seconds))),
1246 ),
1247 Err(_) => render_error(Error::ApiInvalidSeconds),
1248 }
1249 })
1250 }
1251
1252 /// Returns a json structure with all publishers in it.
api_list_pbl(req: Request) -> RoutingResult1253 pub async fn api_list_pbl(req: Request) -> RoutingResult {
1254 aa!(req, Permission::PUB_LIST, {
1255 render_json_res(
1256 req.state()
1257 .publishers()
1258 .map(|publishers| PublisherList::build(&publishers)),
1259 )
1260 })
1261 }
1262
1263 /// Adds a publisher
api_add_pbl(req: Request) -> RoutingResult1264 pub async fn api_add_pbl(req: Request) -> RoutingResult {
1265 aa!(req, Permission::PUB_CREATE, {
1266 let actor = req.actor();
1267 let server = req.state().clone();
1268 match req.json().await {
1269 Ok(pbl) => render_json_res(server.add_publisher(pbl, &actor)),
1270 Err(e) => render_error(e),
1271 }
1272 })
1273 }
1274
1275 /// Removes a publisher. Should be idempotent! If if did not exist then
1276 /// that's just fine.
1277 #[allow(clippy::redundant_clone)] // false positive
api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult1278 pub async fn api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult {
1279 aa!(req, Permission::PUB_DELETE, publisher.clone(), {
1280 let actor = req.actor();
1281 render_empty_res(req.state().remove_publisher(publisher, &actor))
1282 })
1283 }
1284
1285 /// Returns a json structure with publisher details
1286 #[allow(clippy::redundant_clone)] // false positive
api_show_pbl(req: Request, publisher: Handle) -> RoutingResult1287 pub async fn api_show_pbl(req: Request, publisher: Handle) -> RoutingResult {
1288 aa!(
1289 req,
1290 Permission::PUB_READ,
1291 publisher.clone(),
1292 render_json_res(req.state().get_publisher(&publisher))
1293 )
1294 }
1295
1296 //------------ repository_response ---------------------------------------------
1297
1298 #[allow(clippy::redundant_clone)] // false positive
api_repository_response_xml(req: Request, publisher: Handle) -> RoutingResult1299 pub async fn api_repository_response_xml(req: Request, publisher: Handle) -> RoutingResult {
1300 aa!(req, Permission::PUB_READ, publisher.clone(), {
1301 match repository_response(&req, &publisher).await {
1302 Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1303 Err(e) => render_error(e),
1304 }
1305 })
1306 }
1307
1308 #[allow(clippy::redundant_clone)] // false positive
api_repository_response_json(req: Request, publisher: Handle) -> RoutingResult1309 pub async fn api_repository_response_json(req: Request, publisher: Handle) -> RoutingResult {
1310 aa!(req, Permission::PUB_READ, publisher.clone(), {
1311 match repository_response(&req, &publisher).await {
1312 Ok(res) => render_json(res),
1313 Err(e) => render_error(e),
1314 }
1315 })
1316 }
1317
repository_response(req: &Request, publisher: &Handle) -> Result<rfc8183::RepositoryResponse, Error>1318 async fn repository_response(req: &Request, publisher: &Handle) -> Result<rfc8183::RepositoryResponse, Error> {
1319 req.state().repository_response(publisher)
1320 }
1321
api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult1322 pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult {
1323 aa!(req, Permission::CA_UPDATE, parent.clone(), {
1324 let actor = req.actor();
1325 let server = req.state().clone();
1326 match req.json().await {
1327 Ok(child_req) => render_json_res(server.ca_add_child(&parent, child_req, &actor).await),
1328 Err(e) => render_error(e),
1329 }
1330 })
1331 }
1332
api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1333 async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1334 aa!(req, Permission::CA_UPDATE, child.clone(), {
1335 let actor = req.actor();
1336 let server = req.state().clone();
1337 match req.json().await {
1338 Ok(child_req) => render_empty_res(server.ca_child_update(&ca, child, child_req, &actor).await),
1339 Err(e) => render_error(e),
1340 }
1341 })
1342 }
1343
api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1344 pub async fn api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1345 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1346 let actor = req.actor();
1347 render_empty_res(req.state().ca_child_remove(&ca, child, &actor).await)
1348 })
1349 }
1350
api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1351 async fn api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1352 aa!(
1353 req,
1354 Permission::CA_READ,
1355 ca.clone(),
1356 render_json_res(req.state().ca_child_show(&ca, &child).await)
1357 )
1358 }
1359
api_ca_stats_child_connections(req: Request, ca: Handle) -> RoutingResult1360 async fn api_ca_stats_child_connections(req: Request, ca: Handle) -> RoutingResult {
1361 aa!(
1362 req,
1363 Permission::CA_READ,
1364 ca.clone(),
1365 render_json_res(req.state().ca_stats_child_connections(&ca).await)
1366 )
1367 }
1368
api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1369 async fn api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1370 aa!(
1371 req,
1372 Permission::CA_READ,
1373 ca.clone(),
1374 render_json_res(req.state().ca_parent_contact(&ca, child.clone()).await)
1375 )
1376 }
1377
api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1378 async fn api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1379 aa!(
1380 req,
1381 Permission::CA_READ,
1382 ca.clone(),
1383 render_json_res(req.state().ca_parent_response(&ca, child.clone()).await)
1384 )
1385 }
1386
api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult1387 pub async fn api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult {
1388 aa!(req, Permission::CA_READ, ca.clone(), {
1389 match req.state().ca_parent_response(&ca, child.clone()).await {
1390 Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1391 Err(e) => render_error(e),
1392 }
1393 })
1394 }
1395
1396 //------------ Admin: CertAuth -----------------------------------------------
1397
api_all_ca_issues(req: Request) -> RoutingResult1398 async fn api_all_ca_issues(req: Request) -> RoutingResult {
1399 match *req.method() {
1400 Method::GET => aa!(req, Permission::CA_READ, {
1401 let actor = req.actor();
1402 render_json_res(req.state().all_ca_issues(&actor).await)
1403 }),
1404 _ => render_unknown_method(),
1405 }
1406 }
1407
1408 /// Returns the health (state) for a given CA.
api_ca_issues(req: Request, ca: Handle) -> RoutingResult1409 async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult {
1410 match *req.method() {
1411 Method::GET => aa!(
1412 req,
1413 Permission::CA_READ,
1414 ca.clone(),
1415 render_json_res(req.state().ca_issues(&ca).await)
1416 ),
1417 _ => render_unknown_method(),
1418 }
1419 }
1420
api_cas_list(req: Request) -> RoutingResult1421 async fn api_cas_list(req: Request) -> RoutingResult {
1422 aa!(req, Permission::CA_LIST, {
1423 let actor = req.actor();
1424 render_json_res(req.state().ca_list(&actor))
1425 })
1426 }
1427
api_ca_init(req: Request) -> RoutingResult1428 pub async fn api_ca_init(req: Request) -> RoutingResult {
1429 aa!(req, Permission::CA_CREATE, {
1430 let state = req.state().clone();
1431
1432 match req.json().await {
1433 Ok(ca_init) => render_empty_res(state.ca_init(ca_init)),
1434 Err(e) => render_error(e),
1435 }
1436 })
1437 }
1438
api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1439 async fn api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1440 match *req.method() {
1441 Method::POST => aa!(req, Permission::CA_UPDATE, ca.clone(), {
1442 let actor = req.actor();
1443 render_empty_res(req.state().ca_update_id(ca, &actor).await)
1444 }),
1445 Method::GET => match path.next() {
1446 Some("child_request.xml") => api_ca_child_req_xml(req, ca).await,
1447 Some("child_request.json") => api_ca_child_req_json(req, ca).await,
1448 Some("publisher_request.json") => api_ca_publisher_req_json(req, ca).await,
1449 Some("publisher_request.xml") => api_ca_publisher_req_xml(req, ca).await,
1450 _ => render_unknown_method(),
1451 },
1452 _ => render_unknown_method(),
1453 }
1454 }
1455
api_ca_info(req: Request, handle: Handle) -> RoutingResult1456 async fn api_ca_info(req: Request, handle: Handle) -> RoutingResult {
1457 aa!(
1458 req,
1459 Permission::CA_READ,
1460 handle.clone(),
1461 render_json_res(req.state().ca_info(&handle).await)
1462 )
1463 }
1464
api_ca_delete(req: Request, handle: Handle) -> RoutingResult1465 async fn api_ca_delete(req: Request, handle: Handle) -> RoutingResult {
1466 let actor = req.actor();
1467 aa!(
1468 req,
1469 Permission::CA_DELETE,
1470 handle.clone(),
1471 render_json_res(req.state().ca_delete(&handle, &actor).await)
1472 )
1473 }
1474
api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle) -> RoutingResult1475 async fn api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle) -> RoutingResult {
1476 aa!(
1477 req,
1478 Permission::CA_READ,
1479 ca.clone(),
1480 render_json_res(req.state().ca_my_parent_contact(&ca, &parent).await)
1481 )
1482 }
1483
api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult1484 async fn api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult {
1485 aa!(
1486 req,
1487 Permission::CA_READ,
1488 ca.clone(),
1489 render_json_res(req.state().ca_status(&ca).await.map(|s| s.parents().clone()))
1490 )
1491 }
1492
api_ca_aspas(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1493 async fn api_ca_aspas(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1494 match path.next() {
1495 None => match *req.method() {
1496 Method::GET => api_ca_aspas_definitions_show(req, ca).await,
1497 Method::POST => api_ca_aspas_definitions_update(req, ca).await,
1498 _ => render_unknown_method(),
1499 },
1500 // We may need other functions in future, such as 'analyze' or 'try'.
1501 // So keep the base namespace clean and use '/api/v1/aspas/as/<asn>/..'
1502 // for functions on specific ASPA definitions for the given (customer)
1503 // ASN.
1504 Some("as") => {
1505 // get as path parameter, or error
1506 // - get (specific definition)
1507 // - delete
1508 // - update? (definition includes the ASN so this can be in the base path)
1509 match path.path_arg() {
1510 Some(customer) => match *req.method() {
1511 Method::POST => api_ca_aspas_update_aspa(req, ca, customer).await,
1512 Method::DELETE => api_ca_aspas_delete(req, ca, customer).await,
1513 _ => render_unknown_method(),
1514 },
1515 None => render_unknown_method(),
1516 }
1517 }
1518 _ => render_unknown_method(),
1519 }
1520 }
1521
api_ca_children(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1522 async fn api_ca_children(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1523 match path.path_arg() {
1524 Some(child) => match path.next() {
1525 None => match *req.method() {
1526 Method::GET => api_ca_child_show(req, ca, child).await,
1527 Method::POST => api_ca_child_update(req, ca, child).await,
1528 Method::DELETE => api_ca_child_remove(req, ca, child).await,
1529 _ => render_unknown_method(),
1530 },
1531 Some("contact") => api_ca_parent_contact(req, ca, child).await,
1532 Some("parent_response.json") => api_ca_parent_res_json(req, ca, child).await,
1533 Some("parent_response.xml") => api_ca_parent_res_xml(req, ca, child).await,
1534 _ => render_unknown_method(),
1535 },
1536 None => match *req.method() {
1537 Method::POST => api_ca_add_child(req, ca).await,
1538 _ => render_unknown_method(),
1539 },
1540 }
1541 }
1542
api_ca_history_commands(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult1543 async fn api_ca_history_commands(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
1544 match *req.method() {
1545 Method::GET => aa!(req, Permission::CA_READ, handle.clone(), {
1546 // /api/v1/cas/{ca}/history/commands /<rows>/<offset>/<after>/<before>
1547 let mut crit = CommandHistoryCriteria::default();
1548
1549 if let Some(rows) = path.path_arg() {
1550 crit.set_rows(rows);
1551 }
1552
1553 if let Some(offset) = path.path_arg() {
1554 crit.set_offset(offset);
1555 }
1556
1557 if let Some(after) = path.path_arg() {
1558 crit.set_after(after);
1559 }
1560
1561 if let Some(before) = path.path_arg() {
1562 crit.set_before(before);
1563 }
1564 match req.state().ca_history(&handle, crit).await {
1565 Ok(history) => render_json(history),
1566 Err(e) => render_error(e),
1567 }
1568 }),
1569 _ => render_unknown_method(),
1570 }
1571 }
1572
api_ca_history(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1573 async fn api_ca_history(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1574 match path.next() {
1575 Some("details") => api_ca_command_details(req, path, ca).await,
1576 Some("commands") => api_ca_history_commands(req, path, ca).await,
1577 _ => render_unknown_method(),
1578 }
1579 }
1580
1581 #[allow(clippy::redundant_clone)] // false positive
api_ca_command_details(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult1582 async fn api_ca_command_details(req: Request, path: &mut RequestPath, handle: Handle) -> RoutingResult {
1583 // /api/v1/cas/{ca}/command/<command-key>
1584 match path.path_arg() {
1585 Some(key) => match *req.method() {
1586 Method::GET => aa!(req, Permission::CA_READ, handle.clone(), {
1587 match req.state().ca_command_details(&handle, key) {
1588 Ok(details) => render_json(details),
1589 Err(e) => match e {
1590 Error::AggregateStoreError(AggregateStoreError::UnknownCommand(_, _)) => {
1591 render_unknown_resource()
1592 }
1593 _ => render_error(e),
1594 },
1595 }
1596 }),
1597 _ => render_unknown_method(),
1598 },
1599 None => render_unknown_resource(),
1600 }
1601 }
1602
api_ca_child_req_xml(req: Request, handle: Handle) -> RoutingResult1603 async fn api_ca_child_req_xml(req: Request, handle: Handle) -> RoutingResult {
1604 match *req.method() {
1605 Method::GET => aa!(
1606 req,
1607 Permission::CA_READ,
1608 handle.clone(),
1609 match ca_child_req(&req, &handle).await {
1610 Ok(req) => Ok(HttpResponse::xml(req.encode_vec())),
1611 Err(e) => render_error(e),
1612 }
1613 ),
1614 _ => render_unknown_method(),
1615 }
1616 }
1617
api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult1618 async fn api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult {
1619 match *req.method() {
1620 Method::GET => aa!(
1621 req,
1622 Permission::CA_READ,
1623 handle.clone(),
1624 match ca_child_req(&req, &handle).await {
1625 Ok(req) => render_json(req),
1626 Err(e) => render_error(e),
1627 }
1628 ),
1629 _ => render_unknown_method(),
1630 }
1631 }
1632
ca_child_req(req: &Request, handle: &Handle) -> Result<rfc8183::ChildRequest, Error>1633 async fn ca_child_req(req: &Request, handle: &Handle) -> Result<rfc8183::ChildRequest, Error> {
1634 req.state().ca_child_req(handle).await
1635 }
1636
api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult1637 async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult {
1638 match *req.method() {
1639 Method::GET => aa!(
1640 req,
1641 Permission::CA_READ,
1642 handle.clone(),
1643 render_json_res(req.state().ca_publisher_req(&handle).await)
1644 ),
1645 _ => render_unknown_method(),
1646 }
1647 }
1648
api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult1649 async fn api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult {
1650 match *req.method() {
1651 Method::GET => aa!(
1652 req,
1653 Permission::CA_READ,
1654 handle.clone(),
1655 match req.state().ca_publisher_req(&handle).await {
1656 Ok(res) => Ok(HttpResponse::xml(res.encode_vec())),
1657 Err(e) => render_error(e),
1658 }
1659 ),
1660 _ => render_unknown_method(),
1661 }
1662 }
1663
api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult1664 async fn api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult {
1665 aa!(
1666 req,
1667 Permission::CA_READ,
1668 handle.clone(),
1669 render_json_res(req.state().ca_repo_details(&handle).await)
1670 )
1671 }
1672
api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult1673 async fn api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult {
1674 match *req.method() {
1675 Method::GET => aa!(
1676 req,
1677 Permission::CA_READ,
1678 handle.clone(),
1679 render_json_res(req.state().ca_status(&handle).await.map(|status| status.repo().clone()))
1680 ),
1681 _ => render_unknown_method(),
1682 }
1683 }
1684
extract_repository_contact(handle: &Handle, bytes: Bytes) -> Result<RepositoryContact, Error>1685 fn extract_repository_contact(handle: &Handle, bytes: Bytes) -> Result<RepositoryContact, Error> {
1686 let string = String::from_utf8(bytes.to_vec()).map_err(Error::custom)?;
1687
1688 // TODO: Switch based on Content-Type header
1689 if string.starts_with('<') {
1690 if string.contains("<parent_response") {
1691 Err(Error::CaRepoResponseWrongXml(handle.clone()))
1692 } else {
1693 let response = rfc8183::RepositoryResponse::validate(string.as_bytes())
1694 .map_err(|e| Error::CaRepoResponseInvalidXml(handle.clone(), e.to_string()))?;
1695 Ok(RepositoryContact::new(response))
1696 }
1697 } else {
1698 serde_json::from_str(&string).map_err(Error::JsonError)
1699 }
1700 }
1701
api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult1702 async fn api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult {
1703 aa!(req, Permission::CA_UPDATE, handle.clone(), {
1704 let actor = req.actor();
1705 let server = req.state().clone();
1706
1707 match req
1708 .api_bytes()
1709 .await
1710 .map(|bytes| extract_repository_contact(&handle, bytes))
1711 {
1712 Ok(Ok(update)) => render_empty_res(server.ca_repo_update(handle, update, &actor).await),
1713 Ok(Err(e)) | Err(e) => render_error(e),
1714 }
1715 })
1716 }
1717
api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: Option<Handle>) -> RoutingResult1718 async fn api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: Option<Handle>) -> RoutingResult {
1719 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1720 let actor = req.actor();
1721 let server = req.state().clone();
1722
1723 let bytes = match req.api_bytes().await {
1724 Ok(bytes) => bytes,
1725 Err(e) => return render_error(e),
1726 };
1727
1728 match extract_parent_ca_req(&ca, bytes, parent_override) {
1729 Ok(parent_req) => render_empty_res(server.ca_parent_add_or_update(ca, parent_req, &actor).await),
1730 Err(e) => render_error(e),
1731 }
1732 })
1733 }
1734
extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option<Handle>) -> Result<ParentCaReq, Error>1735 fn extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option<Handle>) -> Result<ParentCaReq, Error> {
1736 let string = String::from_utf8(bytes.to_vec()).map_err(Error::custom)?;
1737
1738 // TODO: Switch based on Content-Type header
1739 let req = if string.starts_with('<') {
1740 if string.starts_with("<repository") {
1741 return Err(Error::CaParentResponseWrongXml(ca.clone()));
1742 } else {
1743 let res = rfc8183::ParentResponse::validate(string.as_bytes())
1744 .map_err(|e| Error::CaParentResponseInvalidXml(ca.clone(), e.to_string()))?;
1745
1746 let parent_name = parent_override.unwrap_or_else(|| res.parent_handle().clone());
1747 let contact = ParentCaContact::for_rfc6492(res);
1748 ParentCaReq::new(parent_name, contact)
1749 }
1750 } else {
1751 let req: ParentCaReq = serde_json::from_str(&string).map_err(Error::JsonError)?;
1752 if let Some(parent_override) = parent_override {
1753 if req.handle() != &parent_override {
1754 return Err(Error::Custom(format!(
1755 "Used different parent names on path ({}) and submitted JSON ({}) for adding/updating a parent",
1756 parent_override,
1757 req.handle()
1758 )));
1759 }
1760 }
1761 req
1762 };
1763
1764 Ok(req)
1765 }
1766
api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> RoutingResult1767 async fn api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> RoutingResult {
1768 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1769 let actor = req.actor();
1770 render_empty_res(req.state().ca_parent_remove(ca, parent, &actor).await)
1771 })
1772 }
1773
1774 /// Force a key roll for a CA, i.e. use a max key age of 0 seconds.
api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult1775 async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult {
1776 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1777 let actor = req.actor();
1778 render_empty_res(req.state().ca_keyroll_init(ca, &actor).await)
1779 })
1780 }
1781
1782 /// Force key activation for all new keys, i.e. use a staging period of 0 seconds.
api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult1783 async fn api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult {
1784 aa!(req, Permission::CA_UPDATE, ca.clone(), {
1785 let actor = req.actor();
1786 render_empty_res(req.state().ca_keyroll_activate(ca, &actor).await)
1787 })
1788 }
1789
1790 // -- ASPA functions
1791
1792 /// List the current ASPA definitions for a CA
api_ca_aspas_definitions_show(req: Request, ca: Handle) -> RoutingResult1793 async fn api_ca_aspas_definitions_show(req: Request, ca: Handle) -> RoutingResult {
1794 aa!(req, Permission::ASPAS_READ, ca.clone(), {
1795 let state = req.state().clone();
1796 render_json_res(state.ca_aspas_definitions_show(ca).await)
1797 })
1798 }
1799
1800 /// Add a new ASPA definition for a CA based on the update in the POST
api_ca_aspas_definitions_update(req: Request, ca: Handle) -> RoutingResult1801 async fn api_ca_aspas_definitions_update(req: Request, ca: Handle) -> RoutingResult {
1802 aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1803 let actor = req.actor();
1804 let state = req.state().clone();
1805
1806 match req.json().await {
1807 Err(e) => render_error(e),
1808 Ok(updates) => render_empty_res(state.ca_aspas_definitions_update(ca, updates, &actor).await),
1809 }
1810 })
1811 }
1812
1813 /// Update an existing ASPA definition for a CA based on the update in the POST
api_ca_aspas_update_aspa(req: Request, ca: Handle, customer: AsId) -> RoutingResult1814 async fn api_ca_aspas_update_aspa(req: Request, ca: Handle, customer: AsId) -> RoutingResult {
1815 aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1816 let actor = req.actor();
1817 let state = req.state().clone();
1818
1819 match req.json().await {
1820 Err(e) => render_error(e),
1821 Ok(update) => render_empty_res(state.ca_aspas_update_aspa(ca, customer, update, &actor).await),
1822 }
1823 })
1824 }
1825
1826 /// Delete the ASPA definition for the given CA and customer ASN
api_ca_aspas_delete(req: Request, ca: Handle, customer: AsId) -> RoutingResult1827 async fn api_ca_aspas_delete(req: Request, ca: Handle, customer: AsId) -> RoutingResult {
1828 aa!(req, Permission::ASPAS_UPDATE, ca.clone(), {
1829 let actor = req.actor();
1830 let state = req.state().clone();
1831
1832 let updates = AspaDefinitionUpdates::new(vec![], vec![customer]);
1833 render_empty_res(state.ca_aspas_definitions_update(ca, updates, &actor).await)
1834 })
1835 }
1836
1837 /// Update the route authorizations for this CA
api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult1838 async fn api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult {
1839 aa!(req, Permission::ROUTES_UPDATE, ca.clone(), {
1840 let actor = req.actor();
1841 let state = req.state().clone();
1842
1843 match req.json().await {
1844 Err(e) => render_error(e),
1845 Ok(updates) => render_empty_res(state.ca_routes_update(ca, updates, &actor).await),
1846 }
1847 })
1848 }
1849
1850 /// Tries an update. If the dry-run for it would be successful, and the analysis
1851 /// for the resources in the update have no remaining invalids, apply it. Otherwise
1852 /// return the analysis and a suggestion.
api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult1853 async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult {
1854 aa!(req, Permission::ROUTES_UPDATE, ca.clone(), {
1855 let actor = req.actor();
1856 let state = req.state().clone();
1857
1858 match req.json::<RoaDefinitionUpdates>().await {
1859 Err(e) => render_error(e),
1860 Ok(updates) => {
1861 let server = state;
1862 match server.ca_routes_bgp_dry_run(&ca, updates.clone()).await {
1863 Err(e) => {
1864 // update was rejected, return error
1865 render_error(e)
1866 }
1867 Ok(effect) => {
1868 if !effect.contains_invalids() {
1869 // no issues found, apply
1870 render_empty_res(server.ca_routes_update(ca, updates, &actor).await)
1871 } else {
1872 // remaining invalids exist, advise user
1873 let updates: RouteAuthorizationUpdates = updates.into();
1874 let updates = updates.into_explicit();
1875 let resources = updates.affected_prefixes();
1876
1877 match server.ca_routes_bgp_suggest(&ca, Some(resources)).await {
1878 Err(e) => render_error(e), // should not fail after dry run, but hey..
1879 Ok(suggestion) => render_json(BgpAnalysisAdvice::new(effect, suggestion)),
1880 }
1881 }
1882 }
1883 }
1884 }
1885 }
1886 })
1887 }
1888
1889 /// show the route authorizations for this CA
api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult1890 async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult {
1891 aa!(req, Permission::ROUTES_READ, ca.clone(), {
1892 match req.state().ca_routes_show(&ca).await {
1893 Ok(roas) => render_json(roas),
1894 Err(_) => render_unknown_resource(),
1895 }
1896 })
1897 }
1898
1899 /// Show the state of ROAs vs BGP for this CA
api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1900 async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1901 aa!(req, Permission::ROUTES_ANALYSIS, ca.clone(), {
1902 match path.next() {
1903 Some("full") => render_json_res(req.state().ca_routes_bgp_analysis(&ca).await),
1904 Some("dryrun") => match *req.method() {
1905 Method::POST => {
1906 let state = req.state.clone();
1907 match req.json().await {
1908 Err(e) => render_error(e),
1909 Ok(updates) => render_json_res(state.ca_routes_bgp_dry_run(&ca, updates).await),
1910 }
1911 }
1912 _ => render_unknown_method(),
1913 },
1914 Some("suggest") => match *req.method() {
1915 Method::GET => render_json_res(req.state().ca_routes_bgp_suggest(&ca, None).await),
1916 Method::POST => {
1917 let server = req.state().clone();
1918 match req.json().await {
1919 Err(e) => render_error(e),
1920 Ok(resources) => render_json_res(server.ca_routes_bgp_suggest(&ca, Some(resources)).await),
1921 }
1922 }
1923 _ => render_unknown_method(),
1924 },
1925 _ => render_unknown_method(),
1926 }
1927 })
1928 }
1929
1930 //------------ Admin: Force republish ----------------------------------------
1931
api_republish_all(req: Request) -> RoutingResult1932 async fn api_republish_all(req: Request) -> RoutingResult {
1933 match *req.method() {
1934 Method::POST => aa!(req, Permission::CA_ADMIN, {
1935 render_empty_res(req.state().republish_all().await)
1936 }),
1937 _ => render_unknown_method(),
1938 }
1939 }
1940
api_resync_all(req: Request) -> RoutingResult1941 async fn api_resync_all(req: Request) -> RoutingResult {
1942 match *req.method() {
1943 Method::POST => aa!(req, Permission::CA_ADMIN, {
1944 let actor = req.actor();
1945 render_empty_res(req.state().cas_repo_sync_all(&actor).await)
1946 }),
1947 _ => render_unknown_method(),
1948 }
1949 }
1950
1951 /// Refresh all CAs
api_refresh_all(req: Request) -> RoutingResult1952 async fn api_refresh_all(req: Request) -> RoutingResult {
1953 match *req.method() {
1954 Method::POST => aa!(req, Permission::CA_ADMIN, {
1955 let actor = req.actor();
1956 render_empty_res(req.state().cas_refresh_all(&actor).await)
1957 }),
1958 _ => render_unknown_method(),
1959 }
1960 }
1961
1962 //------------ Serve RRDP Files ----------------------------------------------
1963
rrdp(req: Request) -> RoutingResult1964 async fn rrdp(req: Request) -> RoutingResult {
1965 if !req.path().full().starts_with("/rrdp/") {
1966 Err(req) // Not for us
1967 } else {
1968 let mut full_path: PathBuf = req.state.rrdp_base_path();
1969 let (_, path) = req.path.remaining().split_at(1);
1970 let cache_seconds = if path.ends_with("notification.xml") { 60 } else { 86400 };
1971 full_path.push(path);
1972
1973 match File::open(full_path) {
1974 Ok(mut file) => {
1975 let mut buffer = Vec::new();
1976 file.read_to_end(&mut buffer).unwrap();
1977
1978 Ok(HttpResponse::xml_with_cache(buffer, cache_seconds))
1979 }
1980 _ => Ok(HttpResponse::not_found()),
1981 }
1982 }
1983 }
1984
1985 //------------ Support Resource Tagged Attestations (RTA) ----------------------
1986
api_ca_rta(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult1987 async fn api_ca_rta(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult {
1988 match path.path_arg() {
1989 Some(name) => match *req.method() {
1990 Method::POST => match path.next() {
1991 Some("sign") => api_ca_rta_sign(req, ca, name).await,
1992 Some("multi") => match path.next() {
1993 Some("prep") => api_ca_rta_multi_prep(req, ca, name).await,
1994 Some("cosign") => api_ca_rta_multi_sign(req, ca, name).await,
1995 _ => render_unknown_method(),
1996 },
1997 _ => render_unknown_method(),
1998 },
1999 Method::GET => {
2000 if name.is_empty() {
2001 api_ca_rta_list(req, ca).await
2002 } else {
2003 api_ca_rta_show(req, ca, name).await
2004 }
2005 }
2006 _ => render_unknown_method(),
2007 },
2008 None => match *req.method() {
2009 Method::GET => api_ca_rta_list(req, ca).await,
2010 _ => render_unknown_method(),
2011 },
2012 }
2013 }
2014
api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult2015 async fn api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult {
2016 aa!(
2017 req,
2018 Permission::RTA_LIST,
2019 ca.clone(),
2020 render_json_res(req.state().rta_list(ca).await)
2021 )
2022 }
2023
api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResult2024 async fn api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2025 aa!(
2026 req,
2027 Permission::RTA_READ,
2028 ca.clone(),
2029 render_json_res(req.state().rta_show(ca, name).await)
2030 )
2031 }
2032
api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult2033 async fn api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2034 aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2035 let actor = req.actor();
2036 let state = req.state().clone();
2037 match req.json().await {
2038 Err(e) => render_error(e),
2039 Ok(request) => render_empty_res(state.rta_sign(ca, name, request, &actor).await),
2040 }
2041 })
2042 }
2043
api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> RoutingResult2044 async fn api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2045 aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2046 let actor = req.actor();
2047 let state = req.state().clone();
2048
2049 match req.json().await {
2050 Ok(resources) => render_json_res(state.rta_multi_prep(ca, name, resources, &actor).await),
2051 Err(e) => render_error(e),
2052 }
2053 })
2054 }
2055
api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult2056 async fn api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResult {
2057 aa!(req, Permission::RTA_UPDATE, ca.clone(), {
2058 let actor = req.actor();
2059 let state = req.state().clone();
2060 match req.json().await {
2061 Ok(rta) => render_empty_res(state.rta_multi_cosign(ca, name, rta, &actor).await),
2062 Err(_) => render_error(Error::custom("Cannot decode RTA for co-signing")),
2063 }
2064 })
2065 }
2066
2067 //------------ Tests ---------------------------------------------------------
2068 #[cfg(test)]
2069 mod tests {
2070
2071 // NOTE: This is extensively tested through the functional and e2e tests found under
2072 // the $project/tests dir
2073 use crate::test;
2074 use std::fs;
2075
2076 #[tokio::test]
start_krill_daemon()2077 async fn start_krill_daemon() {
2078 let dir = test::start_krill_with_default_test_config(false, false, false).await;
2079 let _ = fs::remove_dir_all(dir);
2080 }
2081
2082 #[tokio::test]
start_krill_pubd_daemon()2083 async fn start_krill_pubd_daemon() {
2084 let dir = test::start_krill_pubd().await;
2085 let _ = fs::remove_dir_all(dir);
2086 }
2087 }
2088