aboutsummaryrefslogtreecommitdiffstats
path: root/atuin-server/src/settings.rs
diff options
context:
space:
mode:
authorEric Hodel <drbrain@segment7.net>2023-12-27 06:15:48 -0800
committerGitHub <noreply@github.com>2023-12-27 14:15:48 +0000
commitd52e57612942cbe0c6a0dd774fcc2caac8f439d5 (patch)
tree6abc226ffa71156b0ac747529e7effaa21c75c15 /atuin-server/src/settings.rs
parentfeat: add semver checking to client requests (#1456) (diff)
downloadatuin-d52e57612942cbe0c6a0dd774fcc2caac8f439d5.zip
feat: Add TLS to atuin-server (#1457)
* Add TLS to atuin-server atuin as a project already includes most of the dependencies necessary for server-side TLS. This allows `atuin server start` to use a TLS certificate when self-hosting in order to avoid the complication of wrapping it in a TLS-aware proxy server. Configuration is handled similar to the metrics server with its own struct and currently accepts only the private key and certificate file paths. Starting a TLS server and a TCP server are divergent because the tests need to bind to an arbitrary port to avoid collisions across tests. The API to accomplish this for a TLS server is much more verbose. * Fix clippy, fmt * Add TLS section to self-hosting
Diffstat (limited to '')
-rw-r--r--atuin-server/src/settings.rs54
1 files changed, 53 insertions, 1 deletions
diff --git a/atuin-server/src/settings.rs b/atuin-server/src/settings.rs
index d6f1867c..70008fbc 100644
--- a/atuin-server/src/settings.rs
+++ b/atuin-server/src/settings.rs
@@ -1,7 +1,7 @@
use std::{io::prelude::*, path::PathBuf};
use config::{Config, Environment, File as ConfigFile, FileFormat};
-use eyre::{eyre, Result};
+use eyre::{bail, eyre, Context, Result};
use fs_err::{create_dir_all, File};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -36,6 +36,7 @@ pub struct Settings<DbSettings> {
pub register_webhook_url: Option<String>,
pub register_webhook_username: String,
pub metrics: Metrics,
+ pub tls: Tls,
#[serde(flatten)]
pub db_settings: DbSettings,
@@ -67,6 +68,9 @@ impl<DbSettings: DeserializeOwned> Settings<DbSettings> {
.set_default("metrics.enable", false)?
.set_default("metrics.host", "127.0.0.1")?
.set_default("metrics.port", 9001)?
+ .set_default("tls.enable", false)?
+ .set_default("tls.cert_path", "")?
+ .set_default("tls.key_path", "")?
.add_source(
Environment::with_prefix("atuin")
.prefix_separator("_")
@@ -97,3 +101,51 @@ impl<DbSettings: DeserializeOwned> Settings<DbSettings> {
pub fn example_config() -> &'static str {
EXAMPLE_CONFIG
}
+
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+pub struct Tls {
+ pub enable: bool,
+ pub cert_path: PathBuf,
+ pub pkey_path: PathBuf,
+}
+
+impl Tls {
+ pub fn certificates(&self) -> Result<Vec<rustls::Certificate>> {
+ let cert_file = std::fs::File::open(&self.cert_path)
+ .with_context(|| format!("tls.cert_path {:?} is missing", self.cert_path))?;
+ let mut reader = std::io::BufReader::new(cert_file);
+ let certs: Vec<_> = rustls_pemfile::certs(&mut reader)
+ .with_context(|| format!("tls.cert_path {:?} is invalid", self.cert_path))?
+ .into_iter()
+ .map(rustls::Certificate)
+ .collect();
+
+ if certs.is_empty() {
+ bail!(
+ "tls.cert_path {:?} must have at least one certificate",
+ self.cert_path
+ );
+ }
+
+ Ok(certs)
+ }
+
+ pub fn private_key(&self) -> Result<rustls::PrivateKey> {
+ let pkey_file = std::fs::File::open(&self.pkey_path)
+ .with_context(|| format!("tls.pkey_path {:?} is missing", self.pkey_path))?;
+ let mut reader = std::io::BufReader::new(pkey_file);
+ let keys = rustls_pemfile::pkcs8_private_keys(&mut reader)
+ .with_context(|| format!("tls.pkey_path {:?} is not PKCS8-encoded", self.pkey_path))?;
+
+ if keys.is_empty() {
+ bail!(
+ "tls.pkey_path {:?} must have at least one private key",
+ self.pkey_path
+ );
+ }
+
+ let key = rustls::PrivateKey(keys[0].clone());
+
+ Ok(key)
+ }
+}