diff --git a/core/startos/src/net/acme.rs b/core/startos/src/net/acme.rs index 2d562b0df..857ec3e65 100644 --- a/core/startos/src/net/acme.rs +++ b/core/startos/src/net/acme.rs @@ -26,6 +26,7 @@ use crate::context::{CliContext, RpcContext}; use crate::db::model::Database; use crate::db::model::public::AcmeSettings; use crate::db::{DbAccess, DbAccessByKey, DbAccessMut}; +use crate::net::ssl::should_use_cert; use crate::net::tls::{SingleCertResolver, TlsHandler}; use crate::net::web_server::Accept; use crate::prelude::*; @@ -63,20 +64,27 @@ where .and_then(|p| p.as_idx(JsonKey::new_ref(san_info))) { let cert = cert.de().log_err()?; - return Some( - CertifiedKey::from_der( - cert.fullchain - .into_iter() - .map(|c| Ok(CertificateDer::from(c.to_der()?))) - .collect::>() - .log_err()?, - PrivateKeyDer::from(PrivatePkcs8KeyDer::from( - cert.key.0.private_key_to_pkcs8().log_err()?, - )), - &*self.crypto_provider, - ) - .log_err()?, - ); + if cert + .fullchain + .get(0) + .and_then(|c| should_use_cert(&c.0).log_err()) + .unwrap_or(false) + { + return Some( + CertifiedKey::from_der( + cert.fullchain + .into_iter() + .map(|c| Ok(CertificateDer::from(c.to_der()?))) + .collect::>() + .log_err()?, + PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + cert.key.0.private_key_to_pkcs8().log_err()?, + )), + &*self.crypto_provider, + ) + .log_err()?, + ); + } } if !self.in_progress.send_if_modified(|x| { @@ -307,6 +315,16 @@ where return Ok(None); }; let cert = cert.de()?; + if !cert + .fullchain + .get(0) + .map(|c| should_use_cert(&c.0)) + .transpose() + .map_err(Error::from)? + .unwrap_or(false) + { + return Ok(None); + } Ok(Some(( String::from_utf8( cert.key diff --git a/core/startos/src/net/ssl.rs b/core/startos/src/net/ssl.rs index fef0c2b0f..baa18c2e3 100644 --- a/core/startos/src/net/ssl.rs +++ b/core/startos/src/net/ssl.rs @@ -19,7 +19,7 @@ use openssl::x509::extension::{ AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier, }; -use openssl::x509::{X509, X509Builder, X509NameBuilder}; +use openssl::x509::{X509, X509Builder, X509NameBuilder, X509Ref}; use openssl::*; use patch_db::HasModel; use serde::{Deserialize, Serialize}; @@ -48,6 +48,17 @@ pub fn gen_nistp256() -> Result, ErrorStack> { )?)?) } +pub fn should_use_cert(cert: &X509Ref) -> Result { + Ok(cert + .not_before() + .compare(Asn1Time::days_from_now(0)?.as_ref())? + == Ordering::Less + && cert + .not_after() + .compare(Asn1Time::days_from_now(30)?.as_ref())? + == Ordering::Greater) +} + #[derive(Debug, Deserialize, Serialize, HasModel)] #[model = "Model"] #[serde(rename_all = "camelCase")] @@ -83,30 +94,8 @@ impl Model { .map(|m| m.de()) .transpose()? { - if cert_data - .certs - .ed25519 - .not_before() - .compare(Asn1Time::days_from_now(0)?.as_ref())? - == Ordering::Less - && cert_data - .certs - .ed25519 - .not_after() - .compare(Asn1Time::days_from_now(30)?.as_ref())? - == Ordering::Greater - && cert_data - .certs - .nistp256 - .not_before() - .compare(Asn1Time::days_from_now(0)?.as_ref())? - == Ordering::Less - && cert_data - .certs - .nistp256 - .not_after() - .compare(Asn1Time::days_from_now(30)?.as_ref())? - == Ordering::Greater + if should_use_cert(&cert_data.certs.ed25519)? + && should_use_cert(&cert_data.certs.nistp256)? { return Ok(FullchainCertData { root: self.as_root_cert().de()?.0, diff --git a/core/startos/src/service/effects/subcontainer/sync.rs b/core/startos/src/service/effects/subcontainer/sync.rs index 16ffca3cd..6e4930c40 100644 --- a/core/startos/src/service/effects/subcontainer/sync.rs +++ b/core/startos/src/service/effects/subcontainer/sync.rs @@ -150,31 +150,39 @@ impl ExecParams { cmd.env(k, v); } - if let Some(uid) = user.as_deref().and_then(|u| u.parse::().ok()) { - cmd.uid(uid); - } else if let Some(user) = user { - let passwd = std::fs::read_to_string("/etc/passwd") - .with_ctx(|_| (ErrorKind::Filesystem, "read /etc/passwd")); - if passwd.is_err() && user == "root" { - cmd.uid(0); - cmd.gid(0); + if let Some((uid, gid)) = + if let Some(uid) = user.as_deref().and_then(|u| u.parse::().ok()) { + Some((uid, uid)) + } else if let Some(user) = user { + let passwd = std::fs::read_to_string("/etc/passwd") + .with_ctx(|_| (ErrorKind::Filesystem, "read /etc/passwd")); + Some(if passwd.is_err() && user == "root" { + (0, 0) + } else { + let (uid, gid) = passwd? + .lines() + .find_map(|l| { + let mut split = l.trim().split(":"); + if user != split.next()? { + return None; + } + split.next(); // throw away x + Some((split.next()?.parse().ok()?, split.next()?.parse().ok()?)) + // uid gid + }) + .or_not_found(lazy_format!("{user} in /etc/passwd"))?; + (uid, gid) + }) } else { - let (uid, gid) = passwd? - .lines() - .find_map(|l| { - let mut split = l.trim().split(":"); - if user != split.next()? { - return None; - } - split.next(); // throw away x - Some((split.next()?.parse().ok()?, split.next()?.parse().ok()?)) - // uid gid - }) - .or_not_found(lazy_format!("{user} in /etc/passwd"))?; - cmd.uid(uid); - cmd.gid(gid); + None } - }; + { + std::os::unix::fs::chown("/proc/self/fd/0", Some(uid), Some(gid)).log_err(); + std::os::unix::fs::chown("/proc/self/fd/1", Some(uid), Some(gid)).log_err(); + std::os::unix::fs::chown("/proc/self/fd/2", Some(uid), Some(gid)).log_err(); + cmd.uid(uid); + cmd.gid(gid); + } if let Some(workdir) = workdir { cmd.current_dir(workdir); } else { diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index 701d5655a..9f8621d2a 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -869,6 +869,7 @@ pub async fn attach( pty_size: Option, image_id: ImageId, workdir: Option, + user: Option, root_command: &RootCommand, ) -> Result<(), Error> { use axum::extract::ws::Message; @@ -891,6 +892,10 @@ pub async fn attach( .with_extension("env"), ); + if let Some(user) = user { + cmd.arg("--user").arg(&*user); + } + if let Some(workdir) = workdir { cmd.arg("--workdir").arg(workdir); } @@ -1052,6 +1057,7 @@ pub async fn attach( pty_size, image_id, workdir, + user, &root_command, ) .await @@ -1071,6 +1077,45 @@ pub async fn attach( Ok(guid) } +#[derive(Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct ListSubcontainersParams { + pub id: PackageId, +} + +#[derive(Clone, Debug, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +pub struct SubcontainerInfo { + pub name: InternedString, + pub image_id: ImageId, +} + +pub async fn list_subcontainers( + ctx: RpcContext, + ListSubcontainersParams { id }: ListSubcontainersParams, +) -> Result, Error> { + let service = ctx.services.get(&id).await; + let service_ref = service.as_ref().or_not_found(&id)?; + let container = &service_ref.seed.persistent_container; + + let subcontainers = container.subcontainers.lock().await; + + let result: BTreeMap = subcontainers + .iter() + .map(|(guid, subcontainer)| { + ( + guid.clone(), + SubcontainerInfo { + name: subcontainer.name.clone(), + image_id: subcontainer.image_id.clone(), + }, + ) + }) + .collect(); + + Ok(result) +} + async fn get_passwd_command(etc_passwd_path: PathBuf, user: &str) -> RootCommand { async { let mut file = tokio::fs::File::open(etc_passwd_path).await?;