diff --git a/Cargo.lock b/Cargo.lock index 96029b781..4950dd4bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1747,6 +1747,7 @@ dependencies = [ "eth2_config", "eth2_network_config", "exit-future", + "filesystem", "futures", "logging", "parking_lot", diff --git a/book/src/api-vc-auth-header.md b/book/src/api-vc-auth-header.md index fd1c5407f..d09a9e54a 100644 --- a/book/src/api-vc-auth-header.md +++ b/book/src/api-vc-auth-header.md @@ -17,10 +17,6 @@ Authorization Basic api-token-0x03eace4c98e8f77477bb99efb74f9af10d800bd3318f92c3 ## Obtaining the API token -The API token can be obtained via two methods: - -### Method 1: Reading from a file - The API token is stored as a file in the `validators` directory. For most users this is `~/.lighthouse/{network}/validators/api-token.txt`. Here's an example using the `cat` command to print the token to the terminal, but any @@ -31,13 +27,12 @@ $ cat api-token.txt api-token-0x03eace4c98e8f77477bb99efb74f9af10d800bd3318f92c33b719a4644254d4123 ``` -### Method 2: Reading from logs -When starting the validator client it will output a log message containing an -`api-token` field: +When starting the validator client it will output a log message containing the path +to the file containing the api token. ``` -Sep 28 19:17:52.615 INFO HTTP API started api_token: api-token-0x03eace4c98e8f77477bb99efb74f9af10d800bd3318f92c33b719a4644254d4123, listen_address: 127.0.0.1:5062 +Sep 28 19:17:52.615 INFO HTTP API started api_token_file: "$HOME/prater/validators/api-token.txt", listen_address: 127.0.0.1:5062 ``` ## Example diff --git a/common/filesystem/src/lib.rs b/common/filesystem/src/lib.rs index 127fcd55b..b7113d493 100644 --- a/common/filesystem/src/lib.rs +++ b/common/filesystem/src/lib.rs @@ -52,7 +52,7 @@ pub enum Error { UnableToRemoveACLEntry(String), } -/// Creates a file with `600 (-rw-------)` permissions. +/// Creates a file with `600 (-rw-------)` permissions and writes the specified bytes to file. pub fn create_with_600_perms>(path: P, bytes: &[u8]) -> Result<(), Error> { let path = path.as_ref(); let mut file = File::create(&path).map_err(Error::UnableToCreateFile)?; diff --git a/lighthouse/environment/Cargo.toml b/lighthouse/environment/Cargo.toml index 56a78ada1..93f9511cf 100644 --- a/lighthouse/environment/Cargo.toml +++ b/lighthouse/environment/Cargo.toml @@ -19,6 +19,7 @@ futures = "0.3.7" parking_lot = "0.11.0" slog-json = "2.3.0" exit-future = "0.2.0" +filesystem = {"path" = "../../common/filesystem"} [target.'cfg(not(target_family = "unix"))'.dependencies] ctrlc = { version = "3.1.6", features = ["termination"] } diff --git a/lighthouse/environment/src/lib.rs b/lighthouse/environment/src/lib.rs index 46badb73c..07e00c7ea 100644 --- a/lighthouse/environment/src/lib.rs +++ b/lighthouse/environment/src/lib.rs @@ -9,6 +9,7 @@ use eth2_config::Eth2Config; use eth2_network_config::Eth2NetworkConfig; +use filesystem::restrict_file_permissions; use futures::channel::mpsc::{channel, Receiver, Sender}; use futures::{future, StreamExt}; @@ -169,6 +170,9 @@ impl EnvironmentBuilder { .open(&path) .map_err(|e| format!("Unable to open logfile: {:?}", e))?; + restrict_file_permissions(&path) + .map_err(|e| format!("Unable to set file permissions for {:?}: {:?}", path, e))?; + // Setting up the initial logger format and building it. let drain = if let Some(format) = log_format { match format.to_uppercase().as_str() { diff --git a/validator_client/src/http_api/api_secret.rs b/validator_client/src/http_api/api_secret.rs index 53a907848..531180cba 100644 --- a/validator_client/src/http_api/api_secret.rs +++ b/validator_client/src/http_api/api_secret.rs @@ -1,9 +1,10 @@ use eth2::lighthouse_vc::{PK_LEN, SECRET_PREFIX as PK_PREFIX}; +use filesystem::create_with_600_perms; use libsecp256k1::{Message, PublicKey, SecretKey}; use rand::thread_rng; use ring::digest::{digest, SHA256}; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use warp::Filter; /// The name of the file which stores the secret key. @@ -37,6 +38,7 @@ pub const PK_FILENAME: &str = "api-token.txt"; pub struct ApiSecret { pk: PublicKey, sk: SecretKey, + pk_path: PathBuf, } impl ApiSecret { @@ -55,12 +57,20 @@ impl ApiSecret { let sk = SecretKey::random(&mut thread_rng()); let pk = PublicKey::from_secret_key(&sk); - fs::write( + // Create and write the secret key to file with appropriate permissions + create_with_600_perms( &sk_path, eth2_serde_utils::hex::encode(&sk.serialize()).as_bytes(), ) - .map_err(|e| e.to_string())?; - fs::write( + .map_err(|e| { + format!( + "Unable to create file with permissions for {:?}: {:?}", + sk_path, e + ) + })?; + + // Create and write the public key to file with appropriate permissions + create_with_600_perms( &pk_path, format!( "{}{}", @@ -69,7 +79,12 @@ impl ApiSecret { ) .as_bytes(), ) - .map_err(|e| e.to_string())?; + .map_err(|e| { + format!( + "Unable to create file with permissions for {:?}: {:?}", + pk_path, e + ) + })?; } let sk = fs::read(&sk_path) @@ -133,7 +148,7 @@ impl ApiSecret { )); } - Ok(Self { pk, sk }) + Ok(Self { pk, sk, pk_path }) } /// Returns the public key of `self` as a 0x-prefixed hex string. @@ -146,6 +161,11 @@ impl ApiSecret { format!("{}{}", PK_PREFIX, self.pubkey_string()) } + /// Returns the path for the API token file + pub fn api_token_path(&self) -> &PathBuf { + &self.pk_path + } + /// Returns the value of the `Authorization` header which is used for verifying incoming HTTP /// requests. fn auth_header_value(&self) -> String { diff --git a/validator_client/src/http_api/mod.rs b/validator_client/src/http_api/mod.rs index 8e8e1a281..f07ad8952 100644 --- a/validator_client/src/http_api/mod.rs +++ b/validator_client/src/http_api/mod.rs @@ -125,7 +125,7 @@ pub fn serve( } let authorization_header_filter = ctx.api_secret.authorization_header_filter(); - let api_token = ctx.api_secret.api_token(); + let api_token_path = ctx.api_secret.api_token_path(); let signer = ctx.api_secret.signer(); let signer = warp::any().map(move || signer.clone()); @@ -505,7 +505,7 @@ pub fn serve( log, "HTTP API started"; "listen_address" => listening_socket.to_string(), - "api_token" => api_token, + "api_token_file" => ?api_token_path, ); Ok((listening_socket, server))