Skip to content

Commit 11e1d5b

Browse files
authored
Add CLI flag for HTTP API token path (VC) (#6577)
* 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 <[email protected]> * 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
1 parent a6de0d5 commit 11e1d5b

File tree

13 files changed

+96
-18
lines changed

13 files changed

+96
-18
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/src/api-vc-auth-header.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Authorization: Bearer hGut6B8uEujufDXSmZsT0thnxvdvKFBvh
1818
## Obtaining the API token
1919

2020
The API token is stored as a file in the `validators` directory. For most users
21-
this is `~/.lighthouse/{network}/validators/api-token.txt`. Here's an
21+
this is `~/.lighthouse/{network}/validators/api-token.txt`, unless overridden using the
22+
`--http-token-path` CLI parameter. Here's an
2223
example using the `cat` command to print the token to the terminal, but any
2324
text editor will suffice:
2425

book/src/api-vc-endpoints.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Example Response Body:
5353
}
5454
```
5555

56-
> Note: The command provided in this documentation links to the API token file. In this documentation, it is assumed that the API token file is located in `/var/lib/lighthouse/validators/api-token.txt`. If your database is saved in another directory, modify the `DATADIR` accordingly. If you are having permission issue with accessing the API token file, you can modify the header to become `-H "Authorization: Bearer $(sudo cat ${DATADIR}/validators/api-token.txt)"`.
56+
> Note: The command provided in this documentation links to the API token file. In this documentation, it is assumed that the API token file is located in `/var/lib/lighthouse/validators/api-token.txt`. If your database is saved in another directory, modify the `DATADIR` accordingly. If you've specified a custom token path using `--http-token-path`, use that path instead. If you are having permission issue with accessing the API token file, you can modify the header to become `-H "Authorization: Bearer $(sudo cat ${DATADIR}/validators/api-token.txt)"`.
5757
5858
> As an alternative, you can also provide the API token directly, for example, `-H "Authorization: Bearer hGut6B8uEujufDXSmZsT0thnxvdvKFBvh`. In this case, you obtain the token from the file `api-token.txt` and the command becomes:
5959

