Skip to content

Commit 3e26788

Browse files
committed
Add basic JWT-SVID API support
Add wrappers around API method to fetch and validate JWT-SVID. Split X509 and JWT into different directories. Signed-off-by: Hugues de Valon <[email protected]>
1 parent d0ba4ac commit 3e26788

15 files changed

+2273
-668
lines changed

spiffe/Cargo.toml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
name = "spiffe"
33
version = "0.1.0"
44
authors = ["Sabree Blackmon <[email protected]>"]
5+
edition = "2018"
56

67
[dependencies]
7-
openssl = { version = "0.10", features = ["vendored"] }
8-
error-chain = "0.12.0"
9-
hyper = "0.10.0"
10-
protobuf = "~2.0"
11-
grpcio = { git = "https://github.com/heavypackets/grpc-rs", version = "0.3", default-features = false, features = ["protobuf-codec"] }
12-
futures = "0.1.24"
13-
lazy_static = "^1.1"
8+
openssl = "0.10.30"
9+
error-chain = "0.12.4"
10+
hyper = "0.13.8"
11+
protobuf = "2.18.0"
12+
# Waiting for https://github.com/tikv/grpc-rs/pull/499 to be merged
13+
grpcio = { git = "https://github.com/hug-dev/grpc-rs", branch = "no-bindgen", default-features = false, features = ["protobuf-codec"] }
14+
futures = "0.3.6"
15+
lazy_static = "1.4.0"
16+
url = "2.1.1"
17+
log = "0.4.11"
1418

1519
[dev-dependencies]
16-
assert_matches = "1.3"
20+
assert_matches = "1.4.0"

spiffe/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ spiffe:
66
cargo build --color always
77

88
protobuf:
9-
protoc --rust_out=src/api/ --grpc_out=src/api/ --plugin=protoc-gen-grpc=`which grpc_rust_plugin` src/api/workload_api.proto
9+
protoc --rust_out=src/workload/ --grpc_out=src/workload/ --plugin=protoc-gen-grpc=`which grpc_rust_plugin` src/workload/workload_api.proto
1010

1111
test:
1212
cargo test --color always

spiffe/src/lib.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
extern crate futures;
2-
extern crate grpcio;
3-
extern crate hyper;
4-
extern crate openssl;
5-
extern crate protobuf;
6-
7-
#[macro_use]
8-
extern crate error_chain;
9-
#[macro_use]
10-
extern crate lazy_static;
11-
121
pub mod svid;
132
pub mod uri;
143
pub mod workload;

spiffe/src/svid/jwt.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use crate::svid::{SVIDKind, SVID};
2+
use crate::uri::URI;
3+
use error_chain::error_chain;
4+
use std::ops::Deref;
5+
use std::str::FromStr;
6+
7+
error_chain! {
8+
errors {
9+
InvalidURI {
10+
description("An error occured during the parsing of the SPIFFE ID")
11+
display("The SPIFFE ID can not be parsed into a valid SPIFFE URI")
12+
}
13+
}
14+
}
15+
16+
impl SVIDKind for Jwt {}
17+
18+
pub struct Jwt {
19+
svid: String,
20+
}
21+
22+
impl Jwt {
23+
pub fn new(svid: String) -> Jwt {
24+
Jwt { svid }
25+
}
26+
27+
pub fn svid(&self) -> &str {
28+
&self.svid
29+
}
30+
}
31+
32+
impl SVID<Jwt> {
33+
pub fn new(svid: String, uri: &str) -> Result<SVID<Jwt>> {
34+
Ok(SVID::<Jwt> {
35+
doc: Jwt { svid },
36+
uri: URI::from_str(uri).chain_err(|| ErrorKind::InvalidURI)?,
37+
})
38+
}
39+
}
40+
41+
impl Deref for SVID<Jwt> {
42+
type Target = Jwt;
43+
44+
fn deref(&self) -> &Jwt {
45+
let SVID::<Jwt> { doc, .. } = self;
46+
&doc
47+
}
48+
}

spiffe/src/svid/mod.rs

