zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/api/config/config.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "os" 6 "time" 7 8 distspec "github.com/opencontainers/distribution-spec/specs-go" 9 10 extconf "zotregistry.io/zot/pkg/extensions/config" 11 storageConstants "zotregistry.io/zot/pkg/storage/constants" 12 ) 13 14 var ( 15 Commit string //nolint: gochecknoglobals 16 ReleaseTag string //nolint: gochecknoglobals 17 BinaryType string //nolint: gochecknoglobals 18 GoVersion string //nolint: gochecknoglobals 19 20 openIDSupportedProviders = [...]string{"google", "gitlab", "oidc"} //nolint: gochecknoglobals 21 oauth2SupportedProviders = [...]string{"github"} //nolint: gochecknoglobals 22 23 ) 24 25 type StorageConfig struct { 26 RootDirectory string 27 Dedupe bool 28 RemoteCache bool 29 GC bool 30 Commit bool 31 GCDelay time.Duration // applied for blobs 32 GCInterval time.Duration 33 Retention ImageRetention 34 StorageDriver map[string]interface{} `mapstructure:",omitempty"` 35 CacheDriver map[string]interface{} `mapstructure:",omitempty"` 36 } 37 38 type ImageRetention struct { 39 DryRun bool 40 Delay time.Duration // applied for referrers and untagged 41 Policies []RetentionPolicy 42 } 43 44 type RetentionPolicy struct { 45 Repositories []string 46 DeleteReferrers bool 47 DeleteUntagged *bool 48 KeepTags []KeepTagsPolicy 49 } 50 51 type KeepTagsPolicy struct { 52 Patterns []string 53 PulledWithin *time.Duration 54 PushedWithin *time.Duration 55 MostRecentlyPushedCount int 56 MostRecentlyPulledCount int 57 } 58 59 type TLSConfig struct { 60 Cert string 61 Key string 62 CACert string 63 } 64 65 type AuthHTPasswd struct { 66 Path string 67 } 68 69 type AuthConfig struct { 70 FailDelay int 71 HTPasswd AuthHTPasswd 72 LDAP *LDAPConfig 73 Bearer *BearerConfig 74 OpenID *OpenIDConfig 75 APIKey bool 76 } 77 78 type BearerConfig struct { 79 Realm string 80 Service string 81 Cert string 82 } 83 84 type OpenIDConfig struct { 85 Providers map[string]OpenIDProviderConfig 86 } 87 88 type OpenIDProviderConfig struct { 89 Name string 90 ClientID string 91 ClientSecret string 92 KeyPath string 93 Issuer string 94 Scopes []string 95 } 96 97 type MethodRatelimitConfig struct { 98 Method string 99 Rate int 100 } 101 102 type RatelimitConfig struct { 103 Rate *int // requests per second 104 Methods []MethodRatelimitConfig `mapstructure:",omitempty"` 105 } 106 107 //nolint:maligned 108 type HTTPConfig struct { 109 Address string 110 ExternalURL string `mapstructure:",omitempty"` 111 Port string 112 AllowOrigin string // comma separated 113 TLS *TLSConfig 114 Auth *AuthConfig 115 AccessControl *AccessControlConfig `mapstructure:"accessControl,omitempty"` 116 Realm string 117 Ratelimit *RatelimitConfig `mapstructure:",omitempty"` 118 } 119 120 type SchedulerConfig struct { 121 NumWorkers int 122 } 123 124 type LDAPCredentials struct { 125 BindDN string 126 BindPassword string 127 } 128 129 type LDAPConfig struct { 130 CredentialsFile string 131 Port int 132 Insecure bool 133 StartTLS bool // if !Insecure, then StartTLS or LDAPs 134 SkipVerify bool 135 SubtreeSearch bool 136 Address string 137 bindDN string `json:"-"` 138 bindPassword string `json:"-"` 139 UserGroupAttribute string 140 BaseDN string 141 UserAttribute string 142 CACert string 143 } 144 145 func (ldapConf *LDAPConfig) BindDN() string { 146 return ldapConf.bindDN 147 } 148 149 func (ldapConf *LDAPConfig) SetBindDN(bindDN string) *LDAPConfig { 150 ldapConf.bindDN = bindDN 151 152 return ldapConf 153 } 154 155 func (ldapConf *LDAPConfig) BindPassword() string { 156 return ldapConf.bindPassword 157 } 158 159 func (ldapConf *LDAPConfig) SetBindPassword(bindPassword string) *LDAPConfig { 160 ldapConf.bindPassword = bindPassword 161 162 return ldapConf 163 } 164 165 type LogConfig struct { 166 Level string 167 Output string 168 Audit string 169 } 170 171 type GlobalStorageConfig struct { 172 StorageConfig `mapstructure:",squash"` 173 SubPaths map[string]StorageConfig 174 } 175 176 type AccessControlConfig struct { 177 Repositories Repositories `json:"repositories" mapstructure:"repositories"` 178 AdminPolicy Policy 179 Groups Groups 180 Metrics Metrics 181 } 182 183 func (config *AccessControlConfig) AnonymousPolicyExists() bool { 184 if config == nil { 185 return false 186 } 187 188 for _, repository := range config.Repositories { 189 if len(repository.AnonymousPolicy) > 0 { 190 return true 191 } 192 } 193 194 return false 195 } 196 197 type ( 198 Repositories map[string]PolicyGroup 199 Groups map[string]Group 200 ) 201 202 type Group struct { 203 Users []string 204 } 205 206 type PolicyGroup struct { 207 Policies []Policy 208 DefaultPolicy []string 209 AnonymousPolicy []string 210 } 211 212 type Policy struct { 213 Users []string 214 Actions []string 215 Groups []string 216 } 217 218 type Metrics struct { 219 Users []string 220 } 221 222 type Config struct { 223 DistSpecVersion string `json:"distSpecVersion" mapstructure:"distSpecVersion"` 224 GoVersion string 225 Commit string 226 ReleaseTag string 227 BinaryType string 228 Storage GlobalStorageConfig 229 HTTP HTTPConfig 230 Log *LogConfig 231 Extensions *extconf.ExtensionConfig 232 Scheduler *SchedulerConfig `json:"scheduler" mapstructure:",omitempty"` 233 } 234 235 func New() *Config { 236 return &Config{ 237 DistSpecVersion: distspec.Version, 238 GoVersion: GoVersion, 239 Commit: Commit, 240 ReleaseTag: ReleaseTag, 241 BinaryType: BinaryType, 242 Storage: GlobalStorageConfig{ 243 StorageConfig: StorageConfig{ 244 Dedupe: true, 245 GC: true, 246 GCDelay: storageConstants.DefaultGCDelay, 247 GCInterval: storageConstants.DefaultGCInterval, 248 Retention: ImageRetention{}, 249 }, 250 }, 251 HTTP: HTTPConfig{Address: "127.0.0.1", Port: "8080", Auth: &AuthConfig{FailDelay: 0}}, 252 Log: &LogConfig{Level: "debug"}, 253 } 254 } 255 256 func (expConfig StorageConfig) ParamsEqual(actConfig StorageConfig) bool { 257 return expConfig.GC == actConfig.GC && expConfig.Dedupe == actConfig.Dedupe && 258 expConfig.GCDelay == actConfig.GCDelay && expConfig.GCInterval == actConfig.GCInterval 259 } 260 261 // SameFile compare two files. 262 // This method will first do the stat of two file and compare using os.SameFile method. 263 func SameFile(str1, str2 string) (bool, error) { 264 sFile, err := os.Stat(str1) 265 if err != nil { 266 return false, err 267 } 268 269 tFile, err := os.Stat(str2) 270 if err != nil { 271 return false, err 272 } 273 274 return os.SameFile(sFile, tFile), nil 275 } 276 277 func DeepCopy(src, dst interface{}) error { 278 bytes, err := json.Marshal(src) 279 if err != nil { 280 return err 281 } 282 err = json.Unmarshal(bytes, dst) 283 284 return err 285 } 286 287 // Sanitize makes a sanitized copy of the config removing any secrets. 288 func (c *Config) Sanitize() *Config { 289 sanitizedConfig := &Config{} 290 291 if err := DeepCopy(c, sanitizedConfig); err != nil { 292 panic(err) 293 } 294 295 if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil && c.HTTP.Auth.LDAP.bindPassword != "" { 296 sanitizedConfig.HTTP.Auth.LDAP = &LDAPConfig{} 297 298 if err := DeepCopy(c.HTTP.Auth.LDAP, sanitizedConfig.HTTP.Auth.LDAP); err != nil { 299 panic(err) 300 } 301 302 sanitizedConfig.HTTP.Auth.LDAP.bindPassword = "******" 303 } 304 305 return sanitizedConfig 306 } 307 308 func (c *Config) IsLdapAuthEnabled() bool { 309 if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil { 310 return true 311 } 312 313 return false 314 } 315 316 func (c *Config) IsMTLSAuthEnabled() bool { 317 if c.HTTP.TLS != nil && 318 c.HTTP.TLS.Key != "" && 319 c.HTTP.TLS.Cert != "" && 320 c.HTTP.TLS.CACert != "" && 321 !c.IsBasicAuthnEnabled() && 322 !c.HTTP.AccessControl.AnonymousPolicyExists() { 323 return true 324 } 325 326 return false 327 } 328 329 func (c *Config) IsHtpasswdAuthEnabled() bool { 330 if c.HTTP.Auth != nil && c.HTTP.Auth.HTPasswd.Path != "" { 331 return true 332 } 333 334 return false 335 } 336 337 func (c *Config) IsBearerAuthEnabled() bool { 338 if c.HTTP.Auth != nil && 339 c.HTTP.Auth.Bearer != nil && 340 c.HTTP.Auth.Bearer.Cert != "" && 341 c.HTTP.Auth.Bearer.Realm != "" && 342 c.HTTP.Auth.Bearer.Service != "" { 343 return true 344 } 345 346 return false 347 } 348 349 func (c *Config) IsOpenIDAuthEnabled() bool { 350 if c.HTTP.Auth != nil && 351 c.HTTP.Auth.OpenID != nil { 352 for provider := range c.HTTP.Auth.OpenID.Providers { 353 if isOpenIDAuthProviderEnabled(c, provider) { 354 return true 355 } 356 } 357 } 358 359 return false 360 } 361 362 func (c *Config) IsAPIKeyEnabled() bool { 363 if c.HTTP.Auth != nil && c.HTTP.Auth.APIKey { 364 return true 365 } 366 367 return false 368 } 369 370 func (c *Config) IsBasicAuthnEnabled() bool { 371 if c.IsHtpasswdAuthEnabled() || c.IsLdapAuthEnabled() || 372 c.IsOpenIDAuthEnabled() || c.IsAPIKeyEnabled() { 373 return true 374 } 375 376 return false 377 } 378 379 func isOpenIDAuthProviderEnabled(config *Config, provider string) bool { 380 if providerConfig, ok := config.HTTP.Auth.OpenID.Providers[provider]; ok { 381 if IsOpenIDSupported(provider) { 382 if providerConfig.ClientID != "" || providerConfig.Issuer != "" || 383 len(providerConfig.Scopes) > 0 { 384 return true 385 } 386 } else if IsOauth2Supported(provider) { 387 if providerConfig.ClientID != "" || len(providerConfig.Scopes) > 0 { 388 return true 389 } 390 } 391 } 392 393 return false 394 } 395 396 func (c *Config) IsMetricsEnabled() bool { 397 return c.Extensions != nil && c.Extensions.Metrics != nil && *c.Extensions.Metrics.Enable 398 } 399 400 func (c *Config) IsSearchEnabled() bool { 401 return c.Extensions != nil && c.Extensions.Search != nil && *c.Extensions.Search.Enable 402 } 403 404 func (c *Config) IsCveScanningEnabled() bool { 405 return c.IsSearchEnabled() && c.Extensions.Search.CVE != nil 406 } 407 408 func (c *Config) IsUIEnabled() bool { 409 return c.Extensions != nil && c.Extensions.UI != nil && *c.Extensions.UI.Enable 410 } 411 412 func (c *Config) AreUserPrefsEnabled() bool { 413 return c.IsSearchEnabled() && c.IsUIEnabled() 414 } 415 416 func (c *Config) IsMgmtEnabled() bool { 417 return c.IsSearchEnabled() 418 } 419 420 func (c *Config) IsImageTrustEnabled() bool { 421 return c.Extensions != nil && c.Extensions.Trust != nil && *c.Extensions.Trust.Enable 422 } 423 424 // check if tags retention is enabled. 425 func (c *Config) IsRetentionEnabled() bool { 426 var needsMetaDB bool 427 428 for _, retentionPolicy := range c.Storage.Retention.Policies { 429 for _, tagRetentionPolicy := range retentionPolicy.KeepTags { 430 if c.isTagsRetentionEnabled(tagRetentionPolicy) { 431 needsMetaDB = true 432 } 433 } 434 } 435 436 for _, subpath := range c.Storage.SubPaths { 437 for _, retentionPolicy := range subpath.Retention.Policies { 438 for _, tagRetentionPolicy := range retentionPolicy.KeepTags { 439 if c.isTagsRetentionEnabled(tagRetentionPolicy) { 440 needsMetaDB = true 441 } 442 } 443 } 444 } 445 446 return needsMetaDB 447 } 448 449 func (c *Config) isTagsRetentionEnabled(tagRetentionPolicy KeepTagsPolicy) bool { 450 if tagRetentionPolicy.MostRecentlyPulledCount != 0 || 451 tagRetentionPolicy.MostRecentlyPushedCount != 0 || 452 tagRetentionPolicy.PulledWithin != nil || 453 tagRetentionPolicy.PushedWithin != nil { 454 return true 455 } 456 457 return false 458 } 459 460 func (c *Config) IsCosignEnabled() bool { 461 return c.IsImageTrustEnabled() && c.Extensions.Trust.Cosign 462 } 463 464 func (c *Config) IsNotationEnabled() bool { 465 return c.IsImageTrustEnabled() && c.Extensions.Trust.Notation 466 } 467 468 func (c *Config) IsSyncEnabled() bool { 469 return c.Extensions != nil && c.Extensions.Sync != nil && *c.Extensions.Sync.Enable 470 } 471 472 func IsOpenIDSupported(provider string) bool { 473 for _, supportedProvider := range openIDSupportedProviders { 474 if supportedProvider == provider { 475 return true 476 } 477 } 478 479 return false 480 } 481 482 func IsOauth2Supported(provider string) bool { 483 for _, supportedProvider := range oauth2SupportedProviders { 484 if supportedProvider == provider { 485 return true 486 } 487 } 488 489 return false 490 }