providers: implement SOPS provider#58
Conversation
|
Could we for starters use sops cli? |
|
@domenkozar to clarify: Are you saying as part of an MVP to utilize the SOPS CLI and later explore this direct integration with the SOPS code, or are you saying this is a direction you don't want to go down at all? Using the CLI means requiring having that separately installed, but I suppose that's the standard approach for the integrations supported by secretspec. |
|
I'd also be fine using sops-ffi if it was an externally maintained crate |
Ah, but I don't want to maintain it either 😅. When I get around to it I'll refactor this to assume the sops CLI is available in the environment. |
|
@domenkozar Just listened to the Devenv 2.0 Full Time Nix episode and heard you mentioning the FFI-based approach for the SOPS integration 😅. |
I think it's the right way long term, but I really want to maintain it separately. Maybe we create a repo on cachix and then transfer it over? |
Okay, I've parked the current implementation at https://github.com/euphemism/secretspec/tree/sops-integration-via-ffi. I will rework this MVP to call out to the SOPS CLI. |
adfcf2d to
537b654
Compare
|
Okay, back to square one (ish). Invokes the SOPS CLI. Still need to do a heavy review and refactor pass on all of this generated code, and flesh out the test cases. |
e2929ba to
6732d6a
Compare
|
Initial review and refactor done. Getting closer, not quite yet ready to transition out of draft. Some further refactoring and tests writing remains. |
eb729f8 to
67a4708
Compare
26cfccd to
81c1e2e
Compare
2ada7e8 to
4577097
Compare
| 3. `docs/src/content/docs/concepts/providers.md` - Add a row to the "Available Providers" table | ||
| 4. `docs/src/content/docs/reference/providers.md` - Add a provider section **and** a row in the "Security Considerations" table | ||
| 5. `docs/src/pages/index.astro` - Add to the `providerMetadata` array (top of file) **and** to the `secretspec config init` mini-terminal in the hero | ||
| 5. `docs/src/pages/index.astro` - Add to the `providerMetadata` array (top of file). |
There was a problem hiding this comment.
This did not appear to be a thing?
4577097 to
59573dc
Compare
|
@domenkozar This is generally ready for review. |
59573dc to
e9092d4
Compare
| | AWSSM | ✅ AWS KMS | Cloud (AWS) | ✅ Yes | | ||
| | Vault/OpenBao | ✅ Vault encryption | Vault/OpenBao server | ✅ Yes | | ||
| | BWS | ✅ End-to-end | Cloud (Bitwarden) | ✅ Yes | | ||
| | SOPS | ✅ Assorted | Local Filesystem | ✅ Yes | |
There was a problem hiding this comment.
I added an entry to the table here, but apart from the table this page seems vestigial? It is duplication of content available on the individual providers' pages.
028edfb to
fe4324e
Compare
|
The testing workflows are failing for a few reasons. The non-Windows ones are failing here: Implying Node modules are not being installed, so in attempt to address this I set The Windows run fails with: This makes sense, |
fe4324e to
83a85c8
Compare
|
Please emit only the file location plus non-secret attribution and drop the credential params. The values still reach (Not caught today because |
a990f62 to
de7c23a
Compare
Oops. Added a
I believe this is the sum total of sensitive fields, but you should probably double check this. |
de7c23a to
13aee20
Compare
13aee20 to
70c9e0c
Compare
Addressed this. I think. |
domenkozar
left a comment
There was a problem hiding this comment.
Thanks for the contribution! I did a deep review pass on this, including running sops 3.12/3.13 against the new code paths. The yaml/json single-file and directory happy paths work well. There are six blocking issues posted inline: dotenv format is currently broken on both read and write, format inference can panic on common file names, binary mode has no workable semantics, and the Windows CI step cannot resolve its action ref. Happy to discuss any of them.
🤖 Generated with Claude Code
|
|
||
| let path_str = file_path.to_string_lossy().to_string(); | ||
|
|
||
| let decrypted = match self.execute_sops_command(&["-d", &path_str]) { |
There was a problem hiding this comment.
sops picks its store from the file extension, while the provider parses according to the (possibly overridden) ?format= parameter. Neither get nor set passes --input-type/--output-type, so when the two disagree the provider breaks in both directions:
- With the dotenv directory layout used by the docs and test fixtures,
sops://dir/{project}/.env.{profile}.enc?format=dotenv, sops treats.encas its binary store:sops setexits 0 but the key is dropped (verified with sops 3.13.1, decrypt only re-emits thedatakey), soset()reports success while the secret is never stored. - The registered example
sops://secrets.enc.ini?format=json&kms_arn=...makessops -demit INI text, whichserde_json::from_slicethen fails to parse, so everyget()returns an error.
sops decrypt and sops set both accept --input-type/--output-type (json, yaml, dotenv, binary). Passing --input-type whenever ?format= overrides the extension, plus --output-type json on decrypt so there is a single parse path, would fix both cases (and would allow dropping the saphyr and rust-ini parsing arms entirely). One caveat: there is no --input-type ini, so an ini format override on a non-ini extension can't be supported and should be rejected at config parse time.
| steps: | ||
| - uses: actions/checkout@v5 | ||
| - name: Download latest SOPS release | ||
| uses: robinraju/release-downloader@28fc21f50d76778e7023361aa1f863e717d3d56f |
There was a problem hiding this comment.
Two issues with this step:
28fc21f50d76778e7023361aa1f863e7is a truncated 32-character SHA (the full commit is28fc21f50d76778e7023361aa1f863e717d3d56f, tag v1.13). GitHub Actions removed support for abbreviated SHA refs in February 2021 (https://github.blog/changelog/2021-01-21-github-actions-short-sha-deprecation/), so this job fails at workflow resolution and Windows CI goes red for every PR.- Even with the ref fixed,
sops.exelands in$GITHUB_WORKSPACE(release-downloader defaultsout-file-pathto.) and is never added toPATH. cargo runs test binaries with their working directory set to the package root (secretspec/), and since Rust 1.58Command::newon Windows no longer searches the current directory anyway, soCommand::new("sops")hits NotFound and every sops test fails with "The 'sops' CLI is not installed".
Suggest pinning the full SHA, downloading to a fixed directory that gets appended to $GITHUB_PATH, and pinning a specific sops version rather than latest: true (Linux/macOS already get a pinned sops via devenv).
| } else { | ||
| match target_path.extension().and_then(|e| e.to_str()) { | ||
| Some(ext) => { | ||
| SopsFormat::from_str(ext).expect("SOPS provider failed to infer format") |
There was a problem hiding this comment.
This .expect() turns a config mistake into a process abort: sops://secrets.enc without ?format= (a very common sops file naming) panics the CLI with "SOPS provider failed to infer format" inside a TryFrom that is expected to return SecretSpecError. The same pattern exists in infer_format_from_pattern (line 144), which is hit by templated paths like sops://dir/{project}/{profile}.enc, and in detect_format in mod.rs (expect plus unwrap).
These should return an error naming the unrecognized extension and the supported formats instead of panicking.
| // Need to always filter out the fully qualified project.profile.key lookup path, and if the | ||
| // secrets live in multiple files in a directory tree, then also drop the profile.key lookup path, | ||
| // as the files themselves will only contain top-level entries. | ||
| if !lookup_paths.is_empty() && SopsFormat::Ini == format { |
There was a problem hiding this comment.
This flattening only runs for SopsFormat::Ini, so in single-file Env mode set() always passes a nested index to sops:
sops set secrets.env '["myapp"]["production"]["API_KEY"]' '"v"'
The dotenv store is flat, so sops exits 4 with "Could not marshal tree: cannot use complex value in dotenv file" (verified on 3.12.1 and 3.13.1). The default profile's two-level path ["default"]["API_KEY"] fails the same way, so single-file env storage is write-broken for every profile.
Env needs the same flattening as Ini here (or single-file env mode should be rejected up front). Note the flat write also has to line up with the Env read path, which currently ignores the key entirely (see the comment on parse_decrypted_content).
70c9e0c to
a7fc74e
Compare


Hey all,
This implements a SOPS-backed provider, supporting the full range of SOPS capabilities (I think [I am unable to realistically test this]).
This is some LLM output that I am in the process of cleaning up, but I wanted to get the draft in front of you for some initial feedback. Instead of using a re-implementation of SOPS à la rops, this uses Rust's FFI support to interop with the Go library through a C FFI. I feel more comfortable with this from a security perspective, and it allows for utilizing the full feature set/API surface of SOPS.Closes #5