mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
add cli to rotate key (#2525)
* rotate key * handle service with no config
This commit is contained in:
@@ -1 +1 @@
|
|||||||
embassy-cli net dhcp update $interface
|
start-cli net dhcp update $interface
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
use crate::Id;
|
use crate::{Id, InvalidId};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
|
||||||
pub struct InterfaceId(Id);
|
pub struct InterfaceId(Id);
|
||||||
|
impl FromStr for InterfaceId {
|
||||||
|
type Err = InvalidId;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(Id::try_from(s.to_owned())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
impl From<Id> for InterfaceId {
|
impl From<Id> for InterfaceId {
|
||||||
fn from(id: Id) -> Self {
|
fn from(id: Id) -> Self {
|
||||||
Self(id)
|
Self(id)
|
||||||
|
|||||||
15
core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json
generated
Normal file
15
core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM tor WHERE package = $1 AND interface = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962"
|
||||||
|
}
|
||||||
12
core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json
generated
Normal file
12
core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE account SET tor_key = NULL, network_key = gen_random_bytes(32)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": []
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1"
|
||||||
|
}
|
||||||
15
core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json
generated
Normal file
15
core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "DELETE FROM network_keys WHERE package = $1 AND interface = $2",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524"
|
||||||
|
}
|
||||||
@@ -1,14 +1,22 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use clap::ArgMatches;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use models::{Id, InterfaceId, PackageId};
|
use models::{Id, InterfaceId, PackageId};
|
||||||
use openssl::pkey::{PKey, Private};
|
use openssl::pkey::{PKey, Private};
|
||||||
use openssl::sha::Sha256;
|
use openssl::sha::Sha256;
|
||||||
use openssl::x509::X509;
|
use openssl::x509::X509;
|
||||||
use p256::elliptic_curve::pkcs8::EncodePrivateKey;
|
use p256::elliptic_curve::pkcs8::EncodePrivateKey;
|
||||||
use sqlx::PgExecutor;
|
use rpc_toolkit::command;
|
||||||
|
use sqlx::{Acquire, PgExecutor};
|
||||||
use ssh_key::private::Ed25519PrivateKey;
|
use ssh_key::private::Ed25519PrivateKey;
|
||||||
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
use torut::onion::{OnionAddressV3, TorSecretKeyV3};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::config::{configure, ConfigureContext};
|
||||||
|
use crate::context::RpcContext;
|
||||||
|
use crate::control::restart;
|
||||||
|
use crate::disk::fsck::RequiresReboot;
|
||||||
use crate::net::ssl::CertPair;
|
use crate::net::ssl::CertPair;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::crypto::ed25519_expand_key;
|
use crate::util::crypto::ed25519_expand_key;
|
||||||
@@ -271,3 +279,107 @@ pub fn test_keygen() {
|
|||||||
key.tor_key();
|
key.tor_key();
|
||||||
key.openssl_key_nistp256();
|
key.openssl_key_nistp256();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_requires_reboot(arg: RequiresReboot, matches: &ArgMatches) {
|
||||||
|
if arg.0 {
|
||||||
|
println!("Server must be restarted for changes to take effect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command(rename = "rotate-key", display(display_requires_reboot))]
|
||||||
|
pub async fn rotate_key(
|
||||||
|
#[context] ctx: RpcContext,
|
||||||
|
#[arg] package: Option<PackageId>,
|
||||||
|
#[arg] interface: Option<InterfaceId>,
|
||||||
|
) -> Result<RequiresReboot, Error> {
|
||||||
|
let mut pgcon = ctx.secret_store.acquire().await?;
|
||||||
|
let mut tx = pgcon.begin().await?;
|
||||||
|
if let Some(package) = package {
|
||||||
|
let Some(interface) = interface else {
|
||||||
|
return Err(Error::new(
|
||||||
|
eyre!("Must specify interface"),
|
||||||
|
ErrorKind::InvalidRequest,
|
||||||
|
));
|
||||||
|
};
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM tor WHERE package = $1 AND interface = $2",
|
||||||
|
&package,
|
||||||
|
&interface,
|
||||||
|
)
|
||||||
|
.execute(&mut *tx)
|
||||||
|
.await?;
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM network_keys WHERE package = $1 AND interface = $2",
|
||||||
|
&package,
|
||||||
|
&interface,
|
||||||
|
)
|
||||||
|
.execute(&mut *tx)
|
||||||
|
.await?;
|
||||||
|
let new_key =
|
||||||
|
Key::for_interface(&mut *tx, Some((package.clone(), interface.clone()))).await?;
|
||||||
|
let needs_config = ctx
|
||||||
|
.db
|
||||||
|
.mutate(|v| {
|
||||||
|
let installed = v
|
||||||
|
.as_package_data_mut()
|
||||||
|
.as_idx_mut(&package)
|
||||||
|
.or_not_found(&package)?
|
||||||
|
.as_installed_mut()
|
||||||
|
.or_not_found("installed")?;
|
||||||
|
let addrs = installed
|
||||||
|
.as_interface_addresses_mut()
|
||||||
|
.as_idx_mut(&interface)
|
||||||
|
.or_not_found(&interface)?;
|
||||||
|
if let Some(lan) = addrs.as_lan_address_mut().transpose_mut() {
|
||||||
|
lan.ser(&new_key.local_address())?;
|
||||||
|
}
|
||||||
|
if let Some(lan) = addrs.as_tor_address_mut().transpose_mut() {
|
||||||
|
lan.ser(&new_key.tor_address().to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if installed
|
||||||
|
.as_manifest()
|
||||||
|
.as_config()
|
||||||
|
.transpose_ref()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
installed
|
||||||
|
.as_status_mut()
|
||||||
|
.as_configured_mut()
|
||||||
|
.replace(&false)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
tx.commit().await?;
|
||||||
|
if needs_config {
|
||||||
|
configure(
|
||||||
|
&ctx,
|
||||||
|
&package,
|
||||||
|
ConfigureContext {
|
||||||
|
breakages: BTreeMap::new(),
|
||||||
|
timeout: None,
|
||||||
|
config: None,
|
||||||
|
overrides: BTreeMap::new(),
|
||||||
|
dry_run: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
restart(ctx, package).await?;
|
||||||
|
}
|
||||||
|
Ok(RequiresReboot(false))
|
||||||
|
} else {
|
||||||
|
sqlx::query!("UPDATE account SET tor_key = NULL, network_key = gen_random_bytes(32)")
|
||||||
|
.execute(&mut *tx)
|
||||||
|
.await?;
|
||||||
|
let new_key = Key::for_interface(&mut *tx, None).await?;
|
||||||
|
let url = format!("https://{}", new_key.tor_address()).parse()?;
|
||||||
|
ctx.db
|
||||||
|
.mutate(|v| v.as_server_info_mut().as_tor_address_mut().ser(&url))
|
||||||
|
.await?;
|
||||||
|
tx.commit().await?;
|
||||||
|
Ok(RequiresReboot(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub mod wifi;
|
|||||||
|
|
||||||
pub const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
|
pub const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl";
|
||||||
|
|
||||||
#[command(subcommands(tor::tor, dhcp::dhcp, ssl::ssl))]
|
#[command(subcommands(tor::tor, dhcp::dhcp, ssl::ssl, keys::rotate_key))]
|
||||||
pub fn net() -> Result<(), Error> {
|
pub fn net() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,6 @@ lazy_static! {
|
|||||||
static ref PROGRESS_REGEX: Regex = Regex::new("PROGRESS=([0-9]+)").unwrap();
|
static ref PROGRESS_REGEX: Regex = Regex::new("PROGRESS=([0-9]+)").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn random_key() {
|
|
||||||
println!("x'{}'", hex::encode(rand::random::<[u8; 32]>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[command(subcommands(list_services, logs, reset))]
|
#[command(subcommands(list_services, logs, reset))]
|
||||||
pub fn tor() -> Result<(), Error> {
|
pub fn tor() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user