about summary refs log tree commit diff
path: root/users/grfn/xanthous
diff options
context:
space:
mode:
authorGriffin Smith <grfn@gws.fyi>2021-11-08T15·54-0500
committergrfn <grfn@gws.fyi>2021-11-08T17·01+0000
commit0d1980f2d6e923262ee64b360bca34899c805596 (patch)
tree5de58e50622997dba0a4232e20bd9a338f6243b7 /users/grfn/xanthous
parent7d1ebe996c3b5230aa21099179c8643fc36d7cdc (diff)
feat(xanthous/server): Load host secret key from disk r/3028
Rather than randomly generating a new host key every time we run the
server, load the host's secret key from a file on disk at startup, so
that clients don't have to disable host key verification to connect
every time we restart.

Change-Id: I4d283bc919f4825789f686a98c174a71929087a6
Reviewed-on: https://cl.tvl.fyi/c/depot/+/3819
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
Diffstat (limited to 'users/grfn/xanthous')
-rw-r--r--users/grfn/xanthous/server/module.nix6
-rw-r--r--users/grfn/xanthous/server/src/main.rs28
2 files changed, 29 insertions, 5 deletions
diff --git a/users/grfn/xanthous/server/module.nix b/users/grfn/xanthous/server/module.nix
index 11adda955d..cbc10c014d 100644
--- a/users/grfn/xanthous/server/module.nix
+++ b/users/grfn/xanthous/server/module.nix
@@ -24,6 +24,11 @@ in {
         default = depot.users.grfn.xanthous.server.docker;
         description = "OCI image file to run";
       };
+
+      ed25519SecretKeyFile = mkOption {
+        type = with types; uniq string;
+        description = "Path to the ed25519 secret key for the server";
+      };
     };
   };
 
@@ -36,6 +41,7 @@ in {
         "${toString cfg.port}:22"
         "${toString cfg.metricsPort}:9000"
       ];
+      environment.SECRET_KEY_FILE = "/etc/secrets/xanthous-server-secret-key";
     };
   };
 }
diff --git a/users/grfn/xanthous/server/src/main.rs b/users/grfn/xanthous/server/src/main.rs
index 4476fe48a6..9bb31bd9b8 100644
--- a/users/grfn/xanthous/server/src/main.rs
+++ b/users/grfn/xanthous/server/src/main.rs
@@ -1,11 +1,12 @@
 use std::net::SocketAddr;
+use std::path::PathBuf;
 use std::pin::Pin;
 use std::process::Command;
 use std::sync::Arc;
 
 use clap::Parser;
 use color_eyre::eyre::Result;
-use eyre::{bail, eyre};
+use eyre::{bail, Context};
 use futures::future::{ready, Ready};
 use futures::Future;
 use metrics_exporter_prometheus::PrometheusBuilder;
@@ -16,6 +17,9 @@ use thrussh::{
     server::{self, Auth, Session},
     CryptoVec,
 };
+use thrussh_keys::decode_openssh;
+use thrussh_keys::key::KeyPair;
+use tokio::fs::File;
 use tokio::io::{AsyncReadExt, AsyncWriteExt};
 use tokio::net::TcpListener;
 use tokio::select;
@@ -55,17 +59,31 @@ struct Opts {
     #[clap(long, env = "XANTHOUS_BINARY_PATH")]
     xanthous_binary_path: String,
 
+    /// Path to a file containing the ed25519 secret key for the server
+    #[clap(long, env = "SECRET_KEY_FILE")]
+    secret_key_file: PathBuf,
+
     /// Level to log at
     #[clap(long, env = "LOG_LEVEL", default_value = "info")]
     log_level: String,
 }
 
 impl Opts {
-    fn ssh_server_config(&self) -> Result<server::Config> {
+    async fn read_secret_key(&self) -> Result<KeyPair> {
+        let mut file = File::open(&self.secret_key_file)
+            .await
+            .context("Reading secret key file")?;
+        let mut secret_key = Vec::with_capacity(464);
+        file.read_to_end(&mut secret_key).await?;
+        Ok(decode_openssh(&secret_key, None)?)
+    }
+
+    async fn ssh_server_config(&self) -> Result<server::Config> {
+        let key_pair = self.read_secret_key().await?;
+
         Ok(server::Config {
             server_id: "SSH-2.0-xanthous".to_owned(),
-            keys: vec![thrussh_keys::key::KeyPair::generate_ed25519()
-                .ok_or_else(|| eyre!("Could not generate ed25519 key"))?],
+            keys: vec![key_pair],
             ..Default::default()
         })
     }
@@ -301,7 +319,7 @@ async fn main() -> Result<()> {
         .install()?;
     metrics::register();
 
-    let config = Arc::new(opts.ssh_server_config()?);
+    let config = Arc::new(opts.ssh_server_config().await?);
     info!(address = %opts.address, "Listening for new SSH connections");
     let listener = TcpListener::bind(&opts.address).await?;