Skip to content

Commit 94016cb

Browse files
committed
wip: rename realm certificates
1 parent 03a047e commit 94016cb

File tree

12 files changed

+175
-120
lines changed

12 files changed

+175
-120
lines changed

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,17 @@ the attack surface is consequently expanded with appropriate probabilities.
4747
With virtual estates, it's not always clear who is in control of what and
4848
exactly how much control they have.
4949

50-
Control is usually divided among multiple parties. For example, control of your
51-
Github repos is shared between you and Github Inc (and probably Microsoft too).
52-
Control of your physical iPhone is shared between you and Apple (with Apple
53-
argubly having the majority). Control of your password manager is hopefully not
54-
shared with anyone.
50+
Control is usually divided among multiple parties. Some examples:
51+
52+
- Control of your Github repos (a digital asset) is shared between you, Github
53+
Inc, and probably Microsoft too.
54+
55+
- Control of your iPhone (a physical asset) is shared between you and Apple.
56+
Arguably, Apple has more control of it than you do.
57+
58+
- Control of your password manager (a digital asset) is hopefully not shared
59+
with anyone. SaaS password managers have some minimal level of control even
60+
with proper encryption.
5561

5662
While it's impossible to know exactly what percentage of control we have over
5763
our virtual estates, it definitely seems to be trending down. People are willing
@@ -99,7 +105,8 @@ server compromise.
99105
## How it works
100106

101107
Sandpolis runs an agent on your devices and allows you to interact with them
102-
from a client application.
108+
from a client application. A server mediates client/agent communication and
109+
stores historical data about instances in the network.
103110

104111
## Layers
105112

sandpolis-realm/README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ simultaneously while agent instances belong to one realm at a time.
77
As an example, you can have _work_ and _home_ realms that are completely
88
isolated (other than running on the same server).
99

10-
### User membership
10+
### Realm membership
1111

1212
Users can become members of a realm with an associated set of permissions.
1313

@@ -17,5 +17,28 @@ All users are members of a special realm called _default_.
1717

1818
### Realm authentication
1919

20-
All connections to a server instance must be authenticated with a realm using
21-
clientAuth certificates.
20+
All connections to a server instance must be authenticated with a realm using a
21+
TLS certificate.
22+
23+
There are four types of certificate:
24+
25+
#### Realm cluster certificate
26+
27+
Each realm has a single "root" cluster certificate that signs new server,
28+
client, and agent certificates.
29+
30+
#### Realm server certificate
31+
32+
This certificate is used by clients and agents to verify the server is part of
33+
the cluster.
34+
35+
#### Realm client certificate
36+
37+
This certificate is used to authenticate with servers as a client instance. The
38+
server verifies the client certificate was issued by the cluster certificate.
39+
40+
#### Realm agent certificate
41+
42+
Also a clientAuth certificate, but is distinct from regular realm client
43+
certificates so that it's impossible to use an agent cert to authenticate as a
44+
client instance.

