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": true,
60 "API.DisabledAPIs": true,
61 "API.MaxIndexDatabaseRead": true,
62 "API.MaxItemsPerResponse": true,
63 "API.MaxRequestAmplification": true,
64 "API.MaxRequestSize": true,
65 "API.RailsSessionSecretToken": false,
66 "API.RequestTimeout": true,
68 "AuditLogs.MaxAge": true,
69 "AuditLogs.MaxDeleteBatch": true,
70 "AuditLogs.UnloggedAttributes": true,
72 "Collections.BlobSigning": true,
73 "Collections.BlobSigningKey": false,
74 "Collections.BlobSigningTTL": true,
75 "Collections.CollectionVersioning": true,
76 "Collections.DefaultReplication": true,
77 "Collections.DefaultTrashLifetime": true,
78 "Collections.PreserveVersionIfIdle": true,
79 "Collections.TrashSweepInterval": true,
81 "Containers.CloudVMs": true,
82 "Containers.CloudVMs.BootProbeCommand": true,
83 "Containers.CloudVMs.Driver": true,
84 "Containers.CloudVMs.DriverParameters": false,
85 "Containers.CloudVMs.Enable": true,
86 "Containers.CloudVMs.ImageID": true,
87 "Containers.CloudVMs.MaxCloudOpsPerSecond": true,
88 "Containers.CloudVMs.MaxProbesPerSecond": true,
89 "Containers.CloudVMs.PollInterval": true,
90 "Containers.CloudVMs.ProbeInterval": true,
91 "Containers.CloudVMs.ResourceTags": true,
92 "Containers.CloudVMs.ResourceTags.*": true,
93 "Containers.CloudVMs.SSHPort": true,
94 "Containers.CloudVMs.SyncInterval": true,
95 "Containers.CloudVMs.TagKeyPrefix": true,
96 "Containers.CloudVMs.TimeoutBooting": true,
97 "Containers.CloudVMs.TimeoutIdle": true,
98 "Containers.CloudVMs.TimeoutProbe": true,
99 "Containers.CloudVMs.TimeoutShutdown": true,
100 "Containers.CloudVMs.TimeoutSignal": true,
101 "Containers.CloudVMs.TimeoutTERM": true,
102 "Containers.DefaultKeepCacheRAM": true,
103 "Containers.DispatchPrivateKey": true,
104 "Containers.JobsAPI": true,
105 "Containers.JobsAPI.CrunchJobUser": true,
106 "Containers.JobsAPI.CrunchJobWrapper": true,
107 "Containers.JobsAPI.CrunchRefreshTrigger": true,
108 "Containers.JobsAPI.DefaultDockerImage": true,
109 "Containers.JobsAPI.Enable": true,
110 "Containers.JobsAPI.GitInternalDir": true,
111 "Containers.JobsAPI.ReuseJobIfOutputsDiffer": true,
112 "Containers.Logging": true,
113 "Containers.Logging.LimitLogBytesPerJob": true,
114 "Containers.Logging.LogBytesPerEvent": true,
115 "Containers.Logging.LogPartialLineThrottlePeriod": true,
116 "Containers.Logging.LogSecondsBetweenEvents": true,
117 "Containers.Logging.LogThrottleBytes": true,
118 "Containers.Logging.LogThrottleLines": true,
119 "Containers.Logging.LogThrottlePeriod": true,
120 "Containers.Logging.LogUpdatePeriod": true,
121 "Containers.Logging.LogUpdateSize": true,
122 "Containers.Logging.MaxAge": true,
123 "Containers.LogReuseDecisions": true,
124 "Containers.MaxComputeVMs": true,
125 "Containers.MaxDispatchAttempts": true,
126 "Containers.MaxRetryAttempts": true,
127 "Containers.SLURM": true,
128 "Containers.SLURM.Managed": true,
129 "Containers.SLURM.Managed.AssignNodeHostname": true,
130 "Containers.SLURM.Managed.ComputeNodeDomain": false,
131 "Containers.SLURM.Managed.ComputeNodeNameservers": false,
132 "Containers.SLURM.Managed.DNSServerConfDir": true,
133 "Containers.SLURM.Managed.DNSServerConfTemplate": true,
134 "Containers.SLURM.Managed.DNSServerReloadCommand": false,
135 "Containers.SLURM.Managed.DNSServerUpdateCommand": false,
136 "Containers.StaleLockTimeout": true,
137 "Containers.SupportedDockerImageFormats": true,
138 "Containers.UsePreemptibleInstances": true,
140 "Git.Repositories": true,
141 "InstanceTypes": true,
142 "InstanceTypes.*": true,
143 "InstanceTypes.*.*": true,
145 "Login.ProviderAppID": false,
146 "Login.ProviderAppSecret": false,
148 "Mail.EmailFrom": true,
149 "Mail.IssueReporterEmailFrom": true,
150 "Mail.IssueReporterEmailTo": true,
151 "Mail.MailchimpAPIKey": false,
152 "Mail.MailchimpListID": false,
153 "Mail.SendUserSetupNotificationEmail": true,
154 "Mail.SupportEmailAddress": true,
155 "ManagementToken": false,
157 "PostgreSQL.Connection": false,
158 "PostgreSQL.ConnectionPool": true,
159 "RemoteClusters": true,
160 "RemoteClusters.*": true,
161 "RemoteClusters.*.ActivateUsers": true,
162 "RemoteClusters.*.Host": true,
163 "RemoteClusters.*.Insecure": true,
164 "RemoteClusters.*.Proxy": true,
165 "RemoteClusters.*.Scheme": true,
168 "Services.*.ExternalURL": true,
169 "Services.*.InternalURLs": true,
170 "Services.*.InternalURLs.*": true,
171 "Services.*.InternalURLs.*.*": true,
173 "SystemLogs.Format": true,
174 "SystemLogs.LogLevel": true,
175 "SystemLogs.MaxRequestLogParamsSize": true,
176 "SystemRootToken": false,
178 "TLS.Certificate": true,
179 "TLS.Insecure": true,
182 "Users.AdminNotifierEmailFrom": true,
183 "Users.AutoAdminFirstUser": false,
184 "Users.AutoAdminUserWithEmail": false,
185 "Users.AutoSetupNewUsers": true,
186 "Users.AutoSetupNewUsersWithRepository": true,
187 "Users.AutoSetupNewUsersWithVmUUID": true,
188 "Users.AutoSetupUsernameBlacklist": false,
189 "Users.EmailSubjectPrefix": true,
190 "Users.NewInactiveUserNotificationRecipients": false,
191 "Users.NewUserNotificationRecipients": false,
192 "Users.NewUsersAreActive": true,
193 "Users.UserNotifierEmailFrom": true,
194 "Users.UserProfileNotificationAddress": true,
196 "Workbench.ActivationContactLink": true,
197 "Workbench.APIClientConnectTimeout": true,
198 "Workbench.APIClientReceiveTimeout": true,
199 "Workbench.APIResponseCompression": true,
200 "Workbench.ApplicationMimetypesWithViewIcon": true,
201 "Workbench.ApplicationMimetypesWithViewIcon.*": true,
202 "Workbench.ArvadosDocsite": true,
203 "Workbench.ArvadosPublicDataDocURL": true,
204 "Workbench.EnableGettingStartedPopup": true,
205 "Workbench.EnablePublicProjectsPage": true,
206 "Workbench.FileViewersConfigURL": true,
207 "Workbench.LogViewerMaxBytes": true,
208 "Workbench.MultiSiteSearch": true,
209 "Workbench.Repositories": true,
210 "Workbench.RepositoryCache": true,
211 "Workbench.RunningJobLogRecordsToFetch": true,
212 "Workbench.SecretKeyBase": false,
213 "Workbench.SecretToken": false,
214 "Workbench.ShowRecentCollectionsOnDashboard": true,
215 "Workbench.ShowUserAgreementInline": true,
216 "Workbench.ShowUserNotifications": true,
217 "Workbench.SiteName": true,
218 "Workbench.Theme": true,
219 "Workbench.UserProfileFormFields": true,
220 "Workbench.UserProfileFormFields.*": true,
221 "Workbench.UserProfileFormFields.*.Type": true,
222 "Workbench.UserProfileFormFields.*.FormFieldTitle": true,
223 "Workbench.UserProfileFormFields.*.FormFieldDescription": true,
224 "Workbench.UserProfileFormFields.*.Required": true,
225 "Workbench.UserProfileFormMessage": true,
226 "Workbench.VocabularyURL": true,
229 func redactUnsafe(m map[string]interface{}, mPrefix, lookupPrefix string) error {
231 for k, v := range m {
233 safe, ok := whitelist[lookupPrefix+k]
236 safe, ok = whitelist[lookupPrefix+"*"]
239 errs = append(errs, fmt.Sprintf("config bug: key %q not in whitelist map", lookupPrefix+k))
246 if v, ok := v.(map[string]interface{}); ok {
247 err := redactUnsafe(v, mPrefix+k+".", lookupPrefix+lookupKey+".")
249 errs = append(errs, err.Error())
254 return errors.New(strings.Join(errs, "\n"))