-
Notifications
You must be signed in to change notification settings - Fork 361
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,135 additions
and
43 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Secrets | ||
|
||
Because env vars in mise.toml can store sensitive information, mise has built-in support for reading | ||
encrypted secrets from files. Currently, this is done with a [sops](https://getsops.com) implementation | ||
however other secret backends could be added in the future. | ||
|
||
Secrets are `.env.(json|yaml|toml)` files with a simple structure, for example: | ||
|
||
```json | ||
{ | ||
"AWS_ACCESS_KEY_ID": "AKIAIOSFODNN7EXAMPLE", | ||
"AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" | ||
} | ||
``` | ||
|
||
Env vars from this can be imported into a mise config with the following: | ||
|
||
```toml | ||
[env] | ||
_.file = ".env.json" | ||
``` | ||
|
||
mise will automatically use a secret backend like sops if the file is encrypted. | ||
|
||
## sops | ||
|
||
mise uses the rust [rops](https://github.com/gibbz00/rops) library to interact with [sops](https://getsops.com) files. | ||
If you encrypt a sops file, mise will automatically decrypt it when reading the file. sops files can | ||
be in json, yaml, or toml format—however if you want to use toml you'll need to use the rops cli instead | ||
of sops. Otherwise, either sops or rops will work fine. | ||
|
||
::: info | ||
Currently age is the only sops encryption method supported. | ||
::: | ||
|
||
In order to encrypt a file with sops, you'll first need to install it (`mise use -g sops`). You'll | ||
also need to install [age](https://github.com/FiloSottile/age) (`mise use -g age`) to generate a keypair for sops to use | ||
if you have not already done so. | ||
|
||
To generate a keypair with age run the following and note the public key that is output to use | ||
in the next command to `sops`: | ||
|
||
```sh | ||
$ age-keygen -o ~/.config/mise/age.txt | ||
Public key: <public key> | ||
``` | ||
|
||
Assuming we have a `.env.json` file like at the top of this doc, we can now encrypt it with sops: | ||
|
||
```sh | ||
sops encrypt -i --age "<public key>" .env.json | ||
``` | ||
|
||
::: tip | ||
The `-i` here overwrites the file with an encrypted version. This encrypted version is safe to commit | ||
into your repo as without the private key (`~/.config/mise/age.txt` in this case) the file is useless. | ||
|
||
You can later decrypt the file with `sops decrypt -i .env.json` or edit it in EDITOR with `sops edit .env.json`. | ||
However, you'll first need to set SOPS_AGE_KEY_FILE to `~/.config/mise/age.txt` to decrypt the file. | ||
::: | ||
|
||
Lastly, we need to add the file to our mise config which can be done with `mise set _.file=.env.json`. | ||
|
||
Now when you run `mise env` you should see the env vars from the file: | ||
|
||
```sh | ||
$ mise env | ||
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE | ||
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY | ||
``` | ||
|
||
### `sops` Settings | ||
|
||
<script setup> | ||
import Settings from '/components/settings.vue'; | ||
</script> | ||
<Settings child="sops" :level="3" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/usr/bin/env bash | ||
|
||
#mise use sops | ||
age="$(age-keygen 2>&1)" | ||
age_pub="$(echo "$age" | grep "# public key:" | awk '{print $4}')" | ||
MISE_SOPS_AGE_KEY="$(echo "$age" | grep "AGE-SECRET-KEY")" | ||
export MISE_SOPS_AGE_KEY | ||
|
||
# json | ||
echo '{ "SECRET": "mysecret" }' >.env.json | ||
rops encrypt -i --age "$age_pub" .env.json | ||
assert "mise set _.file=.env.json" | ||
assert_contains "mise env" "export SECRET=mysecret" | ||
|
||
export MISE_SOPS_AGE_KEY= | ||
mise settings set sops.age_key_file "~/age.txt" | ||
age_pub="$(age-keygen -o ~/age.txt 2>&1 | awk '{print $3}')" | ||
|
||
# yaml | ||
echo 'SECRET: mysecret' >.env.yaml | ||
rops encrypt -i --age "$age_pub" .env.yaml | ||
assert "mise set _.file=.env.yaml" | ||
assert_contains "mise env" "export SECRET=mysecret" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use crate::config::SETTINGS; | ||
use crate::file::replace_path; | ||
use crate::{file, result}; | ||
use eyre::WrapErr; | ||
use rops::cryptography::cipher::AES256GCM; | ||
use rops::cryptography::hasher::SHA512; | ||
use rops::file::state::EncryptedFile; | ||
use rops::file::RopsFile; | ||
use std::env; | ||
|
||
pub fn decrypt<PT, F>(input: &str, parse_template: PT) -> result::Result<String> | ||
where | ||
PT: Fn(String) -> result::Result<String>, | ||
F: rops::file::format::FileFormat, | ||
{ | ||
static ONCE: std::sync::Once = std::sync::Once::new(); | ||
ONCE.call_once(|| { | ||
if let Some(p) = &SETTINGS.sops.age_key_file { | ||
let p = match parse_template(p.to_string_lossy().to_string()) { | ||
Ok(p) => p, | ||
Err(e) => { | ||
warn!("failed to parse sops age key file: {}", e); | ||
return; | ||
} | ||
}; | ||
if let Ok(raw) = file::read_to_string(replace_path(p)) { | ||
let key = raw | ||
.trim() | ||
.lines() | ||
.filter(|l| !l.starts_with('#')) | ||
.collect::<String>(); | ||
env::set_var("ROPS_AGE", key); | ||
} | ||
} | ||
if let Some(age_key) = &SETTINGS.sops.age_key { | ||
if !age_key.is_empty() { | ||
env::set_var("ROPS_AGE", age_key); | ||
} | ||
} | ||
}); | ||
let f = input | ||
.parse::<RopsFile<EncryptedFile<AES256GCM, SHA512>, F>>() | ||
.wrap_err("failed to parse sops file")?; | ||
Ok(f.decrypt::<F>() | ||
.wrap_err("failed to decrypt sops file")? | ||
.to_string()) | ||
} |