2017-09-12 20:01:12 -07:00
/ *
Package stores acts as a layer between the internal representation of encrypted files and the encrypted files
themselves .
Subpackages implement serialization and deserialization to multiple formats .
This package defines the structure SOPS files should have and conversions to and from the internal representation . Part
of the purpose of this package is to make it easy to change the SOPS file format while remaining backwards - compatible .
* /
2017-08-23 16:36:49 -07:00
package stores
import (
"time"
"fmt"
2023-07-11 21:09:23 +02:00
"github.com/getsops/sops/v3"
"github.com/getsops/sops/v3/age"
"github.com/getsops/sops/v3/azkv"
"github.com/getsops/sops/v3/gcpkms"
"github.com/getsops/sops/v3/hcvault"
"github.com/getsops/sops/v3/kms"
"github.com/getsops/sops/v3/pgp"
2017-08-23 16:36:49 -07:00
)
2017-09-12 20:01:12 -07:00
// SopsFile is a struct used by the stores as a helper to unmarshal the SOPS metadata
2017-08-23 16:36:49 -07:00
type SopsFile struct {
2017-10-03 14:35:28 -07:00
// Metadata is a pointer so we can easily tell when the field is not present
// in the SOPS file by checking for nil. This way we can show the user a
// helpful error message indicating that the metadata wasn't found, instead
// of showing a cryptic parsing error
2018-08-26 00:18:10 -07:00
Metadata * Metadata ` yaml:"sops" json:"sops" ini:"sops" `
2017-08-23 16:36:49 -07:00
}
2017-09-12 09:59:23 -07:00
// Metadata is stored in SOPS encrypted files, and it contains the information necessary to decrypt the file.
2017-08-23 16:36:49 -07:00
// This struct is just used for serialization, and SOPS uses another struct internally, sops.Metadata. It exists
// in order to allow the binary format to stay backwards compatible over time, but at the same time allow the internal
// representation SOPS uses to change over time.
type Metadata struct {
2017-09-18 12:22:36 +03:00
ShamirThreshold int ` yaml:"shamir_threshold,omitempty" json:"shamir_threshold,omitempty" `
KeyGroups [ ] keygroup ` yaml:"key_groups,omitempty" json:"key_groups,omitempty" `
KMSKeys [ ] kmskey ` yaml:"kms" json:"kms" `
GCPKMSKeys [ ] gcpkmskey ` yaml:"gcp_kms" json:"gcp_kms" `
2018-06-17 22:50:30 +02:00
AzureKeyVaultKeys [ ] azkvkey ` yaml:"azure_kv" json:"azure_kv" `
2020-05-05 00:57:51 +05:30
VaultKeys [ ] vaultkey ` yaml:"hc_vault" json:"hc_vault" `
2020-07-04 10:47:06 -07:00
AgeKeys [ ] agekey ` yaml:"age" json:"age" `
2017-09-18 12:22:36 +03:00
LastModified string ` yaml:"lastmodified" json:"lastmodified" `
MessageAuthenticationCode string ` yaml:"mac" json:"mac" `
PGPKeys [ ] pgpkey ` yaml:"pgp" json:"pgp" `
2018-04-08 17:53:54 +03:00
UnencryptedSuffix string ` yaml:"unencrypted_suffix,omitempty" json:"unencrypted_suffix,omitempty" `
2018-04-08 12:43:43 +03:00
EncryptedSuffix string ` yaml:"encrypted_suffix,omitempty" json:"encrypted_suffix,omitempty" `
2020-09-02 13:15:50 -04:00
UnencryptedRegex string ` yaml:"unencrypted_regex,omitempty" json:"unencrypted_regex,omitempty" `
2019-08-14 15:39:21 -04:00
EncryptedRegex string ` yaml:"encrypted_regex,omitempty" json:"encrypted_regex,omitempty" `
2021-12-18 21:50:57 +01:00
MACOnlyEncrypted bool ` yaml:"mac_only_encrypted,omitempty" json:"mac_only_encrypted,omitempty" `
2017-09-18 12:22:36 +03:00
Version string ` yaml:"version" json:"version" `
2017-08-23 16:36:49 -07:00
}
type keygroup struct {
2018-06-17 22:50:30 +02:00
PGPKeys [ ] pgpkey ` yaml:"pgp,omitempty" json:"pgp,omitempty" `
KMSKeys [ ] kmskey ` yaml:"kms,omitempty" json:"kms,omitempty" `
GCPKMSKeys [ ] gcpkmskey ` yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty" `
AzureKeyVaultKeys [ ] azkvkey ` yaml:"azure_kv,omitempty" json:"azure_kv,omitempty" `
2020-05-05 00:57:51 +05:30
VaultKeys [ ] vaultkey ` yaml:"hc_vault" json:"hc_vault" `
2020-07-04 10:47:06 -07:00
AgeKeys [ ] agekey ` yaml:"age" json:"age" `
2017-08-23 16:36:49 -07:00
}
type pgpkey struct {
CreatedAt string ` yaml:"created_at" json:"created_at" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
Fingerprint string ` yaml:"fp" json:"fp" `
}
type kmskey struct {
2017-09-11 13:08:21 -07:00
Arn string ` yaml:"arn" json:"arn" `
Role string ` yaml:"role,omitempty" json:"role,omitempty" `
Context map [ string ] * string ` yaml:"context,omitempty" json:"context,omitempty" `
2017-08-23 16:36:49 -07:00
CreatedAt string ` yaml:"created_at" json:"created_at" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
2019-01-11 14:40:28 +00:00
AwsProfile string ` yaml:"aws_profile" json:"aws_profile" `
2017-08-23 16:36:49 -07:00
}
2017-09-18 12:22:36 +03:00
type gcpkmskey struct {
ResourceID string ` yaml:"resource_id" json:"resource_id" `
CreatedAt string ` yaml:"created_at" json:"created_at" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
}
2020-05-05 00:57:51 +05:30
type vaultkey struct {
VaultAddress string ` yaml:"vault_address" json:"vault_address" `
EnginePath string ` yaml:"engine_path" json:"engine_path" `
KeyName string ` yaml:"key_name" json:"key_name" `
CreatedAt string ` yaml:"created_at" json:"created_at" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
}
2018-06-17 22:50:30 +02:00
type azkvkey struct {
VaultURL string ` yaml:"vault_url" json:"vault_url" `
Name string ` yaml:"name" json:"name" `
Version string ` yaml:"version" json:"version" `
CreatedAt string ` yaml:"created_at" json:"created_at" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
}
2020-07-04 10:47:06 -07:00
type agekey struct {
Recipient string ` yaml:"recipient" json:"recipient" `
EncryptedDataKey string ` yaml:"enc" json:"enc" `
}
2017-09-12 09:59:23 -07:00
// MetadataFromInternal converts an internal SOPS metadata representation to a representation appropriate for storage
2017-08-23 16:36:49 -07:00
func MetadataFromInternal ( sopsMetadata sops . Metadata ) Metadata {
var m Metadata
m . LastModified = sopsMetadata . LastModified . Format ( time . RFC3339 )
m . UnencryptedSuffix = sopsMetadata . UnencryptedSuffix
2018-04-08 12:43:43 +03:00
m . EncryptedSuffix = sopsMetadata . EncryptedSuffix
2020-09-02 13:15:50 -04:00
m . UnencryptedRegex = sopsMetadata . UnencryptedRegex
2019-08-14 15:39:21 -04:00
m . EncryptedRegex = sopsMetadata . EncryptedRegex
2017-08-23 16:36:49 -07:00
m . MessageAuthenticationCode = sopsMetadata . MessageAuthenticationCode
2021-12-18 21:50:57 +01:00
m . MACOnlyEncrypted = sopsMetadata . MACOnlyEncrypted
2017-08-23 16:36:49 -07:00
m . Version = sopsMetadata . Version
2017-09-12 10:58:53 -07:00
m . ShamirThreshold = sopsMetadata . ShamirThreshold
2017-08-23 16:36:49 -07:00
if len ( sopsMetadata . KeyGroups ) == 1 {
group := sopsMetadata . KeyGroups [ 0 ]
m . PGPKeys = pgpKeysFromGroup ( group )
m . KMSKeys = kmsKeysFromGroup ( group )
2017-09-18 12:22:36 +03:00
m . GCPKMSKeys = gcpkmsKeysFromGroup ( group )
2020-05-05 00:57:51 +05:30
m . VaultKeys = vaultKeysFromGroup ( group )
2018-06-17 22:50:30 +02:00
m . AzureKeyVaultKeys = azkvKeysFromGroup ( group )
2020-07-04 10:47:06 -07:00
m . AgeKeys = ageKeysFromGroup ( group )
2017-08-23 16:36:49 -07:00
} else {
for _ , group := range sopsMetadata . KeyGroups {
m . KeyGroups = append ( m . KeyGroups , keygroup {
2018-06-17 22:50:30 +02:00
KMSKeys : kmsKeysFromGroup ( group ) ,
PGPKeys : pgpKeysFromGroup ( group ) ,
GCPKMSKeys : gcpkmsKeysFromGroup ( group ) ,
2020-05-05 00:57:51 +05:30
VaultKeys : vaultKeysFromGroup ( group ) ,
2018-06-17 22:50:30 +02:00
AzureKeyVaultKeys : azkvKeysFromGroup ( group ) ,
2020-07-04 10:47:06 -07:00
AgeKeys : ageKeysFromGroup ( group ) ,
2017-08-23 16:36:49 -07:00
} )
}
}
return m
}
func pgpKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] pgpkey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * pgp . MasterKey :
keys = append ( keys , pgpkey {
Fingerprint : key . Fingerprint ,
EncryptedDataKey : key . EncryptedKey ,
CreatedAt : key . CreationDate . Format ( time . RFC3339 ) ,
} )
}
}
return
}
func kmsKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] kmskey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * kms . MasterKey :
keys = append ( keys , kmskey {
Arn : key . Arn ,
CreatedAt : key . CreationDate . Format ( time . RFC3339 ) ,
EncryptedDataKey : key . EncryptedKey ,
Context : key . EncryptionContext ,
Role : key . Role ,
2019-01-11 14:40:28 +00:00
AwsProfile : key . AwsProfile ,
2017-08-23 16:36:49 -07:00
} )
}
}
return
}
2017-09-18 12:22:36 +03:00
func gcpkmsKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] gcpkmskey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * gcpkms . MasterKey :
keys = append ( keys , gcpkmskey {
ResourceID : key . ResourceID ,
CreatedAt : key . CreationDate . Format ( time . RFC3339 ) ,
EncryptedDataKey : key . EncryptedKey ,
} )
}
}
return
}
2020-05-05 00:57:51 +05:30
func vaultKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] vaultkey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * hcvault . MasterKey :
keys = append ( keys , vaultkey {
VaultAddress : key . VaultAddress ,
EnginePath : key . EnginePath ,
KeyName : key . KeyName ,
CreatedAt : key . CreationDate . Format ( time . RFC3339 ) ,
EncryptedDataKey : key . EncryptedKey ,
} )
}
}
return
}
2018-06-17 22:50:30 +02:00
func azkvKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] azkvkey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * azkv . MasterKey :
keys = append ( keys , azkvkey {
VaultURL : key . VaultURL ,
Name : key . Name ,
Version : key . Version ,
CreatedAt : key . CreationDate . Format ( time . RFC3339 ) ,
EncryptedDataKey : key . EncryptedKey ,
} )
}
}
return
}
2020-07-04 10:47:06 -07:00
func ageKeysFromGroup ( group sops . KeyGroup ) ( keys [ ] agekey ) {
for _ , key := range group {
switch key := key . ( type ) {
case * age . MasterKey :
keys = append ( keys , agekey {
Recipient : key . Recipient ,
EncryptedDataKey : key . EncryptedKey ,
} )
}
}
return
}
2017-09-12 09:59:23 -07:00
// ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation
2017-08-29 12:16:00 -07:00
func ( m * Metadata ) ToInternal ( ) ( sops . Metadata , error ) {
2017-08-23 16:36:49 -07:00
lastModified , err := time . Parse ( time . RFC3339 , m . LastModified )
if err != nil {
2017-08-29 12:16:00 -07:00
return sops . Metadata { } , err
2017-08-23 16:36:49 -07:00
}
groups , err := m . internalKeygroups ( )
if err != nil {
2017-08-29 12:16:00 -07:00
return sops . Metadata { } , err
2017-08-23 16:36:49 -07:00
}
2019-08-14 15:39:21 -04:00
cryptRuleCount := 0
if m . UnencryptedSuffix != "" {
cryptRuleCount ++
}
if m . EncryptedSuffix != "" {
cryptRuleCount ++
}
2020-09-02 13:15:50 -04:00
if m . UnencryptedRegex != "" {
cryptRuleCount ++
}
2019-08-14 15:39:21 -04:00
if m . EncryptedRegex != "" {
cryptRuleCount ++
2018-04-08 17:53:54 +03:00
}
2019-08-14 15:39:21 -04:00
if cryptRuleCount > 1 {
2020-09-02 13:15:50 -04:00
return sops . Metadata { } , fmt . Errorf ( "Cannot use more than one of encrypted_suffix, unencrypted_suffix, encrypted_regex or unencrypted_regex in the same file" )
2019-08-14 15:39:21 -04:00
}
if cryptRuleCount == 0 {
2017-08-23 16:36:49 -07:00
m . UnencryptedSuffix = sops . DefaultUnencryptedSuffix
}
2017-08-29 12:16:00 -07:00
return sops . Metadata {
2017-08-23 16:36:49 -07:00
KeyGroups : groups ,
2017-09-12 10:58:53 -07:00
ShamirThreshold : m . ShamirThreshold ,
2017-08-23 16:36:49 -07:00
Version : m . Version ,
MessageAuthenticationCode : m . MessageAuthenticationCode ,
UnencryptedSuffix : m . UnencryptedSuffix ,
2018-04-08 12:43:43 +03:00
EncryptedSuffix : m . EncryptedSuffix ,
2020-09-02 13:15:50 -04:00
UnencryptedRegex : m . UnencryptedRegex ,
2019-08-14 15:39:21 -04:00
EncryptedRegex : m . EncryptedRegex ,
2021-12-18 21:50:57 +01:00
MACOnlyEncrypted : m . MACOnlyEncrypted ,
2017-08-23 16:36:49 -07:00
LastModified : lastModified ,
} , nil
}
2020-07-04 10:47:06 -07:00
func internalGroupFrom ( kmsKeys [ ] kmskey , pgpKeys [ ] pgpkey , gcpKmsKeys [ ] gcpkmskey , azkvKeys [ ] azkvkey , vaultKeys [ ] vaultkey , ageKeys [ ] agekey ) ( sops . KeyGroup , error ) {
2017-08-23 16:36:49 -07:00
var internalGroup sops . KeyGroup
for _ , kmsKey := range kmsKeys {
k , err := kmsKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2017-09-18 12:22:36 +03:00
for _ , gcpKmsKey := range gcpKmsKeys {
k , err := gcpKmsKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2018-06-17 22:50:30 +02:00
for _ , azkvKey := range azkvKeys {
k , err := azkvKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2020-05-05 00:57:51 +05:30
for _ , vaultKey := range vaultKeys {
k , err := vaultKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2017-08-23 16:36:49 -07:00
for _ , pgpKey := range pgpKeys {
k , err := pgpKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2020-07-04 10:47:06 -07:00
for _ , ageKey := range ageKeys {
k , err := ageKey . toInternal ( )
if err != nil {
return nil , err
}
internalGroup = append ( internalGroup , k )
}
2017-08-23 16:36:49 -07:00
return internalGroup , nil
}
func ( m * Metadata ) internalKeygroups ( ) ( [ ] sops . KeyGroup , error ) {
var internalGroups [ ] sops . KeyGroup
2020-07-04 10:47:06 -07:00
if len ( m . PGPKeys ) > 0 || len ( m . KMSKeys ) > 0 || len ( m . GCPKMSKeys ) > 0 || len ( m . AzureKeyVaultKeys ) > 0 || len ( m . VaultKeys ) > 0 || len ( m . AgeKeys ) > 0 {
internalGroup , err := internalGroupFrom ( m . KMSKeys , m . PGPKeys , m . GCPKMSKeys , m . AzureKeyVaultKeys , m . VaultKeys , m . AgeKeys )
2017-08-23 16:36:49 -07:00
if err != nil {
return nil , err
}
internalGroups = append ( internalGroups , internalGroup )
return internalGroups , nil
} else if len ( m . KeyGroups ) > 0 {
for _ , group := range m . KeyGroups {
2020-07-04 10:47:06 -07:00
internalGroup , err := internalGroupFrom ( group . KMSKeys , group . PGPKeys , group . GCPKMSKeys , group . AzureKeyVaultKeys , group . VaultKeys , group . AgeKeys )
2017-08-23 16:36:49 -07:00
if err != nil {
return nil , err
}
internalGroups = append ( internalGroups , internalGroup )
}
return internalGroups , nil
} else {
return nil , fmt . Errorf ( "No keys found in file" )
}
}
func ( kmsKey * kmskey ) toInternal ( ) ( * kms . MasterKey , error ) {
creationDate , err := time . Parse ( time . RFC3339 , kmsKey . CreatedAt )
if err != nil {
return nil , err
}
return & kms . MasterKey {
Role : kmsKey . Role ,
EncryptionContext : kmsKey . Context ,
EncryptedKey : kmsKey . EncryptedDataKey ,
CreationDate : creationDate ,
Arn : kmsKey . Arn ,
2019-01-11 14:40:28 +00:00
AwsProfile : kmsKey . AwsProfile ,
2017-08-23 16:36:49 -07:00
} , nil
}
2017-09-18 12:22:36 +03:00
func ( gcpKmsKey * gcpkmskey ) toInternal ( ) ( * gcpkms . MasterKey , error ) {
creationDate , err := time . Parse ( time . RFC3339 , gcpKmsKey . CreatedAt )
if err != nil {
return nil , err
}
return & gcpkms . MasterKey {
ResourceID : gcpKmsKey . ResourceID ,
EncryptedKey : gcpKmsKey . EncryptedDataKey ,
CreationDate : creationDate ,
} , nil
}
2018-06-17 22:50:30 +02:00
func ( azkvKey * azkvkey ) toInternal ( ) ( * azkv . MasterKey , error ) {
creationDate , err := time . Parse ( time . RFC3339 , azkvKey . CreatedAt )
if err != nil {
return nil , err
}
return & azkv . MasterKey {
VaultURL : azkvKey . VaultURL ,
Name : azkvKey . Name ,
Version : azkvKey . Version ,
EncryptedKey : azkvKey . EncryptedDataKey ,
CreationDate : creationDate ,
} , nil
}
2020-05-05 00:57:51 +05:30
func ( vaultKey * vaultkey ) toInternal ( ) ( * hcvault . MasterKey , error ) {
creationDate , err := time . Parse ( time . RFC3339 , vaultKey . CreatedAt )
if err != nil {
return nil , err
}
return & hcvault . MasterKey {
VaultAddress : vaultKey . VaultAddress ,
EnginePath : vaultKey . EnginePath ,
KeyName : vaultKey . KeyName ,
CreationDate : creationDate ,
EncryptedKey : vaultKey . EncryptedDataKey ,
} , nil
}
2017-08-23 16:36:49 -07:00
func ( pgpKey * pgpkey ) toInternal ( ) ( * pgp . MasterKey , error ) {
creationDate , err := time . Parse ( time . RFC3339 , pgpKey . CreatedAt )
if err != nil {
return nil , err
}
return & pgp . MasterKey {
EncryptedKey : pgpKey . EncryptedDataKey ,
CreationDate : creationDate ,
Fingerprint : pgpKey . Fingerprint ,
} , nil
}
2019-01-23 10:51:47 +01:00
2020-07-04 10:47:06 -07:00
func ( ageKey * agekey ) toInternal ( ) ( * age . MasterKey , error ) {
return & age . MasterKey {
EncryptedKey : ageKey . EncryptedDataKey ,
Recipient : ageKey . Recipient ,
} , nil
}
2019-07-08 15:32:33 -07:00
// ExampleComplexTree is an example sops.Tree object exhibiting complex relationships
2019-01-23 10:51:47 +01:00
var ExampleComplexTree = sops . Tree {
Branches : sops . TreeBranches {
sops . TreeBranch {
sops . TreeItem {
Key : "hello" ,
Value : ` Welcome to SOPS! Edit this file as you please! ` ,
} ,
sops . TreeItem {
Key : "example_key" ,
Value : "example_value" ,
} ,
sops . TreeItem {
Key : sops . Comment { Value : " Example comment" } ,
Value : nil ,
} ,
sops . TreeItem {
Key : "example_array" ,
Value : [ ] interface { } {
"example_value1" ,
"example_value2" ,
} ,
} ,
sops . TreeItem {
Key : "example_number" ,
Value : 1234.56789 ,
} ,
sops . TreeItem {
Key : "example_booleans" ,
Value : [ ] interface { } { true , false } ,
} ,
} ,
} ,
}
2019-07-08 15:32:33 -07:00
// ExampleSimpleTree is an example sops.Tree object exhibiting only simple relationships
// with only one nested branch and only simple string values
2019-01-23 10:51:47 +01:00
var ExampleSimpleTree = sops . Tree {
Branches : sops . TreeBranches {
sops . TreeBranch {
sops . TreeItem {
Key : "Welcome!" ,
Value : sops . TreeBranch {
sops . TreeItem {
Key : sops . Comment { Value : " This is an example file." } ,
Value : nil ,
} ,
sops . TreeItem {
Key : "hello" ,
Value : "Welcome to SOPS! Edit this file as you please!" ,
} ,
sops . TreeItem {
Key : "example_key" ,
Value : "example_value" ,
} ,
} ,
} ,
} ,
} ,
}
2019-07-08 15:32:33 -07:00
// ExampleFlatTree is an example sops.Tree object exhibiting only simple relationships
// with no nested branches and only simple string values
2019-01-23 10:51:47 +01:00
var ExampleFlatTree = sops . Tree {
Branches : sops . TreeBranches {
sops . TreeBranch {
sops . TreeItem {
Key : sops . Comment { Value : " This is an example file." } ,
Value : nil ,
} ,
sops . TreeItem {
Key : "hello" ,
Value : "Welcome to SOPS! Edit this file as you please!" ,
} ,
sops . TreeItem {
Key : "example_key" ,
Value : "example_value" ,
} ,
2020-01-24 12:03:34 -05:00
sops . TreeItem {
Key : "example_multiline" ,
Value : "foo\nbar\nbaz" ,
} ,
2019-01-23 10:51:47 +01:00
} ,
} ,
}