1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00
Files
sops/config/config.go

349 lines
10 KiB
Go
Raw Normal View History

2017-09-12 20:01:12 -07:00
/*
Package config provides a way to find and load SOPS configuration files
*/
2019-11-18 10:06:58 -08:00
package config //import "go.mozilla.org/sops/v3/config"
2016-08-25 17:51:55 -07:00
import (
"fmt"
"io/ioutil"
"os"
"path"
"regexp"
2016-10-22 08:19:34 -07:00
"github.com/mozilla-services/yaml"
"github.com/sirupsen/logrus"
2019-11-18 10:06:58 -08:00
"go.mozilla.org/sops/v3"
"go.mozilla.org/sops/v3/azkv"
"go.mozilla.org/sops/v3/gcpkms"
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
"go.mozilla.org/sops/v3/hcvault"
2019-11-18 10:06:58 -08:00
"go.mozilla.org/sops/v3/kms"
"go.mozilla.org/sops/v3/logging"
"go.mozilla.org/sops/v3/pgp"
"go.mozilla.org/sops/v3/publish"
2016-08-25 17:51:55 -07:00
)
var log *logrus.Logger
func init() {
log = logging.NewLogger("CONFIG")
}
2016-08-25 17:51:55 -07:00
type fileSystem interface {
Stat(name string) (os.FileInfo, error)
}
type osFS struct {
stat func(string) (os.FileInfo, error)
}
func (fs osFS) Stat(name string) (os.FileInfo, error) {
return fs.stat(name)
}
var fs fileSystem = osFS{stat: os.Stat}
const (
maxDepth = 100
configFileName = ".sops.yaml"
)
// FindConfigFile looks for a sops config file in the current working directory and on parent directories, up to the limit defined by the maxDepth constant.
func FindConfigFile(start string) (string, error) {
filepath := path.Dir(start)
for i := 0; i < maxDepth; i++ {
_, err := fs.Stat(path.Join(filepath, configFileName))
if err != nil {
filepath = path.Join(filepath, "..")
} else {
return path.Join(filepath, configFileName), nil
}
}
return "", fmt.Errorf("Config file not found")
}
type configFile struct {
CreationRules []creationRule `yaml:"creation_rules"`
DestinationRules []destinationRule `yaml:"destination_rules"`
2016-08-25 17:51:55 -07:00
}
2017-08-24 17:24:04 -07:00
type keyGroup struct {
2018-06-17 22:50:30 +02:00
KMS []kmsKey
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
AzureKV []azureKVKey `yaml:"azure_keyvault"`
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
Vault []string `yaml:"hc_vault"`
2018-06-17 22:50:30 +02:00
PGP []string
2017-09-15 14:27:04 -07:00
}
type gcpKmsKey struct {
ResourceID string `yaml:"resource_id"`
}
type kmsKey struct {
2019-01-25 12:42:41 +00:00
Arn string `yaml:"arn"`
Role string `yaml:"role,omitempty"`
Context map[string]*string `yaml:"context"`
AwsProfile string `yaml:"aws_profile"`
2017-08-24 17:24:04 -07:00
}
2018-06-17 22:50:30 +02:00
type azureKVKey struct {
VaultURL string `yaml:"vaultUrl"`
Key string `yaml:"key"`
Version string `yaml:"version"`
}
type destinationRule struct {
PathRegex string `yaml:"path_regex"`
S3Bucket string `yaml:"s3_bucket"`
S3Prefix string `yaml:"s3_prefix"`
GCSBucket string `yaml:"gcs_bucket"`
GCSPrefix string `yaml:"gcs_prefix"`
VaultPath string `yaml:"vault_path"`
VaultAddress string `yaml:"vault_address"`
VaultKVMountName string `yaml:"vault_kv_mount_name"`
VaultKVVersion int `yaml:"vault_kv_version"`
RecreationRule creationRule `yaml:"recreation_rule,omitempty"`
OmitExtensions bool `yaml:"omit_extensions"`
}
2016-08-25 17:51:55 -07:00
type creationRule struct {
PathRegex string `yaml:"path_regex"`
KMS string
2019-01-25 12:42:41 +00:00
AwsProfile string `yaml:"aws_profile"`
PGP string
GCPKMS string `yaml:"gcp_kms"`
2018-06-17 22:50:30 +02:00
AzureKeyVault string `yaml:"azure_keyvault"`
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
VaultURI string `yaml:"hc_vault_transit_uri"`
KeyGroups []keyGroup `yaml:"key_groups"`
ShamirThreshold int `yaml:"shamir_threshold"`
UnencryptedSuffix string `yaml:"unencrypted_suffix"`
EncryptedSuffix string `yaml:"encrypted_suffix"`
UnencryptedRegex string `yaml:"unencrypted_regex"`
2019-08-14 15:39:21 -04:00
EncryptedRegex string `yaml:"encrypted_regex"`
2016-08-25 17:51:55 -07:00
}
// Load loads a sops config file into a temporary struct
func (f *configFile) load(bytes []byte) error {
err := yaml.Unmarshal(bytes, f)
if err != nil {
return fmt.Errorf("Could not unmarshal config file: %s", err)
}
return nil
}
// Config is the configuration for a given SOPS file
type Config struct {
KeyGroups []sops.KeyGroup
ShamirThreshold int
UnencryptedSuffix string
EncryptedSuffix string
UnencryptedRegex string
2019-08-14 15:39:21 -04:00
EncryptedRegex string
Destination publish.Destination
OmitExtensions bool
}
func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[string]*string) ([]sops.KeyGroup, error) {
2017-08-24 17:24:04 -07:00
var groups []sops.KeyGroup
if len(cRule.KeyGroups) > 0 {
for _, group := range cRule.KeyGroups {
var keyGroup sops.KeyGroup
for _, k := range group.PGP {
keyGroup = append(keyGroup, pgp.NewMasterKeyFromFingerprint(k))
}
for _, k := range group.KMS {
keyGroup = append(keyGroup, kms.NewMasterKey(k.Arn, k.Role, k.Context))
2017-08-24 17:24:04 -07:00
}
2017-09-15 14:27:04 -07:00
for _, k := range group.GCPKMS {
2017-09-15 14:40:57 -07:00
keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
2017-09-15 14:27:04 -07:00
}
for _, k := range group.AzureKV {
keyGroup = append(keyGroup, azkv.NewMasterKey(k.VaultURL, k.Key, k.Version))
}
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
for _, k := range group.Vault {
if masterKey, err := hcvault.NewMasterKeyFromURI(k); err == nil {
keyGroup = append(keyGroup, masterKey)
} else {
return nil, err
}
}
groups = append(groups, keyGroup)
}
} else {
var keyGroup sops.KeyGroup
for _, k := range pgp.MasterKeysFromFingerprintString(cRule.PGP) {
keyGroup = append(keyGroup, k)
}
for _, k := range kms.MasterKeysFromArnString(cRule.KMS, kmsEncryptionContext, cRule.AwsProfile) {
keyGroup = append(keyGroup, k)
2016-08-25 17:51:55 -07:00
}
for _, k := range gcpkms.MasterKeysFromResourceIDString(cRule.GCPKMS) {
2017-09-15 14:27:04 -07:00
keyGroup = append(keyGroup, k)
}
azureKeys, err := azkv.MasterKeysFromURLs(cRule.AzureKeyVault)
2018-06-18 16:03:24 +02:00
if err != nil {
return nil, err
}
for _, k := range azureKeys {
2018-06-17 22:50:30 +02:00
keyGroup = append(keyGroup, k)
}
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
vaultKeys, err := hcvault.NewMasterKeysFromURIs(cRule.VaultURI)
if err != nil {
return nil, err
}
for _, k := range vaultKeys {
keyGroup = append(keyGroup, k)
}
groups = append(groups, keyGroup)
}
return groups, nil
}
func loadConfigFile(confPath string) (*configFile, error) {
confBytes, err := ioutil.ReadFile(confPath)
if err != nil {
return nil, fmt.Errorf("could not read config file: %s", err)
}
conf := &configFile{}
err = conf.load(confBytes)
if err != nil {
return nil, fmt.Errorf("error loading config: %s", err)
}
return conf, nil
}
func configFromRule(rule *creationRule, kmsEncryptionContext map[string]*string) (*Config, error) {
2019-08-14 15:39:21 -04:00
cryptRuleCount := 0
if rule.UnencryptedSuffix != "" {
cryptRuleCount++
}
if rule.EncryptedSuffix != "" {
cryptRuleCount++
}
if rule.EncryptedRegex != "" {
cryptRuleCount++
}
if cryptRuleCount > 1 {
return nil, fmt.Errorf("error loading config: cannot use more than one of encrypted_suffix, unencrypted_suffix, or encrypted_regex for the same rule")
}
groups, err := getKeyGroupsFromCreationRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
return &Config{
KeyGroups: groups,
ShamirThreshold: rule.ShamirThreshold,
UnencryptedSuffix: rule.UnencryptedSuffix,
EncryptedSuffix: rule.EncryptedSuffix,
UnencryptedRegex: rule.UnencryptedRegex,
2019-08-14 15:39:21 -04:00
EncryptedRegex: rule.EncryptedRegex,
}, nil
}
func parseDestinationRuleForFile(conf *configFile, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
var rule *creationRule
var dRule *destinationRule
if len(conf.DestinationRules) > 0 {
for _, r := range conf.DestinationRules {
if r.PathRegex == "" {
dRule = &r
rule = &dRule.RecreationRule
break
}
if r.PathRegex != "" {
if match, _ := regexp.MatchString(r.PathRegex, filePath); match {
dRule = &r
rule = &dRule.RecreationRule
break
}
}
}
}
if dRule == nil {
return nil, fmt.Errorf("error loading config: no matching destination found in config")
}
var dest publish.Destination
if dRule != nil {
if dRule.S3Bucket != "" && dRule.GCSBucket != "" && dRule.VaultPath != "" {
Add HashiCorp Vault support (#655) * feat: initial adding of vualt transit backend to sops initial work on integration feat(vault): added cli coomands working for vualt" fix(vault): fixed config with correct tests fix(vault): added vault to keygroup and to keyservice server fixed metadata load * feat(docs): added docs in README.md and in command help fix(doc): fix rst formatting" fix(doc): fix rst formatting * fix(vault): addressed typos and fixes from autrilla feat(cli): moved vault to hc-vault naming * fix(test): typo while rebasing * fix typos and imporve error messages for vault kms * rename package from vault to hcvault * refactor vault keysource url validation * add negative test cases for vault keysource * add hc vault transit config option via objects additional to URIs * remove vault_example.yml * streamline key name to snake case * rename `BackendPath` to `EnginePath` for hc vault * correction in hc-vault-transit commands Signed-off-by: vnzongzna <github@vaibhavk.in> * resolving conflict Signed-off-by: vnzongzna <github@vaibhavk.in> * Apply suggestions from code review Co-Authored-By: Adrian Utrilla <adrianutrilla@gmail.com> * allowing only hc_vault_transit_uri as input Co-Authored-By: gitirabassi Co-Authored-By: ldue Signed-off-by: vnzongzna <github@vaibhavk.in> Co-authored-by: gitirabassi <giacomo@tirabassi.eu> Co-authored-by: ldue <larsduennwald@gmail.com> Co-authored-by: Vaibhav Kaushik <vaibhavkaushik@vaibhavka-ltm1.internal.salesforce.com> Co-authored-by: Adrian Utrilla <adrianutrilla@gmail.com>
2020-05-05 00:57:51 +05:30
return nil, fmt.Errorf("error loading config: more than one destinations were found in a single destination rule, you can only use one per rule")
}
if dRule.S3Bucket != "" {
dest = publish.NewS3Destination(dRule.S3Bucket, dRule.S3Prefix)
}
if dRule.GCSBucket != "" {
dest = publish.NewGCSDestination(dRule.GCSBucket, dRule.GCSPrefix)
}
if dRule.VaultPath != "" {
dest = publish.NewVaultDestination(dRule.VaultAddress, dRule.VaultPath, dRule.VaultKVMountName, dRule.VaultKVVersion)
}
}
config, err := configFromRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
config.Destination = dest
config.OmitExtensions = dRule.OmitExtensions
return config, nil
}
func parseCreationRuleForFile(conf *configFile, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
// If config file doesn't contain CreationRules (it's empty or only contains DestionationRules), assume it does not exist
if conf.CreationRules == nil {
return nil, nil
}
var rule *creationRule
for _, r := range conf.CreationRules {
if r.PathRegex == "" {
rule = &r
break
}
if r.PathRegex != "" {
if match, _ := regexp.MatchString(r.PathRegex, filePath); match {
rule = &r
break
}
}
}
if rule == nil {
return nil, fmt.Errorf("error loading config: no matching creation rules found")
}
config, err := configFromRule(rule, kmsEncryptionContext)
if err != nil {
return nil, err
}
return config, nil
}
// LoadCreationRuleForFile load the configuration for a given SOPS file from the config file at confPath. A kmsEncryptionContext
// should be provided for configurations that do not contain key groups, as there's no way to specify context inside
// a SOPS config file outside of key groups.
func LoadCreationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
conf, err := loadConfigFile(confPath)
if err != nil {
return nil, err
}
return parseCreationRuleForFile(conf, filePath, kmsEncryptionContext)
}
// LoadDestinationRuleForFile works the same as LoadCreationRuleForFile, but gets the "creation_rule" from the matching destination_rule's
// "recreation_rule".
func LoadDestinationRuleForFile(confPath string, filePath string, kmsEncryptionContext map[string]*string) (*Config, error) {
conf, err := loadConfigFile(confPath)
if err != nil {
return nil, err
2016-08-25 17:51:55 -07:00
}
return parseDestinationRuleForFile(conf, filePath, kmsEncryptionContext)
2016-08-25 17:51:55 -07:00
}