1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.arvados.org/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)
30 // ClusterID is not marshalled by default (see `json:"-"`).
31 // Add it back here so it is included in the exported config.
32 m["ClusterID"] = cluster.ClusterID
33 err = redactUnsafe(m, "", "")
37 return json.NewEncoder(w).Encode(m)
40 // whitelist classifies configs as safe/unsafe to reveal through the API
41 // endpoint. Note that endpoint does not require authentication.
43 // Every config entry must either be listed explicitly here along with
44 // all of its parent keys (e.g., "API" + "API.RequestTimeout"), or
45 // have an ancestor listed as false (e.g.,
46 // "PostgreSQL.Connection.password" has an ancestor
47 // "PostgreSQL.Connection" with a false value). Otherwise, it is a bug
48 // which should be caught by tests.
50 // Example: API.RequestTimeout is safe because whitelist["API"] == and
51 // whitelist["API.RequestTimeout"] == true.
53 // Example: PostgreSQL.Connection.password is not safe because
54 // whitelist["PostgreSQL.Connection"] == false.
56 // Example: PostgreSQL.BadKey would cause an error because
57 // whitelist["PostgreSQL"] isn't false, and neither
58 // whitelist["PostgreSQL.BadKey"] nor whitelist["PostgreSQL.*"]
60 var whitelist = map[string]bool{
63 "API.AsyncPermissionsUpdateInterval": false,
64 "API.DisabledAPIs": false,
65 "API.FreezeProjectRequiresDescription": true,
66 "API.FreezeProjectRequiresProperties": true,
67 "API.FreezeProjectRequiresProperties.*": true,
68 "API.KeepServiceRequestTimeout": false,
69 "API.LockBeforeUpdate": false,
70 "API.LogCreateRequestFraction": false,
71 "API.MaxConcurrentRailsRequests": false,
72 "API.MaxConcurrentRequests": false,
73 "API.MaxGatewayTunnels": false,
74 "API.MaxIndexDatabaseRead": false,
75 "API.MaxItemsPerResponse": true,
76 "API.MaxKeepBlobBuffers": false,
77 "API.MaxQueuedRequests": false,
78 "API.MaxQueueTimeForLockRequests": false,
79 "API.MaxRequestAmplification": false,
80 "API.MaxRequestSize": true,
81 "API.MaxTokenLifetime": false,
82 "API.RequestTimeout": true,
83 "API.SendTimeout": true,
84 "API.UnfreezeProjectRequiresAdmin": true,
85 "API.VocabularyPath": false,
86 "API.WebsocketClientEventQueue": false,
87 "API.WebsocketServerEventQueue": false,
89 "AuditLogs.MaxAge": false,
90 "AuditLogs.MaxDeleteBatch": false,
91 "AuditLogs.UnloggedAttributes": false,
94 "Collections.BalanceCollectionBatch": false,
95 "Collections.BalanceCollectionBuffers": false,
96 "Collections.BalancePeriod": false,
97 "Collections.BalancePullLimit": false,
98 "Collections.BalanceTimeout": false,
99 "Collections.BalanceTrashLimit": false,
100 "Collections.BalanceUpdateLimit": false,
101 "Collections.BlobDeleteConcurrency": false,
102 "Collections.BlobMissingReport": false,
103 "Collections.BlobReplicateConcurrency": false,
104 "Collections.BlobSigning": true,
105 "Collections.BlobSigningKey": false,
106 "Collections.BlobSigningTTL": true,
107 "Collections.BlobTrash": false,
108 "Collections.BlobTrashCheckInterval": false,
109 "Collections.BlobTrashConcurrency": false,
110 "Collections.BlobTrashLifetime": false,
111 "Collections.CollectionVersioning": true,
112 "Collections.DefaultReplication": true,
113 "Collections.DefaultTrashLifetime": true,
114 "Collections.ForwardSlashNameSubstitution": true,
115 "Collections.KeepproxyPermission": false,
116 "Collections.ManagedProperties": true,
117 "Collections.ManagedProperties.*": true,
118 "Collections.ManagedProperties.*.*": true,
119 "Collections.PreserveVersionIfIdle": true,
120 "Collections.S3FolderObjects": true,
121 "Collections.TrashSweepInterval": false,
122 "Collections.TrustAllContent": true,
123 "Collections.WebDAVCache": false,
124 "Collections.WebDAVLogEvents": false,
125 "Collections.WebDAVOutputBuffer": false,
126 "Collections.WebDAVPermission": false,
128 "Containers.AlwaysUsePreemptibleInstances": true,
129 "Containers.CloudVMs": false,
130 "Containers.CrunchRunArgumentsList": false,
131 "Containers.CrunchRunCommand": false,
132 "Containers.DefaultKeepCacheRAM": true,
133 "Containers.DispatchPrivateKey": false,
134 "Containers.JobsAPI": true,
135 "Containers.JobsAPI.Enable": true,
136 "Containers.JobsAPI.GitInternalDir": false,
137 "Containers.LocalKeepBlobBuffersPerVCPU": false,
138 "Containers.LocalKeepLogsToContainerLog": false,
139 "Containers.Logging": false,
140 "Containers.LogReuseDecisions": false,
141 "Containers.LSF": false,
142 "Containers.MaxDispatchAttempts": false,
143 "Containers.MaximumPriceFactor": true,
144 "Containers.MaxRetryAttempts": true,
145 "Containers.MinRetryPeriod": true,
146 "Containers.PreemptiblePriceFactor": false,
147 "Containers.ReserveExtraRAM": true,
148 "Containers.RuntimeEngine": true,
149 "Containers.ShellAccess": true,
150 "Containers.ShellAccess.Admin": true,
151 "Containers.ShellAccess.User": true,
152 "Containers.SLURM": false,
153 "Containers.StaleLockTimeout": false,
154 "Containers.SupportedDockerImageFormats": true,
155 "Containers.SupportedDockerImageFormats.*": true,
157 "InstanceTypes": true,
158 "InstanceTypes.*": true,
159 "InstanceTypes.*.*": true,
160 "InstanceTypes.*.*.*": true,
162 "Login.Google": true,
163 "Login.Google.AlternateEmailAddresses": false,
164 "Login.Google.AuthenticationRequestParameters": false,
165 "Login.Google.ClientID": false,
166 "Login.Google.ClientSecret": false,
167 "Login.Google.Enable": true,
168 "Login.IssueTrustedTokens": false,
170 "Login.LDAP.AppendDomain": false,
171 "Login.LDAP.EmailAttribute": false,
172 "Login.LDAP.Enable": true,
173 "Login.LDAP.InsecureTLS": false,
174 "Login.LDAP.MinTLSVersion": false,
175 "Login.LDAP.SearchAttribute": false,
176 "Login.LDAP.SearchBase": false,
177 "Login.LDAP.SearchBindPassword": false,
178 "Login.LDAP.SearchBindUser": false,
179 "Login.LDAP.SearchFilters": false,
180 "Login.LDAP.StartTLS": false,
181 "Login.LDAP.StripDomain": false,
182 "Login.LDAP.URL": false,
183 "Login.LDAP.UsernameAttribute": false,
184 "Login.LoginCluster": true,
185 "Login.OpenIDConnect": true,
186 "Login.OpenIDConnect.AcceptAccessToken": false,
187 "Login.OpenIDConnect.AcceptAccessTokenScope": false,
188 "Login.OpenIDConnect.AuthenticationRequestParameters": false,
189 "Login.OpenIDConnect.ClientID": false,
190 "Login.OpenIDConnect.ClientSecret": false,
191 "Login.OpenIDConnect.EmailClaim": false,
192 "Login.OpenIDConnect.EmailVerifiedClaim": false,
193 "Login.OpenIDConnect.Enable": true,
194 "Login.OpenIDConnect.Issuer": false,
195 "Login.OpenIDConnect.UsernameClaim": false,
197 "Login.PAM.DefaultEmailDomain": false,
198 "Login.PAM.Enable": true,
199 "Login.PAM.Service": false,
200 "Login.RemoteTokenRefresh": true,
202 "Login.Test.Enable": true,
203 "Login.Test.Users": false,
204 "Login.TokenLifetime": false,
205 "Login.TrustedClients": false,
206 "Login.TrustPrivateNetworks": false,
208 "Mail.EmailFrom": false,
209 "Mail.IssueReporterEmailFrom": false,
210 "Mail.IssueReporterEmailTo": false,
211 "Mail.MailchimpAPIKey": false,
212 "Mail.MailchimpListID": false,
213 "Mail.SendUserSetupNotificationEmail": false,
214 "Mail.SupportEmailAddress": true,
215 "ManagementToken": false,
217 "RemoteClusters": true,
218 "RemoteClusters.*": true,
219 "RemoteClusters.*.ActivateUsers": true,
220 "RemoteClusters.*.Host": true,
221 "RemoteClusters.*.Insecure": true,
222 "RemoteClusters.*.Proxy": true,
223 "RemoteClusters.*.Scheme": true,
226 "Services.*.ExternalURL": true,
227 "Services.*.InternalURLs": false,
228 "StorageClasses": true,
229 "StorageClasses.*": true,
230 "StorageClasses.*.Default": true,
231 "StorageClasses.*.Priority": true,
233 "SystemRootToken": false,
235 "TLS.Certificate": false,
236 "TLS.Insecure": true,
239 "Users.ActivatedUsersAreVisibleToOthers": false,
240 "Users.ActivityLoggingPeriod": false,
241 "Users.AdminNotifierEmailFrom": false,
242 "Users.AnonymousUserToken": true,
243 "Users.AutoAdminFirstUser": false,
244 "Users.AutoAdminUserWithEmail": false,
245 "Users.AutoSetupNewUsers": false,
246 "Users.AutoSetupNewUsersWithRepository": false,
247 "Users.AutoSetupNewUsersWithVmUUID": false,
248 "Users.AutoSetupUsernameBlacklist": false,
249 "Users.CanCreateRoleGroups": true,
250 "Users.EmailSubjectPrefix": false,
251 "Users.NewInactiveUserNotificationRecipients": false,
252 "Users.NewUserNotificationRecipients": false,
253 "Users.NewUsersAreActive": false,
254 "Users.PreferDomainForUsername": false,
255 "Users.RoleGroupsVisibleToAll": false,
256 "Users.SyncIgnoredGroups": true,
257 "Users.SyncRequiredGroups": true,
258 "Users.SyncUserAccounts": true,
259 "Users.SyncUserAPITokens": true,
260 "Users.SyncUserGroups": true,
261 "Users.SyncUserSSHKeys": true,
262 "Users.UserNotifierEmailBcc": false,
263 "Users.UserNotifierEmailFrom": false,
264 "Users.UserProfileNotificationAddress": false,
265 "Users.UserSetupMailText": false,
268 "Volumes.*.*": false,
269 "Volumes.*.AccessViaHosts": true,
270 "Volumes.*.AccessViaHosts.*": true,
271 "Volumes.*.AccessViaHosts.*.ReadOnly": true,
272 "Volumes.*.ReadOnly": true,
273 "Volumes.*.Replication": true,
274 "Volumes.*.StorageClasses": true,
275 "Volumes.*.StorageClasses.*": true,
277 "Workbench.ActivationContactLink": false,
278 "Workbench.APIClientConnectTimeout": true,
279 "Workbench.APIClientReceiveTimeout": true,
280 "Workbench.APIResponseCompression": true,
281 "Workbench.ApplicationMimetypesWithViewIcon": true,
282 "Workbench.ApplicationMimetypesWithViewIcon.*": true,
283 "Workbench.ArvadosDocsite": true,
284 "Workbench.ArvadosPublicDataDocURL": true,
285 "Workbench.BannerUUID": true,
286 "Workbench.DefaultOpenIdPrefix": false,
287 "Workbench.DisableSharingURLsUI": true,
288 "Workbench.EnableGettingStartedPopup": true,
289 "Workbench.EnablePublicProjectsPage": true,
290 "Workbench.FileViewersConfigURL": true,
291 "Workbench.IdleTimeout": true,
292 "Workbench.InactivePageHTML": true,
293 "Workbench.LogViewerMaxBytes": true,
294 "Workbench.MultiSiteSearch": true,
295 "Workbench.ProfilingEnabled": true,
296 "Workbench.Repositories": false,
297 "Workbench.RepositoryCache": false,
298 "Workbench.RunningJobLogRecordsToFetch": true,
299 "Workbench.ShowRecentCollectionsOnDashboard": true,
300 "Workbench.ShowUserAgreementInline": true,
301 "Workbench.ShowUserNotifications": true,
302 "Workbench.SiteName": true,
303 "Workbench.SSHHelpHostSuffix": true,
304 "Workbench.SSHHelpPageHTML": true,
305 "Workbench.Theme": true,
306 "Workbench.UserProfileFormFields": true,
307 "Workbench.UserProfileFormFields.*": true,
308 "Workbench.UserProfileFormFields.*.*": true,
309 "Workbench.UserProfileFormFields.*.*.*": true,
310 "Workbench.UserProfileFormMessage": true,
311 "Workbench.WelcomePageHTML": true,
314 func redactUnsafe(m map[string]interface{}, mPrefix, lookupPrefix string) error {
316 for k, v := range m {
318 safe, ok := whitelist[lookupPrefix+k]
321 safe, ok = whitelist[lookupPrefix+"*"]
324 errs = append(errs, fmt.Sprintf("config bug: key %q not in whitelist map", lookupPrefix+k))
331 if v, ok := v.(map[string]interface{}); ok {
332 err := redactUnsafe(v, mPrefix+k+".", lookupPrefix+lookupKey+".")
334 errs = append(errs, err.Error())
339 return errors.New(strings.Join(errs, "\n"))