fix: register NATS/AMQP notify config keys read by parsers

GetNotifyNATS reads user_credentials, nkey_seed and tls_handshake_first
and GetNotifyAMQP reads immediate, but none of them were registered in
DefaultNATSKVS/DefaultAMQPKVS or the help schema, so CheckValidKeys
rejected any enable=on target carrying them. Worse, the legacy config
migration wrote exactly these keys - including the env var name
MINIO_NOTIFY_NATS_USER_CREDENTIALS used as a config key, because the
NATSUserCredentials constant doubled as both - so a migrated NATS config
failed validation on every load, and the FetchEnabledTargets fail-fast
then silently disabled all bucket notification targets.

- Register user_credentials/nkey_seed/tls_handshake_first (NATS) and
  immediate (AMQP) in the default KVS and help schema; split
  NATSUserCredentials into a real config key plus EnvNATSUserCredentials
  (all env var names byte-stable)
- Fix legacy migration: SetNotifyNATS writes the proper key;
  SetNotifyAMQP no longer writes cfg.Immediate under the internal key
  and now carries both immediate and internal
- Tolerate the legacy MINIO_NOTIFY_NATS_USER_CREDENTIALS key written by
  pre-fix migrations (NATS-scoped, load path only) with fallback read;
  env > user_credentials > legacy key
- Print key names only, never values, in the invalid-keys error of both
  CheckValidKeys forms; rejected values can carry credentials
- Add an AST-based audit test asserting parser reads, migration writes
  and help entries stay within the registered key set for all ten notify
  subsystems, with floor assertions so collector drift fails loudly
- Document (unchanged) FetchEnabledTargets fail-fast and pin it with a
  characterization test

Known same-class gap left in place and pinned by the audit's allowlist:
SetNotifyPostgres/SetNotifyMySQL write five unregistered DSN-era keys;
tracked for a follow-up issue.

Closes #39

Co-authored-by: ChatGPT <noreply@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Feng Ruohang
2026-08-03 23:39:06 +08:00
parent fe6dc47804
commit 162ded3438
8 changed files with 1029 additions and 7 deletions
+15 -2
View File
@@ -624,6 +624,19 @@ func LookupSite(siteKV KVS, regionKV KVS) (s Site, err error) {
return s, err
}
// invalidKeyNames returns a comma separated list of the key names in kvs.
//
// Only names are returned, never values: this list is embedded in errors that
// are written to the server log and printed by `mc`, and a rejected key may
// well be carrying a credential.
func invalidKeyNames(kvs KVS) string {
names := make([]string, 0, len(kvs))
for _, kv := range kvs {
names = append(names, kv.Key)
}
return strings.Join(names, ", ")
}
// CheckValidKeys - checks if inputs KVS has the necessary keys,
// returns error if it find extra or superfluous keys.
func CheckValidKeys(subSys string, kv KVS, validKVS KVS, deprecatedKeys ...string) error {
@@ -648,7 +661,7 @@ func CheckValidKeys(subSys string, kv KVS, validKVS KVS, deprecatedKeys ...strin
}
if len(nkv) > 0 {
return Errorf(
"found invalid keys (%s) for '%s' sub-system, use 'mc admin config reset myminio %s' to fix invalid keys", nkv.String(), subSys, subSys)
"found invalid keys (%s) for '%s' sub-system, use 'mc admin config reset myminio %s' to fix invalid keys", invalidKeyNames(nkv), subSys, subSys)
}
return nil
}
@@ -1093,7 +1106,7 @@ func (c Config) CheckValidKeys(subSys string, deprecatedKeys []string) error {
if len(invalidKV) > 0 {
return Errorf(
"found invalid keys (%s) for '%s:%s' sub-system, use 'mc admin config reset myminio %s:%s' to fix invalid keys",
invalidKV.String(), subSys, tgt, subSys, tgt)
invalidKeyNames(invalidKV), subSys, tgt, subSys, tgt)
}
}
return nil