Skip to content

Commit 06fa965

Browse files
committed
Allow to have space-separated keys in SOPS_AGE_KEY.
Signed-off-by: Felix Fontein <felix@fontein.de>
1 parent e3e51e9 commit 06fa965

2 files changed

Lines changed: 46 additions & 15 deletions

File tree

age/encrypted_keys.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err
106106
return fileKey, err
107107
}
108108

109-
func unwrapIdentities(location string, reader io.Reader) (ParsedIdentities, error) {
109+
func unwrapIdentities(location string, reader io.Reader, allowMultipleKeysPerLine bool) (ParsedIdentities, error) {
110110
b := bufio.NewReader(reader)
111111
p, _ := b.Peek(14) // length of "age-encryption" and "-----BEGIN AGE"
112112
peeked := string(p)
@@ -180,7 +180,7 @@ func unwrapIdentities(location string, reader io.Reader) (ParsedIdentities, erro
180180
return ids, nil
181181
// An unencrypted age identity file.
182182
default:
183-
ids, err := parseIdentities(b)
183+
ids, err := parseIdentities(b, allowMultipleKeysPerLine)
184184
if err != nil {
185185
return nil, fmt.Errorf("failed to parse '%s' age identities: %w", location, err)
186186
}

age/keysource.go

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (i *ParsedIdentities) Import(identity ...string) error {
145145
// one identity per line
146146
r := strings.NewReader(strings.Join(identity, "\n"))
147147

148-
identities, err := parseIdentities(r)
148+
identities, err := parseIdentities(r, false)
149149
if err != nil {
150150
return fmt.Errorf("failed to parse and add to age identities: %w", err)
151151
}
@@ -399,17 +399,25 @@ func getUserConfigDir() (string, error) {
399399
return os.UserConfigDir()
400400
}
401401

402+
type identityReader struct {
403+
reader io.Reader
404+
allowMultipleKeysPerLine bool
405+
}
406+
402407
// loadIdentities attempts to load the age identities based on runtime
403408
// environment configurations (e.g. SopsAgeKeyEnv, SopsAgeKeyFileEnv,
404409
// SopsAgeSshPrivateKeyFileEnv, SopsAgeKeyUserConfigPath). It will load all
405410
// found references, and expects at least one configuration to be present.
406411
func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
407412
identities, unusedLocations, errs := key.loadAgeSSHIdentities()
408413

409-
var readers = make(map[string]io.Reader, 0)
414+
var readers = make(map[string]identityReader, 0)
410415

411416
if ageKey, ok := os.LookupEnv(SopsAgeKeyEnv); ok {
412-
readers[SopsAgeKeyEnv] = strings.NewReader(ageKey)
417+
readers[SopsAgeKeyEnv] = identityReader{
418+
reader: strings.NewReader(ageKey),
419+
allowMultipleKeysPerLine: true,
420+
}
413421
} else {
414422
unusedLocations = append(unusedLocations, SopsAgeKeyEnv)
415423
}
@@ -420,7 +428,10 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
420428
errs = append(errs, fmt.Errorf("failed to open %s file: %w", SopsAgeKeyFileEnv, err))
421429
} else {
422430
defer f.Close()
423-
readers[SopsAgeKeyFileEnv] = f
431+
readers[SopsAgeKeyFileEnv] = identityReader{
432+
reader: f,
433+
allowMultipleKeysPerLine: false,
434+
}
424435
}
425436
} else {
426437
unusedLocations = append(unusedLocations, SopsAgeKeyFileEnv)
@@ -431,7 +442,10 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
431442
if err != nil {
432443
errs = append(errs, err)
433444
} else {
434-
readers[SopsAgeKeyCmdEnv] = bytes.NewReader(out)
445+
readers[SopsAgeKeyCmdEnv] = identityReader{
446+
reader: bytes.NewReader(out),
447+
allowMultipleKeysPerLine: false,
448+
}
435449
}
436450
} else {
437451
unusedLocations = append(unusedLocations, SopsAgeKeyCmdEnv)
@@ -449,12 +463,15 @@ func (key *MasterKey) loadIdentities() (ParsedIdentities, []string, errSet) {
449463
unusedLocations = append(unusedLocations, ageKeyFilePath)
450464
} else if err == nil {
451465
defer f.Close()
452-
readers[ageKeyFilePath] = f
466+
readers[ageKeyFilePath] = identityReader{
467+
reader: f,
468+
allowMultipleKeysPerLine: false,
469+
}
453470
}
454471
}
455472

456473
for location, r := range readers {
457-
ids, err := unwrapIdentities(location, r)
474+
ids, err := unwrapIdentities(location, r.reader, r.allowMultipleKeysPerLine)
458475
if err != nil {
459476
errs = append(errs, err)
460477
} else {
@@ -505,7 +522,9 @@ func parseRecipient(recipient string) (age.Recipient, error) {
505522
// parseIdentities attempts to parse one or more age identities from the provided reader.
506523
// One identity per line.
507524
// Empty lines and lines starting with "#" are ignored.
508-
func parseIdentities(r io.Reader) (ParsedIdentities, error) {
525+
// If allowMultipleKeysPerLine is true, every non-empty lines is split by words,
526+
// and every word is parsed as an identity.
527+
func parseIdentities(r io.Reader, allowMultipleKeysPerLine bool) (ParsedIdentities, error) {
509528
var identities ParsedIdentities
510529

511530
scanner := bufio.NewScanner(r)
@@ -517,12 +536,24 @@ func parseIdentities(r io.Reader) (ParsedIdentities, error) {
517536
continue
518537
}
519538

520-
parsed, err := parseIdentity(line)
521-
if err != nil {
522-
return nil, err
539+
if allowMultipleKeysPerLine {
540+
lineScanner := bufio.NewScanner(strings.NewReader(line))
541+
lineScanner.Split(bufio.ScanWords)
542+
for lineScanner.Scan() {
543+
word := lineScanner.Text()
544+
parsed, err := parseIdentity(word)
545+
if err != nil {
546+
return nil, err
547+
}
548+
identities = append(identities, parsed)
549+
}
550+
} else {
551+
parsed, err := parseIdentity(line)
552+
if err != nil {
553+
return nil, err
554+
}
555+
identities = append(identities, parsed)
523556
}
524-
525-
identities = append(identities, parsed)
526557
}
527558

528559
return identities, nil

0 commit comments

Comments
 (0)