1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.curoverse.com/arvados.git/sdk/go/arvados"
17 // ExportJSON writes a JSON object with the safe (non-secret) portions
18 // of the cluster config to w.
19 func ExportJSON(w io.Writer, cluster *arvados.Cluster) error {
20 buf, err := json.Marshal(cluster)
24 var m map[string]interface{}
25 err = json.Unmarshal(buf, &m)
29 err = redactUnsafe(m, "", "")
33 return json.NewEncoder(w).Encode(m)
36 // whitelist classifies configs as safe/unsafe to reveal to
37 // unauthenticated clients.
39 // Every config entry must either be listed explicitly here along with
40 // all of its parent keys (e.g., "API" + "API.RequestTimeout"), or
41 // have an ancestor listed as false (e.g.,
42 // "PostgreSQL.Connection.password" has an ancestor
43 // "PostgreSQL.Connection" with a false value). Otherwise, it is a bug
44 // which should be caught by tests.
46 // Example: API.RequestTimeout is safe because whitelist["API"] == and
47 // whitelist["API.RequestTimeout"] == true.
49 // Example: PostgreSQL.Connection.password is not safe because
50 // whitelist["PostgreSQL.Connection"] == false.
52 // Example: PostgreSQL.BadKey would cause an error because
53 // whitelist["PostgreSQL"] isn't false, and neither
54 // whitelist["PostgreSQL.BadKey"] nor whitelist["PostgreSQL.*"]
56 var whitelist = map[string]bool{
59 "API.AsyncPermissionsUpdateInterval": false,
60 "API.DisabledAPIs": false,
61 "API.MaxIndexDatabaseRead": false,
62 "API.MaxItemsPerResponse": true,
63 "API.MaxRequestAmplification": false,
64 "API.MaxRequestSize": true,
65 "API.RailsSessionSecretToken": false,
66 "API.RequestTimeout": true,
68 "AuditLogs.MaxAge": false,
69 "AuditLogs.MaxDeleteBatch": false,
70 "AuditLogs.UnloggedAttributes": false,
72 "Collections.BlobSigning": true,
73 "Collections.BlobSigningKey": false,
74 "Collections.BlobSigningTTL": true,
75 "Collections.CollectionVersioning": false,
76 "Collections.DefaultReplication": true,
77 "Collections.DefaultTrashLifetime": true,
78 "Collections.PreserveVersionIfIdle": true,
79 "Collections.TrashSweepInterval": false,
81 "Containers.CloudVMs": false,
82 "Containers.DefaultKeepCacheRAM": true,
83 "Containers.DispatchPrivateKey": false,
84 "Containers.JobsAPI": true,
85 "Containers.JobsAPI.CrunchJobUser": false,
86 "Containers.JobsAPI.CrunchJobWrapper": false,
87 "Containers.JobsAPI.CrunchRefreshTrigger": false,
88 "Containers.JobsAPI.DefaultDockerImage": false,
89 "Containers.JobsAPI.Enable": true,
90 "Containers.JobsAPI.GitInternalDir": false,
91 "Containers.JobsAPI.ReuseJobIfOutputsDiffer": false,
92 "Containers.Logging": false,
93 "Containers.LogReuseDecisions": false,
94 "Containers.MaxComputeVMs": false,
95 "Containers.MaxDispatchAttempts": false,
96 "Containers.MaxRetryAttempts": true,
97 "Containers.SLURM": false,
98 "Containers.StaleLockTimeout": false,
99 "Containers.SupportedDockerImageFormats": true,
100 "Containers.UsePreemptibleInstances": true,
102 "InstanceTypes": true,
103 "InstanceTypes.*": true,
104 "InstanceTypes.*.*": true,
107 "ManagementToken": false,
109 "RemoteClusters": true,
110 "RemoteClusters.*": true,
111 "RemoteClusters.*.ActivateUsers": true,
112 "RemoteClusters.*.Host": true,
113 "RemoteClusters.*.Insecure": true,
114 "RemoteClusters.*.Proxy": true,
115 "RemoteClusters.*.Scheme": true,
118 "Services.*.ExternalURL": true,
119 "Services.*.InternalURLs": false,
121 "SystemRootToken": false,
127 func redactUnsafe(m map[string]interface{}, mPrefix, lookupPrefix string) error {
129 for k, v := range m {
131 safe, ok := whitelist[lookupPrefix+k]
134 safe, ok = whitelist[lookupPrefix+"*"]
137 errs = append(errs, fmt.Sprintf("config bug: key %q not in whitelist map", lookupPrefix+k))
144 if v, ok := v.(map[string]interface{}); ok {
145 err := redactUnsafe(v, mPrefix+k+".", lookupPrefix+lookupKey+".")
147 errs = append(errs, err.Error())
152 return errors.New(strings.Join(errs, "\n"))