Besides the OCI Distribution API that is used e.g. by docker pull/push
, Keppel provides its own REST API
for managing Keppel accounts.
This document uses the terminology defined in the README.md.
- Error responses always have
Content-Type: text/plain
. - Account names must conform to the regex
^[a-z0-9-]{1,48}$
, that is, they may not be longer than 48 chars and may only contain lowercase letters, digits and dashes.
The authentication method for this API depends on which auth driver is used by the Keppel instance in question:
- When the auth driver is
keystone
, all endpoints require a Keystone token to be present in theX-Auth-Token
header. Only Keystone v3 is supported. - When the auth driver is
keystone
, Keppel's service URL can be found in the Keystone service catalog under the service typekeppel
.
The OCI Distribution API usually uses OAuth-like bearer tokens, but in Keppel, it can also be made to use the same
authentication method as the API specified in this document. To do so, add the request header Authorization: keppel
and the same request headers as on this API to a request for the OCI Distribution API. Conversely, the Keppel API can be
used with the bearer token auth scheme prescribed by the OCI Distribution API. The Keppel API will render the respective
auth challenges when API requests are made without any form of authentication.
By default, the OCI Distribution API is structured such that the account name is prepended to all repository names. For
instance, a Docker image reference like registry.example.com/foo/bar:latest
refers to the repository called bar
within the account called foo
.
However, if the Keppel instance has been set up thusly, the OCI Distribution API is also offered for each account
individually. This is called a domain-remapped API in Keppel, because it is inspired by a similar concept of the
same name in OpenStack Swift. The domain-remapped analog of the example image reference above would be
foo.registry.example.com/bar:latest
, i.e. the account name has become part of the domain name.
This is particularly useful if you have an external replica account of Docker Hub, since dockerd only accepts plain
hostnames in its registry-mirrors
option. Therefore something like registry.example.com/dockerhubmirror
would not
work, but dockerhubmirror.registry.example.com
does work.
The domain-remapped domain names only offer the OCI Distribution API and the GET /keppel/v1/auth
endpoint. The Keppel
API itself can only be accessed through the respective Keppel instance's main domain name.
Shows information about this Keppel API. Authentication is not required. On success, returns 200 and a JSON response like this:
{
"auth_driver": "keystone"
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
auth_driver |
string | The authentication driver used by this Keppel instance. This is important to know for clients using the Keppel API to decide how to obtain an authorization for the API. |
Lists all accounts that the user has access to. On success, returns 200 and a JSON response body like this:
{
"accounts": [
{
"name": "firstaccount",
"auth_tenant_id": "firsttenant",
"rbac_policies": [
{
"match_repository": "library/.*",
"permissions": [ "anonymous_pull" ]
},
{
"match_repository": "library/alpine",
"match_username": "exampleuser@secondtenant",
"permissions": [ "pull", "push" ]
}
],
"gc_policies": [
{
"match_repository": ".*",
"time_constraint": {
"on": "last_pulled_at",
"newer_than": {
"value": 7,
"unit": "d"
}
},
"action": "protect"
},
{
"match_repository": ".*/webapp",
"except_repository": "example/webapp",
"match_untagged": true,
"action": "delete"
}
]
},
{
"name": "secondaccount",
"auth_tenant_id": "secondtenant",
"rbac_policies": [],
"replication": {
"strategy": "on_first_use",
"upstream": "keppel.example.com"
},
"validation": {
"required_labels": [ "maintainers", "source_repo" ]
}
}
]
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
accounts[].name |
string | Name of this account. |
accounts[].auth_tenant_id |
string | ID of auth tenant that regulates access to this account. |
accounts[].gc_policies |
list of objects or omitted | Policies for garbage collection (automated deletion of images) for repositories in this account. GC policies apply in addition to the regular garbage collection runs performed by Keppel that clean up unreferenced objects of all kinds. GC policies are ordered by priority: Earlier policies take precedence over later policies. |
accounts[].gc_policies[].match_repository |
string | Required. The GC policy applies to all repositories in this account whose name matches this regex. The leading account name and slash is stripped from the repository name before matching. The notes on regexes below apply. |
accounts[].gc_policies[].except_repository |
string or omitted | If given, matching repositories will be excluded from this GC policy, even if they match the match_repository regex. The syntax and mechanics of matching are otherwise identical to match_repository above. |
accounts[].gc_policies[].match_tag |
string or omitted | The GC policy applies to all images in matching repositories that have a tag whose name matches this regex. The notes on regexes below apply. |
accounts[].gc_policies[].except_tag |
string or omitted | If given, images with matching tag names will be excluded from this GC policy, even if they match the match_tag regex. The syntax and mechanics of matching are otherwise identical to match_tag above. |
accounts[].gc_policies[].only_untagged |
bool or omitted | If true, the GC policy applies only to those images that do not have any tags. |
accounts[].gc_policies[].time_constraint |
object | If given, the GC policy only applies to images matching the time constraint specified herein. |
accounts[].gc_policies[].time_constraint.on |
string | The timestamp attribute on each image on which this time constraint operates. Either pushed_at or last_pulled_at . For the purposes of GC policy evaluation, if an image has never been pulled, its last_pulled_at timestamp will be set to the UNIX epoch (1970-01-01 00:00:00 UTC). |
accounts[].gc_policies[].time_constraint.oldest accounts[].gc_policies[].time_constraint.newest |
integer or omitted | If set, the GC policy only applies to at most that many images within each repository, specifically to those that are oldest/newest ones when ordered by the timestamp attribute specified in the time_constraint.on key. These constraints are forbidden for policies with action "delete" to ensure that GC runs are idempotent. |
accounts[].gc_policies[].time_constraint.older_than accounts[].gc_policies[].time_constraint.newer_than |
duration or omitted | If set, the GC policy only applies to at most images whose timestamp (as selected by the time_constraint.on key) is older/newer than the given age. Durations are given as a JSON object with the keys value (integer) and unit (string), e.g. {"value": 4, "unit": "d"} for 4 days. The units s (second), m (minute), h (hour), d (day), w (7 days) and y (365 days) are understood. |
accounts[].gc_policies[].action |
string | One of: delete (to delete matching images) or protect (to not delete matching images, even if another policy with a lower priority would want to). |
accounts[].state |
string | The state of the account. Only shown when there is a specific state to report. See below for possible values and details. |
accounts[].rbac_policies |
list of objects | Policies for rule-based access control (RBAC) to repositories in this account. RBAC policies are evaluated in addition to the permissions granted by the auth tenant. |
accounts[].rbac_policies[].match_cidr |
string | The RBAC policy applies to requests which originate from an IP address that matches the CIDR. |
accounts[].rbac_policies[].match_repository |
string | The RBAC policy applies to all repositories in this account whose name matches this regex. The leading account name and slash is stripped from the repository name before matching. The notes on regexes below apply. |
accounts[].rbac_policies[].match_username |
string | The RBAC policy applies to all users whose name matches this regex. Refer to the documentation of your auth driver for the syntax of usernames. The notes on regexes below apply. |
accounts[].rbac_policies[].permissions |
list of strings | The permissions granted by the RBAC policy. Acceptable values include pull , push , delete , anonymous_pull and anonymous_first_pull . When pull , push or delete are included, match_username is not empty. When anonymous_pull or anonymous_first_pull is included, match_username is empty. anonymous_first_pull is only relevant for external replica accounts and allows unauthenticated users to replicate tags. It should always be combined with an appropriate match_* rule. |
accounts[].replication |
object or omitted | Replication configuration for this account, if any. See below for details. |
accounts[].platform_filter |
list of objects or omitted | Only allowed for replica accounts. If not empty, when replicating an image list manifest (i.e. a multi-architecture image), only submanifests matching one of the given platforms will be replicated. Each entry must have the same format as the manifests[].platform field in the OCI Image Index Specification. |
accounts[].validation |
object or omitted | Validation rules for this account. When included, pushing blobs and manifests not satisfying these validation rules may be rejected. |
accounts[].validation.required_labels |
list of strings | When non-empty, image manifests must include all these labels. (Labels can be set on an image using the Dockerfile's LABEL command.) |
The values of fields with names like match_...
and except_...
are regular expressions, using the
syntax defined by Go's stdlib regex parser. The anchors ^
and $
are implied
at both ends of the regex, and need not be added explicitly.
This section describes the different possible configurations for accounts[].replication
.
When an authorized user pulls a manifest which does not exist in this registry yet, the same manifest will be queried in the respective primary account. The primary account must have the same name as this account and must be located in one of the upstream registries configured by the Keppel operator. If this query returns a result, the manifest and all blobs referenced by it will be pulled from the upstream registry into the local one. Note that:
- Manifests and blobs can not be deleted directly, but will be cleaned up once they disappear from the upstream registry.
- Accounts with this replication strategy will not allow direct push access. Images can only be added to these accounts through replication.
The following fields are shown on accounts configured with this strategy:
Field | Type | Explanation |
---|---|---|
accounts[].replication.strategy |
string | The string on_first_use . |
accounts[].replication.upstream |
string | The hostname of the upstream registry. Must be one of the peers configured for this registry by its operator. |
This behaves mostly identically to on_first_use
, but can pull from any registry implementing the OCI Distribution
Spec, including public registries like Docker Hub or GCR. Since there is no way for Keppel to negotiate service users
with these registries, the user must supply pull credentials (or else anonymous access is used for pulling, meaning that
only publicly accessible images can be replicated). Note that:
- Accounts with this strategy can be replicated from by other peer registries. For instance, an account with
on_first_use
in a peer registry can pull from an account withfrom_external_on_first_use
in this registry. - Pulling an image from this account requires a non-anonymous token when the image is pulled for the first time. This is a safety measure to prevent external users from leeching off some other team who configured their account to pull from a popular public registry and enabled anonymous pulling. In this scenario, only the team members of the team hosting the account can decide to host images in the account by explicitly pulling them for the first time.
The following fields are shown on accounts configured with this strategy:
Field | Type | Explanation |
---|---|---|
accounts[].replication.strategy |
string | The string from_external_on_first_use . |
accounts[].replication.upstream.url |
string | The URL from which images are pulled. This may refer to either a public registry's domain name (e.g. registry-1.docker.io for Docker Hub) or a subpath below its domain name (e.g. gcr.io/google_containers ). |
accounts[].replication.upstream.username accounts[].replication.upstream.password |
string, optional | The credentials that this registry logs in with to replicate images from upstream. If not given, anonymous login is used. |
Note that the accounts[].replication.upstream.password
field is omitted from GET responses for security reasons.
When accounts[].state
is deleting
, the following differences in behavior apply to this account:
- For primary accounts (i.e. accounts that are not replicas), no new blobs or manifests may be pushed. Only pulling and deleting are allowed.
- For replica accounts, no new blobs or manifests will be replicated. Pulling is still allowed, but it becomes possible to delete blobs and manifests.
Sending a DELETE request on an account moves it into state = "deleting"
and schedules the deletion of everything that belongs to the account, including manifests and blobs.
Shows information about an individual account. Returns 404 if no account with the given name exists, or if the user does not have access to it. Otherwise returns 200 and a JSON response body like this:
{
"account": {
"name": "firstaccount",
"auth_tenant_id": "firsttenant",
"rbac_policies": [
{
"match_repository": "library/.*",
"permissions": [ "anonymous_pull" ]
},
{
"match_repository": "library/alpine",
"match_username": "exampleuser@secondtenant",
"permissions": [ "pull", "push" ]
}
]
}
}
The .account
object's contents are equivalent to the corresponding entry in .accounts[]
as returned by
GET /keppel/v1/accounts
.
Creates or updates the account with the given name. The request body must be a JSON document following the same schema as the response from the corresponding GET endpoint, except that:
account.name
may not be present (the name is already given in the URL), andaccount.auth_tenant_id
andaccount.replication
may not be changed for existing accounts.
On success, returns 200 and a JSON response body like from the corresponding GET endpoint.
When creating a replica account, it may be necessary to supply a sublease token in the X-Keppel-Sublease-Token
header. The sublease token must have been issued by the Keppel instance hosting the corresponding primary account, via
the POST /keppel/v1/accounts/:name/sublease endpoint. If a sublease token is
required, but the correct one was not supplied, 403 (Forbidden) will be returned.
Deletes the given account. On success, returns 204 (No Content).
Accounts can only be deleted after all manifests and blobs have been deleted from the account and its backing storage. If these requirements are not met, 409 (Conflict) will be returned along with a JSON response body like this:
{
"remaining_manifests": {
"count": 23,
"next": [
{
"repository": "library/alpine",
"digest": "sha256:54c5b3dd459d5ef778bb2fa1e23a5fb0e1b62ae66970bcb436e8f81a1a1a8e41",
},
{
"repository": "library/alpine",
"digest": "sha256:721fe5d2ca0c3f66b596df049b23619d14b9912f88344dea3b5335ad007f11a3",
},
...
]
},
"remaining_blobs": {
"count": 42
},
"error": "cannot delete this primary account because replicas are still attached to it"
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
remaining_manifests |
object | If this field is included, there are still manifests left in the account that the client has to delete before the account deletion can proceed. A list of manifests can be found in the remaining_manifests.next field. |
remaining_manifests.count |
integer | The total number of manifests that are still stored in this account. This value can be used to present a progress indication to the user. |
remaining_manifests.next |
array of objects | A list of manifests that are still stored in this account. To proceed with the account deletion, the client shall delete these manifests first, then restart the DELETE request on the account. The length of this list is capped to prevent excessive response sizes, so the number of entries in this list may be less than remaining_manifests.count . In this case, the next DELETE request on the account will show the next set of manifests that needs to be deleted. |
remaining_manifests.next[].repository |
string | The repository (within this account) where this manifest is stored. |
remaining_manifests.next[].digest |
string | The digest of this manifest. |
remaining_blobs |
object | If this field is included, there are still blobs left in the account. There is no API for deleting blobs, but when this field is included, it indicates that Keppel has scheduled a garbage collection to cleanup these blobs. The client shall restart the DELETE request on the account after some time (e.g. 15 seconds) to observe whether garbage collection is finished. |
remaining_blobs.count |
integer | The total number of blobs that are still stored in this account. This value can be used to present a progress indication to the user. |
error |
string | If this field is included, the account deletion was attempted, but failed. This field contains a human-readable error message describing the problem. |
Unlike in the artificial example above, usually only one of the top-level fields will be present at a time. Each
top-level field represents a distinct phase of the account deletion process: First all manifests need to be deleted (so
only remaining_manifests
would be shown), then all blobs need to be garbage-collected (so only remaining_blobs
would
be shown), then the account itself can be deleted (so only error
would be shown if necessary).
Issues a sublease token for the given account. A sublease token can be redeemed exactly once to create a replica account connected to this account in another Keppel instance. On success, returns 200 and a JSON response body like this:
{
"sublease_token": "oingoojei6aejab0Too4"
}
The sublease token mechanism is optional. If the .sublease_token
field comes back empty, it means that no sublease
token needs to be presented when creating a replica of this primary account.
Sublease tokens can only be issued for primary accounts. If the account in question is a replica account, 400 (Bad Request) is returned.
If this Keppel is configured to use its bundled Trivy security scanner, this endpoint returns a list of user-defined policies for how Keppel processes reports generated by Trivy for images in the respective account. On success, returns 200 and a JSON response body like this:
{
"policies": [
{
"match_repository": ".*",
"match_vulnerability_id": ".*",
"except_fix_released": true,
"action": {
"ignore": true,
"assessment": "risk accepted: vulnerabilities without an available fix are not actionable"
}
},
{
"managed_by_user": "mytechnicaluser@mydomain",
"match_repository": "my-python-app|my-other-image",
"match_vulnerability_id": "CVE-2022-40897",
"action": {
"severity": "Low",
"assessment": "adjusted severity: python-setuptools cannot be invoked through user requests"
}
}
]
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
policies |
array of objects | Each policy matches a certain set of vulnerabilities on images in a certain set of repositories. Policies can be used to adjust the severity of vulnerabilities or ignore them altogether in certain situations. |
policies[].managed_by_user |
string or omitted | If shown, the policy can only be edited by the user account with the given name. |
policies[].match_repository |
string | The policy applies to all repositories in this account whose name matches this regex. The leading account name and slash is stripped from the repository name before matching. The notes on regexes below apply. |
policies[].except_repository |
string or omitted | If given, matching repositories will be excluded from this policy, even if they match the match_repository regex. The syntax and mechanics of matching are otherwise identical to match_repository above. |
policies[].match_vulnerability_id |
string | The policy applies to all vulnerabilities whose ID (as shown in the VulnerabilityID field of Trivy's JSON report format) matches this regex. In most cases, the vulnerability ID is a CVE number like CVE-2014-0160 . The notes on regexes below apply. |
policies[].except_vulnerability_id |
string or omitted | If given, matching vulnerabilities will be excluded from this policy, even if they match the match_vulnerability_id regex. The syntax and mechanics of matching are otherwise identical to match_vulnerability_id above. |
policies[].except_fix_released |
bool or omitted | If true, the policy applies only to those vulnerabilities for which no fixed version has been released to the distribution repository (that is, the FixedVersion field is missing in Trivy's JSON report format). |
policies[].action |
object | The effect that this policy will have on matching vulnerabilities reported for images in matching repositories. |
policies[].action.assessment |
string | A human-readable description of the reasoning behind this policy (maximum 1 KiB). |
policies[].action.ignore |
bool or omitted | If true, matching vulnerabilities will be ignored when computing the aggregated vulnerability status of the respective image manifest. This is the same effect as if action.severity was set to Clean , but the intent is clearer. |
policies[].action.severity |
string or omitted | If present, matching vulnerabilities will be treated as having the given severity when computing the aggregated vulnerability status of the respective image manifest. Acceptable values include Low , Medium , High and Critical . |
The values of string fields with names like match_...
and except_...
are regular expressions, using the
syntax defined by Go's stdlib regex parser. The anchors ^
and $
are implied
at both ends of the regex, and need not be added explicitly.
If multiple policies match the same vulnerability, only the action of the first policy (in order of appearance in the
policies
list) will take effect.
If this Keppel is configured to use its bundled Trivy security scanner, this endpoint replaces the list of user-defined policies for how Keppel processes reports generated by Trivy for images in the respective account. The request body must be a JSON document following the same schema as the response from the corresponding GET endpoint, except that:
policies[].managed_by_user
may contain the special value$REQUESTER
to indicate the requesting user. This value will be replaced with the actual name of the requesting user.
On success, returns 200 and a JSON response body like from the corresponding GET endpoint.
Lists repositories within the account with the given name. On success, returns 200 and a JSON response body like this:
{
"repositories": [
{
"name": "foo0001",
"manifest_count": 23,
"tag_count": 2,
"size_bytes": 103876423,
"pushed_at": 1575467980
},
...,
{
"name": "foo1000",
"manifest_count": 10,
"tag_count": 0,
"size_bytes": 29862877,
"pushed_at": 1575468024
}
],
"truncated": true
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
repositories[].name |
string | Name of this repository. |
repositories[].manifest_count |
integer | Number of manifests that are stored in this repository. |
repositories[].tag_count |
integer | Number of tags that exist in this repository. |
repositories[].size_bytes |
integer | Size sum for all blobs in this repository. This correctly deduplicates layers shared between multiple manifests, but does not count the manifest's own size (only the blobs referenced therein). |
repositories[].pushed_at |
UNIX timestamp | When a manifest was pushed into the registry most recently. |
truncated |
boolean | Indicates whether marker-based pagination must be used to retrieve the rest of the result. |
Because an account may contain a potentially large number of repos, the implementation may employ marker-based
pagination. If the .truncated
field is present and true, only a partial result is shown. The next page of results
can be obtained by resending the GET request with the query parameter marker
set to the name of the last repository in
the current result list, for instance
GET /keppel/v1/accounts/$ACCOUNT_NAME/repositories?marker=foo1000
for the example response shown above. The last page of results will have truncated
omitted or set to false.
Deletes the specified repository and all manifests in it. Returns 204 (No Content) on success.
Returns 409 (Conflict) if the repository still contains manifests. All manifests in the repository must be deleted before the repository can be deleted.
Note the underscore in the last path element. Since repository names may contain slashes themselves, the underscore is necessary to distinguish the reserved word _manifests
from a path component in the repository name.
Lists manifests (and, indirectly, tags) in the given repository in the given account. On success, returns 200 and a JSON response body like this:
{
"manifests": [
{
"digest": "sha256:622cb3371c1a08096eaac564fb59acccda1fcdbe13a9dd10b486e6463c8c2525",
"media_type": "application/vnd.docker.distribution.manifest.v2+json",
"size_bytes": 10518718,
"pushed_at": 1575468024,
"last_pulled_at": 1575554424,
"tags": [
{
"name": "latest",
"pushed_at": 1575468024,
"last_pulled_at": 1575550824
}
],
"labels": {
"maintainers": "Stefan"
},
"gc_status": {
"protected_by_recent_upload": true
},
"vulnerability_status": "Clean"
},
{
"digest": "sha256:5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03",
"media_type": "application/vnd.oci.image.manifest.v1+json",
"size_bytes": 2791084,
"pushed_at": 1575467980,
"last_pulled_at": null,
"vulnerability_status": "High"
}
]
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
manifests[].digest |
string | The canonical digest of this manifest. |
manifests[].media_type |
string | The MIME type of the canonical form of this manifest. |
manifests[].size_bytes |
integer | Total size of this manifest and all layers referenced by it in the backing storage. |
manifests[].pushed_at |
UNIX timestamp | When this manifest was pushed into the registry. |
manifests[].last_pulled_at |
UNIX timestamp or null | When this manifest was last pulled from the registry (or null if it was never pulled). |
manifests[].tags |
array | All tags that currently resolve to this manifest. |
manifests[].tags[].name |
string | The name of this tag. |
manifests[].tags[].pushed_at |
string | When this tag was last updated in the registry. |
manifests[].tags[].last_pulled_at |
UNIX timestamp or null | When this manifest was last pulled from the registry using this tag name (or null if it was never pulled from this tag). |
manifests[].labels |
object of strings | Free-form labels maintained by the user (labels are set on an image using the Dockerfile's LABEL command). The contents of this field may be interpreted by Keppel and might trigger special behavior, e.g. when validation.required_labels is configured for an account. |
manifests[].gc_status |
object or omitted | Omitted if policy-guided garbage collection has not encountered this manifest yet. Otherwise contains a status report from the last GC run. If this object is shown, it will contain exactly one of the following attributes. |
manifests[].gc_status.protected_by_recent_upload |
true or omitted | If true, this manifest was protected from deletion during the last GC run because it was uploaded too recently (within 10 minutes of the GC run). |
manifests[].gc_status.protected_by_parent |
string or omitted | If shown, this manifest was protected from deletion during the last GC run because there is a parent manifest that references it. The field contains the parent manifest's digest. If the manifest is referenced by multiple parent manifests, it is not defined which parent manifest's digest will be shown. |
manifests[].gc_status.protected_by_policy |
object or omitted | If shown, this manifest was protected from deletion during the last GC run because of a matching policy with the "protect" action. The object will contain the policy definition in the same format as described above for accounts[].gc_policies[] . |
manifests[].gc_status.relevant_policies |
array of objects or omitted | If shown, this manifest was not protected from deletion during the last GC run, but no deleting policy matched either. The array will contain the definitions of all deleting policies that could apply to this manifest, in the same format as described above for accounts[].gc_policies[] . |
manifests[].vulnerability_status |
string | Either Clean (no vulnerabilities have been found in this image), Pending (vulnerability scanning is not enabled on this server or is still in progress for this image or has failed for this image), Error (vulnerability scanning failed for this image or an image referenced in this manifest), or any of the following severity strings: Unknown , Low , Medium , High , Critical . The full vulnerability report can be retrieved with a separate API call. |
manifests[].vulnerability_scan_error |
string | Only shown if vulnerability_status is Error or Unsupported . Contains the error message from Trivy that explains why this image could not be scanned (for status Error ) or an error message from Keppel that explains why this image was not submitted to Trivy (for status Unsupported ). When vulnerability_status is Error or Unsupported because scanning failed for an image referenced in this manifest, the error message will be shown on the referenced manifest instead of on this manifest. |
truncated |
boolean | Indicates whether marker-based pagination must be used to retrieve the rest of the result. |
Deletes the specified manifest and all tags pointing to it. Returns 204 (No Content) on success. The digest that identifies the manifest must be that manifest's canonical digest, otherwise 404 is returned.
If this Keppel is configured to use its bundled Trivy security scanner, this endpoint retrieves a report for the specified manifest from Trivy. If the manifest exists and a vulnerability report is available for it, returns 200 (OK) and a JSON response body containing the vulnerability report in the format defined by Trivy, possibly enriched as described below.
The output format can be selected with the format
query parameter. Supported values include:
json
(default) for Trivy's default vulnerability report format, andspdx-json
for the image's SBOM in the SPDX-compliant JSON format.
Returns 404 (Not Found) if the specified manifest does not exist.
Otherwise, returns 204 (No Content) if the manifest does not directly reference any image layers and thus cannot be scanned for vulnerabilities itself.
Otherwise, returns 405 (Method Not Allowed) if the manifest exists, but its vulnerability status (see above) is either Pending
or Error
.
(This case should technically also be a 404, but the different status code allows clients to disambiguate the nonexistence of the manifest from the nonexistence of the vulnerability report.)
Note that, when manifests reference other manifests (the most common case being multi-arch images referencing their constituent single-arch images), the vulnerability status of the parent manifest aggregates over the vulnerability statuses of its child manifests, but its vulnerability report only covers image layers directly referenced by the parent manifest. Clients displaying the vulnerability report for a multi-arch image manifest or any other manifest referencing child manifests should recursively fetch the vulnerability reports of all child manifests and show a merged representation as appropriate for their use case.
Reports in the format json
can be enriched by Keppel by adding the top-level key X-Keppel-Applicable-Policies
. This
key appears if security scan policies are maintained on the account containing the image manifest, and at least one
policy applies to at least one vulnerability that exists in the report. If the key exists, it contains an object whose
keys are vulnerability IDs, with each respective value being the policy that applies to the vulnerabilities with this
ID. This information can be used by user agents to understand how Keppel computed the vulnerability status of the full
image manifest from the individual vulnerabilities.
Deletes the specified tag, without deleting the manifest it points to. Returns 204 (No Content) on success.
This endpoint is reserved for the authentication workflow of the OCI Distribution API.
This endpoint is only used for internal communication between Keppel registries and cannot be used by outside users.
An upstream registry can send this request to a downstream registry to issue a username and password for it. These credentials allow pull access to the entire upstream registry via the usual Registry V2 auth process. The downstream registry is expected to store these credentials for use in content replication. On success, returns 204 (No Content). The request body must be a JSON document like this:
{
"peer": "keppel.example.org",
"username": "[email protected]",
"password": "EaxiYoo8ju7Ohvukooch"
}
The following fields must be included:
Field | Type | Explanation |
---|---|---|
peer |
string | The hostname of the registry for which those credentials are valid. |
username password |
string | Credentials granting global pull access to that registry. |
Shows information about the peers known to this registry. This information is vital for users who want to create a replica account, since they have to know which peers are eligible. On success, returns 200 and a JSON response body like this:
{
"peers": [
{ "hostname": "keppel.example.org" },
{ "hostname": "keppel.example.com" }
]
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
peers |
list of objects | List of peers known to this registry. |
peers[].hostname |
string | Hostname of this peer. |
Shows information about resource usage and limits for the given auth tenant. On success, returns 200 and a JSON response body like this:
{
"manifests": {
"quota": 1000,
"usage": 42
}
}
The following fields may be returned:
Field | Type | Explanation |
---|---|---|
manifests.quota |
integer | Maximum number of manifests that can be pushed to repositories in accounts belonging to this auth tenant. |
manifests.usage |
integer | How many manifests exist in repositories in accounts belonging to this auth tenant. |
Updates the configuration for this auth tenant. The request body must be a JSON document following the same schema
as the response from the corresponding GET endpoint, except that the .usage
fields may not be present.
On success, returns 200 and a JSON response body like from the corresponding GET endpoint.