mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2026-05-07 12:00:48 +02:00
add optional encryption to rsa_key.pem
This commit is contained in:
parent
f21a3adae2
commit
18a1b00aa7
3 changed files with 58 additions and 6 deletions
55
src/auth.rs
55
src/auth.rs
|
|
@ -69,14 +69,59 @@ pub async fn initialize_keys() -> Result<(), Error> {
|
|||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
let (priv_key, priv_key_buffer) = if let Some(priv_key_buffer) = priv_key_buffer {
|
||||
(Rsa::private_key_from_pem(priv_key_buffer.to_vec().as_slice())?, priv_key_buffer.to_vec())
|
||||
let passphrase = CONFIG.rsa_key_passphrase();
|
||||
|
||||
let (priv_key, priv_key_buffer) = if let Some(stored_buffer) = priv_key_buffer {
|
||||
let mut passphrase_error: Option<Error> = None;
|
||||
let mut key_is_unencrypted = true;
|
||||
|
||||
// The callback is only invoked if the key file is encrypted. For unencrypted keys it is never called.
|
||||
let rsa = Rsa::private_key_from_pem_callback(stored_buffer.to_vec().as_slice(), |buf| {
|
||||
key_is_unencrypted = false;
|
||||
if passphrase.is_empty() {
|
||||
// Only reached when key is encrypted. Return any error to abort the callback;
|
||||
// the actual error message is stored in passphrase_error above.
|
||||
passphrase_error = Some(Error::other(
|
||||
"Private RSA key is encrypted but RSA_KEY_PASSPHRASE is not configured",
|
||||
));
|
||||
return Err(openssl::error::ErrorStack::get());
|
||||
}
|
||||
let bytes = passphrase.as_bytes();
|
||||
buf[..bytes.len()].copy_from_slice(bytes);
|
||||
Ok(bytes.len())
|
||||
});
|
||||
|
||||
let rsa = match rsa {
|
||||
Ok(r) => r,
|
||||
Err(_) if passphrase_error.is_some() => return Err(passphrase_error.unwrap().into()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
if key_is_unencrypted && !passphrase.is_empty() {
|
||||
warn!(
|
||||
"RSA key passphrase is configured but the existing private key '{}' is not encrypted.",
|
||||
CONFIG.private_rsa_key()
|
||||
);
|
||||
}
|
||||
|
||||
// EncodingKey requires an unencrypted PEM, so export the in-memory key regardless of how it was stored on disk.
|
||||
let unencrypted_pem = rsa.private_key_to_pem()?;
|
||||
(rsa, unencrypted_pem)
|
||||
} else {
|
||||
let rsa_key = Rsa::generate(2048)?;
|
||||
let priv_key_buffer = rsa_key.private_key_to_pem()?;
|
||||
operator.write(&rsa_key_filename, priv_key_buffer.clone()).await?;
|
||||
|
||||
// Store encrypted on disk if a passphrase is configured, otherwise store plaintext.
|
||||
let stored_pem = if passphrase.is_empty() {
|
||||
rsa_key.private_key_to_pem()?
|
||||
} else {
|
||||
use openssl::symm::Cipher;
|
||||
rsa_key.private_key_to_pem_passphrase(Cipher::aes_256_cbc(), passphrase.as_bytes())?
|
||||
};
|
||||
operator.write(&rsa_key_filename, stored_pem).await?;
|
||||
info!("Private key '{}' created correctly", CONFIG.private_rsa_key());
|
||||
(rsa_key, priv_key_buffer)
|
||||
|
||||
let unencrypted_pem = rsa_key.private_key_to_pem()?;
|
||||
(rsa_key, unencrypted_pem)
|
||||
};
|
||||
let pub_key_buffer = priv_key.public_key_to_pem()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -520,6 +520,8 @@ make_config! {
|
|||
templates_folder: String, false, auto, |c| format!("{}/templates", c.data_folder);
|
||||
/// Session JWT key
|
||||
rsa_key_filename: String, false, auto, |c| format!("{}/rsa_key", c.data_folder);
|
||||
/// RSA key passphrase |> Passphrase used to encrypt the private RSA key file on disk (leave empty for unencrypted)
|
||||
rsa_key_passphrase: String, false, def, String::new();
|
||||
/// Web vault folder
|
||||
web_vault_folder: String, false, def, "web-vault/".to_string();
|
||||
},
|
||||
|
|
@ -959,6 +961,11 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> {
|
|||
err!(format!("`DATABASE_MIN_CONNS` must be smaller than or equal to `DATABASE_MAX_CONNS`.",));
|
||||
}
|
||||
|
||||
// 1024 = OpenSSL's PEM_BUFSIZE, the size of the buffer passed to the passphrase callback
|
||||
if cfg.rsa_key_passphrase.as_bytes().len() > 1024 {
|
||||
err!("`RSA_KEY_PASSPHRASE` must not exceed 1024 bytes");
|
||||
}
|
||||
|
||||
if let Some(log_file) = &cfg.log_file {
|
||||
if std::fs::OpenOptions::new().append(true).create(true).open(log_file).is_err() {
|
||||
err!("Unable to write to log file", log_file);
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ async fn main() -> Result<(), Error> {
|
|||
|
||||
check_data_folder().await;
|
||||
auth::initialize_keys().await.unwrap_or_else(|e| {
|
||||
error!("Error creating private key '{}'\n{e:?}\nExiting Vaultwarden!", CONFIG.private_rsa_key());
|
||||
error!("Error creating or loading private key '{}'\n{e:?}\nExiting Vaultwarden!", CONFIG.private_rsa_key());
|
||||
exit(1);
|
||||
});
|
||||
check_web_vault();
|
||||
|
|
|
|||
Loading…
Reference in a new issue