Skip to content

Commit

Permalink
Emit plain file when key is empty
Browse files Browse the repository at this point in the history
Co-Authored-By: Slaier <slaier@users.noreply.github.com>
  • Loading branch information
2 people authored and Mic92 committed Jul 25, 2024
1 parent aff2f88 commit 316504a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 11 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,44 @@ This is how it can be included in your `configuration.nix`:
}
```

## Emit plain file for yaml and json formats

By default, sops-nix extracts a single key from yaml and json files. If you
need the plain file instead of extracting a specific key from the input document,
you can set `key` to an empty string.

For example, the input document `my-config.yaml` likes this:

```yaml
my-secret1: ENC[AES256_GCM,data:tkyQPQODC3g=,iv:yHliT2FJ74EtnLIeeQtGbOoqVZnF0q5HiXYMJxYx6HE=,tag:EW5LV4kG4lcENaN2HIFiow==,type:str]
my-secret2: ENC[AES256_GCM,data:tkyQPQODC3g=,iv:yHliT2FJ74EtnLIeeQtGbOoqVZnF0q5HiXYMJxYx6HE=,tag:EW5LV4kG4lcENaN2HIFiow==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
...
```

This is how it can be included in your NixOS module:

```nix
{
sops.secrets.my-config = {
format = "yaml";
sopsFile = ./my-config.yaml;
key = "";
};
}
```

Then, it will be mounted as `/run/secrets/my-config`:

```yaml
my-secret1: hello
my-secret2: hello
```

## Use with home manager

sops-nix also provides a home-manager module.
Expand Down
13 changes: 12 additions & 1 deletion modules/home-manager/sops.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ let

key = lib.mkOption {
type = lib.types.str;
default = name;
default = if cfg.defaultSopsKey != null then cfg.defaultSopsKey else name;
description = ''
Key used to lookup in the sops file.
No tested data structures are supported right now.
This option is ignored if format is binary.
"" means whole file.
'';
};

Expand Down Expand Up @@ -132,6 +133,16 @@ in {
'';
};

defaultSopsKey = mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Default key used to lookup in all secrets.
This option is ignored if format is binary.
"" means whole file.
'';
};

validateSopsFiles = lib.mkOption {
type = lib.types.bool;
default = true;
Expand Down
13 changes: 12 additions & 1 deletion modules/sops/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ let
};
key = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
default = if cfg.defaultSopsKey != null then cfg.defaultSopsKey else config._module.args.name;
description = ''
Key used to lookup in the sops file.
No tested data structures are supported right now.
This option is ignored if format is binary.
"" means whole file.
'';
};
path = lib.mkOption {
Expand Down Expand Up @@ -161,6 +162,16 @@ in {
'';
};

defaultSopsKey = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Default key used to lookup in all secrets.
This option is ignored if format is binary.
"" means whole file.
'';
};

validateSopsFiles = lib.mkOption {
type = lib.types.bool;
default = true;
Expand Down
30 changes: 21 additions & 9 deletions pkgs/sops-install-secrets/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,20 @@ func decryptSecret(s *secret, sourceFiles map[string]plainData) error {
case Binary, Dotenv, Ini:
sourceFile.binary = plain
case Yaml:
if err := yaml.Unmarshal(plain, &sourceFile.data); err != nil {
return fmt.Errorf("cannot parse yaml of '%s': %w", s.SopsFile, err)
if s.Key == "" {
sourceFile.binary = plain
} else {
if err := yaml.Unmarshal(plain, &sourceFile.data); err != nil {
return fmt.Errorf("Cannot parse yaml of '%s': %w", s.SopsFile, err)
}
}
case JSON:
if err := json.Unmarshal(plain, &sourceFile.data); err != nil {
return fmt.Errorf("cannot parse json of '%s': %w", s.SopsFile, err)
if s.Key == "" {
sourceFile.binary = plain
} else {
if err := json.Unmarshal(plain, &sourceFile.data); err != nil {
return fmt.Errorf("Cannot parse json of '%s': %w", s.SopsFile, err)
}
}
default:
return fmt.Errorf("secret of type %s in %s is not supported", s.Format, s.SopsFile)
Expand All @@ -287,11 +295,15 @@ func decryptSecret(s *secret, sourceFiles map[string]plainData) error {
case Binary, Dotenv, Ini:
s.value = sourceFile.binary
case Yaml, JSON:
strVal, err := recurseSecretKey(sourceFile.data, s.Key)
if err != nil {
return fmt.Errorf("secret %s in %s is not valid: %w", s.Name, s.SopsFile, err)
if s.Key == "" {
s.value = sourceFile.binary
} else {
strVal, err := recurseSecretKey(sourceFile.data, s.Key)
if err != nil {
return fmt.Errorf("secret %s in %s is not valid: %w", s.Name, s.SopsFile, err)
}
s.value = []byte(strVal)
}
s.value = []byte(strVal)
}
sourceFiles[s.SopsFile] = sourceFile
return nil
Expand Down Expand Up @@ -454,7 +466,7 @@ func (app *appContext) validateSopsFile(s *secret, file *secretFile) error {
s.Name, s.SopsFile, s.Format,
file.firstSecret.Format, file.firstSecret.Name)
}
if app.checkMode != Manifest && (s.Format != Binary && s.Format != Dotenv && s.Format != Ini) {
if app.checkMode != Manifest && !(s.Format == Binary || s.Format == Dotenv || s.Format == Ini) && s.Key != "" {
_, err := recurseSecretKey(file.keys, s.Key)
if err != nil {
return fmt.Errorf("secret %s in %s is not valid: %w", s.Name, s.SopsFile, err)
Expand Down

0 comments on commit 316504a

Please sign in to comment.