book/src/help_vc.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Options:
6969
this server (e.g., http://localhost:5062).
7070
--http-port <PORT>
7171
Set the listen TCP port for the RESTful HTTP API server.
72+
--http-token-path <HTTP_TOKEN_PATH>
73+
Path to file containing the HTTP API token for validator client
74+
authentication. If not specified, defaults to
75+
{validators-dir}/api-token.txt.
7276
--log-format <FORMAT>
7377
Specifies the log format used when emitting logs to the terminal.
7478
[possible values: JSON]

lighthouse/tests/validator_client.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,21 @@ fn http_store_keystore_passwords_in_secrets_dir_present() {
344344
.with_config(|config| assert!(config.http_api.store_passwords_in_secrets_dir));
345345
}
346346

347+
#[test]
348+
fn http_token_path_flag() {
349+
let dir = TempDir::new().expect("Unable to create temporary directory");
350+
CommandLineTest::new()
351+
.flag("http", None)
352+
.flag("http-token-path", dir.path().join("api-token.txt").to_str())
353+
.run()
354+
.with_config(|config| {
355+
assert_eq!(
356+
config.http_api.http_token_path,
357+
dir.path().join("api-token.txt")
358+
);
359+
});
360+
}
361+
347362
// Tests for Metrics flags.
348363
#[test]
349364
fn metrics_flag() {

validator_client/http_api/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ account_utils = { workspace = true }
1313
bls = { workspace = true }
1414
beacon_node_fallback = { workspace = true }
1515
deposit_contract = { workspace = true }
16+
directory = { workspace = true }
1617
doppelganger_service = { workspace = true }
18+
dirs = { workspace = true }
1719
graffiti_file = { workspace = true }
1820
eth2 = { workspace = true }
1921
eth2_keystore = { workspace = true }

validator_client/http_api/src/api_secret.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::fs;
55
use std::path::{Path, PathBuf};
66
use warp::Filter;
77

8-
/// The name of the file which stores the API token.
8+
/// The default name of the file which stores the API token.
99
pub const PK_FILENAME: &str = "api-token.txt";
1010

1111
pub const PK_LEN: usize = 33;
@@ -31,14 +31,32 @@ pub struct ApiSecret {
3131
impl ApiSecret {
3232
/// If the public key is already on-disk, use it.
3333
///
34-
/// The provided `dir` is a directory containing `PK_FILENAME`.
34+
/// The provided `pk_path` is a path containing API token.
3535
///
3636
/// If the public key file is missing on disk, create a new key and
3737
/// write it to disk (over-writing any existing files).
38-
pub fn create_or_open<P: AsRef<Path>>(dir: P) -> Result<Self, String> {
39-
let pk_path = dir.as_ref().join(PK_FILENAME);
38+
pub fn create_or_open<P: AsRef<Path>>(pk_path: P) -> Result<Self, String> {
39+
let pk_path = pk_path.as_ref();
40+
41+
// Check if the path is a directory
42+
if pk_path.is_dir() {
43+
return Err(format!(
44+
"API token path {:?} is a directory, not a file",
45+
pk_path
46+
));
47+
}
4048

4149
if !pk_path.exists() {
50+
// Create parent directories if they don't exist
51+
if let Some(parent) = pk_path.parent() {
52+
std::fs::create_dir_all(parent).map_err(|e| {
53+
format!(
54+
"Unable to create parent directories for {:?}: {:?}",
55+
pk_path, e
56+
)
57+
})?;
58+
}
59+
4260
let length = PK_LEN;
4361
let pk: String = thread_rng()
4462
.sample_iter(&Alphanumeric)
@@ -47,21 +65,24 @@ impl ApiSecret {
4765
.collect();
4866

4967
// Create and write the public key to file with appropriate permissions
50-
create_with_600_perms(&pk_path, pk.to_string().as_bytes()).map_err(|e| {
68+
create_with_600_perms(pk_path, pk.to_string().as_bytes()).map_err(|e| {
5169
format!(
5270
"Unable to create file with permissions for {:?}: {:?}",
5371
pk_path, e
5472
)
5573
})?;
5674
}
5775

58-
let pk = fs::read(&pk_path)
59-
.map_err(|e| format!("cannot read {}: {}", PK_FILENAME, e))?
76+
let pk = fs::read(pk_path)
77+
.map_err(|e| format!("cannot read {}: {}", pk_path.display(), e))?
6078
.iter()
6179
.map(|&c| char::from(c))
6280
.collect();
6381

64-
Ok(Self { pk, pk_path })
82+
Ok(Self {
83+
pk,
84+
pk_path: pk_path.to_path_buf(),
85+
})
6586
}
6687

6788
/// Returns the API token.

validator_client/http_api/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod remotekeys;
77
mod tests;
88

99
pub mod test_utils;
10+
pub use api_secret::PK_FILENAME;
1011

1112
use graffiti::{delete_graffiti, get_graffiti, set_graffiti};
1213

@@ -23,6 +24,7 @@ use beacon_node_fallback::CandidateInfo;
2324
use create_validator::{
2425
create_validators_mnemonic, create_validators_web3signer, get_voting_password_storage,
2526
};
27+
use directory::{DEFAULT_HARDCODED_NETWORK, DEFAULT_ROOT_DIR, DEFAULT_VALIDATOR_DIR};
2628
use eth2::lighthouse_vc::{
2729
std_types::{AuthResponse, GetFeeRecipientResponse, GetGasLimitResponse},
2830
types::{
@@ -99,17 +101,25 @@ pub struct Config {
99101
pub allow_origin: Option<String>,
100102
pub allow_keystore_export: bool,
101103
pub store_passwords_in_secrets_dir: bool,
104+
pub http_token_path: PathBuf,
102105
}
103106

104107
impl Default for Config {
105108
fn default() -> Self {
109+
let http_token_path = dirs::home_dir()
110+
.unwrap_or_else(|| PathBuf::from("."))
111+
.join(DEFAULT_ROOT_DIR)
112+
.join(DEFAULT_HARDCODED_NETWORK)
113+
.join(DEFAULT_VALIDATOR_DIR)
114+
.join(PK_FILENAME);
106115
Self {
107116
enabled: false,
108117
listen_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
109118
listen_port: 5062,
110119
allow_origin: None,
111120
allow_keystore_export: false,
112121
store_passwords_in_secrets_dir: false,
122+
http_token_path,
113123
}
114124
}
115125
}

validator_client/http_api/src/test_utils.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::api_secret::PK_FILENAME;
12
use crate::{ApiSecret, Config as HttpConfig, Context};
23
use account_utils::validator_definitions::ValidatorDefinitions;
34
use account_utils::{
@@ -73,6 +74,7 @@ impl ApiTester {
7374

7475
let validator_dir = tempdir().unwrap();
7576
let secrets_dir = tempdir().unwrap();
77+
let token_path = tempdir().unwrap().path().join(PK_FILENAME);
7678

7779
let validator_defs = ValidatorDefinitions::open_or_create(validator_dir.path()).unwrap();
7880

@@ -85,7 +87,7 @@ impl ApiTester {
8587
.await
8688
.unwrap();
8789

88-
let api_secret = ApiSecret::create_or_open(validator_dir.path()).unwrap();
90+
let api_secret = ApiSecret::create_or_open(token_path).unwrap();
8991
let api_pubkey = api_secret.api_token();
9092

9193
let config = ValidatorStoreConfig {
@@ -177,6 +179,7 @@ impl ApiTester {
177179
allow_origin: None,
178180
allow_keystore_export: true,
179181
store_passwords_in_secrets_dir: false,
182+
http_token_path: tempdir().unwrap().path().join(PK_FILENAME),
180183
}
181184
}
182185

@@ -199,8 +202,8 @@ impl ApiTester {
199202
}
200203

201204
pub fn invalid_token_client(&self) -> ValidatorClientHttpClient {
202-
let tmp = tempdir().unwrap();
203-
let api_secret = ApiSecret::create_or_open(tmp.path()).unwrap();
205+
let tmp = tempdir().unwrap().path().join("invalid-token.txt");
206+
let api_secret = ApiSecret::create_or_open(tmp).unwrap();
204207
let invalid_pubkey = api_secret.api_token();
205208
ValidatorClientHttpClient::new(self.url.clone(), invalid_pubkey).unwrap()
206209
}

validator_client/http_api/src/tests.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ impl ApiTester {
6363

6464
let validator_dir = tempdir().unwrap();
6565
let secrets_dir = tempdir().unwrap();
66+
let token_path = tempdir().unwrap().path().join("api-token.txt");
6667

6768
let validator_defs = ValidatorDefinitions::open_or_create(validator_dir.path()).unwrap();
6869

@@ -75,7 +76,7 @@ impl ApiTester {
7576
.await
7677
.unwrap();
7778

78-
let api_secret = ApiSecret::create_or_open(validator_dir.path()).unwrap();
79+
let api_secret = ApiSecret::create_or_open(&token_path).unwrap();
7980
let api_pubkey = api_secret.api_token();
8081

8182
let spec = Arc::new(E::default_spec());
@@ -127,6 +128,7 @@ impl ApiTester {
127128
allow_origin: None,
128129
allow_keystore_export: true,
129130
store_passwords_in_secrets_dir: false,
131+
http_token_path: token_path,
130132
},
131133
sse_logging_components: None,
132134
log,
@@ -161,8 +163,8 @@ impl ApiTester {
161163
}
162164

163165
pub fn invalid_token_client(&self) -> ValidatorClientHttpClient {
164-
let tmp = tempdir().unwrap();
165-
let api_secret = ApiSecret::create_or_open(tmp.path()).unwrap();
166+
let tmp = tempdir().unwrap().path().join("invalid-token.txt");
167+
let api_secret = ApiSecret::create_or_open(tmp).unwrap();
166168
let invalid_pubkey = api_secret.api_token();
167169
ValidatorClientHttpClient::new(self.url.clone(), invalid_pubkey.clone()).unwrap()
168170
}

0 commit comments

Comments
 (0)