1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Secret-reference resolution for HashiCorp Vault, AWS Secrets Manager, and
//! Azure Key Vault — no vendor SDK for any of the three, matching this
//! crate's existing `storage-s3`/`storage-azure`/`sso` modules.
//!
//! JWT signing keys, DB credentials, and TLS keys are ordinarily just plain
//! `RWS_CONFIG_*`/application env vars. [`resolve`] lets any of those values
//! instead be a *reference* to a secret held in a managed secrets store:
//!
//! | Prefix | Backend | Example |
//! |---|---|---|
//! | `vault://path#field` | HashiCorp Vault (KV v2) | `vault://secret/myapp/db#password` |
//! | `aws-sm://name` or `aws-sm://name#field` | AWS Secrets Manager | `aws-sm://prod/db-password` |
//! | `azkv://vault-name/secret-name` | Azure Key Vault | `azkv://my-kv/db-password` |
//!
//! A value that doesn't start with one of these prefixes is returned
//! unchanged — resolution is purely additive, not a new required step for
//! every existing plain-value config var.
//!
//! # Example
//!
//! ```rust,no_run
//! use rust_web_server::secrets;
//!
//! // VAULT_ADDR / VAULT_TOKEN (Vault), AWS_REGION + credentials (Secrets
//! // Manager), or AZURE_KEY_VAULT_TENANT_ID + co. (Key Vault) must already
//! // be set in the environment for the corresponding prefix to resolve.
//! let db_password = secrets::resolve("vault://secret/myapp/db#password")?;
//! # Ok::<(), secrets::SecretsError>(())
//! ```
//!
//! # Automatic env var resolution
//!
//! [`resolve_env_vars`] scans every currently-set `RWS_`-prefixed environment
//! variable and rewrites in place (via `std::env::set_var`) any whose value
//! matches one of the prefixes above. `Server::setup()` calls this
//! automatically (right after `entry_point::bootstrap()`, so it sees the
//! fully layered config) once this feature is enabled, so **any**
//! `RWS_CONFIG_*` value — `RWS_CONFIG_TLS_KEY_FILE`, a config-driven proxy's
//! `token_env`-referenced variable, anything — can be a secret reference
//! with no code changes, matching the "additive to every existing
//! `RWS_CONFIG_*`/`RWS_*` env var" goal this feature was built for.
//!
//! # Failure mode: fail fast, not fail open
//!
//! A value that *looks* like a secret reference but fails to resolve (wrong
//! token, unreachable backend, missing field, ...) is a startup error, not a
//! silently-ignored one — the alternative would be a server starting up with
//! a JWT signing key or DB password literally equal to the string
//! `"vault://secret/myapp/db#password"`, which is worse than refusing to
//! start at all.
use fmt;
/// Error resolving a secret reference — a network failure, an
/// authentication failure, a missing field, or a malformed reference.
;
const VAULT_PREFIX: &str = "vault://";
const AWS_SM_PREFIX: &str = "aws-sm://";
const AZURE_KV_PREFIX: &str = "azkv://";
/// Resolves `value` if it starts with a recognized secret-reference prefix
/// (`vault://`, `aws-sm://`, `azkv://`); otherwise returns it unchanged.
///
/// See the [module docs](self) for the prefix formats and each backend's
/// required environment variables.
/// Scans every environment variable whose name starts with `RWS_` and
/// rewrites in place (via `std::env::set_var`) any whose *value* matches a
/// recognized secret-reference prefix. Variables that don't match are left
/// untouched — this only ever narrows down to the ones that opted in by
/// using one of the prefixes.
///
/// Called automatically from `entry_point::bootstrap()` when this feature is
/// enabled; exposed publicly so a library user driving their own startup
/// sequence (not calling `bootstrap()`) can call it explicitly instead.