sops icon indicating copy to clipboard operation
sops copied to clipboard

Breaking multiline formating in yaml as soon as a line end with a space

Open n0rad opened this issue 2 years ago • 10 comments

:wave:

As soon as a line in a multiline value end with a space, sops breaks the multiline formatting and convert to single line with \n

test: |
  line one
  line two with space at the end 
  line three
$ sops --version
sops 3.7.3 (latest)
$ sops -e test.yaml > test.enc.yaml
$ sops -d test.enc.yaml
test: "line one\nline two with space at the end \nline three\n"

n0rad avatar Mar 05 '23 00:03 n0rad

The content of the YAML is identical, it's only the YAML formatting that changes. If you load both versions with a YAML parser, you obtain the same result. That's intentional, sops never promised to keep the formatting of YAML, only the content.

felixfontein avatar Mar 05 '23 11:03 felixfontein

Sure, I know that it's the same content, but if I do:

test: |
  line one
  line two **without** space at the end
  line three
$ sops -e test.yaml > test.enc.yaml
$ sops -d test.enc.yaml
test: |
    line one
    line two **without** space at the end
    line three

As anyone would expected.

When using sops to store kubernetes secret configuration file encrypted in git for example, coming back to the configuration file and having it one lined definitely break the user experience.

n0rad avatar Mar 05 '23 11:03 n0rad

definitely break the user experience.

I strongly disagree on that. The aim is to emit an equivalent YAML file containing the same information (including comments). That's what sops does, and what happens in your case as well.

If you want to retain formatting of the YAML file, you probably have to look for another tool. Alternatively let sops encrypt the YAML file as binary data, then you get the exact same content back.

felixfontein avatar Mar 05 '23 13:03 felixfontein

So let me explain the full workflow with the real use case, maybe I'm doing it wrong:

I'm using sops with age to commit kubernetes secrets to git and let flux applying it. I setup a .sops.yaml file in a parent folder with the public key and configuration to encrypt data part of secrets. I'm using vscode-sops VScode plugin to call sops as soon as an encrypted file is open, to edit and re-encrypting file when closing it.

Using this workflow all I have to do to edit a secret in VScode is to open, edit, close, commit.

I hit the issue while trying to encrypt wireguard configuration file.

apiVersion: v1
stringData:
    wg0.conf: |
        [Interface]
        Address = 10.43.0.1/24
        ListenPort = 6530
        PrivateKey = EM2K6hssd2G3snCZqk2LsAe/gzvhtwerMTbKCff7iVNvvv0tWI=

        [Peer]
        # srv42
        PublicKey = faFB5CfMSzxlvfxvfdv7X7CZfFtudGVDr+YxnUitcyMuO57u6i9jU=
        AllowedIPs = 10.43.0.2/32

kind: Secret
metadata:
    name: wireguard-server
    namespace: default

The workflow is working perfectly as expected, until I wanted to add a second peer so I open the file and prepare the configuration block. But I did not had the public key yet so closed the file as is:

apiVersion: v1
stringData:
    wg0.conf: |
        [Interface]
        Address = 10.43.0.1/24
        ListenPort = 6530
        PrivateKey = EM2K6hssd2G3snCZqk2LsAe/gzvhtwerMTbKCff7iVNvvv0tWI=

        [Peer]
        # srv42
        PublicKey = faFB5CfMSzxlvfxvfdv7X7CZfFtudGVDr+YxnUitcyMuO57u6i9jU=
        AllowedIPs = 10.43.0.2/32

        [Peer]
        # srv43
        PublicKey = 
        AllowedIPs = 10.43.0.3/32

kind: Secret
metadata:
    name: wireguard-server
    namespace: default

and re-opened it when I had the key but the unencrypted version is now:

apiVersion: v1
stringData:
    wg0.conf: "[Interface]\nAddress = 10.43.0.1/24\nListenPort = 6530\nPrivateKey = EM2K6hssd2G3snCZqk2LsAe/gzvhtwerMTbKCff7iVNvvv0tWI=\n\n[Peer]\n# srv42\nPublicKey = faFB5CfMSzxlvfxvfdv7X7CZfFtudGVDr+YxnUitcyMuO57u6i9jU=\nAllowedIPs = 10.43.0.2/32\n\n[Peer]\n# srv43\nPublicKey = \nAllowedIPs = 10.43.0.3/32\n"
kind: Secret
metadata:
    name: wireguard-server
    namespace: default

Yes YAML content is technically identical, but it's now unmaintainable.

As you guest it, it would be the same for any plain secret file you want to push to kubernetes and any situation where you would leave a trailing space at any line in the configuration file.

n0rad avatar Mar 05 '23 20:03 n0rad

@n0rad have you found a solution for this? This is unfortunate for maintaining yaml files in git.

joel-teratis avatar Jul 01 '24 12:07 joel-teratis

Nope, I did look for a solution for now but it's ok for my own usage since I always double check each time by closing the file and opening it back to confirm the format.

On the other side, it's a kind of blocker for pushing sops at work for 250+ engineers where I will not ask them to do the check. I suppose we could implement some CI check to do validation but the workflow will be horrible. In the meantime we stay with sealedSecrets.

n0rad avatar Jul 01 '24 12:07 n0rad

@n0rad you can store the Secret's values in sops-encrypted files and assemble them into a Kubernetes Secret by putting the following in your kustomization.yaml:


generators:
  - |
    apiVersion: viaduct.ai/v1
    kind: ksops-exec
    metadata:
      name: secret-generator
    secretFrom:
      - metadata:
          name: wireguard-server
        files:
          - wg0.ini

(This requires KSOPS.)

Since .ini is supported by sops, you can even encrypt only the private key by using the following .sops.yaml:

creation_rules:
  - encrypted_regex: "^(PrivateKey)$"

Then your encrypted wg0.ini will look like this:

[Interface]
Address    = 10.43.0.1/24
ListenPort = 6530
PrivateKey = ENC[AES256_GCM,data:erKILuevnjazTJoxzugfQ9Ss85t0nEaCbK7yyEyCPgJySc94bg+Hy9CQiZSt3f7gZHoX,iv:6gsdFlHDRjTdFJynQ8LEN98UkCDrjD9zDrKbggX4rcw=,tag:qNvwl6Axs6t3HOWR2verOQ==,type:str]

[Peer]
; srv42
PublicKey  = faFB5CfMSzxlvfxvfdv7X7CZfFtudGVDr+YxnUitcyMuO57u6i9jU=
AllowedIPs = 10.43.0.2/32

[sops]
...

haslersn avatar Sep 29 '24 12:09 haslersn

This seems to be an issue when using any UTF-8 emote in the text. Eg. 🙂

MiniaczQ avatar Jun 25 '25 08:06 MiniaczQ

I just hit the same issue today on a completely different topic in helm resource rendering, making me thing the issue actually lives in go's yaml, more than sops

n0rad avatar Jul 02 '25 10:07 n0rad

@n0rad The same issue occurs when using the helm values command. This happens when there is a space right before a line break in the yaml file

There's a space after "PublicKey=" in your yaml

        [Peer]
        # srv43
        PublicKey = 
        AllowedIPs = 10.43.0.3/32

akdldid avatar Aug 19 '25 07:08 akdldid