mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-21 08:57:47 +02:00
231 lines
8.1 KiB
Rust
231 lines
8.1 KiB
Rust
//! Actix-web based HTTP server for the publication server.
|
|
//!
|
|
//! Here we deal with booting and setup, and once active deal with parsing
|
|
//! arguments and routing of requests, typically handing off to the
|
|
//! daemon::api::endpoints functions for processing and responding.
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
|
|
use actix_session::CookieSession;
|
|
use actix_web::http::StatusCode;
|
|
use actix_web::web::{delete, get, post, scope, Path};
|
|
use actix_web::{guard, middleware, web};
|
|
use actix_web::{App, HttpResponse, HttpServer};
|
|
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
|
|
|
use bcder::decode;
|
|
|
|
use crate::daemon::auth::{is_logged_in, login, logout, AUTH_COOKIE_NAME};
|
|
use crate::daemon::config::Config;
|
|
use crate::daemon::endpoints;
|
|
use crate::daemon::endpoints::*;
|
|
use crate::daemon::http::ssl;
|
|
use crate::daemon::krillserver;
|
|
use crate::daemon::krillserver::KrillServer;
|
|
|
|
//------------ AppServer -----------------------------------------------------
|
|
|
|
#[derive(Clone)]
|
|
pub struct AppServer(Arc<RwLock<KrillServer>>);
|
|
|
|
impl AppServer {
|
|
pub fn read(&self) -> RwLockReadGuard<KrillServer> {
|
|
self.0.read().unwrap()
|
|
}
|
|
|
|
pub fn write(&self) -> RwLockWriteGuard<KrillServer> {
|
|
self.0.write().unwrap()
|
|
}
|
|
}
|
|
|
|
pub fn start(config: &Config) -> Result<(), Error> {
|
|
let server = {
|
|
let krill = KrillServer::build(config)?;
|
|
AppServer(Arc::new(RwLock::new(krill)))
|
|
};
|
|
|
|
let https_builder = https_builder(config)?;
|
|
|
|
HttpServer::new(move || {
|
|
App::new()
|
|
.data(server.clone())
|
|
.wrap(middleware::Logger::default())
|
|
.wrap(
|
|
CookieSession::signed(&[0; 32])
|
|
.name(AUTH_COOKIE_NAME)
|
|
.secure(true),
|
|
)
|
|
.route("/health", get().to(endpoints::health))
|
|
// API end-points
|
|
.service(
|
|
scope("/api/v1")
|
|
// Health
|
|
.route("/health", get().to(api_health))
|
|
// Repositories and their publishers (both embedded and remote)
|
|
.route("/publishers", get().to(list_pbl))
|
|
.route("/publishers", post().to(add_pbl))
|
|
.route("/publishers/{handle}", get().to(show_pbl))
|
|
.route("/publishers/{handle}", delete().to(remove_pbl))
|
|
.route(
|
|
"/publishers/{handle}/response.xml",
|
|
get().to(repository_response),
|
|
)
|
|
// CAs (both embedded and remote)
|
|
.route("/cas", post().to(ca_init))
|
|
.route("/cas", get().to(cas))
|
|
.route("/cas/{ca}", get().to(ca_info))
|
|
.route("/cas/{ca}/id", post().to(ca_update_id))
|
|
.route("/cas/{ca}/history", get().to(ca_history))
|
|
.route("/cas/{ca}/child_request", get().to(ca_child_req))
|
|
.route("/cas/{ca}/parents", post().to(ca_add_parent))
|
|
.route("/cas/{ca}/parents/{parent}", post().to(ca_update_parent))
|
|
.route("/cas/{ca}/parents/{parent}", delete().to(ca_remove_parent))
|
|
.route("/cas/{ca}/children", post().to(ca_add_child))
|
|
.route(
|
|
"/cas/{ca}/parent_contact/{child}",
|
|
get().to(ca_parent_contact),
|
|
)
|
|
.route("/cas/{ca}/children/{child}", get().to(ca_show_child))
|
|
.route("/cas/{ca}/children/{child}", post().to(ca_child_update))
|
|
.route("/cas/{ca}/children/{child}", delete().to(ca_child_remove))
|
|
.route("/cas/{ca}/keys/roll_init", post().to(ca_kr_init))
|
|
.route("/cas/{ca}/keys/roll_activate", post().to(ca_kr_activate))
|
|
.route("/cas/{ca}/routes", post().to(ca_routes_update))
|
|
// Republish ALL CAs
|
|
.route("/republish", post().to(republish_all))
|
|
// Force refresh of ALL CAs
|
|
.route("/refresh_all", post().to(refresh_all)),
|
|
)
|
|
// Logged in users for the API
|
|
.route("/ui/is_logged_in", get().to(is_logged_in))
|
|
.route("/ui/login", post().to(login))
|
|
.route("/ui/logout", post().to(logout))
|
|
// Identity exchanges for remote publishers
|
|
.route("/rfc8181/{handle}", post().to(rfc8181))
|
|
// Provisioning for remote krill clients
|
|
.route("/rfc6492/{handle}", post().to(rfc6492))
|
|
// Public TA related methods
|
|
.route("/ta/ta.tal", get().to(tal))
|
|
.route("/ta/ta.cer", get().to(ta_cer))
|
|
// RRDP repository
|
|
.route("/rrdp/{path:.*}", get().to(serve_rrdp_files))
|
|
// Catch all (not found or not allowed)
|
|
.default_service(
|
|
// 404 for GET request
|
|
web::resource("")
|
|
.route(web::get().to(not_found))
|
|
// all requests that are not `GET`
|
|
.route(
|
|
web::route()
|
|
.guard(guard::Not(guard::Get()))
|
|
.to(HttpResponse::MethodNotAllowed),
|
|
),
|
|
)
|
|
})
|
|
.bind_ssl(config.socket_addr(), https_builder)?
|
|
.run()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Used to set up HTTPS. Creates keypair and self signed certificate
|
|
/// if config has 'use_ssl=test'.
|
|
fn https_builder(config: &Config) -> Result<SslAcceptorBuilder, Error> {
|
|
if config.test_ssl() {
|
|
ssl::create_key_cert_if_needed(&config.data_dir)
|
|
.map_err(|e| Error::Other(format!("{}", e)))?;
|
|
}
|
|
|
|
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())
|
|
.map_err(|e| Error::Other(format!("{}", e)))?;
|
|
|
|
builder
|
|
.set_private_key_file(config.https_key_file(), SslFiletype::PEM)
|
|
.map_err(|e| Error::Other(format!("{}", e)))?;
|
|
|
|
builder
|
|
.set_certificate_chain_file(config.https_cert_file())
|
|
.map_err(|e| Error::Other(format!("{}", e)))?;
|
|
|
|
Ok(builder)
|
|
}
|
|
|
|
// XXX TODO: use a better handler that does not load everything into
|
|
// memory first, and set the correct headers for caching.
|
|
// See also:
|
|
// https://github.com/actix/actix-website/blob/master/content/docs/static-files.md
|
|
// https://www.keycdn.com/blog/http-cache-headers
|
|
fn serve_rrdp_files(server: web::Data<AppServer>, path: Path<String>) -> HttpResponse {
|
|
let mut full_path = server.read().rrdp_base_path();
|
|
full_path.push(path.into_inner());
|
|
match File::open(full_path) {
|
|
Ok(mut file) => {
|
|
use std::io::Read;
|
|
let mut buffer = Vec::new();
|
|
file.read_to_end(&mut buffer).unwrap();
|
|
|
|
HttpResponse::build(StatusCode::OK).body(buffer)
|
|
}
|
|
_ => HttpResponse::build(StatusCode::NOT_FOUND).finish(),
|
|
}
|
|
}
|
|
|
|
//------------ Error ---------------------------------------------------------
|
|
|
|
#[derive(Debug, Display)]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum Error {
|
|
#[display(fmt = "{}", _0)]
|
|
ServerError(krillserver::Error),
|
|
|
|
#[display(fmt = "{}", _0)]
|
|
JsonError(serde_json::Error),
|
|
|
|
#[display(fmt = "Cannot decode request: {}", _0)]
|
|
DecodeError(decode::Error),
|
|
|
|
#[display(fmt = "Wrong path")]
|
|
WrongPath,
|
|
|
|
#[display(fmt = "{}", _0)]
|
|
IoError(io::Error),
|
|
|
|
#[display(fmt = "{}", _0)]
|
|
Other(String),
|
|
}
|
|
|
|
impl From<serde_json::Error> for Error {
|
|
fn from(e: serde_json::Error) -> Self {
|
|
Error::JsonError(e)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Error {
|
|
fn from(e: io::Error) -> Self {
|
|
Error::IoError(e)
|
|
}
|
|
}
|
|
|
|
impl From<krillserver::Error> for Error {
|
|
fn from(e: krillserver::Error) -> Self {
|
|
Error::ServerError(e)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {
|
|
fn description(&self) -> &str {
|
|
"An error happened"
|
|
}
|
|
}
|
|
|
|
impl actix_web::ResponseError for Error {
|
|
fn error_response(&self) -> HttpResponse {
|
|
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).body(format!("{}", self))
|
|
}
|
|
}
|
|
|
|
//------------ Tests ---------------------------------------------------------
|
|
|
|
// Tested in tests/integration_test.rs
|