Merge branch '21642-io-panel-collection-tab-bug' into main. Closes #21642
[arvados.git] / lib / config / export.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package config
6
7 import (
8         "encoding/json"
9         "errors"
10         "fmt"
11         "io"
12         "strings"
13
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15 )
16
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)
21         if err != nil {
22                 return err
23         }
24         var m map[string]interface{}
25         err = json.Unmarshal(buf, &m)
26         if err != nil {
27                 return err
28         }
29
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, "", "")
34         if err != nil {
35                 return err
36         }
37         return json.NewEncoder(w).Encode(m)
38 }
39
40 // whitelist classifies configs as safe/unsafe to reveal through the API
41 // endpoint. Note that endpoint does not require authentication.
42 //
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.
49 //
50 // Example: API.RequestTimeout is safe because whitelist["API"] == and
51 // whitelist["API.RequestTimeout"] == true.
52 //
53 // Example: PostgreSQL.Connection.password is not safe because
54 // whitelist["PostgreSQL.Connection"] == false.
55 //
56 // Example: PostgreSQL.BadKey would cause an error because
57 // whitelist["PostgreSQL"] isn't false, and neither
58 // whitelist["PostgreSQL.BadKey"] nor whitelist["PostgreSQL.*"]
59 // exists.
60 var whitelist = map[string]bool{
61         // | sort -t'"' -k2,2
62         "API":                                      true,
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,
88         "AuditLogs":                                false,
89         "AuditLogs.MaxAge":                         false,
90         "AuditLogs.MaxDeleteBatch":                 false,
91         "AuditLogs.UnloggedAttributes":             false,
92         "ClusterID":                                true,
93         "Collections":                              true,
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,
127         "Containers":                               true,
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,
156         "Git":                                  false,
157         "InstanceTypes":                        true,
158         "InstanceTypes.*":                      true,
159         "InstanceTypes.*.*":                    true,
160         "InstanceTypes.*.*.*":                  true,
161         "Login":                                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,
169         "Login.LDAP":                                          true,
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,
196         "Login.PAM":                                           true,
197         "Login.PAM.DefaultEmailDomain":                        false,
198         "Login.PAM.Enable":                                    true,
199         "Login.PAM.Service":                                   false,
200         "Login.RemoteTokenRefresh":                            true,
201         "Login.Test":                                          true,
202         "Login.Test.Enable":                                   true,
203         "Login.Test.Users":                                    false,
204         "Login.TokenLifetime":                                 false,
205         "Login.TrustedClients":                                false,
206         "Login.TrustPrivateNetworks":                          false,
207         "Mail":                                                true,
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,
216         "PostgreSQL":                                          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,
224         "Services":                                            true,
225         "Services.*":                                          true,
226         "Services.*.ExternalURL":                              true,
227         "Services.*.InternalURLs":                             false,
228         "StorageClasses":                                      true,
229         "StorageClasses.*":                                    true,
230         "StorageClasses.*.Default":                            true,
231         "StorageClasses.*.Priority":                           true,
232         "SystemLogs":                                          false,
233         "SystemRootToken":                                     false,
234         "TLS":                                                 false,
235         "TLS.Certificate":                                     false,
236         "TLS.Insecure":                                        true,
237         "TLS.Key":                                             false,
238         "Users":                                               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,
266         "Volumes":                                             true,
267         "Volumes.*":                                           true,
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,
276         "Workbench":                                           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,
312 }
313
314 func redactUnsafe(m map[string]interface{}, mPrefix, lookupPrefix string) error {
315         var errs []string
316         for k, v := range m {
317                 lookupKey := k
318                 safe, ok := whitelist[lookupPrefix+k]
319                 if !ok {
320                         lookupKey = "*"
321                         safe, ok = whitelist[lookupPrefix+"*"]
322                 }
323                 if !ok {
324                         errs = append(errs, fmt.Sprintf("config bug: key %q not in whitelist map", lookupPrefix+k))
325                         continue
326                 }
327                 if !safe {
328                         delete(m, k)
329                         continue
330                 }
331                 if v, ok := v.(map[string]interface{}); ok {
332                         err := redactUnsafe(v, mPrefix+k+".", lookupPrefix+lookupKey+".")
333                         if err != nil {
334                                 errs = append(errs, err.Error())
335                         }
336                 }
337         }
338         if len(errs) > 0 {
339                 return errors.New(strings.Join(errs, "\n"))
340         }
341         return nil
342 }