Lines changed: 3 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -1,227 +1,12 @@
1-
use std::fmt;
2-
use std::fs;
3-
use std::ops::Deref;
4-
use std::path::Path;
1+
pub mod jwt;
2+
pub mod x509;
53

6-
type OpenSSlX509Cert = ::openssl::x509::X509;
7-
8-
use uri::URI;
9-
10-
error_chain!{
11-
errors {
12-
InvalidFilePath(path: String) {
13-
description("An IO error during the parsing of an SVID")
14-
display("Unable to parse SVID: Invalid file path {}", path)
15-
}
16-
17-
InvalidPEM {
18-
description("An error during the parsing of an SVID PEM")
19-
display("Unable to parse SVID: Not a valid PEM")
20-
}
21-
22-
InvalidDER {
23-
description("An error during the parsing of an SVID DER")
24-
display("Unable to parse SVID: Not a valid DER")
25-
}
26-
27-
InvalidSAN {
28-
description("An error during the validation of SVID SANs")
29-
display("Unable to parse SVID: SANs do not contain a valid SPIFFE URI")
30-
}
31-
32-
MultipleURIFound(first: String, next: String) {
33-
description("An error during the validation of SVID certificate")
34-
display("Multiple valid SPIFFE URIs found in SVID: {} & {}", first, next)
35-
}
36-
}
37-
38-
links {
39-
Uri(::uri::Error, ::uri::ErrorKind);
40-
}
41-
42-
foreign_links {
43-
SSL(::openssl::error::ErrorStack);
44-
Io(::std::io::Error);
45-
}
46-
}
47-
48-
impl<'a> From<&'a Path> for ErrorKind {
49-
fn from(path: &'a Path) -> Self {
50-
ErrorKind::InvalidFilePath(path.to_str().unwrap_or("").to_string())
51-
}
52-
}
4+
use crate::uri::URI;
535

546
pub trait SVIDKind {}
557

