Implement standard keystore API (#2736)

## Issue Addressed

Implements the standard key manager API from https://ethereum.github.io/keymanager-APIs/, formerly https://github.com/ethereum/beacon-APIs/pull/151
Related to https://github.com/sigp/lighthouse/issues/2557

## Proposed Changes

- [x] Add all of the new endpoints from the standard API: GET, POST and DELETE.
- [x] Add a `validators.enabled` column to the slashing protection database to support atomic disable + export.
- [x] Add tests for all the common sequential accesses of the API
- [x] Add tests for interactions with remote signer validators
- [x] Add end-to-end tests for migration of validators from one VC to another
- [x] Implement the authentication scheme from the standard (token bearer auth)

## Additional Info

The `enabled` column in the validators SQL database is necessary to prevent a race condition when exporting slashing protection data. Without the slashing protection database having a way of knowing that a key has been disabled, a concurrent request to sign a message could insert a new record into the database. The `delete_concurrent_with_signing` test exercises this code path, and was indeed failing before the `enabled` column was added.

The validator client authentication has been modified from basic auth to bearer auth, with basic auth preserved for backwards compatibility.
This commit is contained in:
Michael Sproul
2022-01-30 23:22:04 +00:00
parent ee000d5219
commit e961ff60b4
32 changed files with 2284 additions and 127 deletions

View File

@@ -162,25 +162,32 @@ impl ApiSecret {
}
/// Returns the path for the API token file
pub fn api_token_path(&self) -> &PathBuf {
&self.pk_path
pub fn api_token_path(&self) -> PathBuf {
self.pk_path.clone()
}
/// Returns the value of the `Authorization` header which is used for verifying incoming HTTP
/// requests.
fn auth_header_value(&self) -> String {
format!("Basic {}", self.api_token())
/// 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_value();
let expected = self.auth_header_values();
warp::any()
.map(move || expected.clone())
.and(warp::filters::header::header("Authorization"))
.and_then(move |expected: String, header: String| async move {
if header == expected {
.and_then(move |expected: Vec<String>, header: String| async move {
if expected.contains(&header) {
Ok(())
} else {
Err(warp_utils::reject::invalid_auth(header))