|
1 | | -# Rust SPIFFE Library |
| 1 | +# Rust SPIFFE |
2 | 2 |
|
3 | | -This utility library enables interaction with the [SPIFFE Workload API](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Workload_API.md). It allows fetching of X.509 and JWT SVIDs, bundles and supports watch/stream updates. The types in the library are in compliance with [SPIFFE standards](https://github.com/spiffe/spiffe/tree/main/standards). More about SPIFFE can be found at [spiffe.io](https://spiffe.io/). |
| 3 | +A Rust library for interacting with the **SPIFFE Workload API**. |
| 4 | +It provides idiomatic access to SPIFFE identities and trust material, including: |
| 5 | + |
| 6 | +- X.509 SVIDs and bundles |
| 7 | +- JWT SVIDs and bundles |
| 8 | +- Streaming updates (watch semantics) |
| 9 | +- Strongly typed SPIFFE primitives compliant with the SPIFFE standards |
| 10 | + |
| 11 | +For background on SPIFFE, see <https://spiffe.io>. |
| 12 | +For the Workload API specification, see the |
| 13 | +[SPIFFE Workload API standard](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Workload_API.md). |
4 | 14 |
|
5 | 15 | [](https://crates.io/crates/spiffe) |
6 | 16 | [](https://github.com/maxlambrecht/rust-spiffe/actions/workflows/ci.yml) |
7 | 17 | [](https://docs.rs/spiffe) |
8 | | -[](https://github.com/maxlambrecht/rust-spiffe/blob/main/LICENSE) |
| 18 | +[](LICENSE) |
9 | 19 |
|
10 | | -## Getting Started |
| 20 | +--- |
11 | 21 |
|
12 | | -Include `spiffe` in your `Cargo.toml` dependencies to get both the SPIFFE types (`spiffe-types`) and the Workload API |
13 | | -client (`workload-api`) by default: |
| 22 | +## Installation |
| 23 | + |
| 24 | +Add `spiffe` to your `Cargo.toml`: |
14 | 25 |
|
15 | 26 | ```toml |
16 | 27 | [dependencies] |
17 | 28 | spiffe = "0.6.7" |
18 | | -``` |
| 29 | +```` |
19 | 30 |
|
20 | | -## Examples of Usage |
| 31 | +This includes both SPIFFE core types and a Workload API client. |
21 | 32 |
|
22 | | -### Creating a `WorkloadApiClient` |
| 33 | +--- |
23 | 34 |
|
24 | | -Create client using the endpoint socket path: |
| 35 | +## Quick Start |
25 | 36 |
|
26 | | -```rust |
27 | | -let mut client = WorkloadApiClient::new_from_path("unix:/tmp/spire-agent/public/api.sock").await?; |
28 | | -``` |
| 37 | +### Create a Workload API client |
29 | 38 |
|
30 | | -Or by using the `SPIFFE_ENDPOINT_SOCKET` environment variable: |
| 39 | +Using an explicit socket path: |
31 | 40 |
|
32 | 41 | ```rust |
33 | | -let mut client = WorkloadApiClient::default().await?; |
34 | | -``` |
| 42 | +use spiffe::WorkloadApiClient; |
35 | 43 |
|
36 | | -### Fetching X.509 Materials |
| 44 | +let client = WorkloadApiClient::new_from_path( |
| 45 | + "unix:///tmp/spire-agent/public/api.sock", |
| 46 | +).await?; |
| 47 | +``` |
37 | 48 |
|
38 | | -Fetch the default X.509 SVID, a set of X.509 bundles, all X.509 materials, or watch for updates on the X.509 context and bundles. |
| 49 | +Or via the `SPIFFE_ENDPOINT_SOCKET` environment variable: |
39 | 50 |
|
40 | 51 | ```rust |
41 | | -// fetch the default X.509 SVID |
42 | | -let x509_svid: X509Svid = client.fetch_x509_svid().await?; |
| 52 | +use spiffe::WorkloadApiClient; |
43 | 53 |
|
44 | | -// fetch a set of X.509 bundles (X.509 public key authorities) |
45 | | -let x509_bundles: X509BundleSet = client.fetch_x509_bundles().await?; |
| 54 | +let client = WorkloadApiClient::default().await?; |
| 55 | +``` |
| 56 | + |
| 57 | +--- |
46 | 58 |
|
47 | | -// fetch all the X.509 materials (SVIDs and bundles) |
48 | | -let x509_context: X509Context = client.fetch_x509_context().await?; |
| 59 | +## X.509 identities |
49 | 60 |
|
50 | | -// get the X.509 chain of certificates from the SVID |
51 | | -let cert_chain: &Vec<Certificate> = x509_svid.cert_chain(); |
| 61 | +### Fetch X.509 materials directly |
| 62 | + |
| 63 | +```rust |
| 64 | +use spiffe::{TrustDomain, X509Context}; |
52 | 65 |
|
53 | | -// get the private key from the SVID |
54 | | -let private_key: &PrivateKey = x509_svid.private_key(); |
| 66 | +let svid = client.fetch_x509_svid().await?; |
| 67 | +let bundles = client.fetch_x509_bundles().await?; |
| 68 | +let context: X509Context = client.fetch_x509_context().await?; |
55 | 69 |
|
56 | | -// parse a SPIFFE trust domain |
57 | 70 | let trust_domain = TrustDomain::try_from("example.org")?; |
| 71 | +let bundle = bundles.get_bundle(&trust_domain)?; |
| 72 | +``` |
58 | 73 |
|
59 | | -// get the X.509 bundle associated to the trust domain |
60 | | -let x509_bundle: &X509Bundle = x509_bundles.get_bundle(&trust_domain)?; |
61 | | - |
62 | | -// get the X.509 authorities (public keys) in the bundle |
63 | | -let x509_authorities: &Vec<Certificate> = x509_bundle.authorities(); |
64 | | - |
65 | | -// watch for updates on the X.509 context |
66 | | -let mut x509_context_stream = client.stream_x509_contexts().await?; |
67 | | -while let Some(x509_context_update) = x509_context_stream.next().await { |
68 | | - match x509_context_update { |
69 | | - Ok(update) => { |
70 | | - // handle the updated X509Context |
71 | | - } |
72 | | - Err(e) => { |
73 | | - // handle the error |
74 | | - } |
75 | | - } |
76 | | -} |
| 74 | +### Watch for updates |
| 75 | + |
| 76 | +```rust |
| 77 | +let mut stream = client.stream_x509_contexts().await?; |
77 | 78 |
|
78 | | -// watch for updates on the X.509 bundles |
79 | | -let mut x509_bundle_stream = client.stream_x509_bundles().await?; |
80 | | -while let Some(x509_bundle_update) = x509_bundle_stream.next().await { |
81 | | - match x509_bundle_update { |
82 | | - Ok(update) => { |
83 | | - // handle the updated X509 bundle |
84 | | - } |
85 | | - Err(e) => { |
86 | | - // handle the error |
87 | | - } |
88 | | - } |
| 79 | +while let Some(update) = stream.next().await { |
| 80 | + let context = update?; |
| 81 | + // react to updated SVIDs / bundles |
89 | 82 | } |
90 | 83 | ``` |
91 | 84 |
|
92 | | -### Fetching X.509 Materials using `X509Source` |
| 85 | +--- |
93 | 86 |
|
94 | | -A convenient way to fetch X.509 materials is by using the `X509Source`: |
| 87 | +## X.509Source (recommended) |
| 88 | + |
| 89 | +`X509Source` maintains a locally cached, automatically refreshed view of X.509 |
| 90 | +SVIDs and bundles. |
95 | 91 |
|
96 | 92 | ```rust |
97 | 93 | use spiffe::X509Source; |
98 | | -use spiffe::BundleSource; |
99 | | -use spiffe::TrustDomain; |
100 | | -use spiffe::X509Svid; |
101 | | -use spiffe::SvidSource; |
102 | 94 |
|
103 | | -async fn fetch_x509_materials() -> Result<(), Box<dyn std::error::Error>> { |
104 | | - // Create a new X509Source |
105 | | - let x509_source = X509Source::default().await?; |
| 95 | +let source = X509Source::new().await?; |
106 | 96 |
|
107 | | - // Fetch the SVID |
108 | | - let svid = x509_source.get_svid()?.ok_or("No X509Svid found")?; |
| 97 | +// Default SVID |
| 98 | +let svid = source.get_svid()?.expect("no SVID available"); |
109 | 99 |
|
110 | | - // Fetch the bundle for a specific trust domain |
111 | | - let trust_domain = spiffe::TrustDomain::new("example.org"); // Replace with the appropriate trust domain |
112 | | - let bundle = x509_source.get_bundle_for_trust_domain(&trust_domain)?.ok_or("No bundle found for trust domain")?; |
113 | | - |
114 | | - Ok(()) |
115 | | -} |
| 100 | +// Bundle for a trust domain |
| 101 | +let bundle = source |
| 102 | + .get_bundle_for_trust_domain(&"example.org".try_into()?)? |
| 103 | + .expect("no bundle found"); |
116 | 104 | ``` |
117 | 105 |
|
118 | | -### Fetching and Validating JWT Tokens and Bundles |
| 106 | +--- |
119 | 107 |
|
120 | | -Fetch JWT tokens, parse and validate them, fetch JWT bundles, or watch for updates on the JWT bundles. |
| 108 | +## JWT identities |
| 109 | + |
| 110 | +### Fetch and validate JWT SVIDs |
121 | 111 |
|
122 | 112 | ```rust |
123 | | -// parse a SPIFFE ID to ask a token for |
| 113 | +use spiffe::{JwtSvid, SpiffeId}; |
| 114 | + |
124 | 115 | let spiffe_id = SpiffeId::try_from("spiffe://example.org/my-service")?; |
125 | 116 |
|
126 | | -// fetch a jwt token for the provided SPIFFE-ID and with the target audience `service1.com` |
127 | | -let jwt_token = client.fetch_jwt_token(&["audience1", "audience2"], Some(&spiffe_id)).await?; |
| 117 | +let jwt = client |
| 118 | + .fetch_jwt_svid(&["audience1", "audience2"], Some(&spiffe_id)) |
| 119 | + .await?; |
| 120 | +``` |
128 | 121 |
|
129 | | -// fetch the jwt token and parses it as a `JwtSvid` |
130 | | -let jwt_svid = client.fetch_jwt_svid(&["audience1", "audience2"], Some(&spiffe_id)).await?; |
| 122 | +### Fetch JWT bundles |
131 | 123 |
|
132 | | -// fetch a set of jwt bundles (public keys for validating jwt token) |
133 | | -let jwt_bundles = client.fetch_jwt_bundles().await?; |
| 124 | +```rust |
| 125 | +use spiffe::TrustDomain; |
134 | 126 |
|
135 | | -// parse a SPIFFE trust domain |
| 127 | +let bundles = client.fetch_jwt_bundles().await?; |
136 | 128 | let trust_domain = TrustDomain::try_from("example.org")?; |
| 129 | +let bundle = bundles.get_bundle(&trust_domain)?; |
| 130 | +``` |
| 131 | + |
| 132 | +### Watch JWT bundle updates |
| 133 | + |
| 134 | +```rust |
| 135 | +let mut stream = client.stream_jwt_bundles().await?; |
137 | 136 |
|
138 | | -// get the JWT bundle associated to the trust domain |
139 | | -let jwt_bundle: &JwtBundle = jwt_bundles.get_bundle(&trust_domain)?; |
140 | | - |
141 | | -// get the JWT authorities (public keys) in the bundle |
142 | | -let jwt_authority: &JwtAuthority = jwt_bundle.find_jwt_authority("a_key_id")?; |
143 | | - |
144 | | -// parse a `JwtSvid` validating the token signature with a JWT bundle source. |
145 | | -let validated_jwt_svid = JwtSvid::parse_and_validate(&jwt_token, &jwt_bundles_set, &["service1.com"])?; |
146 | | - |
147 | | -// watch for updates on the JWT bundles |
148 | | -let mut jwt_bundle_stream = client.stream_jwt_bundles().await?; |
149 | | -while let Some(jwt_bundle_update) = jwt_bundle_stream.next().await { |
150 | | - match jwt_bundle_update { |
151 | | - Ok(update) => { |
152 | | - // handle the updated JWT bundle |
153 | | - } |
154 | | - Err(e) => { |
155 | | - // handle the error |
156 | | - } |
157 | | - } |
| 137 | +while let Some(update) = stream.next().await { |
| 138 | + let bundles = update?; |
| 139 | + // react to updated JWT authorities |
158 | 140 | } |
159 | 141 | ``` |
160 | 142 |
|
161 | | -For more detailed examples and additional features, refer to the [documentation](https://docs.rs/spiffe). |
| 143 | +--- |
| 144 | + |
| 145 | +## Documentation |
| 146 | + |
| 147 | +API documentation and additional examples are available on [docs.rs](https://docs.rs/spiffe). |
| 148 | + |
| 149 | +--- |
162 | 150 |
|
163 | 151 | ## License |
164 | 152 |
|
165 | | -This library is licensed under the Apache License. See the [LICENSE.md](../LICENSE) file for details. |
| 153 | +Licensed under the Apache License, Version 2.0. |
| 154 | +See [LICENSE](../LICENSE) for details. |
0 commit comments