Skip to content

feat: add SOPS for secrets #6

@tonnico

Description

@tonnico

Add support for SOPS secrets.
https://github.com/getsops/sops

Currently haloy supports only secrets with 1password. I worked already alot with sops and would like to add this feature into haloy.

SOPS files can have multiple formats. E.g. yaml, json and also dotenv. I would like to support all of them.
In the past I used https://github.com/dbsystel/cdk-sops-secrets.

Idea

Think about something like this:

secret_providers:
  sops:
    development:
      file: ./secrets.dev.env

env:
  - name: CLIENT_ID
    from:
      secret: sops:development.CLIENT_ID
  - name: CLIENT_SECRET
    from:
      secret: sops:development.CLIENT_SECRET
Sops encypted secrets.dev.env
{
	"data": "ENC[AES256_GCM,data:vcqWg4azs9RdQY8dywNmnYDTBLctM45K4S5kvILowWwZZB7bSFN+v6TfNTRpN04jGFM=,iv:TWfGfAbB0X+oWFNsNJ4taU+am01bS6u54CAMUove6rs=,tag:/Cv2w/ZPbkmE1vGDD1ujrQ==,type:str]",
	"sops": {
		"age": [
			{
				"recipient": "age15ce8f74ehcm7qckew5cerdrjp3xnz7rs2fcqpp86ghrlrhsww95qxtu22x",
				"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNSGVlQUIwNDZoVDZLMXRJ\na2dKVkR0WGZJZU5DWTllMHd5aFUvdzc2MmdjCnJ6MEEzNERsMFRiYWNrdFdWQmtT\ndDd2WU5ZM3NWWUswdXl2SmtIcjZDRjgKLS0tIC9pRlBHald3aXJOY3BxcmhrcVJ3\ndjVDWk9LNllPSTlXRUVSU3dNcGNsSXMKByfwoeIe7ENkWbJFsdS/+/1zjRhOyvwZ\n/hPHht7TU/Iy9S4PXW/dsosav9yGp4YOwrZIcHEdz66A33j4MQJAmA==\n-----END AGE ENCRYPTED FILE-----\n"
			}
		],
		"lastmodified": "2026-03-30T11:42:11Z",
		"mac": "ENC[AES256_GCM,data:/up6MNZcn7q7WIWgql/WzztTi472CarJ2t8DtMGco6DlRGGHxx4y71tUxXOulTxGCrrbooLzblVspc5mEtHqIMEZnbsOdNYdqzLgMa5CAjMWejQeMNIEzT191lV+4CeBMBOPBj0AX49jz4GTJAT4cwE/jE6AR0mXCScud4b0vxo=,iv:p4qRbuq/96iQPHE4MyYOQFFdOoqoAFRIEv4S2nNS8/o=,tag:3WfRypwjNo80OPctnP8RIg==,type:str]",
		"version": "3.12.2"
	}
}

For json/yaml secrets you could do something like this:

secret_providers:
  sops:
    development:
      file: ./secrets.dev.yaml

env:
  - name: CLIENT_ID
    from:
      secret: sops:development.client.id
  - name: CLIENT_SECRET
    from:
      secret: sops:development.client.secret
Sops nested yaml
client:
    id: ENC[AES256_GCM,data:RiEi,iv:TfEnebGEcqykXZW81snYiYU/0dBij6Zqo08/2G6LEjY=,tag:QHHsHI/FPbXAIhoKgJW7RA==,type:str]
    secret: ENC[AES256_GCM,data:Tb70,iv:yA6zN07pSMTJe3ParrgWFhXRC239TOT5eIO2A9bHtY0=,tag:0qr6ukfpt9II/HenhllRCg==,type:str]
foo_unencrypted:
    text: foo
sops:
    age:
        - recipient: age15ce8f74ehcm7qckew5cerdrjp3xnz7rs2fcqpp86ghrlrhsww95qxtu22x
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIWlpEeVU3dTlsYWhGT2pI
            VnY0T21vMWxORUpyTkhFQTdNdXlmUW51U0VvCldCaHZxaktDZEFITU02Tjhmc2hk
            d3psWkYrVUZsRytBTm1uMUVXckh3YzQKLS0tIEhxdmFwMno0VjhFOXNRU05wbHlF
            a21HcHFuQU1CajhZZjZWRXRKazJqQmcKPzuCI5uqIv2lopoJpwFyCkImHAcVWf6k
            cfBXCxx9gFnFxNgzgxyC5Ua+XeIf7LsOy3OF8q1N0zBwpN0ox7zq/g==
            -----END AGE ENCRYPTED FILE-----
    lastmodified: "2026-03-30T11:46:40Z"
    mac: ENC[AES256_GCM,data:b3ll2qfFtiO+zW5ZMRN8fp3vqWjCUS208NtiX1ah+dMuJa8jG8IQVzuJdf+zEvICsdnsNLCmFia0xZfurGulJOHQ0FUIYaWgtyJkQabVV2hr9aTa7M8xRmA06c7xJWStSUVQ2vcVkRuVy++EjpXRSTGF6W/v/xH6SyEpTdRiZrY=,iv:6jdJSNltcLA2+YazbL0b3gW1xNZiGb3SDGmUWFoRDVE=,tag:PNbUMBqmT/Dq54Mg/37owg==,type:str]
    unencrypted_suffix: _unencrypted
    version: 3.12.2

Probably it is a good idea to make a breaking change on the .-notation.

Instead of secret: sops:development.client.id you could have secret: sops:development:client.id.

Changes

  • Adding SOPSSourceConfig with File and Format to secret_providers.go
  • Adding provider_sops.go with fetchFromSOPS
  • Adding sops provider to resolve_secrets.go

Since SOPS is written in Go, this could be a first-class feature via "github.com/getsops/sops/v3/decrypt".

We need to resolve the path for ResolveSecrets relative to the config file's location.
In this case, every call to ResolveSecrets would need to receive the configPath.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions