Skip to content

Commit

Permalink
Support SOPS_AGE_KEY environment variable
Browse files Browse the repository at this point in the history
  • Loading branch information
choffmeister committed Feb 14, 2022
1 parent 2395f07 commit 086c11d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.rst
Expand Up @@ -197,6 +197,8 @@ configuration directory. On Linux, this would be ``$XDG_CONFIG_HOME/sops/keys.tx
On macOS, this would be ``$HOME/Library/Application Support/sops/keys.txt``. On
Windows, this would be ``%AppData%\sops\keys.txt``. You can specify the location
of this file manually by setting the environment variable **SOPS_AGE_KEY_FILE**.
Alternatively you can provide the the key(s) directly by setting the **SOPS_AGE_KEY**
environment variable.

The contents of this key file should be a list of age X25519 identities, one
per line. Lines beginning with ``#`` are considered comments and ignored. Each
Expand Down
49 changes: 34 additions & 15 deletions age/keysource.go
Expand Up @@ -96,27 +96,46 @@ func (key *MasterKey) SetEncryptedDataKey(enc []byte) {

// Decrypt decrypts the EncryptedKey field with the age identity and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
ageKeyFilePath, ok := os.LookupEnv("SOPS_AGE_KEY_FILE")
var ageKeyReader io.Reader
var ageKeyReaderName string

if ageKeyReader == nil {
ageKey, ok := os.LookupEnv("SOPS_AGE_KEY")
if ok {
ageKeyReader = strings.NewReader(ageKey)
ageKeyReaderName = "environment variable"
}
}

if !ok {
userConfigDir, err := os.UserConfigDir()
if ageKeyReader == nil {
ageKeyFilePath, ok := os.LookupEnv("SOPS_AGE_KEY_FILE")
if ok {
ageKeyFile, err := os.Open(ageKeyFilePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer ageKeyFile.Close()
ageKeyReader = ageKeyFile
ageKeyReaderName = ageKeyFilePath
}
}

if ageKeyReader == nil {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return nil, fmt.Errorf("user config directory could not be determined: %w", err)
}

ageKeyFilePath = filepath.Join(userConfigDir, "sops", "age", "keys.txt")
}

ageKeyFile, err := os.Open(ageKeyFilePath)

if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
ageKeyFilePath := filepath.Join(userConfigDir, "sops", "age", "keys.txt")
ageKeyFile, err := os.Open(ageKeyFilePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer ageKeyFile.Close()
ageKeyReader = ageKeyFile
ageKeyReaderName = ageKeyFilePath
}

defer ageKeyFile.Close()

identities, err := age.ParseIdentities(ageKeyFile)
identities, err := age.ParseIdentities(ageKeyReader)

if err != nil {
return nil, err
Expand All @@ -127,7 +146,7 @@ func (key *MasterKey) Decrypt() ([]byte, error) {
r, err := age.Decrypt(ar, identities...)

if err != nil {
return nil, fmt.Errorf("no age identity found in %q that could decrypt the data", ageKeyFilePath)
return nil, fmt.Errorf("no age identity found in %q that could decrypt the data", ageKeyReaderName)
}

var b bytes.Buffer
Expand Down
27 changes: 27 additions & 0 deletions age/keysource_test.go
@@ -1,6 +1,7 @@
package age

import (
"io/ioutil"
"os"
"path"
"runtime"
Expand Down Expand Up @@ -71,6 +72,32 @@ DOMAIN=files.127.0.0.1.nip.io`

_, filename, _, _ := runtime.Caller(0)
err = os.Setenv("SOPS_AGE_KEY_FILE", path.Join(path.Dir(filename), "keys.txt"))
defer os.Unsetenv("SOPS_AGE_KEY_FILE")
assert.NoError(err)

decryptedKey, err := key.Decrypt()
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}

func TestAgeEnv(t *testing.T) {
assert := assert.New(t)

key, err := MasterKeyFromRecipient("age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")

assert.NoError(err)
assert.Equal("age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw", key.ToString())

dataKey := []byte("abcdefghijklmnopqrstuvwxyz123456")

err = key.Encrypt(dataKey)
assert.NoError(err)

_, filename, _, _ := runtime.Caller(0)
keysBytes, err := ioutil.ReadFile(path.Join(path.Dir(filename), "keys.txt"))
assert.NoError(err)
err = os.Setenv("SOPS_AGE_KEY", string(keysBytes))
defer os.Unsetenv("SOPS_AGE_KEY")
assert.NoError(err)

decryptedKey, err := key.Decrypt()
Expand Down

0 comments on commit 086c11d

Please sign in to comment.