Support non-interactive TLS passphrase input

next
Sayan Nandan 3 years ago
parent d54652c21e
commit 5a0d3017a5

@ -102,6 +102,9 @@ All changes in this project will be noted in this file.
- **Snapshot isolation for strong actions**: This makes strong actions
extremely reliable when compared to the earlier releases
- Non-interactive TLS private key passphrase input: Just save your password in some
file and then pass `--tlspassin /path/to/passfile.txt`. You can do the same by using the
`tlspassin` key under SSL in the configuration file
- TLS port can now be set to a custom port via CLI arguments

@ -8,10 +8,9 @@
# This is a *REQUIRED* key
[server]
host = "127.0.0.1" # The IP address to which you want sdb to bind to
port = 2003 # The port to which you want sdb to bind to
# Set `noart` to true if you want to disable terminal artwork
noart = false
maxcon = 50000 # set the maximum number of clients that the server can accept
port = 2003 # The port to which you want sdb to bind to
noart = false # Set `noart` to true if you want to disable terminal artwork
maxcon = 50000 # set the maximum number of clients that the server can accept
# This key is *OPTIONAL*
[bgsave]
@ -22,8 +21,8 @@ every = 120
# This key is *OPTIONAL*
[snapshot]
every = 3600 # Make a snapshot after every 1 hour (60min * 60sec= 3600secs)
atmost = 4 # Keep the 4 most recent snapshots
every = 3600 # Make a snapshot after every 1 hour (60min * 60sec= 3600secs)
atmost = 4 # Keep the 4 most recent snapshots
failsafe = true # stops accepting writes if snapshotting fails
# This key is *OPTIONAL*, used for TLS/SSL config
@ -31,4 +30,5 @@ failsafe = true # stops accepting writes if snapshotting fails
key = "/path/to/keyfile.pem"
chain = "/path/to/chain.pem"
port = 2004
only = true # optional to enable SSL-only requests
only = true # optional to enable SSL-only requests
passin = "/path/to/cert/passphrase.txt" # optional to programmatically verify the TLS cert

@ -85,6 +85,12 @@ args:
takes_value: true
value_name: sslport
help: Set a custom SSL port to bind to
- tlspassin:
required: false
long: tlspassin
takes_value: true
value_name: tlspassin
help: Path to the file containing the passphrase for the TLS certificate
- stopwriteonfail:
required: false
long: stop-write-on-fail

