-
Notifications
You must be signed in to change notification settings - Fork 251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(config): add vault provider #798
Conversation
Hello maintainers, I would like to add a new |
@FrankYang0529 Thanks for this - great to see a secrets-friendly provider! Re your question, config providers are currently wired up in the spin/crates/trigger/src/lib.rs Line 104 in 13f7916
I believe you'd need to provide a way for an operator to add non-default config providers and specify their settings, e.g. a command line option. It might be worth briefly writing up a proposal, either here or on the original issue, before diving into the code for that. The reason I suggest that is that we're likely to have other non-default config providers in future, and it's worth planning the Vault UI with one eye on scaling it to those as yet unknown providers. (For example, I'd be wary of a Vault-specific command line option a la |
We have discussed in the past having some sort of "environment" or "profile" configuration outside of the application manifest for this sort of thing. This would also be useful for splitting "dev" vs "prod" config a la #389. |
For issue #389 and #661, my proposal is as following:
|
becd76c
to
b657297
Compare
Hi @itowlson, I updated the PR and added For wasmtime config like wasm_backtrace_details in #389, I will add in a follow-up PR. Test step
|
crates/config/src/provider/vault.rs
Outdated
pub fn new(url: impl AsRef<str>, token: impl AsRef<str>) -> Self { | ||
Self { | ||
url: url.as_ref().to_string(), | ||
token: token.as_ref().to_string(), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
pub fn new(url: impl AsRef<str>, token: impl AsRef<str>) -> Self { | |
Self { | |
url: url.as_ref().to_string(), | |
token: token.as_ref().to_string(), | |
} | |
pub fn new(url: impl Into<String>, token: impl Into<String>) -> Self { | |
Self { | |
url: url.into(), | |
token: token.into(), | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated it. Thank you.
crates/config/src/provider/vault.rs
Outdated
#[derive(Debug, Deserialize, Serialize)] | ||
struct Secret { | ||
value: String, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make sense to either not derive Debug
here or - if Debug
is required by something else - write a custom impl that doesn't actually write out value
. It is very easy to accidentally log secrets this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is also https://docs.rs/secrecy/latest/secrecy/ for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure how to use secrecy package, so I just removed Debug
here. Thanks!
crates/config/src/provider/vault.rs
Outdated
let keys = key.0.split('_').collect::<Vec<_>>(); | ||
if keys.len() == 1 { | ||
return Err(anyhow!("vault key must contain the mount path")); | ||
} | ||
let mount = keys[0]; | ||
let path = keys[1..].join("/"); | ||
let secret: Secret = kv2::read(&client, mount, &path).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like an awkward way to handle paths, though admittedly the constraints on config keys make this tricky.
Would it make sense for the provider config to include "mount_path" and "prefix" settings to join with here? i.e. something like (untested):
// struct VaultProvider {
mount: String,
prefix: Option<String>,
let keys = key.0.split('_').collect::<Vec<_>>(); | |
if keys.len() == 1 { | |
return Err(anyhow!("vault key must contain the mount path")); | |
} | |
let mount = keys[0]; | |
let path = keys[1..].join("/"); | |
let secret: Secret = kv2::read(&client, mount, &path).await?; | |
let path = match &self.prefix { | |
Some(prefix) => &format!("{prefix}/{key}"), | |
None => key.0, | |
}; | |
let secret: Secret = kv2::read(&client, &self.mount, path).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From vault document, we can enable many secret engines on different paths (mount). I am not sure whether users want to get secrets from different paths in an application. If yes, we probably need to leave flexibility here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure whether users want to get secrets from different paths in an application.
That should be possible with the changes below by having multiple Vault providers. I think treating _
specially here is very confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sense! Thank you. Updated it.
crates/trigger/src/config.rs
Outdated
// Config for config providers and wasmtime config | ||
#[derive(Debug, Default, Deserialize)] | ||
pub struct TriggerExecutorBuilderConfig { | ||
pub vault_config: Option<VaultConfig>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good for the file structure here to be slightly more generic, allowing for (future) provider plugins and multiple providers of the same type. Perhaps something like:
[[config_provider]]
type = "vault"
# url = ...
note that order would matter here; Providers are checked in order
This is pretty straightforward with serde: https://serde.rs/enum-representations.html#internally-tagged
I think it would be something like (untested):
#[derive(Debug, Default, Deserialize)]
pub struct TriggerExecutorBuilderConfig {
#[serde(rename = "config_provider", default)]
pub config_providers: Vec<ConfigProvider>,
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum ConfigProvider {
Vault(VaultConfig),
// Could add later e.g.: Env(EnvConfig),
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! It looks much cleaner now!
7f57e0a
to
7418b3d
Compare
Looking pretty good to me. I'd like some others to review as well since |
Hi @lann, thanks for taking the time to review this. I also add e2e test for the vault config provider. However, there may have some issues to start vault server on Windows. Anyway, I will test it on my repo FrankYang0529#6. Looks forward to your new sharing on KubeCon. 👍🏻 |
e8d336c
to
62fbe7a
Compare
Hmm, not sure why we have following error in CI. I can run
|
I changed to use @lann, is it possible to add larger runners? Default runners only have 14G SSD. |
@vdice if you get the chance could you check this out? |
62fbe7a
to
6344b66
Compare
I moved config test from |
@lann @FrankYang0529 would it help to split out some/all of the tests into separate workflow(s) from the main build, so they run on a separate runner? Or would we continue to run into space issues with the default drive size? Either way, we'd want to track upgrading to larger runners in a separate issue, as it brings billing/cost implications:
|
Yeah, I think we still have space issues, because we still need to install dependencies like bindle, hippo, nomad, etc.
If we want to use larger runners, it's good to separate all tests, because it is calculated by minutes. We can try to minimize the cost. |
This all looks great! My only suggestion is to replace references to "config-" as "runtime-config-" (i.e flags and toml sections) to more precisely reflect the "usage semantics" of these configuration values (i.e. runtime). However, if people like it as is, consider this comment an ignoreable nit. |
Do we want to add an example app that demonstrates use of the vault config provider as well -- for better visibility/awareness of the functionality? Would be great to get some docs around this feature as well, perhaps with the aforementioned example; something along the lines of #798 (comment) (or whatever the latest flow might be). However, since we're now housing Spin documentation in fermyon/developer, this will have to be a follow-up. |
Signed-off-by: Frank Yang <yangpoan@gmail.com>
6344b66
to
926709e
Compare
Yeah, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patience with us through KubeCon. I'm going to give others the rest of today for feedback but LGTM.
Create a draft PR in fermyon/developer#191. Thanks! |
Thanks! |
This is looking GREAT, thank you so much for your contribution! |
resolve #661