sandpolis-realm/src/lib.rs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,14 @@ impl RealmLayer {
4444
// Create default realm if it doesn't exist
4545
if database.get(Some("default".parse()?)).await.is_err() {
4646
debug!("Creating default realm");
47-
let db = database.get(None).await?;
48-
let rw = db.rw_transaction()?;
49-
if rw
50-
.get()
51-
.secondary::<RealmData>(RealmDataKey::name, "default".parse::<RealmName>()?)?
52-
.is_none()
53-
{
54-
rw.insert(RealmData::default());
55-
rw.commit()?;
56-
}
47+
let db = database.get(Some("default".parse()?)).await?;
48+
49+
let default_realm = RealmData::default();
5750

5851
#[cfg(feature = "server")]
5952
let ca = realm.insert_document(
6053
"ca",
61-
RealmCaCert::new(
54+
RealmClusterCert::new(
6255
instance_layer.data.value().cluster_id,
6356
realm.data.name.clone(),
6457
)?,
@@ -70,6 +63,12 @@ impl RealmLayer {
7063
ca.data
7164
.server_cert(instance_layer.data.value().instance_id)?,
7265
)?;
66+
67+
{
68+
let rw = db.rw_transaction()?;
69+
rw.insert(default_realm);
70+
rw.commit()?;
71+
}
7372
}
7473

7574
// Load all realm databases
@@ -112,22 +111,27 @@ pub struct RealmData {
112111
#[secondary_key(unique)]
113112
pub name: RealmName,
114113
pub owner: UserName,
114+
115+
pub cluster_cert: Option<RealmClusterCert>,
115116
}
116117

117-
/// The realm's global CA cert. These have a lifetime of 100 years.
118-
#[derive(Serialize, Deserialize, Debug)]
119-
pub struct RealmCaCert {
118+
/// The realm's global CA certificate.
119+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
120+
pub struct RealmClusterCert {
120121
pub name: RealmName,
122+
/// PEM-encoded certificate
121123
pub cert: String,
122-
pub key: String,
124+
/// PEM-encoded key
125+
pub key: Option<String>,
123126
}
124127

125-
/// Each server in the cluster gets its own server certificate. These have a
126-
/// lifetime 1 year.
127-
#[derive(Serialize, Deserialize, Debug)]
128+
/// Each server in the cluster gets its own server certificate.
129+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
128130
pub struct RealmServerCert {
131+
/// PEM-encoded certificate
129132
pub cert: String,
130-
pub key: String,
133+
/// PEM-encoded key
134+
pub key: Option<String>,
131135
}
132136

133137
impl RealmServerCert {
@@ -154,13 +158,13 @@ impl RealmServerCert {
154158
}
155159
}
156160

157-
/// A _client_ certificate (not as in "client" instance) used to authenticate
158-
/// with a server instance.
161+
/// Realm certificate for client instances that can authenticate with a server
162+
/// instance against a particular realm.
159163
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
160164
pub struct RealmClientCert {
161165
pub ca: String,
162166
pub cert: String,
163-
pub key: String,
167+
pub key: Option<String>,
164168
}
165169

166170
impl RealmClientCert {
@@ -190,7 +194,7 @@ impl RealmClientCert {
190194
// Combine cert and key together
191195
let mut bundle = Vec::new();
192196
bundle.extend_from_slice(&self.cert.as_bytes());
193-
bundle.extend_from_slice(&self.key.as_bytes());
197+
bundle.extend_from_slice(&self.key.ok_or_else(|| anyhow!("No key"))?.as_bytes());
194198
Ok(reqwest::Identity::from_pem(&bundle)?)
195199
}
196200

@@ -221,8 +225,17 @@ impl RealmClientCert {
221225
}
222226
}
223227

228+
/// Realm certificate for agent instances that can authenticate with a server
229+
/// instance against a particular realm.
230+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
231+
pub struct RealmAgentCert {
232+
pub ca: String,
233+
pub cert: String,
234+
pub key: String,
235+
}
236+
224237
pub enum RealmPermission {
225-
/// Right to create realms on the server
238+
/// Right to create new realms on the server
226239
Create,
227240
/// Right to view all realms on the server
228241
List,

sandpolis-realm/src/server.rs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
//! Server implementation
2-
3-
use super::RealmCaCert;
41
use super::RealmClientCert;
2+
use super::RealmClusterCert;
53
use super::RealmData;
64
use super::RealmName;
75
use super::RealmServerCert;
86
use anyhow::Result;
7+
use anyhow::anyhow;
98
use anyhow::bail;
109
use axum::{
1110
Extension, extract::Request, middleware::AddExtension, middleware::Next, response::Response,
@@ -36,14 +35,15 @@ use std::io;
3635
use std::sync::Arc;
3736
use tempfile::TempDir;
3837
use tempfile::tempdir;
38+
use time::Duration;
3939
use time::OffsetDateTime;
4040
use tokio::io::{AsyncRead, AsyncWrite};
4141
use tokio_rustls::server::TlsStream;
4242
use tower::Layer;
4343
use tracing::debug;
4444
use x509_parser::prelude::{FromDer, X509Certificate};
4545

46-
impl super::RealmCaCert {
46+
impl super::RealmClusterCert {
4747
/// Generate a new realm CA certificate.
4848
pub fn new(cluster_id: ClusterId, name: RealmName) -> Result<Self> {
4949
// Generate key
@@ -53,6 +53,7 @@ impl super::RealmCaCert {
5353
let mut cert_params = CertificateParams::default();
5454
cert_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
5555
cert_params.not_before = OffsetDateTime::now_utc();
56+
cert_params.not_after = OffsetDateTime::now_utc().saturating_add(Duration::days(36780));
5657
cert_params.subject_alt_names = vec![SanType::DnsName(cluster_id.to_string().try_into()?)];
5758

5859
// TODO still needed?
@@ -68,7 +69,7 @@ impl super::RealmCaCert {
6869
Ok(Self {
6970
name,
7071
cert: cert.pem(),
71-
key: keypair.serialize_pem(),
72+
key: Some(keypair.serialize_pem()),
7273
})
7374
}
7475

@@ -78,10 +79,12 @@ impl super::RealmCaCert {
7879
Ok(CertificateParams::from_ca_cert_der(
7980
&pem::parse(&self.cert)?.into_contents().try_into()?,
8081
)?
81-
.self_signed(&KeyPair::from_pem(&self.key)?)?)
82+
.self_signed(&KeyPair::from_pem(
83+
&self.key.ok_or_else(|| anyhow!("No key"))?,
84+
)?)?)
8285
}
8386

84-
/// Generate a new _clientAuth_ certificate signed by the realm's CA.
87+
/// Generate a new realm certificate for client instances.
8588
pub fn client_cert(&self) -> Result<RealmClientCert> {
8689
// Generate key
8790
let keypair = KeyPair::generate()?;
@@ -92,10 +95,10 @@ impl super::RealmCaCert {
9295
.extended_key_usages
9396
.push(ExtendedKeyUsagePurpose::ClientAuth);
9497
cert_params.not_before = OffsetDateTime::now_utc();
98+
cert_params.not_after = OffsetDateTime::now_utc().saturating_add(Duration::days(365));
9599
cert_params
96100
.distinguished_name
97101
.push(DnType::CommonName, &*self.name);
98-
// TODO not_after of 1 month
99102

100103
// Generate the certificate signed by the CA
101104
let cert = cert_params.signed_by(&keypair, &self.ca()?, &KeyPair::from_pem(&self.key)?)?;
@@ -104,11 +107,11 @@ impl super::RealmCaCert {
104107
Ok(RealmClientCert {
105108
ca: self.ca()?.pem(),
106109
cert: cert.pem(),
107-
key: keypair.serialize_pem(),
110+
key: Some(keypair.serialize_pem()),
108111
})
109112
}
110113

111-
/// Generate a new _serverAuth_ certificate signed by the realm's CA.
114+
/// Generate a new realm certificate for server instances.
112115
pub fn server_cert(&self, server_id: InstanceId) -> Result<RealmServerCert> {
113116
if !server_id.is_type(InstanceType::Server) {
114117
bail!("A server ID is required");
@@ -123,18 +126,22 @@ impl super::RealmCaCert {
123126
.extended_key_usages
124127
.push(ExtendedKeyUsagePurpose::ServerAuth);
125128
cert_params.not_before = OffsetDateTime::now_utc();
129+
cert_params.not_after = OffsetDateTime::now_utc().saturating_add(Duration::days(365));
126130
cert_params.subject_alt_names = vec![SanType::DnsName(
127131
format!("{server_id}.{}", self.name).try_into()?,
128132
)];
129-
// TODO not_after of 1 year
130133

131134
// Generate the certificate signed by the CA
132-
let cert = cert_params.signed_by(&keypair, &self.ca()?, &KeyPair::from_pem(&self.key)?)?;
135+
let cert = cert_params.signed_by(
136+
&keypair,
137+
&self.ca()?,
138+
&KeyPair::from_pem(&self.key.ok_or_else(|| anyhow!("No key"))?)?,
139+
)?;
133140

134141
debug!(cert = ?cert.params(), "Generated new realm server certificate");
135142
Ok(RealmServerCert {
136143
cert: cert.pem(),
137-
key: keypair.serialize_pem(),
144+
key: Some(keypair.serialize_pem()),
138145
})
139146
}
140147
}
@@ -157,7 +164,7 @@ mod test_realm_ca {
157164

158165
#[test]
159166
fn test_generate_and_authenticate() -> Result<()> {
160-
let ca = RealmCaCert::new(ClusterId::default(), "default".parse()?)?;
167+
let ca = RealmClusterCert::new(ClusterId::default(), "default".parse()?)?;
161168
let client = ca.client_cert()?;
162169
let server = ca.server_cert(InstanceId::new_server())?;
163170

@@ -207,38 +214,53 @@ pub struct TlsData {
207214
peer_certificates: Option<Vec<CertificateDer<'static>>>,
208215
}
209216

217+
/// Accepts TLS connections with realm certificates.
210218
#[derive(Debug, Clone)]
211219
pub struct RealmAcceptor(RustlsAcceptor);
212220

213221
impl RealmAcceptor {
214-
pub fn new(realms: Collection<RealmData>) -> Result<Self> {
222+
pub fn new(realms: Vec<RealmData>) -> Result<Self> {
215223
let mut roots = RootCertStore::empty();
216224
let mut sni_resolver = ResolvesServerCertUsingSni::new();
217225

218226
let config = ServerConfig::builder();
219227

220-
for realm in realms.documents() {
221-
let realm = realm?;
222-
let ca: Document<RealmCaCert> = realm.get_document("ca")?.unwrap();
223-
let server: Document<RealmServerCert> = realm.get_document("server")?.unwrap();
224-
225-
roots.add(pem::parse(&ca.data.cert)?.into_contents().try_into()?)?;
228+
for realm in realms {
229+
// Add cluster cert as a CA cert to the root store
230+
{
231+
let cluster_cert: &RealmClusterCert = realm
232+
.cluster_cert
233+
.as_ref()
234+
.ok_or_else(|| anyhow!("No cluster cert")?);
226235

227-
let private_key = config
228-
.crypto_provider()
229-
.key_provider
230-
.load_private_key(PrivateKeyDer::from_pem_slice(&server.data.key.as_bytes())?)?;
231-
232-
sni_resolver.add(
233-
&server.data.subject_name()?,
234-
rustls::sign::CertifiedKey::new(
235-
vec![pem::parse(&server.data.cert)?.into_contents().try_into()?],
236-
private_key,
237-
),
238-
)?;
236+
roots.add(pem::parse(&cluster_cert.cert)?.into_contents().try_into()?)?;
237+
}
239238

240-
// TODO
241-
break;
239+
// Add server cert to the SNI resolver
240+
{
241+
let server_cert: Document<RealmServerCert> = realm.get_document("server")?.unwrap();
242+
let private_key = config.crypto_provider().key_provider.load_private_key(
243+
PrivateKeyDer::from_pem_slice(
244+
&server_cert
245+
.data
246+
.key
247+
.ok_or_else(|| anyhow!("No server key")?)
248+
.as_bytes(),
249+
)?,
250+
)?;
251+
252+
sni_resolver.add(
253+
&server_cert.data.subject_name()?,
254+
rustls::sign::CertifiedKey::new(
255+
vec![
256+
pem::parse(&server_cert.data.cert)?
257+
.into_contents()
258+
.try_into()?,
259+
],
260+
private_key,
261+
),
262+
)?;
263+
}
242264
}
243265

244266
Ok(Self(RustlsAcceptor::new(RustlsConfig::from_config(

0 commit comments

Comments
 (0)