@ -182,6 +182,7 @@ pub struct KeySslOpts {
chain: String,
port: u16,
only: Option<bool>,
passin: Option<String>,
}
#[derive(Deserialize, Debug, PartialEq)]
@ -189,11 +190,17 @@ pub struct SslOpts {
pub key: String,
pub chain: String,
pub port: u16,
pub passfile: Option<String>,
}
impl SslOpts {
pub const fn new(key: String, chain: String, port: u16) -> Self {
SslOpts { key, chain, port }
pub const fn new(key: String, chain: String, port: u16, passfile: Option<String>) -> Self {
SslOpts {
key,
chain,
port,
passfile,
}
}
}
@ -308,6 +315,7 @@ impl ParsedConfig {
key: sslopts.key,
chain: sslopts.chain,
port: sslopts.port,
passfile: sslopts.passin,
},
host: cfg_info.server.host,
}
@ -317,6 +325,7 @@ impl ParsedConfig {
key: sslopts.key,
chain: sslopts.chain,
port: sslopts.port,
passfile: sslopts.passin,
},
host: cfg_info.server.host,
port: cfg_info.server.port,
@ -434,6 +443,7 @@ pub fn get_config_file_or_return_cfg() -> Result<ConfigType<ParsedConfig, String
let sslkey = matches.value_of("sslkey");
let sslchain = matches.value_of("sslchain");
let maxcon = matches.value_of("maxcon");
let passfile = matches.value_of("tlspassin");
let cli_has_overrideable_args = host.is_some()
|| port.is_some()
|| noart
@ -445,6 +455,7 @@ pub fn get_config_file_or_return_cfg() -> Result<ConfigType<ParsedConfig, String
|| sslkey.is_some()
|| maxcon.is_some()
|| custom_ssl_port
|| passfile.is_some()
|| sslonly;
if filename.is_some() && cli_has_overrideable_args {
return Err(ConfigError::CfgError(
@ -588,9 +599,16 @@ pub fn get_config_file_or_return_cfg() -> Result<ConfigType<ParsedConfig, String
}
(Some(key), Some(chain)) => {
if sslonly {
PortConfig::new_secure_only(host, SslOpts::new(key, chain, sslport))
PortConfig::new_secure_only(
host,
SslOpts::new(key, chain, sslport, passfile.map(|v| v.to_string())),
)
} else {
PortConfig::new_multi(host, port, SslOpts::new(key, chain, sslport))
PortConfig::new_multi(
host,
port,
SslOpts::new(key, chain, sslport, passfile.map(|v| v.to_string())),
)
}
}
_ => {
@ -743,7 +761,8 @@ mod tests {
SslOpts::new(
"/path/to/keyfile.pem".into(),
"/path/to/chain.pem".into(),
2004
2004,
Some("/path/to/cert/passphrase.txt".to_owned())
)
),
MAXIMUM_CONNECTION_LIMIT

@ -174,7 +174,7 @@ impl MultiListener {
pub fn new_secure_only(base: BaseListener, ssl: SslOpts) -> Result<Self, String> {
let bindaddr = bindaddr!(base);
let slf = MultiListener::SecureOnly(
SslListener::new_pem_based_ssl_connection(ssl.key, ssl.chain, base)
SslListener::new_pem_based_ssl_connection(ssl.key, ssl.chain, base, ssl.passfile)
.map_err(|e| format!("Couldn't bind to secure port: {}", e))?,
);
log::info!("Server started on: skyhash-secure://{}", bindaddr);
@ -188,9 +188,13 @@ impl MultiListener {
) -> Result<Self, String> {
let sec_bindaddr = bindaddr!(ssl_base_listener);
let insec_binaddr = bindaddr!(tcp_base_listener);
let secure_listener =
SslListener::new_pem_based_ssl_connection(ssl.key, ssl.chain, ssl_base_listener)
.map_err(|e| format!("Couldn't bind to secure port: {}", e))?;
let secure_listener = SslListener::new_pem_based_ssl_connection(
ssl.key,
ssl.chain,
ssl_base_listener,
ssl.passfile,
)
.map_err(|e| format!("Couldn't bind to secure port: {}", e))?;
let insecure_listener = Listener {
base: tcp_base_listener,
};

@ -30,7 +30,11 @@ use crate::dbnet::tcp::Connection;
use crate::dbnet::BaseListener;
use crate::dbnet::Terminator;
use libsky::TResult;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod};
use std::fs;
use std::io::Error as IoError;
use std::pin::Pin;
use tokio::net::TcpStream;
use tokio::time::{self, Duration};
@ -48,12 +52,36 @@ impl SslListener {
key_file: String,
chain_file: String,
base: BaseListener,
tls_passfile: Option<String>,
) -> TResult<Self> {
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
acceptor.set_private_key_file(key_file, SslFiletype::PEM)?;
acceptor.set_certificate_chain_file(chain_file)?;
let acceptor = acceptor.build();
Ok(SslListener { base, acceptor })
let mut acceptor_builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
// cert is the same for both
acceptor_builder.set_certificate_chain_file(chain_file)?;
if let Some(tls_passfile) = tls_passfile {
// first read in the private key
let tls_private_key = fs::read(key_file).map_err(|e: IoError| {
format!("Failed to read TLS private key file with error: {}", e)
})?;
// read the passphrase because the passphrase file stream was provided
let tls_keyfile_stream = fs::read(tls_passfile).map_err(|e: IoError| {
format!(
"Failed to read TLS private key passphrase file with error: {}",
e
)
})?;
// decrypt the private key
let pkey = Rsa::private_key_from_pem_passphrase(&tls_private_key, &tls_keyfile_stream)?;
let pkey = PKey::from_rsa(pkey)?;
// set the private key for the acceptor
acceptor_builder.set_private_key(&pkey)?;
} else {
// no passphrase, needs interactive
acceptor_builder.set_private_key_file(key_file, SslFiletype::PEM)?;
}
Ok(SslListener {
base,
acceptor: acceptor_builder.build(),
})
}
async fn accept(&mut self) -> TResult<SslStream<TcpStream>> {
let mut backoff = 1;

Loading…
Cancel
Save