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  }