mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-10 04:01:51 +00:00
* Add cli flag for HTTP API token path (VC) * Add http_token_path_flag test * Add pre-check for directory case & Fix test utils * Update docs * Apply review: move http_token_path into validator_http_api config * Lint * Make diff lesser to replace PK_FILENAME * Merge branch 'unstable' into feature/cli-token-path * Applt review: help_vc.md Co-authored-by: chonghe <44791194+chong-he@users.noreply.github.com> * Fix help for cli * Fix issues on ci * Merge branch 'unstable' into feature/cli-token-path * Merge branch 'unstable' into feature/cli-token-path * Merge branch 'unstable' into feature/cli-token-path * Merge branch 'unstable' into feature/cli-token-path
129 lines
4.3 KiB
Rust
129 lines
4.3 KiB
Rust
use filesystem::create_with_600_perms;
|
|
use rand::distributions::Alphanumeric;
|
|
use rand::{thread_rng, Rng};
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use warp::Filter;
|
|
|
|
/// The default name of the file which stores the API token.
|
|
pub const PK_FILENAME: &str = "api-token.txt";
|
|
|
|
pub const PK_LEN: usize = 33;
|
|
|
|
/// Contains a randomly generated string which is used for authorization of requests to the HTTP API.
|
|
///
|
|
/// Provides convenience functions to ultimately provide:
|
|
///
|
|
/// - Verification of proof-of-knowledge of the public key in `self` for incoming HTTP requests,
|
|
/// via the `Authorization` header.
|
|
///
|
|
/// The aforementioned scheme was first defined here:
|
|
///
|
|
/// https://github.com/sigp/lighthouse/issues/1269#issuecomment-649879855
|
|
///
|
|
/// This scheme has since been tweaked to remove VC response signing and secp256k1 key generation.
|
|
/// https://github.com/sigp/lighthouse/issues/5423
|
|
pub struct ApiSecret {
|
|
pk: String,
|
|
pk_path: PathBuf,
|
|
}
|
|
|
|
impl ApiSecret {
|
|
/// If the public key is already on-disk, use it.
|
|
///
|
|
/// The provided `pk_path` is a path containing API token.
|
|
///
|
|
/// If the public key file is missing on disk, create a new key and
|
|
/// write it to disk (over-writing any existing files).
|
|
pub fn create_or_open<P: AsRef<Path>>(pk_path: P) -> Result<Self, String> {
|
|
let pk_path = pk_path.as_ref();
|
|
|
|
// Check if the path is a directory
|
|
if pk_path.is_dir() {
|
|
return Err(format!(
|
|
"API token path {:?} is a directory, not a file",
|
|
pk_path
|
|
));
|
|
}
|
|
|
|
if !pk_path.exists() {
|
|
// Create parent directories if they don't exist
|
|
if let Some(parent) = pk_path.parent() {
|
|
std::fs::create_dir_all(parent).map_err(|e| {
|
|
format!(
|
|
"Unable to create parent directories for {:?}: {:?}",
|
|
pk_path, e
|
|
)
|
|
})?;
|
|
}
|
|
|
|
let length = PK_LEN;
|
|
let pk: String = thread_rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(length)
|
|
.map(char::from)
|
|
.collect();
|
|
|
|
// Create and write the public key to file with appropriate permissions
|
|
create_with_600_perms(pk_path, pk.to_string().as_bytes()).map_err(|e| {
|
|
format!(
|
|
"Unable to create file with permissions for {:?}: {:?}",
|
|
pk_path, e
|
|
)
|
|
})?;
|
|
}
|
|
|
|
let pk = fs::read(pk_path)
|
|
.map_err(|e| format!("cannot read {}: {}", pk_path.display(), e))?
|
|
.iter()
|
|
.map(|&c| char::from(c))
|
|
.collect();
|
|
|
|
Ok(Self {
|
|
pk,
|
|
pk_path: pk_path.to_path_buf(),
|
|
})
|
|
}
|
|
|
|
/// Returns the API token.
|
|
pub fn api_token(&self) -> String {
|
|
self.pk.clone()
|
|
}
|
|
|
|
/// Returns the path for the API token file
|
|
pub fn api_token_path(&self) -> PathBuf {
|
|
self.pk_path.clone()
|
|
}
|
|
|
|
/// Returns the values of the `Authorization` header which indicate a valid incoming HTTP
|
|
/// request.
|
|
///
|
|
/// For backwards-compatibility we accept the token in a basic authentication style, but this is
|
|
/// technically invalid according to RFC 7617 because the token is not a base64-encoded username
|
|
/// and password. As such, bearer authentication should be preferred.
|
|
fn auth_header_values(&self) -> Vec<String> {
|
|
vec![
|
|
format!("Basic {}", self.api_token()),
|
|
format!("Bearer {}", self.api_token()),
|
|
]
|
|
}
|
|
|
|
/// Returns a `warp` header which filters out request that have a missing or inaccurate
|
|
/// `Authorization` header.
|
|
pub fn authorization_header_filter(&self) -> warp::filters::BoxedFilter<()> {
|
|
let expected = self.auth_header_values();
|
|
warp::any()
|
|
.map(move || expected.clone())
|
|
.and(warp::filters::header::header("Authorization"))
|
|
.and_then(move |expected: Vec<String>, header: String| async move {
|
|
if expected.contains(&header) {
|
|
Ok(())
|
|
} else {
|
|
Err(warp_utils::reject::invalid_auth(header))
|
|
}
|
|
})
|
|
.untuple_one()
|
|
.boxed()
|
|
}
|
|
}
|