Documentation ¶
Index ¶
- Variables
- func AppendQuery(urlStr string, query url.Values) string
- func AtLeastZero[I interface{ ... }](x I) uint64
- func BuildBasicAuthHeader(userName, password string) string
- func DoesAccountExist(db gorp.SqlExecutor, name models.AccountName) (bool, error)
- func FindAccount(db gorp.SqlExecutor, name models.AccountName) (*models.Account, error)
- func FindBlobByAccountName(db gorp.SqlExecutor, blobDigest digest.Digest, accountName models.AccountName) (*models.Blob, error)
- func FindBlobByRepository(db gorp.SqlExecutor, blobDigest digest.Digest, repo models.Repository) (*models.Blob, error)
- func FindBlobByRepositoryName(db gorp.SqlExecutor, blobDigest digest.Digest, repoName string, ...) (*models.Blob, error)
- func FindManifest(db gorp.SqlExecutor, repo models.Repository, manifestDigest digest.Digest) (*models.Manifest, error)
- func FindManifestByRepositoryName(db gorp.SqlExecutor, repoName string, accountName models.AccountName, ...) (*models.Manifest, error)
- func FindOrCreateRepository(db gorp.SqlExecutor, name string, accountName models.AccountName) (*models.Repository, error)
- func FindQuotas(db gorp.SqlExecutor, authTenantID string) (*models.Quotas, error)
- func FindReducedAccount(db gorp.SqlExecutor, name models.AccountName) (*models.ReducedAccount, error)
- func FindRepository(db gorp.SqlExecutor, name string, accountName models.AccountName) (*models.Repository, error)
- func FindRepositoryByID(db gorp.SqlExecutor, id int64) (*models.Repository, error)
- func FindUploadByRepository(db gorp.SqlExecutor, uuid string, repo models.Repository) (*models.Upload, error)
- func GenerateStorageID() string
- func GetManifestUsage(db gorp.SqlExecutor, quotas models.Quotas) (uint64, error)
- func GetPeerFromAccount(db gorp.SqlExecutor, account models.Account) (models.Peer, error)
- func GetRedisOptions(prefix string) (*redis.Options, error)
- func GetSecurityInfo(db gorp.SqlExecutor, repoID int64, manifestDigest digest.Digest) (*models.TrivySecurityInfo, error)
- func MaxMaybeTime(t1, t2 *time.Time) *time.Time
- func MaybeTimeToUnix(t *time.Time) *int64
- func MinMaybeTime(t1, t2 *time.Time) *time.Time
- func MountBlobIntoRepo(db gorp.SqlExecutor, blob models.Blob, repo models.Repository) error
- func OriginalRequestURL(r *http.Request) url.URL
- func ParseIssuerKey(in string) (crypto.PrivateKey, error)
- func SetTaskName(taskName string)
- func SetupHTTPClient()
- type Account
- type AccountManagementDriver
- type AuditContext
- type Auditor
- type AuthDriver
- type ClaimResult
- type Configuration
- type DB
- type Duration
- type FederationDriver
- type GCPolicy
- type GCStatus
- type GCTimeConstraint
- type InboundCacheDriver
- type ManifestForSync
- type ParsedManifest
- type Permission
- type RBACPermission
- type RBACPolicy
- type RateLimitDriver
- type RateLimitEngine
- type RateLimitedAction
- type RegistryV2Error
- func (e *RegistryV2Error) Error() string
- func (e *RegistryV2Error) WithDetail(detail any) *RegistryV2Error
- func (e *RegistryV2Error) WithHeader(key string, values ...string) *RegistryV2Error
- func (e *RegistryV2Error) WithStatus(status int) *RegistryV2Error
- func (e *RegistryV2Error) WriteAsAuthResponseTo(w http.ResponseWriter)
- func (e *RegistryV2Error) WriteAsRegistryV2ResponseTo(w http.ResponseWriter, r *http.Request)
- func (e *RegistryV2Error) WriteAsTextTo(w http.ResponseWriter)
- type RegistryV2ErrorCode
- type ReplicaSyncPayload
- type ReplicationExternalPeerSpec
- type ReplicationPolicy
- type ReplicationStrategy
- type SecurityScanPolicy
- func (p SecurityScanPolicy) MatchesRepository(repo models.Repository) bool
- func (p SecurityScanPolicy) MatchesVulnerability(vuln stypes.DetectedVulnerability) bool
- func (p SecurityScanPolicy) String() string
- func (p SecurityScanPolicy) Validate(path string) (errs errext.ErrorSet)
- func (p SecurityScanPolicy) VulnerabilityStatus() models.VulnerabilityStatus
- type SecurityScanPolicyAction
- type SecurityScanPolicySet
- type StorageDriver
- type StoredBlobInfo
- type StoredManifestInfo
- type SubleaseToken
- type TagForSync
- type UserIdentity
- type UserType
- type ValidationPolicy
Constants ¶
This section is empty.
Variables ¶
var AccountManagementDriverRegistry pluggable.Registry[AccountManagementDriver]
AccountManagementDriverRegistry is a pluggable.Registry for AccountManagementDriver implementations.
var AuthDriverRegistry pluggable.Registry[AuthDriver]
AuthDriverRegistry is a pluggable.Registry for AuthDriver implementations.
var ErrAuthDriverMismatch = errors.New("given AuthDriver is not supported by this driver")
ErrAuthDriverMismatch is returned by Init() methods on most driver interfaces, to indicate that the driver in question does not work with the selected AuthDriver.
var ErrCannotGenerateURL = errors.New("URLForBlob() is not supported")
ErrCannotGenerateURL is returned by StorageDriver.URLForBlob() when the StorageDriver does not support blob URLs.
var (
ErrIncompatibleReplicationPolicy = errors.New("cannot change replication policy on existing account")
)
var ErrNoSuchPrimaryAccount = errors.New("no such primary account")
ErrNoSuchPrimaryAccount is returned by FederationDriver.FindPrimaryAccount if no peer has the given primary account.
var FederationDriverRegistry pluggable.Registry[FederationDriver]
FederationDriverRegistry is a pluggable.Registry for FederationDriver implementations.
var InboundCacheDriverRegistry pluggable.Registry[InboundCacheDriver]
InboundCacheDriverRegistry is a pluggable.Registry for InboundCacheDriver implementations.
var RateLimitDriverRegistry pluggable.Registry[RateLimitDriver]
RateLimitDriverRegistry is a pluggable.Registry for RateLimitDriver implementations.
var StorageDriverRegistry pluggable.Registry[StorageDriver]
StorageDriverRegistry is a pluggable.Registry for StorageDriver implementations.
var UserIdentityRegistry pluggable.Registry[UserIdentity]
UserIdentityRegistry is a pluggable.Registry for UserIdentity implementations.
Functions ¶
func AppendQuery ¶
AppendQuery adds additional query parameters to an existing unparsed URL.
func AtLeastZero ¶
AtLeastZero safely converts int or int64 values (which might come from DB.SelectInt() or from IO reads/writes) to uint64 by clamping negative values to 0.
func BuildBasicAuthHeader ¶
BuildBasicAuthHeader constructs the value of an "Authorization" HTTP header for the given basic auth credentials.
func DoesAccountExist ¶
func DoesAccountExist(db gorp.SqlExecutor, name models.AccountName) (bool, error)
DoesAccountExist checks if an account with the given name exists in the DB.
func FindAccount ¶
func FindAccount(db gorp.SqlExecutor, name models.AccountName) (*models.Account, error)
FindAccount works similar to db.SelectOne(), but returns nil instead of sql.ErrNoRows if no account exists with this name.
func FindBlobByAccountName ¶
func FindBlobByAccountName(db gorp.SqlExecutor, blobDigest digest.Digest, accountName models.AccountName) (*models.Blob, error)
FindBlobByAccountName is a convenience wrapper around db.SelectOne(). If the blob in question does not exist, sql.ErrNoRows is returned.
func FindBlobByRepository ¶
func FindBlobByRepository(db gorp.SqlExecutor, blobDigest digest.Digest, repo models.Repository) (*models.Blob, error)
FindBlobByRepository is a convenience wrapper around db.SelectOne(). If the blob in question does not exist, sql.ErrNoRows is returned.
func FindBlobByRepositoryName ¶
func FindBlobByRepositoryName(db gorp.SqlExecutor, blobDigest digest.Digest, repoName string, accountName models.AccountName) (*models.Blob, error)
FindBlobByRepositoryName is a convenience wrapper around db.SelectOne(). If the blob in question does not exist, sql.ErrNoRows is returned.
func FindManifest ¶
func FindManifest(db gorp.SqlExecutor, repo models.Repository, manifestDigest digest.Digest) (*models.Manifest, error)
FindManifest is a convenience wrapper around db.SelectOne(). If the manifest in question does not exist, sql.ErrNoRows is returned.
func FindManifestByRepositoryName ¶
func FindManifestByRepositoryName(db gorp.SqlExecutor, repoName string, accountName models.AccountName, manifestDigest digest.Digest) (*models.Manifest, error)
FindManifestByRepositoryName is a convenience wrapper around db.SelectOne(). If the manifest in question does not exist, sql.ErrNoRows is returned.
func FindOrCreateRepository ¶
func FindOrCreateRepository(db gorp.SqlExecutor, name string, accountName models.AccountName) (*models.Repository, error)
FindOrCreateRepository works similar to db.SelectOne(), but autovivifies a Repository record when none exists yet.
func FindQuotas ¶
FindQuotas works similar to db.SelectOne(), but returns nil instead of sql.ErrNoRows if no quota set exists for this auth tenant.
func FindReducedAccount ¶
func FindReducedAccount(db gorp.SqlExecutor, name models.AccountName) (*models.ReducedAccount, error)
FindReducedAccount is like FindAccount, but it returns a ReducedAccount instead. This can be significantly faster than FindAccount if only the most common stuff is needed.
func FindRepository ¶
func FindRepository(db gorp.SqlExecutor, name string, accountName models.AccountName) (*models.Repository, error)
FindRepository is a convenience wrapper around db.SelectOne(). If the repository in question does not exist, sql.ErrNoRows is returned.
func FindRepositoryByID ¶
func FindRepositoryByID(db gorp.SqlExecutor, id int64) (*models.Repository, error)
FindRepositoryByID is a convenience wrapper around db.SelectOne(). If the repository in question does not exist, sql.ErrNoRows is returned.
func FindUploadByRepository ¶
func FindUploadByRepository(db gorp.SqlExecutor, uuid string, repo models.Repository) (*models.Upload, error)
FindUploadByRepository is a convenience wrapper around db.SelectOne(). If the upload in question does not exist, sql.ErrNoRows is returned.
func GenerateStorageID ¶
func GenerateStorageID() string
GenerateStorageID generates a new random storage ID for use with keppel.StorageDriver.AppendToBlob().
func GetManifestUsage ¶
GetManifestUsage returns how many manifests currently exist in repos in accounts connected to this quota set's auth tenant.
func GetPeerFromAccount ¶
GetPeerFromAccount returns the peer of the account given.
Returns sql.ErrNoRows if the configured peer does not exist.
func GetRedisOptions ¶
GetRedisOptions returns a redis.Options by getting the required parameters from environment variables:
REDIS_PASSWORD, REDIS_HOSTNAME, REDIS_PORT, and REDIS_DB_NUM.
The environment variable keys are prefixed with the provided prefix.
func GetSecurityInfo ¶
func GetSecurityInfo(db gorp.SqlExecutor, repoID int64, manifestDigest digest.Digest) (*models.TrivySecurityInfo, error)
func MaybeTimeToUnix ¶
MaybeTimeToUnix casts a time.Time instance into its UNIX timestamp while preserving nil-ness.
func MountBlobIntoRepo ¶
func MountBlobIntoRepo(db gorp.SqlExecutor, blob models.Blob, repo models.Repository) error
MountBlobIntoRepo creates an entry in the blob_mounts database table.
func OriginalRequestURL ¶
OriginalRequestURL returns the URL that the original requester used when sending an HTTP request. This inspects the X-Forwarded-* set of headers to identify reverse proxying.
func ParseIssuerKey ¶
func ParseIssuerKey(in string) (crypto.PrivateKey, error)
ParseIssuerKey parses the contents of the KEPPEL_ISSUER_KEY variable.
func SetTaskName ¶
func SetTaskName(taskName string)
func SetupHTTPClient ¶
func SetupHTTPClient()
Types ¶
type Account ¶
type Account struct { Name models.AccountName `json:"name"` AuthTenantID string `json:"auth_tenant_id"` GCPolicies []GCPolicy `json:"gc_policies,omitempty"` RBACPolicies []RBACPolicy `json:"rbac_policies"` ReplicationPolicy *ReplicationPolicy `json:"replication,omitempty"` State string `json:"state,omitempty"` ValidationPolicy *ValidationPolicy `json:"validation,omitempty"` PlatformFilter models.PlatformFilter `json:"platform_filter,omitempty"` // TODO: deprecated, and remove InMaintenance bool `json:"in_maintenance"` Metadata *map[string]string `json:"metadata"` }
Account represents an account in the API.
type AccountManagementDriver ¶
type AccountManagementDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. Init() error // Called by a jobloop for every account every once in a while (e.g. every hour). // // Returns the desired account configuration if the account is managed. // The jobloop will apply the account in the DB accordingly. // // Returns nil if the account was managed, and now shall be deleted. // The jobloop will clean up the manifests, blobs, repos and the account. ConfigureAccount(accountName models.AccountName) (*Account, []SecurityScanPolicy, error) // Called by a jobloop every once in a while (e.g. every hour). // // If new names appear in the list, the jobloop will create the // respective accounts as configured by ConfigureAccount(). ManagedAccountNames() ([]models.AccountName, error) }
AccountManagementDriver is a pluggable interface for receiving account configuration from an external system. Accounts can either be managed by this driver, or created and maintained by users through the Keppel API.
func NewAccountManagementDriver ¶
func NewAccountManagementDriver(pluginTypeID string) (AccountManagementDriver, error)
NewAccountManagementDriver creates a new AuthDriver using one of the plugins registered with AccountManagementDriver.
type AuditContext ¶
type AuditContext struct { UserIdentity UserIdentity Request *http.Request }
AuditContext collects arguments that business logic methods need only for generating audit events.
type Auditor ¶
type Auditor interface { // Record forwards the given audit event to the audit log. // EventParameters.Observer will be filled by the auditor. Record(params audittools.EventParameters) }
Auditor is a component that forwards audit events to the appropriate logs. It is used by some of the API modules.
func InitAuditTrail ¶
InitAuditTrail initializes a Auditor from the configuration variables found in the environment.
type AuthDriver ¶
type AuthDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. The supplied *redis.Client can be stored // for caching authorizations, but only if it is non-nil. Init(context.Context, *redis.Client) error // AuthenticateUser authenticates the user identified by the given username // and password. Note that usernames may not contain colons, because // credentials are encoded by clients in the "username:password" format. AuthenticateUser(ctx context.Context, userName, password string) (UserIdentity, *RegistryV2Error) // AuthenticateUserFromRequest reads credentials from the given incoming HTTP // request to authenticate the user which makes this request. The // implementation shall follow the conventions of the concrete backend, e.g. a // OAuth backend could try to read a Bearer token from the Authorization // header, whereas an OpenStack auth driver would look for a Keystone token in the // X-Auth-Token header. // // If the request contains no auth headers at all, (nil, nil) shall be // returned to trigger the codepath for anonymous users. AuthenticateUserFromRequest(r *http.Request) (UserIdentity, *RegistryV2Error) }
AuthDriver represents an authentication backend that supports multiple tenants. A tenant is a scope where users can be authorized to perform certain actions. For example, in OpenStack, a Keppel tenant is a Keystone project.
func NewAuthDriver ¶
func NewAuthDriver(ctx context.Context, pluginTypeID string, rc *redis.Client) (AuthDriver, error)
NewAuthDriver creates a new AuthDriver using one of the plugins registered with AuthDriverRegistry.
type ClaimResult ¶
type ClaimResult int
ClaimResult is an enum returned by FederationDriver.ClaimAccountName().
const ( // ClaimSucceeded indicates that ClaimAccountName() returned with a nil error. ClaimSucceeded ClaimResult = iota // ClaimFailed indicates that ClaimAccountName() returned with an error // because the user did not have permission to claim the account in question. ClaimFailed // ClaimErrored indicates that ClaimAccountName() returned with an error // because of an unexpected problem on the server side. ClaimErrored )
type Configuration ¶
type Configuration struct { APIPublicHostname string AnycastAPIPublicHostname string DatabaseURL *url.URL JWTIssuerKeys []crypto.PrivateKey AnycastJWTIssuerKeys []crypto.PrivateKey Trivy *trivy.Config }
Configuration contains all configuration values that are not specific to a certain driver.
func ParseConfiguration ¶
func ParseConfiguration() Configuration
ParseConfiguration obtains a keppel.Configuration instance from the corresponding environment variables. Aborts on error.
func (Configuration) ReverseProxyAnycastRequestToPeer ¶
func (cfg Configuration) ReverseProxyAnycastRequestToPeer(w http.ResponseWriter, r *http.Request, peerHostName string) error
ReverseProxyAnycastRequestToPeer takes a http.Request for the anycast API and reverse-proxies it to a different keppel-api in this Keppel's peer group.
If an error is returned, no response has been written and the caller is responsible for producing the error response.
type Duration ¶
Duration is a time.Duration with custom JSON marshalling/unmarshalling logic.
func (Duration) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface.
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface.
type FederationDriver ¶
type FederationDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. // // Implementations should inspect the auth driver to ensure that the // federation driver can work with this authentication method, or return // ErrAuthDriverMismatch otherwise. Init(context.Context, AuthDriver, Configuration) error // ClaimAccountName is called when creating a new account, and returns nil if // and only if this Keppel is allowed to use `account.Name` for the given new // `account`. // // For some drivers, creating a replica account requires confirmation from the // Keppel hosting the primary account. This is done by issuing a sublease // token secret on the primary account using IssueSubleaseTokenSecret(), then // presenting this `subleaseTokenSecret` to this method. // // The implementation MUST be idempotent. If a call returned nil, a subsequent // call with the same `account` must also return nil unless // ForfeitAccountName() was called in between. ClaimAccountName(ctx context.Context, account models.Account, subleaseTokenSecret string) (ClaimResult, error) // IssueSubleaseTokenSecret may only be called on existing primary accounts, // not on replica accounts. It generates a secret one-time token that other // Keppels can use to verify that the caller is allowed to create a replica // account for this primary account. // // Sublease tokens are optional. If ClaimAccountName does not inspect its // `subleaseTokenSecret` parameter, this method shall return ("", nil). IssueSubleaseTokenSecret(ctx context.Context, account models.Account) (string, error) // ForfeitAccountName is the inverse operation of ClaimAccountName. It is used // when deleting an account and releases this Keppel's claim on the account // name. ForfeitAccountName(ctx context.Context, account models.Account) error // RecordExistingAccount is called regularly for each account in our database. // The driver implementation can use this call to ensure that the existence of // this account is tracked in its storage. (We don't expect this to require // any actual work during normal operation. The purpose of this mechanism is // to aid in switching between federation drivers.) // // The `now` argument contains the value of time.Now(). It may refer to an // artificial wall clock during unit tests. RecordExistingAccount(ctx context.Context, account models.Account, now time.Time) error // FindPrimaryAccount is used to redirect anycast requests for accounts that // do not exist locally. It shell return the hostname of the peer that hosts // the primary account. If no account with this name exists anywhere, // ErrNoSuchPrimaryAccount shall be returned. FindPrimaryAccount(ctx context.Context, accountName models.AccountName) (peerHostName string, err error) }
FederationDriver is the abstract interface for a strategy that coordinates the claiming of account names across Keppel deployments.
func NewFederationDriver ¶
func NewFederationDriver(ctx context.Context, pluginTypeID string, ad AuthDriver, cfg Configuration) (FederationDriver, error)
NewFederationDriver creates a new FederationDriver using one of the plugins registered with FederationDriverRegistry.
type GCPolicy ¶
type GCPolicy struct { RepositoryRx regexpext.BoundedRegexp `json:"match_repository"` NegativeRepositoryRx regexpext.BoundedRegexp `json:"except_repository,omitempty"` TagRx regexpext.BoundedRegexp `json:"match_tag,omitempty"` NegativeTagRx regexpext.BoundedRegexp `json:"except_tag,omitempty"` OnlyUntagged bool `json:"only_untagged,omitempty"` TimeConstraint *GCTimeConstraint `json:"time_constraint,omitempty"` Action string `json:"action"` }
GCPolicy is a policy enabling optional garbage collection runs in an account. It is stored in serialized form in the GCPoliciesJSON field of type Account.
func ParseGCPolicies ¶
ParseGCPolicies parses the GC policies for the given account.
func (GCPolicy) MatchesRepository ¶
MatchesRepository evaluates the repository regexes in this policy.
func (GCPolicy) MatchesTags ¶
MatchesTags evaluates the tag regexes in this policy for a complete set of tag names belonging to a single manifest.
func (GCPolicy) MatchesTimeConstraint ¶
func (g GCPolicy) MatchesTimeConstraint(manifest models.Manifest, allManifestsInRepo []models.Manifest, now time.Time) bool
MatchesTimeConstraint evaluates the time constraint in this policy for the given manifest. A full list of all manifests in this repo must be supplied in order to evaluate "newest" and "oldest" time constraints. The final argument must be equivalent to time.Now(); it is given explicitly to allow for simulated clocks during unit tests.
type GCStatus ¶
type GCStatus struct { // True if the manifest was uploaded less than 10 minutes ago and is therefore // protected from GC. ProtectedByRecentUpload bool `json:"protected_by_recent_upload,omitempty"` // If a parent manifest references this manifest and thus protects it from GC, // contains the parent manifest's digest. ProtectedByParentManifest string `json:"protected_by_parent,omitempty"` // If a policy with action "protect" applies to this image, contains the // definition of the policy. ProtectedByPolicy *GCPolicy `json:"protected_by_policy,omitempty"` // If the image is not protected, contains all policies with action "delete" // that could delete this image in the future. RelevantPolicies []GCPolicy `json:"relevant_policies,omitempty"` }
GCStatus documents the current status of a manifest with regard to image GC. It is stored in serialized form in the GCStatusJSON field of type Manifest.
Since GCStatus objects describe images that currently exist in the DB, they only describe policy decisions that led to no cleanup.
func (GCStatus) IsProtected ¶
IsProtected returns whether any of the ProtectedBy... fields is filled.
type GCTimeConstraint ¶
type GCTimeConstraint struct { FieldName string `json:"on"` OldestCount uint64 `json:"oldest,omitempty"` NewestCount uint64 `json:"newest,omitempty"` MinAge Duration `json:"older_than,omitempty"` MaxAge Duration `json:"newer_than,omitempty"` }
GCTimeConstraint appears in type GCPolicy.
type InboundCacheDriver ¶
type InboundCacheDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. Init(context.Context, Configuration) error // LoadManifest pulls a manifest from the cache. If the given manifest is not // cached, or if the cache entry has expired, sql.ErrNoRows shall be returned. // // time.Now() is given in the second argument to allow for tests to use an // artificial wall clock. LoadManifest(ctx context.Context, location models.ImageReference, now time.Time) (contents []byte, mediaType string, err error) // StoreManifest places a manifest in the cache for later retrieval. // // time.Now() is given in the last argument to allow for tests to use an // artificial wall clock. StoreManifest(ctx context.Context, location models.ImageReference, contents []byte, mediaType string, now time.Time) error }
InboundCacheDriver is the abstract interface for a caching strategy for manifests and tags residing in an external registry.
func NewInboundCacheDriver ¶
func NewInboundCacheDriver(ctx context.Context, pluginTypeID string, cfg Configuration) (InboundCacheDriver, error)
NewInboundCacheDriver creates a new InboundCacheDriver using one of the plugins registered with InboundCacheDriverRegistry.
type ManifestForSync ¶
type ManifestForSync struct { Digest digest.Digest `json:"digest"` LastPulledAt *int64 `json:"last_pulled_at,omitempty"` Tags []TagForSync `json:"tags,omitempty"` }
ManifestForSync represents a manifest in the _sync_replica API endpoint.
(This type is declared in this package because it gets used in both internal/api/peer and internal/tasks.)
type ParsedManifest ¶
type ParsedManifest interface { // FindImageConfigBlob returns the descriptor of the blob containing this // manifest's image configuration, or nil if the manifest does not have an image // configuration. FindImageConfigBlob() *distribution.Descriptor // FindImageLayerBlobs returns the descriptors of the blobs containing this // manifest's image layers, or an empty list if the manifest does not have layers. FindImageLayerBlobs() []distribution.Descriptor // BlobReferences returns all blobs referenced by this manifest. BlobReferences() []distribution.Descriptor // ManifestReferences returns all manifests referenced by this manifest. ManifestReferences(pf models.PlatformFilter) []manifestlist.ManifestDescriptor // AcceptableAlternates returns the subset of ManifestReferences() that is // acceptable as alternate representations of this manifest. When a client // asks for this manifest, but the Accept header does not match the manifest // itself, the API will look for an acceptable alternate to serve instead. AcceptableAlternates(pf models.PlatformFilter) []manifestlist.ManifestDescriptor }
ParsedManifest is an interface that can interrogate manifests about the blobs and submanifests referenced therein.
func ParseManifest ¶
func ParseManifest(mediaType string, contents []byte) (ParsedManifest, distribution.Descriptor, error)
ParseManifest parses a manifest. It also returns a Descriptor describing the manifest itself.
type Permission ¶
type Permission string
Permission is an enum used by AuthDriver.
const ( // CanViewAccount is the permission for viewing account metadata. CanViewAccount Permission = "view" // CanPullFromAccount is the permission for pulling images from this account. CanPullFromAccount Permission = "pull" // CanPushToAccount is the permission for pushing images to this account. CanPushToAccount Permission = "push" // CanDeleteFromAccount is the permission for deleting manifests from this account. CanDeleteFromAccount Permission = "delete" // CanChangeAccount is the permission for creating and updating accounts. CanChangeAccount Permission = "change" // CanViewQuotas is the permission for viewing an auth tenant's quotas. CanViewQuotas Permission = "viewquota" // CanChangeQuotas is the permission for changing an auth tenant's quotas. CanChangeQuotas Permission = "changequota" )
type RBACPermission ¶
type RBACPermission string
RBACPermission enumerates permissions that can be granted by an RBAC policy.
const ( GrantsPull RBACPermission = "pull" GrantsPush RBACPermission = "push" GrantsDelete RBACPermission = "delete" GrantsAnonymousPull RBACPermission = "anonymous_pull" GrantsAnonymousFirstPull RBACPermission = "anonymous_first_pull" )
type RBACPolicy ¶
type RBACPolicy struct { CidrPattern string `json:"match_cidr,omitempty"` RepositoryPattern regexpext.BoundedRegexp `json:"match_repository,omitempty"` UserNamePattern regexpext.BoundedRegexp `json:"match_username,omitempty"` Permissions []RBACPermission `json:"permissions"` }
RBACPolicy is a policy granting user-defined access to repos in an account. It is stored in serialized form in the RBACPoliciesJSON field of type Account.
func ParseRBACPolicies ¶
func ParseRBACPolicies(account models.Account) ([]RBACPolicy, error)
ParseRBACPolicies parses the RBAC policies for the given account.
func ParseRBACPoliciesField ¶
func ParseRBACPoliciesField(buf string) ([]RBACPolicy, error)
ParseRBACPoliciesField is like ParseRBACPolicies, but only takes the RBACPoliciesJSON field of type Account instead of the whole Account.
This is useful when the full Account has not been loaded from the DB.
func (RBACPolicy) Matches ¶
func (r RBACPolicy) Matches(ip, repoName, userName string) bool
Matches evaluates the cidr and regexes in this policy.
func (*RBACPolicy) ValidateAndNormalize ¶
func (r *RBACPolicy) ValidateAndNormalize(strategy ReplicationStrategy) error
ValidateAndNormalize performs some normalizations and returns an error if this policy is invalid.
type RateLimitDriver ¶
type RateLimitDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. // // Implementations should inspect the auth driver to ensure that the // federation driver can work with this authentication method, or return // ErrAuthDriverMismatch otherwise. Init(AuthDriver, Configuration) error // GetRateLimit shall return nil if the given action has no rate limit. GetRateLimit(account models.ReducedAccount, action RateLimitedAction) *redis_rate.Limit }
RateLimitDriver is a pluggable strategy that determines the rate limits of each account.
func NewRateLimitDriver ¶
func NewRateLimitDriver(pluginTypeID string, ad AuthDriver, cfg Configuration) (RateLimitDriver, error)
NewRateLimitDriver creates a new RateLimitDriver using one of the plugins registered with RateLimitDriverRegistry.
type RateLimitEngine ¶
type RateLimitEngine struct { Driver RateLimitDriver Client *redis.Client }
RateLimitEngine provides the rate-limiting interface used by the API implementation.
func (RateLimitEngine) RateLimitAllows ¶
func (e RateLimitEngine) RateLimitAllows(ctx context.Context, remoteAddr string, account models.ReducedAccount, action RateLimitedAction, amount uint64) (bool, *redis_rate.Result, error)
RateLimitAllows checks whether the given action on the given account is allowed by the account's rate limit.
type RateLimitedAction ¶
type RateLimitedAction string
RateLimitedAction is an enum of all actions that can be rate-limited.
const ( // BlobPullAction is a RateLimitedAction. BlobPullAction RateLimitedAction = "pullblob" // BlobPushAction is a RateLimitedAction. BlobPushAction RateLimitedAction = "pushblob" // ManifestPullAction is a RateLimitedAction. ManifestPullAction RateLimitedAction = "pullmanifest" // ManifestPushAction is a RateLimitedAction. ManifestPushAction RateLimitedAction = "pushmanifest" // AnycastBlobBytePullAction is a RateLimitedAction. // It refers to blobs being pulled from other regions via anycast. // The `amount` given to RateLimitAllows() shall be the blob size in bytes. AnycastBlobBytePullAction RateLimitedAction = "pullblobbytesanycast" // TrivyReportRetrieveAction is a RateLimitedAction. // It refers to reports being retrieved from keppel through the trivy proxy from trivy itself. TrivyReportRetrieveAction RateLimitedAction = "retrievetrivyreport" )
type RegistryV2Error ¶
type RegistryV2Error struct { Code RegistryV2ErrorCode `json:"code"` Message string `json:"message"` // Detail is always a string for errors generated by Keppel, but may be a JSON // object (i.e. map[string]any or similar) for errors coming from // keppel-registry. Detail any `json:"detail"` Status int `json:"-"` Headers http.Header `json:"-"` }
RegistryV2Error is the error type expected by clients of the docker-registry v2 API.
func AsRegistryV2Error ¶
func AsRegistryV2Error(err error) *RegistryV2Error
AsRegistryV2Error tries to cast `err` into RegistryV2Error. If `err` is not a RegistryV2Error, it gets wrapped in ErrUnknown instead.
func (*RegistryV2Error) Error ¶
func (e *RegistryV2Error) Error() string
Error implements the builtin/error interface.
func (*RegistryV2Error) WithDetail ¶
func (e *RegistryV2Error) WithDetail(detail any) *RegistryV2Error
WithDetail adds detail information to this error.
func (*RegistryV2Error) WithHeader ¶
func (e *RegistryV2Error) WithHeader(key string, values ...string) *RegistryV2Error
WithHeader adds a HTTP response header to this error.
func (*RegistryV2Error) WithStatus ¶
func (e *RegistryV2Error) WithStatus(status int) *RegistryV2Error
WithStatus changes the HTTP status code for this error.
func (*RegistryV2Error) WriteAsAuthResponseTo ¶
func (e *RegistryV2Error) WriteAsAuthResponseTo(w http.ResponseWriter)
WriteAsAuthResponseTo reports this error in the format used by the Auth API endpoint.
func (*RegistryV2Error) WriteAsRegistryV2ResponseTo ¶
func (e *RegistryV2Error) WriteAsRegistryV2ResponseTo(w http.ResponseWriter, r *http.Request)
WriteAsRegistryV2ResponseTo reports this error in the format used by the Registry V2 API.
func (*RegistryV2Error) WriteAsTextTo ¶
func (e *RegistryV2Error) WriteAsTextTo(w http.ResponseWriter)
WriteAsTextTo reports this error in a plain text format.
type RegistryV2ErrorCode ¶
type RegistryV2ErrorCode string
RegistryV2ErrorCode is the closed set of error codes that can appear in type RegistryV2Error.
const ( ErrBlobUnknown RegistryV2ErrorCode = "BLOB_UNKNOWN" ErrBlobUploadInvalid RegistryV2ErrorCode = "BLOB_UPLOAD_INVALID" ErrBlobUploadUnknown RegistryV2ErrorCode = "BLOB_UPLOAD_UNKNOWN" ErrDigestInvalid RegistryV2ErrorCode = "DIGEST_INVALID" ErrManifestBlobUnknown RegistryV2ErrorCode = "MANIFEST_BLOB_UNKNOWN" ErrManifestInvalid RegistryV2ErrorCode = "MANIFEST_INVALID" ErrManifestUnknown RegistryV2ErrorCode = "MANIFEST_UNKNOWN" ErrManifestUnverified RegistryV2ErrorCode = "MANIFEST_UNVERIFIED" ErrNameInvalid RegistryV2ErrorCode = "NAME_INVALID" ErrNameUnknown RegistryV2ErrorCode = "NAME_UNKNOWN" ErrSizeInvalid RegistryV2ErrorCode = "SIZE_INVALID" ErrTagInvalid RegistryV2ErrorCode = "TAG_INVALID" ErrDenied RegistryV2ErrorCode = "DENIED" ErrUnsupported RegistryV2ErrorCode = "UNSUPPORTED" // not in opencontainers/distribution-spec, but appears in github.com/docker/distribution ErrUnknown RegistryV2ErrorCode = "UNKNOWN" ErrTooManyRequests RegistryV2ErrorCode = "TOOMANYREQUESTS" )
Possible values for RegistryV2ErrorCode.
func (RegistryV2ErrorCode) With ¶
func (c RegistryV2ErrorCode) With(msg string, args ...any) *RegistryV2Error
With is a convenience function for constructing type RegistryV2Error.
type ReplicaSyncPayload ¶
type ReplicaSyncPayload struct {
Manifests []ManifestForSync `json:"manifests"`
}
ReplicaSyncPayload is the format for request bodies and response bodies of the sync-replica API endpoint.
(This type is declared in this package because it gets used in both internal/api/peer and internal/tasks.)
func (ReplicaSyncPayload) DigestForTag ¶
func (p ReplicaSyncPayload) DigestForTag(name string) digest.Digest
DigestForTag returns the digest of the manifest that this tag points to, or the empty string if the tag does not exist in this payload.
func (ReplicaSyncPayload) HasManifest ¶
func (p ReplicaSyncPayload) HasManifest(manifestDigest digest.Digest) bool
HasManifest returns whether there is a manifest with the given digest in this payload.
type ReplicationExternalPeerSpec ¶
type ReplicationExternalPeerSpec struct { URL string `json:"url"` UserName string `json:"username,omitempty"` Password string `json:"password,omitempty"` }
ReplicationExternalPeerSpec appears in type ReplicationPolicy.
type ReplicationPolicy ¶
type ReplicationPolicy struct { Strategy ReplicationStrategy `json:"strategy"` // only for `on_first_use` UpstreamPeerHostName string `json:"upstream_peer_hostname"` // only for `from_external_on_first_use` ExternalPeer ReplicationExternalPeerSpec `json:"external_peer"` }
ReplicationPolicy represents a replication policy in the API.
func RenderReplicationPolicy ¶
func RenderReplicationPolicy(account models.Account) *ReplicationPolicy
RenderReplicationPolicy builds a ReplicationPolicy object out of the information in the given account model.
func (ReplicationPolicy) ApplyToAccount ¶
func (r ReplicationPolicy) ApplyToAccount(account *models.Account) error
ApplyToAccount validates this policy and stores it in the given account model.
WARNING 1: For existing accounts, the caller must ensure that the policy uses the same replication strategy as the given account already does.
WARNING 2: For internal replica accounts, the caller must ensure that the UpstreamPeerHostName refers to a known peer. This method does not do it itself because callers often need to do other things with the peer, too.
func (ReplicationPolicy) MarshalJSON ¶
func (r ReplicationPolicy) MarshalJSON() ([]byte, error)
MarshalJSON implements the json.Marshaler interface.
func (*ReplicationPolicy) UnmarshalJSON ¶
func (r *ReplicationPolicy) UnmarshalJSON(buf []byte) error
UnmarshalJSON implements the json.Unmarshaler interface.
type ReplicationStrategy ¶
type ReplicationStrategy string
ReplicationStrategy is an enum that appears in type ReplicationPolicy.
const ( NoReplicationStrategy ReplicationStrategy = "" OnFirstUseStrategy ReplicationStrategy = "on_first_use" FromExternalOnFirstUseStrategy ReplicationStrategy = "from_external_on_first_use" )
type SecurityScanPolicy ¶
type SecurityScanPolicy struct { //NOTE: We have code that uses slices.Contains() to locate policies. Be careful // when adding fields that cannot be meaningfully compared with the == operator. ManagingUserName string `json:"managed_by_user,omitempty"` RepositoryRx regexpext.BoundedRegexp `json:"match_repository"` NegativeRepositoryRx regexpext.BoundedRegexp `json:"except_repository,omitempty"` VulnerabilityIDRx regexpext.BoundedRegexp `json:"match_vulnerability_id"` NegativeVulnerabilityIDRx regexpext.BoundedRegexp `json:"except_vulnerability_id,omitempty"` ExceptFixReleased bool `json:"except_fix_released,omitempty"` Action SecurityScanPolicyAction `json:"action"` }
SecurityScanPolicy is a policy enabling user-defined adjustments to vulnerability reports generated by Trivy.
func (SecurityScanPolicy) MatchesRepository ¶
func (p SecurityScanPolicy) MatchesRepository(repo models.Repository) bool
MatchesRepository evaluates the repository regexes in this policy.
func (SecurityScanPolicy) MatchesVulnerability ¶
func (p SecurityScanPolicy) MatchesVulnerability(vuln stypes.DetectedVulnerability) bool
MatchesVulnerability evaluates the vulnerability regexes and checkin this policy.
func (SecurityScanPolicy) String ¶
func (p SecurityScanPolicy) String() string
String returns the JSON representation of this policy (for use in log and error messages).
func (SecurityScanPolicy) Validate ¶
func (p SecurityScanPolicy) Validate(path string) (errs errext.ErrorSet)
Validate returns errors if this policy is invalid.
When constructing error messages, `path` is prepended to all field names. This allows identifying the location of the policy within a larger data structure.
func (SecurityScanPolicy) VulnerabilityStatus ¶
func (p SecurityScanPolicy) VulnerabilityStatus() models.VulnerabilityStatus
VulnerabilityStatus returns the status that this policy forces for matching vulnerabilities in matching repos.
type SecurityScanPolicyAction ¶
type SecurityScanPolicyAction struct { Assessment string `json:"assessment"` Ignore bool `json:"ignore,omitempty"` Severity models.VulnerabilityStatus `json:"severity,omitempty"` }
SecurityScanPolicyAction appears in type SecurityScanPolicy.
type SecurityScanPolicySet ¶
type SecurityScanPolicySet []SecurityScanPolicy
SecurityScanPolicySet contains convenience functions for operating on a list of SecurityScanPolicy (like those found in Account.SecurityScanPoliciesJSON).
func GetSecurityScanPolicies ¶
func GetSecurityScanPolicies(account models.Account, repo models.Repository) (SecurityScanPolicySet, error)
SecurityScanPoliciesFor deserializes this account's security scan policies and returns the subset that match the given repository.
func (SecurityScanPolicySet) EnrichReport ¶
func (s SecurityScanPolicySet) EnrichReport(payload *trivy.ReportPayload) error
EnrichReport computes and inserts the "X-Keppel-Applicable-Policies" field if the report is `--format json`. Other formats are not altered.
func (SecurityScanPolicySet) PolicyForVulnerability ¶
func (s SecurityScanPolicySet) PolicyForVulnerability(vuln stypes.DetectedVulnerability) *SecurityScanPolicy
PolicyForVulnerability returns the first policy from this set that matches the vulnerability, or nil if no policy matches.
type StorageDriver ¶
type StorageDriver interface { pluggable.Plugin // Init is called before any other interface methods, and allows the plugin to // perform first-time initialization. // // Implementations should inspect the auth driver to ensure that the // federation driver can work with this authentication method, or return // ErrAuthDriverMismatch otherwise. Init(AuthDriver, Configuration) error // `storageID` identifies blobs within an account. (The storage ID is // different from the digest: The storage ID gets chosen at the start of the // upload, when we don't know the full digest yet.) `chunkNumber` identifies // how often AppendToBlob() has already been called for this account and // storageID. For the first call to AppendToBlob(), `chunkNumber` will be 1. // The second call will have a `chunkNumber` of 2, and so on. // // If `chunkLength` is non-nil, the implementation may assume that `chunk` // will yield that many bytes, and return keppel.ErrSizeInvalid when that // turns out not to be true. AppendToBlob(ctx context.Context, account models.ReducedAccount, storageID string, chunkNumber uint32, chunkLength *uint64, chunk io.Reader) error // FinalizeBlob() is called at the end of the upload, after the last // AppendToBlob() call for that blob. `chunkCount` identifies how often // AppendToBlob() was called. FinalizeBlob(ctx context.Context, account models.ReducedAccount, storageID string, chunkCount uint32) error // AbortBlobUpload() is used to clean up after an error in AppendToBlob() or // FinalizeBlob(). It is the counterpart of DeleteBlob() for when any part of // the blob upload failed. AbortBlobUpload(ctx context.Context, account models.ReducedAccount, storageID string, chunkCount uint32) error ReadBlob(ctx context.Context, account models.ReducedAccount, storageID string) (contents io.ReadCloser, sizeBytes uint64, err error) // If the blob can be retrieved by a publicly accessible URL, URLForBlob shall // return it. Otherwise ErrCannotGenerateURL shall be returned to instruct the // caller fall back to ReadBlob(). URLForBlob(ctx context.Context, account models.ReducedAccount, storageID string) (string, error) // DeleteBlob may assume that FinalizeBlob() has been called. If an error // occurred before or during FinalizeBlob(), AbortBlobUpload() will be called // instead. DeleteBlob(ctx context.Context, account models.ReducedAccount, storageID string) error ReadManifest(ctx context.Context, account models.ReducedAccount, repoName string, digest digest.Digest) ([]byte, error) WriteManifest(ctx context.Context, account models.ReducedAccount, repoName string, digest digest.Digest, contents []byte) error DeleteManifest(ctx context.Context, account models.ReducedAccount, repoName string, digest digest.Digest) error // This method shall only be used as a positive signal for the existence of a // blob or manifest in the storage, not as a negative signal: If we expect a // blob or manifest to be in the storage, but it does not show up in these // lists, that does not necessarily mean it does not exist in the storage. // This is because storage implementations may be backed by object stores with // eventual consistency. ListStorageContents(ctx context.Context, account models.ReducedAccount) (blobs []StoredBlobInfo, manifests []StoredManifestInfo, err error) // This method is called before a new account is set up in the DB. The // StorageDriver can use this opportunity to check for any reasons why the // account would not be functional once it is persisted in our DB. CanSetupAccount(ctx context.Context, account models.ReducedAccount) error // This method can be used by the StorageDriver to perform last-minute cleanup // on an account that we are about to delete. This cleanup should be // reversible; we might bail out of the account deletion afterwards if the // deletion in the DB fails. CleanupAccount(ctx context.Context, account models.ReducedAccount) error }
StorageDriver is the abstract interface for a multi-tenant-capable storage backend.
func NewStorageDriver ¶
func NewStorageDriver(pluginTypeID string, ad AuthDriver, cfg Configuration) (StorageDriver, error)
NewStorageDriver creates a new StorageDriver using one of the factory functions registered with RegisterStorageDriver().
type StoredBlobInfo ¶
type StoredBlobInfo struct { StorageID string // ChunkCount is 0 for finalized blobs (that can be deleted with DeleteBlob) // or >0 for ongoing uploads (that can be deleted with AbortBlobUpload). ChunkCount uint32 }
StoredBlobInfo is returned by StorageDriver.ListStorageContents().
type StoredManifestInfo ¶
type StoredManifestInfo struct { RepoName string Digest digest.Digest }
StoredManifestInfo is returned by StorageDriver.ListStorageContents().
type SubleaseToken ¶
type SubleaseToken struct { AccountName models.AccountName `json:"account"` PrimaryHostname string `json:"primary"` Secret string `json:"secret"` }
SubleaseToken is the internal structure of a sublease token. Only the secret is passed on to the federation driver. The other attributes are only informational. GUIs/CLIs can display these data to the user for confirmation when the token is entered.
func ParseSubleaseToken ¶
func ParseSubleaseToken(in string) (SubleaseToken, error)
ParseSubleaseToken constructs a SubleaseToken from its serialized form.
func (SubleaseToken) Serialize ¶
func (t SubleaseToken) Serialize() string
Serialize returns the Base64-encoded JSON of this token. This is the format that gets passed to the user.
type TagForSync ¶
type TagForSync struct { Name string `json:"name"` LastPulledAt *int64 `json:"last_pulled_at,omitempty"` }
TagForSync represents a tag in the _sync_replica API endpoint.
(This type is declared in this package because it gets used in both internal/api/peer and internal/tasks.)
type UserIdentity ¶
type UserIdentity interface { pluggable.Plugin // Returns whether the given auth tenant grants the given permission to this user. // The AnonymousUserIdentity always returns false. HasPermission(perm Permission, tenantID string) bool // Identifies the type of user that was authenticated. UserType() UserType // Returns the name of the user that was authenticated. This should be the // same format that is given as the first argument of AuthenticateUser(). // The AnonymousUserIdentity always returns the empty string. UserName() string // If this identity is backed by a Keystone token, return a UserInfo for that // token. Returns nil otherwise, especially for all anonymous and peer users. // // If non-nil, the Keppel API will submit OpenStack CADF audit events. UserInfo() audittools.UserInfo // SerializeToJSON serializes this UserIdentity instance into JSON for // inclusion in a token payload. SerializeToJSON() (payload []byte, err error) // DeserializeFromJSON deserializes the given token payload (as returned by // SerializeToJSON) into the callee. This is always called on a fresh // instance created by UserIdentityFactory.Instantiate(). DeserializeFromJSON(payload []byte, ad AuthDriver) error }
UserIdentity describes the identity and access rights of a user. For regular users, it is returned by methods in the AuthDriver interface. For all other types of users, it is implicitly created in helper methods higher up in the stack.
func DeserializeUserIdentity ¶
func DeserializeUserIdentity(typeID string, payload []byte, ad AuthDriver) (UserIdentity, error)
DeserializeUserIdentity deserializes a UserIdentity payload. This is the reverse of UserIdentity.SerializeToJSON().
type UserType ¶
type UserType int
UserType is an enum that identifies the general type of user. User types are important because certain API endpoints or certain behavior is restricted to specific user types. For example, anonymous users may not cause implicit replications to occur, and peer users are exempt from rate limits.
const ( // RegularUser is the UserType for regular users that authenticated via the AuthDriver. RegularUser UserType = iota // AnonymousUser is the UserType for unauthenticated users. AnonymousUser // PeerUser is the UserType for peer users, i.e. other Keppel instances using the API as a peer. PeerUser // TrivyUser is the UserType for tokens issued to Trivy. TrivyUser // JanitorUser is a dummy UserType for when the janitor needs an Authorization for audit logging purposes. JanitorUser )
type ValidationPolicy ¶
type ValidationPolicy struct {
RequiredLabels []string `json:"required_labels,omitempty"`
}
ValidationPolicy represents a validation policy in the API.
func RenderValidationPolicy ¶
func RenderValidationPolicy(account models.ReducedAccount) *ValidationPolicy
RenderValidationPolicy builds a ValidationPolicy object out of the information in the given account model.
func (ValidationPolicy) ApplyToAccount ¶
func (v ValidationPolicy) ApplyToAccount(account *models.Account) *RegistryV2Error
ApplyToAccount validates this policy and stores it in the given account model.
Source Files ¶
- account.go
- account_management_driver.go
- auditor.go
- auth_driver.go
- config.go
- database.go
- database_helpers.go
- duration.go
- errors.go
- federation_driver.go
- gc_policy.go
- http.go
- inbound_cache_driver.go
- manifest.go
- peer.go
- ratelimit_driver.go
- rbac_policy.go
- replica_sync.go
- replication.go
- reverse_proxy.go
- security_scan_policy.go
- storage_driver.go
- sublease_token.go
- user_identity.go
- utils.go
- validation.go