mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 20:38:08 +08:00
b80deb9927
unified both local and s3 ImageStore logic into a single ImageStore added a new driver interface for common file/dirs manipulations to be implemented by different storage types refactor(gc): drop umoci dependency, implemented internal gc added retentionDelay config option that specifies the garbage collect delay for images without tags this will also clean manifests which are part of an index image (multiarch) that no longer exist. fix(dedupe): skip blobs under .sync/ directory if startup dedupe is running while also syncing is running ignore blobs under sync's temporary storage fix(storage): do not allow image indexes modifications when deleting a manifest verify that it is not part of a multiarch image and throw a MethodNotAllowed error to the client if it is. we don't want to modify multiarch images Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
370 lines
8.5 KiB
Go
370 lines
8.5 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/getlantern/deepcopy"
|
|
distspec "github.com/opencontainers/distribution-spec/specs-go"
|
|
|
|
extconf "zotregistry.io/zot/pkg/extensions/config"
|
|
storageConstants "zotregistry.io/zot/pkg/storage/constants"
|
|
)
|
|
|
|
var (
|
|
Commit string //nolint: gochecknoglobals
|
|
ReleaseTag string //nolint: gochecknoglobals
|
|
BinaryType string //nolint: gochecknoglobals
|
|
GoVersion string //nolint: gochecknoglobals
|
|
|
|
openIDSupportedProviders = [...]string{"google", "gitlab", "oidc"} //nolint: gochecknoglobals
|
|
oauth2SupportedProviders = [...]string{"github"} //nolint: gochecknoglobals
|
|
|
|
)
|
|
|
|
type StorageConfig struct {
|
|
RootDirectory string
|
|
Dedupe bool
|
|
RemoteCache bool
|
|
GC bool
|
|
Commit bool
|
|
GCDelay time.Duration
|
|
GCInterval time.Duration
|
|
GCReferrers bool
|
|
UntaggedImageRetentionDelay time.Duration
|
|
StorageDriver map[string]interface{} `mapstructure:",omitempty"`
|
|
CacheDriver map[string]interface{} `mapstructure:",omitempty"`
|
|
}
|
|
|
|
type TLSConfig struct {
|
|
Cert string
|
|
Key string
|
|
CACert string
|
|
}
|
|
|
|
type AuthHTPasswd struct {
|
|
Path string
|
|
}
|
|
|
|
type AuthConfig struct {
|
|
FailDelay int
|
|
HTPasswd AuthHTPasswd
|
|
LDAP *LDAPConfig
|
|
Bearer *BearerConfig
|
|
OpenID *OpenIDConfig
|
|
APIKey bool
|
|
}
|
|
|
|
type BearerConfig struct {
|
|
Realm string
|
|
Service string
|
|
Cert string
|
|
}
|
|
|
|
type OpenIDConfig struct {
|
|
Providers map[string]OpenIDProviderConfig
|
|
}
|
|
|
|
type OpenIDProviderConfig struct {
|
|
Name string
|
|
ClientID string
|
|
ClientSecret string
|
|
KeyPath string
|
|
Issuer string
|
|
Scopes []string
|
|
}
|
|
|
|
type MethodRatelimitConfig struct {
|
|
Method string
|
|
Rate int
|
|
}
|
|
|
|
type RatelimitConfig struct {
|
|
Rate *int // requests per second
|
|
Methods []MethodRatelimitConfig `mapstructure:",omitempty"`
|
|
}
|
|
|
|
//nolint:maligned
|
|
type HTTPConfig struct {
|
|
Address string
|
|
ExternalURL string `mapstructure:",omitempty"`
|
|
Port string
|
|
AllowOrigin string // comma separated
|
|
TLS *TLSConfig
|
|
Auth *AuthConfig
|
|
AccessControl *AccessControlConfig `mapstructure:"accessControl,omitempty"`
|
|
Realm string
|
|
Ratelimit *RatelimitConfig `mapstructure:",omitempty"`
|
|
}
|
|
|
|
type SchedulerConfig struct {
|
|
NumWorkers int
|
|
}
|
|
|
|
type LDAPConfig struct {
|
|
Port int
|
|
Insecure bool
|
|
StartTLS bool // if !Insecure, then StartTLS or LDAPs
|
|
SkipVerify bool
|
|
SubtreeSearch bool
|
|
Address string
|
|
BindDN string
|
|
UserGroupAttribute string
|
|
BindPassword string
|
|
BaseDN string
|
|
UserAttribute string
|
|
CACert string
|
|
}
|
|
|
|
type LogConfig struct {
|
|
Level string
|
|
Output string
|
|
Audit string
|
|
}
|
|
|
|
type GlobalStorageConfig struct {
|
|
StorageConfig `mapstructure:",squash"`
|
|
SubPaths map[string]StorageConfig
|
|
}
|
|
|
|
type AccessControlConfig struct {
|
|
Repositories Repositories `json:"repositories" mapstructure:"repositories"`
|
|
AdminPolicy Policy
|
|
Groups Groups
|
|
}
|
|
|
|
func (config *AccessControlConfig) AnonymousPolicyExists() bool {
|
|
if config == nil {
|
|
return false
|
|
}
|
|
|
|
for _, repository := range config.Repositories {
|
|
if len(repository.AnonymousPolicy) > 0 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type (
|
|
Repositories map[string]PolicyGroup
|
|
Groups map[string]Group
|
|
)
|
|
|
|
type Group struct {
|
|
Users []string
|
|
}
|
|
|
|
type PolicyGroup struct {
|
|
Policies []Policy
|
|
DefaultPolicy []string
|
|
AnonymousPolicy []string
|
|
}
|
|
|
|
type Policy struct {
|
|
Users []string
|
|
Actions []string
|
|
Groups []string
|
|
}
|
|
|
|
type Config struct {
|
|
DistSpecVersion string `json:"distSpecVersion" mapstructure:"distSpecVersion"`
|
|
GoVersion string
|
|
Commit string
|
|
ReleaseTag string
|
|
BinaryType string
|
|
Storage GlobalStorageConfig
|
|
HTTP HTTPConfig
|
|
Log *LogConfig
|
|
Extensions *extconf.ExtensionConfig
|
|
Scheduler *SchedulerConfig `json:"scheduler" mapstructure:",omitempty"`
|
|
}
|
|
|
|
func New() *Config {
|
|
return &Config{
|
|
DistSpecVersion: distspec.Version,
|
|
GoVersion: GoVersion,
|
|
Commit: Commit,
|
|
ReleaseTag: ReleaseTag,
|
|
BinaryType: BinaryType,
|
|
Storage: GlobalStorageConfig{
|
|
StorageConfig: StorageConfig{
|
|
GC: true, GCReferrers: true, GCDelay: storageConstants.DefaultGCDelay,
|
|
UntaggedImageRetentionDelay: storageConstants.DefaultUntaggedImgeRetentionDelay,
|
|
GCInterval: storageConstants.DefaultGCInterval, Dedupe: true,
|
|
},
|
|
},
|
|
HTTP: HTTPConfig{Address: "127.0.0.1", Port: "8080", Auth: &AuthConfig{FailDelay: 0}},
|
|
Log: &LogConfig{Level: "debug"},
|
|
}
|
|
}
|
|
|
|
func (expConfig StorageConfig) ParamsEqual(actConfig StorageConfig) bool {
|
|
return expConfig.GC == actConfig.GC && expConfig.Dedupe == actConfig.Dedupe &&
|
|
expConfig.GCDelay == actConfig.GCDelay && expConfig.GCInterval == actConfig.GCInterval
|
|
}
|
|
|
|
// SameFile compare two files.
|
|
// This method will first do the stat of two file and compare using os.SameFile method.
|
|
func SameFile(str1, str2 string) (bool, error) {
|
|
sFile, err := os.Stat(str1)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
tFile, err := os.Stat(str2)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return os.SameFile(sFile, tFile), nil
|
|
}
|
|
|
|
// Sanitize makes a sanitized copy of the config removing any secrets.
|
|
func (c *Config) Sanitize() *Config {
|
|
sanitizedConfig := &Config{}
|
|
if err := deepcopy.Copy(sanitizedConfig, c); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil && c.HTTP.Auth.LDAP.BindPassword != "" {
|
|
sanitizedConfig.HTTP.Auth.LDAP = &LDAPConfig{}
|
|
|
|
if err := deepcopy.Copy(sanitizedConfig.HTTP.Auth.LDAP, c.HTTP.Auth.LDAP); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
sanitizedConfig.HTTP.Auth.LDAP.BindPassword = "******"
|
|
}
|
|
|
|
return sanitizedConfig
|
|
}
|
|
|
|
func (c *Config) IsLdapAuthEnabled() bool {
|
|
if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsHtpasswdAuthEnabled() bool {
|
|
if c.HTTP.Auth != nil && c.HTTP.Auth.HTPasswd.Path != "" {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsBearerAuthEnabled() bool {
|
|
if c.HTTP.Auth != nil &&
|
|
c.HTTP.Auth.Bearer != nil &&
|
|
c.HTTP.Auth.Bearer.Cert != "" &&
|
|
c.HTTP.Auth.Bearer.Realm != "" &&
|
|
c.HTTP.Auth.Bearer.Service != "" {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsOpenIDAuthEnabled() bool {
|
|
if c.HTTP.Auth != nil &&
|
|
c.HTTP.Auth.OpenID != nil {
|
|
for provider := range c.HTTP.Auth.OpenID.Providers {
|
|
if isOpenIDAuthProviderEnabled(c, provider) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsAPIKeyEnabled() bool {
|
|
if c.HTTP.Auth != nil && c.HTTP.Auth.APIKey {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsBasicAuthnEnabled() bool {
|
|
if c.IsHtpasswdAuthEnabled() || c.IsLdapAuthEnabled() ||
|
|
c.IsOpenIDAuthEnabled() || c.IsAPIKeyEnabled() {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isOpenIDAuthProviderEnabled(config *Config, provider string) bool {
|
|
if providerConfig, ok := config.HTTP.Auth.OpenID.Providers[provider]; ok {
|
|
if IsOpenIDSupported(provider) {
|
|
if providerConfig.ClientID != "" || providerConfig.Issuer != "" ||
|
|
len(providerConfig.Scopes) > 0 {
|
|
return true
|
|
}
|
|
} else if IsOauth2Supported(provider) {
|
|
if providerConfig.ClientID != "" || len(providerConfig.Scopes) > 0 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *Config) IsSearchEnabled() bool {
|
|
return c.Extensions != nil && c.Extensions.Search != nil && *c.Extensions.Search.Enable
|
|
}
|
|
|
|
func (c *Config) IsUIEnabled() bool {
|
|
return c.Extensions != nil && c.Extensions.UI != nil && *c.Extensions.UI.Enable
|
|
}
|
|
|
|
func (c *Config) AreUserPrefsEnabled() bool {
|
|
return c.IsSearchEnabled() && c.IsUIEnabled()
|
|
}
|
|
|
|
func (c *Config) IsMgmtEnabled() bool {
|
|
return c.IsSearchEnabled()
|
|
}
|
|
|
|
func (c *Config) IsImageTrustEnabled() bool {
|
|
return c.Extensions != nil && c.Extensions.Trust != nil && *c.Extensions.Trust.Enable
|
|
}
|
|
|
|
func (c *Config) IsCosignEnabled() bool {
|
|
return c.IsImageTrustEnabled() && c.Extensions.Trust.Cosign
|
|
}
|
|
|
|
func (c *Config) IsNotationEnabled() bool {
|
|
return c.IsImageTrustEnabled() && c.Extensions.Trust.Notation
|
|
}
|
|
|
|
func (c *Config) IsSyncEnabled() bool {
|
|
return c.Extensions != nil && c.Extensions.Sync != nil && *c.Extensions.Sync.Enable
|
|
}
|
|
|
|
func IsOpenIDSupported(provider string) bool {
|
|
for _, supportedProvider := range openIDSupportedProviders {
|
|
if supportedProvider == provider {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func IsOauth2Supported(provider string) bool {
|
|
for _, supportedProvider := range oauth2SupportedProviders {
|
|
if supportedProvider == provider {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|