56-
pub type Bundle = Vec<u8>;
57-
pub type Key = Vec<u8>;
58-
59-
pub struct X509 {
60-
cert: OpenSSlX509Cert,
61-
key: Option<Key>,
62-
bundle: Option<Bundle>,
63-
}
64-
65-
impl X509 {
66-
pub fn new(cert: OpenSSlX509Cert, key: Option<Key>, bundle: Option<Bundle>) -> X509 {
67-
X509 {
68-
cert,
69-
key: match key {
70-
Some(k) => Some(k.to_vec()),
71-
None => None,
72-
},
73-
bundle: match bundle {
74-
Some(b) => Some(b.to_vec()),
75-
None => None,
76-
},
77-
}
78-
}
79-
80-
pub fn cert(&self) -> &OpenSSlX509Cert {
81-
&self.cert
82-
}
83-
84-
pub fn key(&self) -> Option<&Vec<u8>> {
85-
match self.key {
86-
Some(ref k) => Some(&k),
87-
None => None,
88-
}
89-
}
90-
91-
pub fn bundle(&self) -> Option<&Vec<u8>> {
92-
match self.bundle {
93-
Some(ref b) => Some(&b),
94-
None => None,
95-
}
96-
}
97-
}
98-
99-
impl fmt::Debug for X509 {
100-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101-
let X509 { cert, .. } = self;
102-
write!(
103-
f,
104-
"OpenSSL X509 Certificate: {{ {:?} }}",
105-
cert.to_pem().unwrap_or_else(|_| vec![])
106-
)
107-
}
108-
}
109-
110-
impl SVIDKind for X509 {}
111-
1128
#[derive(Debug)]
1139
pub struct SVID<T: SVIDKind> {
11410
doc: T,
11511
uri: URI,
11612
}
117-
118-
impl SVID<X509> {
119-
pub fn from_pem(pem: &[u8], key: Option<Key>, bundle: Option<Bundle>) -> Result<SVID<X509>> {
120-
let cert = OpenSSlX509Cert::from_pem(pem).chain_err(|| ErrorKind::InvalidPEM)?;
121-
122-
match SVID::<X509>::parse_uri(&cert) {
123-
Ok(uri) => Ok(SVID::<X509> {
124-
doc: X509::new(cert, key, bundle),
125-
uri,
126-
}),
127-
Err(e) => Err(e.chain_err(|| ErrorKind::InvalidSAN)),
128-
}
129-
}
130-
131-
pub fn from_path(path: &Path, key: Option<&Path>, bundle: Option<&Path>) -> Result<SVID<X509>> {
132-
let contents = fs::read(path).chain_err(|| path)?;
133-
let cert =
134-
OpenSSlX509Cert::from_pem(contents.as_slice()).chain_err(|| ErrorKind::InvalidPEM)?;
135-
136-
let key_contents = match key {
137-
Some(path) => Some(fs::read(path).chain_err(|| path)?.to_vec()),
138-
None => None,
139-
};
140-
let bundle_contents = match bundle {
141-
Some(path) => Some(fs::read(path).chain_err(|| path)?.to_vec()),
142-
None => None,
143-
};
144-
145-
match SVID::<X509>::parse_uri(&cert) {
146-
Ok(uri) => Ok(SVID::<X509> {
147-
doc: X509::new(cert, key_contents, bundle_contents),
148-
uri,
149-
}),
150-
Err(e) => Err(e.chain_err(|| ErrorKind::InvalidPEM)),
151-
}
152-
}
153-
154-
pub fn from_der(der: &[u8], key: Option<Key>, bundle: Option<Bundle>) -> Result<SVID<X509>> {
155-
let cert = OpenSSlX509Cert::from_der(der).chain_err(|| ErrorKind::InvalidDER)?;
156-
157-
match SVID::<X509>::parse_uri(&cert) {
158-
Ok(uri) => Ok(SVID::<X509> {
159-
doc: X509::new(cert, key, bundle),
160-
uri,
161-
}),
162-
Err(e) => Err(e.chain_err(|| ErrorKind::InvalidSAN)),
163-
}
164-
}
165-
166-
pub fn from_x509(
167-
cert: OpenSSlX509Cert,
168-
key: Option<Key>,
169-
bundle: Option<Bundle>,
170-
) -> Result<SVID<X509>> {
171-
match SVID::<X509>::parse_uri(&cert) {
172-
Ok(uri) => Ok(SVID::<X509> {
173-
doc: X509::new(cert, key, bundle),
174-
uri,
175-
}),
176-
Err(e) => Err(e.chain_err(|| ErrorKind::InvalidPEM)),
177-
}
178-
}
179-
180-
pub fn uri(&self) -> &URI {
181-
let SVID::<X509> { uri, .. } = self;
182-
&uri
183-
}
184-
185-
pub fn x509(&self) -> &X509 {
186-
let SVID::<X509> { doc, .. } = self;
187-
&doc
188-
}
189-
190-
pub fn match_spiffe_uri(&self, uri: &str) -> Result<bool> {
191-
Ok(self.uri().to_string().eq_ignore_ascii_case(uri))
192-
}
193-
194-
fn parse_uri(cert: &OpenSSlX509Cert) -> Result<URI> {
195-
let sans = match cert.subject_alt_names() {
196-
Some(val) => val,
197-
None => Err(ErrorKind::InvalidSAN)?,
198-
};
199-
200-
let mut validated_uri: Option<URI> = None;
201-
// Only allows one valid SPIFFE uri in SAN field per SPIFFE specification - returns error if multiple found
202-
for san_entry in sans {
203-
if let Some(uri) = san_entry.uri() {
204-
if let Ok(spiffe_uri) = uri.parse::<URI>() {
205-
if validated_uri.is_some() {
206-
Err(ErrorKind::MultipleURIFound(
207-
validated_uri.unwrap().to_string(),
208-
uri.to_string(),
209-
))?;
210-
}
211-
validated_uri = Some(spiffe_uri);
212-
}
213-
}
214-
}
215-
216-
validated_uri.ok_or_else(|| Error::from(ErrorKind::InvalidSAN))
217-
}
218-
}
219-
220-
impl Deref for SVID<X509> {
221-
type Target = X509;
222-
223-
fn deref(&self) -> &X509 {
224-
let SVID::<X509> { doc, .. } = self;
225-
&doc
226-
}
227-
}

0 commit comments

Comments
 (0)