From 17b5c02ef01a96ae5fe29c1b886ade5737aaad44 Mon Sep 17 00:00:00 2001 From: James Cleverley-Prance Date: Tue, 7 Jul 2026 13:25:49 +0000 Subject: [PATCH] feat: add keyctl support --- pkg/config/config.go | 2 ++ pkg/granted/settings/set.go | 51 ++++++++++++++++++++++-------- pkg/securestorage/securestorage.go | 10 ++++++ 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index a4464e52..08ca33b4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -108,6 +108,8 @@ type KeyringConfig struct { FileDir *string `toml:",omitempty"` LibSecretCollectionName *string `toml:",omitempty"` PassDir *string `toml:",omitempty"` // PassDir is the pass password-store directory, ~/ is resolved to the users' home dir + KeyCtlScope *string `toml:",omitempty"` + KeyCtlPerm *uint32 `toml:",omitempty"` } type Registry struct { diff --git a/pkg/granted/settings/set.go b/pkg/granted/settings/set.go index ebe85955..6223a8ec 100644 --- a/pkg/granted/settings/set.go +++ b/pkg/granted/settings/set.go @@ -31,12 +31,14 @@ var SetConfigCommand = cli.Command{ cfg.Keyring = &config.KeyringConfig{} } - // custom mapping for the keychain fields because the field options generator doesn;t work for nillable fields - fieldMap["Keyring.Backend"] = keyringFields{&cfg.Keyring.Backend} - fieldMap["Keyring.KeychainName"] = keyringFields{&cfg.Keyring.KeychainName} - fieldMap["Keyring.FileDir"] = keyringFields{&cfg.Keyring.FileDir} - fieldMap["Keyring.LibSecretCollectionName"] = keyringFields{&cfg.Keyring.LibSecretCollectionName} - fieldMap["Keyring.PassDir"] = keyringFields{&cfg.Keyring.PassDir} + // custom mapping for the keychain fields because the field options generator doesn't work for nillable fields + fieldMap["Keyring.Backend"] = keyringFields[string]{&cfg.Keyring.Backend} + fieldMap["Keyring.KeychainName"] = keyringFields[string]{&cfg.Keyring.KeychainName} + fieldMap["Keyring.FileDir"] = keyringFields[string]{&cfg.Keyring.FileDir} + fieldMap["Keyring.LibSecretCollectionName"] = keyringFields[string]{&cfg.Keyring.LibSecretCollectionName} + fieldMap["Keyring.PassDir"] = keyringFields[string]{&cfg.Keyring.PassDir} + fieldMap["Keyring.KeyCtlScope"] = keyringFields[string]{&cfg.Keyring.KeyCtlScope} + fieldMap["Keyring.KeyCtlPerm"] = keyringFields[uint32]{&cfg.Keyring.KeyCtlPerm} fields := make([]string, 0, len(fieldMap)) for k := range fieldMap { @@ -115,8 +117,28 @@ var SetConfigCommand = cli.Command{ return err } } + case reflect.Uint32: + if !c.IsSet("value") { + prompt = &survey.Input{ + Message: fmt.Sprintf("Enter new value for %s:", selectedFieldName), + Default: fmt.Sprintf("%v", selectedField.Value()), + } + err = survey.AskOne(prompt, &value) + if err != nil { + return err + } + } else { + valueUint := c.String("value") + value, err = strconv.ParseUint(valueUint, 10, 32) + if err != nil { + return err + } + value = uint32(value.(uint64)) + + } } + err = selectedField.Set(value) if err != nil { return err @@ -138,24 +160,25 @@ type Field interface { Kind() reflect.Kind } -type keyringFields struct { +type keyringFields[T string | uint32] struct { // double pointer here is a pointer to a pointer value in the config // so that we can initialise it if it is unset - field **string + field **T } -func (f keyringFields) Set(value any) error { +func (f keyringFields[T]) Set(value any) error { if *f.field == nil { - *f.field = new(string) + *f.field = new(T) } - **f.field = value.(string) + **f.field = value.(T) return nil } -func (f keyringFields) Value() any { +func (f keyringFields[T]) Value() any { return grab.Value(grab.Value(f.field)) } -func (f keyringFields) Kind() reflect.Kind { - return reflect.String +func (f keyringFields[T]) Kind() reflect.Kind { + v := reflect.ValueOf(f.Value()) + return v.Kind() } type field struct { diff --git a/pkg/securestorage/securestorage.go b/pkg/securestorage/securestorage.go index c8b4b458..c827d3e5 100644 --- a/pkg/securestorage/securestorage.go +++ b/pkg/securestorage/securestorage.go @@ -135,6 +135,10 @@ func (s *SecureStorage) openKeyring() (keyring.Keyring, error) { FileDir: secureStoragePath, FilePasswordFunc: keyring.FixedStringPrompt(os.Getenv("CF_KEYRING_FILE_PASSWORD")), + + // Linux kernel keyring + KeyCtlScope: "user", + KeyCtlPerm: 0x3f0f0000, } // enable debug logging if the verbose flag is set in the CLI @@ -156,6 +160,12 @@ func (s *SecureStorage) openKeyring() (keyring.Keyring, error) { if cfg.Keyring.PassDir != nil { c.PassDir = *cfg.Keyring.PassDir } + if cfg.Keyring.KeyCtlScope != nil { + c.KeyCtlScope = *cfg.Keyring.KeyCtlScope + } + if cfg.Keyring.KeyCtlPerm != nil { + c.KeyCtlPerm = *cfg.Keyring.KeyCtlPerm + } } k, err := keyring.Open(c)