├── brigade.json ├── img ├── example-tuf-repository.png └── example-tuf-in-toto-repository.png ├── Dockerfile.ajv ├── OWNERS ├── schema ├── relocation-mapping.schema.json ├── status.schema.json ├── claim.schema.json ├── claim-result.schema.json ├── parameter-sources.schema.json ├── dependencies.schema.json ├── definitions.schema.json └── bundle.schema.json ├── LICENSE ├── 210-on-disk-representation.md ├── examples ├── 810.01-parameter-sources.json ├── 103.01-relocation-mapping.json ├── 804.01-status.json ├── 804.02-status.json ├── 400.01-claim-result.json ├── 804.03-status.json ├── 500.01-dependencies.json ├── 101.01-bundle.json ├── 101.02-bundle.json ├── 101.03-bundle.json └── 400.01-claim.json ├── 807-registries-known-implementations.md ├── 803-repositories.md ├── .github └── CODEOWNERS ├── scripts ├── validate-url.sh └── validate.sh ├── Makefile ├── 000-index.md ├── governance.md ├── CONTRIBUTING.md ├── 802-credential-sets.md ├── brigade.js ├── 200-CNAB-registries.md ├── 810-well-known-custom-extensions.md ├── 801-declarative-images.md ├── 804-well-known-custom-actions.md ├── 303-verification-workflows.md ├── 500-CNAB-dependencies.md ├── 104-bundle-formats.md ├── 302-signing-workflows.md ├── 805-airgap.md ├── 201-representing-CNAB-in-OCI.md ├── 102-invocation-image.md ├── README.md ├── 300-CNAB-security.md ├── 806-security-known-implementations.md ├── 100-CNAB.md ├── 901-process.md ├── 103-bundle-runtime.md ├── 301-metadata-repositories.md └── 400-claims.md /brigade.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@brigadecore/brigade-utils": "0.4.1" 4 | } 5 | } -------------------------------------------------------------------------------- /img/example-tuf-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnabio/cnab-spec/HEAD/img/example-tuf-repository.png -------------------------------------------------------------------------------- /Dockerfile.ajv: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine 2 | 3 | RUN apk add --no-cache curl jq 4 | 5 | RUN npm install -g ajv-cli@3.3.0 -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | maintainers: 2 | - radu-matei 3 | - technosophos 4 | - jeremyrickard 5 | - silvin-lubecki 6 | - jlegrone 7 | - carolynvs -------------------------------------------------------------------------------- /img/example-tuf-in-toto-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnabio/cnab-spec/HEAD/img/example-tuf-in-toto-repository.png -------------------------------------------------------------------------------- /schema/relocation-mapping.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": { 4 | "type": "string" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The specifications in this repository are licensed under the Open Web Foundation Agreements as set forth in the CONTRIBUTING.md document. -------------------------------------------------------------------------------- /210-on-disk-representation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: On Disk Representation of a CNAB bundle 3 | weight: 210 4 | --- 5 | 6 | # On Disk Representation of a CNAB bundle 7 | 8 | TODO: 9 | -------------------------------------------------------------------------------- /examples/810.01-parameter-sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "tfstate": { 3 | "priority": [ 4 | "output" 5 | ], 6 | "sources": { 7 | "output": { 8 | "name": "tfstate" 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /807-registries-known-implementations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Known implementations of CNAB Registry 3 | weight: 807 4 | --- 5 | 6 | TODO: 7 | 8 | As of July 2020, [the list of known container registries that support CNAB bundles can be found on the CNAB website](https://cnab.io/registries/). 9 | -------------------------------------------------------------------------------- /examples/103.01-relocation-mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "example/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687": "my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687", 3 | "outside/helloworld:0.1.0": "my.registry/helloworld:0.1.0" 4 | } -------------------------------------------------------------------------------- /examples/804.01-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "backend": { 4 | "status": "Ready" 5 | }, 6 | "front-end": { 7 | "message": "Failed to pull Docker image mginx:latest", 8 | "status": "Failed" 9 | } 10 | }, 11 | "message": "Component front-end failed to deploy properly", 12 | "status": "Failed" 13 | } 14 | -------------------------------------------------------------------------------- /803-repositories.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Registries 3 | --- 4 | 5 | # CNAB Registries 6 | 7 | CNAB bundles can be stored in [OCI Distribution-compliant repository](https://github.com/opencontainers/distribution-spec/blob/master/spec.md) 8 | 9 | _This section is moved to [200-CNAB-registries](200-CNAB-registries.md)_ 10 | 11 | 12 | Next section: [Well known custom actions](804-well-known-custom-actions.md) 13 | -------------------------------------------------------------------------------- /examples/804.02-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "gke": { 4 | "components": { 5 | "kube-api": { 6 | "status": "Ready" 7 | }, 8 | "network": { 9 | "status": "Ready" 10 | }, 11 | "scheduler": { 12 | "status": "Ready" 13 | } 14 | }, 15 | "status": "Ready" 16 | }, 17 | "wordpress": { 18 | "status": "Ready" 19 | } 20 | }, 21 | "status": "Ready" 22 | } 23 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See the CODEOWNERS syntax at https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax 2 | 3 | # These owners will be the default owners for everything in 4 | # the repo. Unless a later match takes precedence, 5 | # @global-owner1 and @global-owner2 will be requested for 6 | # review when someone opens a pull request. 7 | * @radu-matei @technosophos @jeremyrickard @silvin-lubecki @jlegrone @carolynvs 8 | -------------------------------------------------------------------------------- /examples/400.01-claim-result.json: -------------------------------------------------------------------------------- 1 | { 2 | "claimId": "01E5G8ZYP714JVM8NHTJQ4FH15", 3 | "custom": {}, 4 | "id": "01E2ZZ2FKSE0V41DCXFCSW5D1M", 5 | "created": "2018-08-30T20:39:55.549002887-06:00", 6 | "message": "", 7 | "outputs": { 8 | "clientCert": { 9 | "contentDigest":"sha256:aaa..." 10 | }, 11 | "hostName": { 12 | "contentDigest":"sha256:bbb..." 13 | }, 14 | "port": { 15 | "contentDigest":"sha256:ccc..." 16 | } 17 | }, 18 | "status": "succeeded" 19 | } 20 | -------------------------------------------------------------------------------- /examples/804.03-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "backend": { 4 | "com.example.scale": { 5 | "actual": 3, 6 | "desired": 3, 7 | "failed": 0, 8 | "pending": 0 9 | }, 10 | "status": "Ready" 11 | }, 12 | "front-end": { 13 | "com.example.scale": { 14 | "actual": 2, 15 | "desired": 3, 16 | "failed": 0, 17 | "pending": 1 18 | }, 19 | "status": "Pending" 20 | } 21 | }, 22 | "status": "Pending" 23 | } 24 | -------------------------------------------------------------------------------- /examples/500.01-dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": { 3 | "blob": { 4 | "bundle": "somecloud/blob-storage" 5 | }, 6 | "mongo": { 7 | "bundle": "somecloud/mongo", 8 | "version":{ 9 | "prereleases": true 10 | } 11 | }, 12 | "mysql": { 13 | "bundle": "somecloud/mysql", 14 | "version": { 15 | "prereleases": true, 16 | "ranges": [ 17 | "5.7.x" 18 | ] 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /scripts/validate-url.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### 3 | # Validates that the schema files are served via the https://cnab.io domain, 4 | # as many of the schema contain $refs to each other via URLs on this domain. 5 | # 6 | # If a new schema is added to this repository, a new redirect rule can be 7 | # added to the cnabio/cnab.io repository. 8 | # See https://github.com/cnabio/cnab.io/blob/main/static/_redirects 9 | # 10 | ### 11 | 12 | set -eou pipefail 13 | 14 | for schema in $(ls -1 schema); do 15 | url="https://cnab.io/v1/${schema}" 16 | echo "Validating that ${url} returns well-formed json" 17 | curl -sfL "${url}" | jq > /dev/null 2>&1 \ 18 | || ( echo "${url} doesn't appear to be available or is invalid" && exit 1 ) 19 | done 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION ?= $(shell git describe --tags 2> /dev/null || echo v0) 2 | BASE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 3 | VALIDATOR_IMG := cnabio/cnab-spec.ajv 4 | 5 | .PHONY: build-validator 6 | build-validator: 7 | @docker build -f Dockerfile.ajv -t $(VALIDATOR_IMG) . 8 | 9 | .PHONY: validate 10 | validate: build-validator 11 | @docker run --rm \ 12 | -v $(BASE_DIR):/root \ 13 | -w /root \ 14 | $(VALIDATOR_IMG) ./scripts/validate.sh 15 | 16 | .PHONY: validate-url 17 | validate-url: build-validator 18 | @docker run --rm \ 19 | -v $(BASE_DIR):/root \ 20 | -w /root \ 21 | $(VALIDATOR_IMG) ./scripts/validate-url.sh 22 | 23 | .PHONY: build-validator-local 24 | build-validator-local: 25 | @npm install -g ajv-cli@3.3.0 26 | 27 | .PHONY: validate-local 28 | validate-local: build-validator-local 29 | ./scripts/validate.sh 30 | 31 | .PHONY: validate-url-local 32 | validate-url-local: build-validator-local 33 | ./scripts/validate-url.sh 34 | 35 | # AZURE_STORAGE_CONNECTION_STRING will be used for auth in the following target 36 | .PHONY: publish 37 | publish: 38 | @az storage blob upload-batch -d schema/$(VERSION) -s schema 39 | -------------------------------------------------------------------------------- /000-index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Index 3 | weight: 001 4 | --- 5 | 6 | # CNAB Specifications 7 | 8 | 1. [Cloud Native Application Bundle Core 1.2.0 (CNAB1)](100-CNAB.md) 9 | - [The bundle.json File](101-bundle-json.md) 10 | - [The Invocation Image Format](102-invocation-image.md) 11 | - [The Bundle Runtime](103-bundle-runtime.md) 12 | - [Bundle Formats (Thick and Thin)](104-bundle-formats.md) 13 | 2. [Cloud Native Application Bundle Registry 1.0.0 (CNAB-Reg)](200-CNAB-registries.md) 14 | 3. [Cloud Native Application Bundle Security 1.0.0 (CNAB-Sec)](300-CNAB-security.md) 15 | 4. [Cloud Native Application Bundle Claims 1.0.0 (CNAB-Claims1)](400-claims.md) 16 | 5. [Cloud Native Application Bundle Dependencies 1.0.0 (CNAB-Dependencies)](500-CNAB-dependencies.md) 17 | 6. Non-normative Sections 18 | - [Declarative Bundles](801-declarative-images.md) 19 | - [Credential Sets](802-credential-sets.md) 20 | - [Repositories](803-repositories.md) 21 | - [Well known custom actions](804-well-known-custom-actions.md) 22 | - [Disconnected Scenarios](805-airgap.md) 23 | - [Known implementations for CNAB Registries](807-registries-known-implementations.md) 24 | - [Well known custom extensions](810-well-known-custom-extensions.md) 25 | 6. Process Documentation 26 | - [Specification Process](901-process.md) 27 | -------------------------------------------------------------------------------- /schema/status.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/v1/status.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "definitions": { 5 | "component": { 6 | "properties": { 7 | "additionalProperties": true, 8 | "components": { 9 | "additionalProperties": { 10 | "$ref": "#" 11 | }, 12 | "type": "object" 13 | }, 14 | "message": { 15 | "description": "details about the current status", 16 | "type": "string" 17 | }, 18 | "status": { 19 | "description": "string identifying the status of a component (Ready, Pending, Failed are well known values)", 20 | "type": "string" 21 | } 22 | }, 23 | "type": "object" 24 | } 25 | }, 26 | "properties": { 27 | "additionalProperties": true, 28 | "components": { 29 | "additionalProperties": { 30 | "$ref": "#/definitions/component" 31 | }, 32 | "type": "object" 33 | }, 34 | "message": { 35 | "description": "details about the current status", 36 | "type": "string" 37 | }, 38 | "status": { 39 | "description": "string identifying the status of a component (Ready, Pending, Failed are well known values)", 40 | "type": "string" 41 | } 42 | }, 43 | "title": "CNAB Status json schema", 44 | "type": "object" 45 | } 46 | -------------------------------------------------------------------------------- /schema/claim.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/v1/claim.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "properties": { 5 | "additionalProperties": false, 6 | "action": { 7 | "description": "the name of the action. One of the built-ins (install, uninstall, upgrade) or a custom action.", 8 | "type": "string" 9 | }, 10 | "bundle": { 11 | "$ref": "https://cnab.io/v1/bundle.schema.json", 12 | "description": "The bundle descriptor" 13 | }, 14 | "bundleReference": { 15 | "description": "A canonical reference to the bundle used in the last action", 16 | "type": "string" 17 | }, 18 | "created": { 19 | "description": "The date created, as an ISO-8601 Extended Format date string, as specified in the ECMAScript standard", 20 | "type": "string" 21 | }, 22 | "custom": { 23 | "$comment": "reserved for custom extensions" 24 | }, 25 | "id": { 26 | "description": "the ID (a ULID)", 27 | "type": "string" 28 | }, 29 | "installation": { 30 | "description": "the name of the installation", 31 | "type": "string" 32 | }, 33 | "parameters": { 34 | "description": "key/value pairs of parameter name to parameter value.", 35 | "type": "object" 36 | }, 37 | "revision": { 38 | "description": "the revision ID (ideally a ULID)", 39 | "type": "string" 40 | } 41 | }, 42 | "required": [ 43 | "action", 44 | "bundle", 45 | "created", 46 | "id", 47 | "installation", 48 | "revision" 49 | ], 50 | "title": "CNAB Claims json schema", 51 | "type": "object" 52 | } 53 | -------------------------------------------------------------------------------- /governance.md: -------------------------------------------------------------------------------- 1 | # Governance 2 | 3 | ## Project Maintainers 4 | [Project maintainers](OWNERS) are responsible for activities around maintaining and updating the CNAB specification. Final decisions on the spec reside with the project maintainers. 5 | 6 | Maintainers MUST remain active. If they are unresponsive for >3 months, they will be automatically removed unless a [super-majority](https://en.wikipedia.org/wiki/Supermajority#Two-thirds_vote) of the other project maintainers agrees to extend the period to be greater than 3 months. 7 | 8 | New maintainers can be added to the project by a [super-majority](https://en.wikipedia.org/wiki/Supermajority#Two-thirds_vote) vote of the existing maintainers. A potential maintainer may be nominated by an existing maintainer. A vote is conducted in private between the current maintainers over the course of a one week voting period. At the end of the week, votes are counted and a pull request is made on the repo adding the new maintainer to the [OWNERS](OWNERS) file. 9 | 10 | A maintainer may step down by submitting an [issue](https://github.com/deislabs/cnab-spec/issues/new) stating their intent. 11 | 12 | Changes to this governance document require a pull request with approval from a [super-majority](https://en.wikipedia.org/wiki/Supermajority#Two-thirds_vote) of the current maintainers. 13 | 14 | ## Code of Conduct 15 | This project has adopted the [Microsoft Open Source Code of conduct](https://opensource.microsoft.com/codeofconduct/). 16 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact opencode@microsoft.com (mailto:opencode@microsoft.com) with any additional questions or comments. 17 | -------------------------------------------------------------------------------- /schema/claim-result.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/v1/claim-result.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "definitions": { 5 | "output": { 6 | "description": "A value that is produced by running an invocation image", 7 | "type": "object", 8 | "properties": { 9 | "contentDigest": { 10 | "description": "A cryptographic hash digest of the contents of the output that can be used to validate the output", 11 | "type": "string" 12 | } 13 | }, 14 | "additionalProperties": true 15 | } 16 | }, 17 | "properties": { 18 | "additionalProperties": false, 19 | "claimId": { 20 | "description": "The claim ID", 21 | "type": "string" 22 | }, 23 | "id": { 24 | "description": "The ID (a ULID)", 25 | "type": "string" 26 | }, 27 | "message": { 28 | "description": "The last message from the invocation image or runtime", 29 | "type": "string" 30 | }, 31 | "created": { 32 | "description": "The date created, as an ISO-8601 Extended Format date string, as specified in the ECMAScript standard", 33 | "type": "string" 34 | }, 35 | "custom": { 36 | "$comment": "reserved for custom extensions" 37 | }, 38 | "outputs": { 39 | "additionalProperties": { 40 | "$ref": "#/definitions/output" 41 | }, 42 | "description": "Map of outputs generated by the operation.", 43 | "type": "object" 44 | }, 45 | "status": { 46 | "description": "The status of the operation", 47 | "enum": [ 48 | "canceled", 49 | "failed", 50 | "succeeded", 51 | "pending", 52 | "running", 53 | "unknown" 54 | ], 55 | "type": "string" 56 | } 57 | }, 58 | "required": [ 59 | "claimId", 60 | "id", 61 | "created", 62 | "status" 63 | ], 64 | "title": "CNAB Claim Result json schema", 65 | "type": "object" 66 | } 67 | -------------------------------------------------------------------------------- /schema/parameter-sources.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/specs/v1/parameter-sources.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "title": "CNAB Parameter Sources Extension JSON Schema", 5 | "definitions": { 6 | "parameter-source": { 7 | "type": "object", 8 | "description": "", 9 | "properties": { 10 | "priority": { 11 | "type": "array", 12 | "description": "Array of source types in the priority order that they should be used to populated the parameter", 13 | "items": { 14 | "type": "string", 15 | "enum": ["output"] 16 | }, 17 | "minItems": 1 18 | }, 19 | "sources":{ 20 | "type": "object", 21 | "description": "A map of key/value pairs of a source type and definition for the parameter value", 22 | "properties": { 23 | "output": { "$ref": "#/definitions/output" } 24 | }, 25 | "additionalProperties": false, 26 | "minProperties": 1 27 | } 28 | }, 29 | "minProperties": 1, 30 | "additionalProperties": false 31 | }, 32 | "output": { 33 | "type": "object", 34 | "description": "Specifies that the source of the parameter is an output", 35 | "properties": { 36 | "name": { 37 | "type": "string", 38 | "description": "The name of the source output", 39 | "minLength": 1 40 | } 41 | }, 42 | "additionalProperties": false, 43 | "required": ["name"] 44 | } 45 | }, 46 | "type": "object", 47 | "description": "Key/value pairs of parameter sources where the key is the parameter name", 48 | "additionalProperties": { 49 | "$ref": "#/definitions/parameter-source" 50 | }, 51 | "minProperties": 1 52 | } -------------------------------------------------------------------------------- /scripts/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### 3 | # Use this to validate that the files in examples/* accord with the schema in schema/ 4 | # 5 | # To get `ajv`, run `npm install -g ajv-cli` 6 | ### 7 | 8 | set -eou pipefail 9 | 10 | # Test all of the bundle files against the bundle schema 11 | for json in $(ls -1 examples/*-bundle.json); do 12 | schema="schema/bundle.schema.json" 13 | echo "Testing json '$json' against schema '$schema'" 14 | ajv test -s $schema -d $json --valid 15 | done 16 | 17 | # Test all of the claim files against the claim schema. 18 | for json in $(ls -1 examples/*-claim.json); do 19 | schema="schema/claim.schema.json" 20 | echo "Testing json '$json' against schema '$schema'" 21 | ajv test -s $schema -d $json --valid -r schema/bundle.schema.json 22 | done 23 | 24 | # Test all of the claim result files against the claim result schema. 25 | for json in $(ls -1 examples/*-claim-result.json); do 26 | schema="schema/claim-result.schema.json" 27 | echo "Testing json '$json' against schema '$schema'" 28 | ajv test -s $schema -d $json --valid 29 | done 30 | 31 | # Test all of the dependency files against the dependencies schema 32 | for json in $(ls -1 examples/*-dependencies.json); do 33 | schema="schema/dependencies.schema.json" 34 | echo "Testing json '$json' against schema '$schema'" 35 | ajv test -s $schema -d $json --valid 36 | done 37 | 38 | # Test all of the parameter sources files against the parameter sources schema 39 | for json in $(ls -1 examples/*-parameter-sources.json); do 40 | schema="schema/parameter-sources.schema.json" 41 | echo "Testing json '$json' against schema '$schema'" 42 | ajv test -s $schema -d $json --valid 43 | done 44 | 45 | # Test all of the relocation mapping files against the relocation mapping schema. 46 | for json in $(ls -1 examples/*-relocation-mapping.json); do 47 | schema="schema/relocation-mapping.schema.json" 48 | echo "Testing json '$json' against schema '$schema'" 49 | ajv test -s $schema -d $json --valid 50 | done 51 | 52 | # Test all of the status files against the status schema. 53 | for json in $(ls -1 examples/*-status.json); do 54 | schema="schema/status.schema.json" 55 | echo "Testing json '$json' against schema '$schema'" 56 | ajv test -s $schema -d $json --valid -r schema/bundle.schema.json 57 | done 58 | 59 | -------------------------------------------------------------------------------- /examples/101.01-bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credentials": { 3 | "hostkey": { 4 | "env": "HOST_KEY", 5 | "path": "/etc/hostkey.txt" 6 | } 7 | }, 8 | "custom": { 9 | "com.example.backup-preferences": { 10 | "frequency": "daily" 11 | }, 12 | "com.example.duffle-bag": { 13 | "icon": "https://example.com/icon.png", 14 | "iconType": "PNG" 15 | } 16 | }, 17 | "definitions": { 18 | "http_port": { 19 | "default": 80, 20 | "maximum": 10240, 21 | "minimum": 10, 22 | "type": "integer" 23 | }, 24 | "port": { 25 | "maximum": 65535, 26 | "minimum": 1024, 27 | "type": "integer" 28 | }, 29 | "string": { 30 | "type": "string" 31 | }, 32 | "x509Certificate": { 33 | "contentEncoding": "base64", 34 | "contentMediaType": "application/x-x509-user-cert", 35 | "type": "string", 36 | "writeOnly": true 37 | } 38 | }, 39 | "description": "An example 'thin' helloworld Cloud-Native Application Bundle", 40 | "images": { 41 | "my-microservice": { 42 | "contentDigest": "sha256:aaaaaaaaaaaa...", 43 | "description": "my microservice", 44 | "image": "example/microservice:1.2.3" 45 | } 46 | }, 47 | "invocationImages": [ 48 | { 49 | "contentDigest": "sha256:aaaaaaa...", 50 | "image": "example/helloworld:0.1.0", 51 | "imageType": "docker" 52 | } 53 | ], 54 | "maintainers": [ 55 | { 56 | "email": "matt.butcher@microsoft.com", 57 | "name": "Matt Butcher", 58 | "url": "https://example.com" 59 | } 60 | ], 61 | "name": "helloworld", 62 | "outputs": { 63 | "clientCert": { 64 | "definition": "x509Certificate", 65 | "path": "/cnab/app/outputs/clientCert" 66 | }, 67 | "hostName": { 68 | "applyTo": [ 69 | "install" 70 | ], 71 | "definition": "string", 72 | "description": "the hostname produced installing the bundle", 73 | "path": "/cnab/app/outputs/hostname" 74 | }, 75 | "port": { 76 | "definition": "port", 77 | "path": "/cnab/app/outputs/port" 78 | } 79 | }, 80 | "parameters": { 81 | "backend_port": { 82 | "definition": "http_port", 83 | "description": "The port that the back-end will listen on", 84 | "destination": { 85 | "env": "BACKEND_PORT" 86 | } 87 | } 88 | }, 89 | "schemaVersion": "v1.0.0", 90 | "version": "0.1.2" 91 | } -------------------------------------------------------------------------------- /examples/101.02-bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credentials": { 3 | "hostkey": { 4 | "env": "HOST_KEY", 5 | "path": "/etc/hostkey.txt" 6 | }, 7 | "image_token": { 8 | "env": "AZ_IMAGE_TOKEN", 9 | "applyTo": ["install"] 10 | }, 11 | "kubeconfig": { 12 | "path": "/home/.kube/config" 13 | } 14 | }, 15 | "definitions": { 16 | "http_port": { 17 | "default": 80, 18 | "maximum": 10240, 19 | "minimum": 10, 20 | "type": "integer" 21 | }, 22 | "port": { 23 | "maximum": 65535, 24 | "minimum": 1024, 25 | "type": "integer" 26 | }, 27 | "string": { 28 | "type": "string" 29 | }, 30 | "x509Certificate": { 31 | "contentEncoding": "base64", 32 | "contentMediaType": "application/x-x509-user-cert", 33 | "type": "string", 34 | "writeOnly": true 35 | } 36 | }, 37 | "description": "An example 'thick' helloworld Cloud-Native Application Bundle", 38 | "images": { 39 | "my-microservice": { 40 | "contentDigest": "sha256:bbbbbbbbbbbb...", 41 | "description": "helloworld microservice", 42 | "image": "example/helloworld:0.1.2", 43 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 44 | "labels": { 45 | "architecture": "amd64", 46 | "os": "linux" 47 | }, 48 | "size": 1337 49 | } 50 | }, 51 | "invocationImages": [ 52 | { 53 | "contentDigest": "sha256:aaaaaaaaaaaa...", 54 | "image": "example/helloworld:1.2.3", 55 | "imageType": "docker", 56 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 57 | "labels": { 58 | "architecture": "amd64", 59 | "os": "linux" 60 | }, 61 | "size": 1337 62 | } 63 | ], 64 | "name": "helloworld", 65 | "outputs": { 66 | "clientCert": { 67 | "definition": "x509Certificate", 68 | "path": "/cnab/app/outputs/clientCert" 69 | }, 70 | "hostName": { 71 | "applyTo": [ 72 | "install" 73 | ], 74 | "definition": "string", 75 | "description": "the hostname produced installing the bundle", 76 | "path": "/cnab/app/outputs/hostname" 77 | }, 78 | "port": { 79 | "definition": "port", 80 | "path": "/cnab/app/outputs/port" 81 | } 82 | }, 83 | "parameters": { 84 | "backend_port": { 85 | "definition": "http_port", 86 | "description": "The port that the backend will listen on", 87 | "destination": { 88 | "path": "/path/to/backend_port" 89 | } 90 | } 91 | }, 92 | "schemaVersion": "v1.0.0", 93 | "version": "1.0.0" 94 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Proposing Changes and LGTM Policy 4 | To propose a change or ask a question, please open an issue in the issue queue before submitting a pull request(PR). All PRs must be reviewed and approved (LGTMed) by 2 core maintainers before being merged. Maintainers are specified in the [OWNERS](OWNERS) file. 5 | 6 | ## Support Channels 7 | Whether you are a user or contributor, official support channels include: 8 | - [Issues](https://github.com/deislabs/cnab-spec/issues) 9 | - #cnab Slack channel in the [CNCF Slack org](https://slack.cncf.io/) 10 | 11 | Weekly community meetings are held every Wednesday from 9am-10am PT on zoom. More information can be found on [this agenda document](https://hackmd.io/TfLYtyRnS8Kfx4ekgLwWLg). 12 | 13 | ## DCO Requirements 14 | This project welcomes contributions and suggestions. To contribute to the specification, you must sign off every commit you contributed as acknowledgement of the [DCO](https://developercertificate.org/). The repository's DCO bot will check to ensure that every commit is signed, and will block any unsigned commits. 15 | 16 | The easiest way to sign-off a commit is to use the Git `--signoff/-s` flag: `git commit -s`. 17 | 18 | ## Specifications subject to the Open Web Foundation Agreements. 19 | In addition, by making a contribution to specifications in this repository, you, on behalf of yourself, your employer, and its affiliates, are making those contributions subject to the obligations set forth in the [OWF Contributor License Agreement 1.0 - Copyright and Patent](http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owf-contributor-license-agreement-1-0---copyright-and-patent). 20 | Final specifications developed in this repository will be subject to the [Open Web Foundation Final Specification Agreement (“OWFa 1.0”)](http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0). OWFa 1.0 will be applied as follows: 21 | - The maintainer will notify all contributors to a designated specification in writing via provided contact information of the start of a 30 day review period, after which the specification will be subject to the OWFa 1.0. 22 | - During that 30 day period, contributors may provide written notice to the maintainer that the contributor is not making the forgoing commitment under OWFa 1.0 for the designated specification (“Exclusion”). 23 | - Upon the end of that 30 day notice period, those contributors who have not issued an Exclusion, on behalf of themselves, their employer, and its affiliates, will, without further action, be subject to the obligations set forth in the OWFa 1.0 for the designated specification. 24 | 25 | ## Code of Conduct 26 | This project has adopted the [Microsoft Open Source Code of conduct](https://opensource.microsoft.com/codeofconduct/). 27 | Please maintain respectful communication in the issue queue, PR queue, and all other communication channels. 28 | -------------------------------------------------------------------------------- /examples/101.03-bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "credentials": { 3 | "hostkey": { 4 | "env": "HOST_KEY", 5 | "path": "/etc/hostkey.txt" 6 | } 7 | }, 8 | "custom": { 9 | "com.example.backup-preferences": { 10 | "frequency": "daily" 11 | }, 12 | "com.example.duffle-bag": { 13 | "icon": "https://example.com/icon.png", 14 | "iconType": "PNG" 15 | }, 16 | "io.cnab.dependencies": [ 17 | { 18 | "sequence": ["mysql"], 19 | "requires": { 20 | "bundle": "azure/mysql", 21 | "version": { 22 | "prereleases": "true", 23 | "range": "5.7.x" 24 | } 25 | } 26 | } 27 | ] 28 | }, 29 | "definitions": { 30 | "http_port": { 31 | "default": 80, 32 | "maximum": 10240, 33 | "minimum": 10, 34 | "type": "integer" 35 | }, 36 | "port": { 37 | "maximum": 65535, 38 | "minimum": 1024, 39 | "type": "integer" 40 | }, 41 | "string": { 42 | "type": "string" 43 | }, 44 | "x509Certificate": { 45 | "contentEncoding": "base64", 46 | "contentMediaType": "application/x-x509-user-cert", 47 | "type": "string", 48 | "writeOnly": true 49 | } 50 | }, 51 | "description": "An example 'thin' helloworld Cloud-Native Application Bundle", 52 | "images": { 53 | "my-microservice": { 54 | "contentDigest": "sha256:aaaaaaaaaaaa...", 55 | "description": "my microservice", 56 | "image": "example/microservice:1.2.3" 57 | } 58 | }, 59 | "invocationImages": [ 60 | { 61 | "contentDigest": "sha256:aaaaaaa...", 62 | "image": "example/helloworld:0.1.0", 63 | "imageType": "docker" 64 | } 65 | ], 66 | "maintainers": [ 67 | { 68 | "email": "matt.butcher@microsoft.com", 69 | "name": "Matt Butcher", 70 | "url": "https://example.com" 71 | } 72 | ], 73 | "name": "helloworld", 74 | "outputs": { 75 | "clientCert": { 76 | "definition": "x509Certificate", 77 | "path": "/cnab/app/outputs/clientCert" 78 | }, 79 | "hostName": { 80 | "applyTo": [ 81 | "install" 82 | ], 83 | "definition": "string", 84 | "description": "the hostname produced installing the bundle", 85 | "path": "/cnab/app/outputs/hostname" 86 | }, 87 | "port": { 88 | "definition": "port", 89 | "path": "/cnab/app/outputs/port" 90 | } 91 | }, 92 | "parameters": { 93 | "backend_port": { 94 | "definition": "http_port", 95 | "description": "The port that the back-end will listen on", 96 | "destination": { 97 | "env": "BACKEND_PORT" 98 | } 99 | } 100 | }, 101 | "requiredExtensions": [ 102 | "io.cnab.dependencies" 103 | ], 104 | "schemaVersion": "v1.0.0", 105 | "version": "0.1.2" 106 | } -------------------------------------------------------------------------------- /802-credential-sets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Credential Sets 3 | weight: 802 4 | --- 5 | 6 | # Credential Sets 7 | 8 | This is a non-normative section that describes how credentials can be passed into an invocation image. This strategy is implemented by `duffle`. 9 | 10 | ## Credential Mapping 11 | 12 | In the bundle descriptor, credentials are declared like this: 13 | 14 | ```json 15 | { 16 | "credentials": { 17 | "hostkey": { 18 | "env": "HOST_KEY", 19 | "path": "/etc/hostkey.txt" 20 | }, 21 | "image_token": { 22 | "env": "AZ_IMAGE_TOKEN" 23 | }, 24 | "kubeconfig": { 25 | "path": "/home/.kube/config" 26 | } 27 | } 28 | } 29 | ``` 30 | 31 | The description above indicates that there are three credentials required to execute this bundle. 32 | 33 | The names on each credential are unique within the bundle: 34 | 35 | - `kubeconfig` 36 | - `image_token` 37 | - `hostkey` 38 | 39 | That name can be used to reference the credential from tooling. Consequently, tooling may choose to construct a map from an external value to the credential name. 40 | 41 | Duffle, for example, maps local credentials to bundle credentials by _credential sets_. Here is an example: 42 | 43 | ```yaml 44 | name: test_credentials 45 | created: 2020-01-20T06:00:00.00000-06:00 46 | modified: 2020-01-28T12:30:00.11193-06:00 47 | credentials: 48 | - name: kubeconfig 49 | source: 50 | path: $HOME/.kube/config 51 | - name: image_token 52 | source: 53 | value: "1234aaaaa" 54 | - name: hostkey 55 | source: 56 | env: HOSTKEY 57 | ``` 58 | 59 | This credential set tells the client application (`duffle`) to map CNAB bundle credential sections to certain local values: 60 | 61 | - When a bundle requests `kubeconfig`, a file will be loaded from the local file system and injected into the container at `/home/.kube/config` 62 | - When a bundle requests `image_token`, the literal value `1234aaaaa` will be loaded into the environment variable `AZ_IMAGE_TOKEN` 63 | - When a bundle requests `hostkey`, the local environment variable `$HOSTKEY` will be dereferenced, and its value injected into the container's `HOST_KEY` variable as well as the container's filesystem path `/etc/hostkey.txt`. 64 | 65 | Similar tooling could choose to load the values by name from a database, vault, or file. 66 | 67 | The created and modified timestamps are in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. 68 | 69 | ## Credential Injection 70 | 71 | Credentials must be injected into the runtime of the invocation image. The following strategies are known to work: 72 | 73 | - Environment Variables: 74 | - Injected via environment variable services (Docker and Kubernetes) 75 | - Loaded into VMs via cloud-init 76 | - Paths: 77 | - Mounted as volumes (or wrappers, such as Kubernetes secrets) 78 | - Injected at runtime into the top layer of the container 79 | - Loaded into VMs via cloud-init 80 | 81 | Next section: [Well known custom actions](804-well-known-custom-actions.md) 82 | -------------------------------------------------------------------------------- /schema/dependencies.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/specs/v1/dependencies.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "title": "CNAB Dependencies JSON Schema", 5 | "definitions": { 6 | "sequence":{ 7 | "type": "array", 8 | "description": "Defines the criteria for sequencing bundles in a pre-defined order. The sequence for each occurrence is a way for the bundle to reference the order of a dependency", 9 | "items": { 10 | "type": "string" 11 | } 12 | }, 13 | "requires": { 14 | "type": "object", 15 | "description": "Defines the criteria for the dependent bundle. The key for each dependency is a way for the bundle to reference the dependency", 16 | "patternProperties": { 17 | ".*": { 18 | "$ref": "#/definitions/dependency" 19 | } 20 | }, 21 | "minProperties": 1 22 | }, 23 | "dependency": { 24 | "type": "object", 25 | "properties": { 26 | "bundle": { 27 | "type": "string", 28 | "description": "A reference to a bundle in the format REGISTRY/NAME." 29 | }, 30 | "version": { 31 | "$ref": "#/definitions/version" 32 | } 33 | }, 34 | "additionalProperties": false, 35 | "required": [ 36 | "bundle" 37 | ] 38 | }, 39 | "version": { 40 | "type": "object", 41 | "description": "A set of criteria applied to the bundle version when selecting an acceptable version of the bundle.", 42 | "properties": { 43 | "ranges": { 44 | "type": "array", 45 | "description": "An array of allowed version ranges. Versions are specified using semver, with or without a leading 'v'. An x in the place of the minor or patch place can be used to specify a wildcard. Ranges can be specified by separating the two versions with a dash, the dash must be surrounded by spaces in order to disambiguate from prerelease tags.", 46 | "items": { 47 | "type": "string" 48 | }, 49 | "minItems": 1 50 | }, 51 | "prereleases": { 52 | "type": "boolean", 53 | "description": "Specifies if prerelease versions of the bundle are allowed.", 54 | "default": false 55 | } 56 | }, 57 | "additionalProperties": false, 58 | "minProperties": 1 59 | } 60 | }, 61 | "type": "object", 62 | "properties": { 63 | "requires": { 64 | "$ref": "#/definitions/requires" 65 | } 66 | }, 67 | "additionalProperties": false, 68 | "minProperties": 1 69 | } -------------------------------------------------------------------------------- /brigade.js: -------------------------------------------------------------------------------- 1 | const { events, Job, Group } = require("brigadier"); 2 | const { Check } = require("@brigadecore/brigade-utils"); 3 | 4 | const projectName = "cnab-spec"; 5 | // Currently a very lenient regex. 6 | // Could be made more strict. Some examples: cnab-core-1.0.0, cnab-claim-1.0.0-DRAFT+abc1234 7 | const releaseTagRegex = /^refs\/tags\/(.*)/; 8 | 9 | // Event handlers 10 | 11 | events.on("exec", (e, p) => { 12 | return Group.runAll([ 13 | validate(), 14 | validateURL() 15 | ]); 16 | }); 17 | events.on("check_suite:requested", runSuite); 18 | events.on("check_suite:rerequested", runSuite); 19 | events.on("check_run:rerequested", runSuite); 20 | events.on("issue_comment:created", (e, p) => Check.handleIssueComment(e, p, runSuite)); 21 | events.on("issue_comment:edited", (e, p) => Check.handleIssueComment(e, p, runSuite)); 22 | events.on("push", (e, p) => { 23 | let matchStr = e.revision.ref.match(releaseTagRegex); 24 | if (matchStr) { 25 | let matchTokens = Array.from(matchStr); 26 | let version = matchTokens[1]; 27 | return publish(p, version).run(); 28 | } 29 | }); 30 | 31 | // Functions/Helpers 32 | 33 | function validate() { 34 | var validator = new Job(`${projectName}-validate`, "node:8-alpine"); 35 | validator.streamLogs = true; 36 | 37 | validator.tasks = [ 38 | "apk add --update make curl jq", 39 | "cd /src", 40 | "make validate-local", 41 | ]; 42 | 43 | return validator; 44 | } 45 | 46 | function validateURL() { 47 | var validator = new Job(`${projectName}-validate-url`, "node:8-alpine"); 48 | validator.streamLogs = true; 49 | 50 | validator.tasks = [ 51 | "apk add --update make curl jq", 52 | "cd /src", 53 | "make validate-url-local", 54 | ]; 55 | 56 | return validator; 57 | } 58 | 59 | function publish(p, version) { 60 | var publisher = new Job(`${projectName}-publish`, "node:8-alpine"); 61 | 62 | publisher.env.AZURE_STORAGE_CONNECTION_STRING = p.secrets.azureStorageConnectionString; 63 | publisher.tasks.push( 64 | "apk add --update make curl", 65 | // Fetch az cli needed for publishing 66 | "curl -sLO https://github.com/carolynvs/az-cli/releases/download/v0.3.2/az-linux-amd64 && \ 67 | chmod +x az-linux-amd64 && \ 68 | mv az-linux-amd64 /usr/local/bin/az", 69 | "cd /src", 70 | `VERSION=${version} make publish` 71 | ); 72 | 73 | return publisher; 74 | } 75 | 76 | // Here we can add additional Check Runs, which will run in parallel and 77 | // report their results independently to GitHub 78 | function runSuite(e, p) { 79 | return runValidation(e, p, validate) 80 | .then(() => { 81 | if (e.revision.ref == "main") { 82 | validateURL().run(); 83 | } 84 | }) 85 | .catch(e => {console.error(e.toString())}); 86 | } 87 | 88 | // runValidation is a Check Run that is ran as part of a Checks Suite 89 | function runValidation(e, p, jobFunc) { 90 | var check = new Check(e, p, jobFunc(), 91 | `https://brigadecore.github.io/kashti/builds/${e.buildID}`); 92 | return check.run(); 93 | } 94 | -------------------------------------------------------------------------------- /examples/400.01-claim.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": "install", 3 | "bundle": { 4 | "credentials": { 5 | "hostkey": { 6 | "env": "HOST_KEY", 7 | "path": "/etc/hostkey.txt" 8 | } 9 | }, 10 | "custom": { 11 | "com.example.backup-preferences": { 12 | "frequency": "daily" 13 | }, 14 | "com.example.duffle-bag": { 15 | "icon": "https://example.com/icon.png", 16 | "iconType": "PNG" 17 | } 18 | }, 19 | "definitions": { 20 | "http_port": { 21 | "default": 80, 22 | "maximum": 10240, 23 | "minimum": 10, 24 | "type": "integer" 25 | }, 26 | "port": { 27 | "maximum": 65535, 28 | "minimum": 1024, 29 | "type": "integer" 30 | }, 31 | "string": { 32 | "type": "string" 33 | }, 34 | "x509Certificate": { 35 | "contentEncoding": "base64", 36 | "contentMediaType": "application/x-x509-user-cert", 37 | "type": "string", 38 | "writeOnly": true 39 | } 40 | }, 41 | "description": "An example 'thin' helloworld Cloud-Native Application Bundle", 42 | "images": { 43 | "my-microservice": { 44 | "contentDigest": "sha256:aaaaaaaaaaaa...", 45 | "description": "my microservice", 46 | "image": "example/microservice:1.2.3" 47 | } 48 | }, 49 | "invocationImages": [ 50 | { 51 | "contentDigest": "sha256:aaaaaaa...", 52 | "image": "example/helloworld:0.1.0", 53 | "imageType": "docker" 54 | } 55 | ], 56 | "maintainers": [ 57 | { 58 | "email": "matt.butcher@microsoft.com", 59 | "name": "Matt Butcher", 60 | "url": "https://example.com" 61 | } 62 | ], 63 | "name": "helloworld", 64 | "outputs": { 65 | "clientCert": { 66 | "definition": "x509Certificate", 67 | "path": "/cnab/app/outputs/clientCert" 68 | }, 69 | "hostName": { 70 | "applyTo": [ 71 | "install" 72 | ], 73 | "definition": "string", 74 | "description": "the hostname produced installing the bundle", 75 | "path": "/cnab/app/outputs/hostname" 76 | }, 77 | "port": { 78 | "definition": "port", 79 | "path": "/cnab/app/outputs/port" 80 | } 81 | }, 82 | "parameters": { 83 | "backend_port": { 84 | "definition": "http_port", 85 | "description": "The port that the back-end will listen on", 86 | "destination": { 87 | "env": "BACKEND_PORT" 88 | } 89 | } 90 | }, 91 | "schemaVersion": "v1.0.0", 92 | "version": "0.1.2" 93 | }, 94 | "bundleReference": "hub.example.com/technosophos/hellohelm@sha256:eec03d9da1bad36b3e3a4526cc774153f7024a94f25df8d2dc3ca5602fc5273d", 95 | "created": "2018-08-30T20:39:55.549002887-06:00", 96 | "custom": {}, 97 | "id": "01E5G8ZYP714JVM8NHTJQ4FH15", 98 | "installation": "technosophos.helloworld", 99 | "parameters": { 100 | "backend_port": 81 101 | }, 102 | "revision": "01CP6XM0KVB9V1BQDZ9NK8VP29" 103 | } 104 | -------------------------------------------------------------------------------- /200-CNAB-registries.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Registries 3 | weight: 200 4 | --- 5 | 6 | # CNAB Registries 1.0 7 | 8 | Draft, Feb. 2020 9 | 10 | ![cnab-registry](https://user-images.githubusercontent.com/686194/61753147-2b387a80-ad63-11e9-8a63-f250bcdf06b0.png) 11 | 12 | --- 13 | 14 | This specification describes how CNAB bundles can be stored inside of OCI Registries. 15 | 16 | This specification, the CNAB Registries specification, is not part of the CNAB Core specification. An implementation of CNAB Core MAY NOT implement this specification, yet still claim compliance with CNAB Core. A CNAB implementation MUST NOT claim to be CNAB Registries-compliant unless it meets this specification. 17 | 18 | > Previous versions of the CNAB Core draft specification included a repository protocol. That work is completely superseded by this specification. 19 | 20 | The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). The use of these keywords in this document are statements about how a CNAB implementation may fulfill the CNAB registry storage specification only. 21 | 22 | The CNAB Core 1.0 specification (CNAB1) does not dictate how bundles should be distributed. This is intentional, so that organizations that already have a way of distributing artifacts may continue to use it. 23 | This document, the CNAB Registries specification, is not part of the core specification, and an implementation of CNAB Core MAY NOT implement it, yet still claim compliance with CNAB Core. A CNAB implementation MUST NOT claim to be CNAB Registries compliant unless it meets this specification. 24 | 25 | This specification proposes the use of [OCI registries][oci-org] to distribute CNAB artifacts. 26 | 27 | ## Approach 28 | 29 | Container registries provide a reliable and highly scalable distribution for container images. Implementations of container registries can be found in cloud provider services, vendor services, and open source projects. More and more artifact types are distributed with OCI registries, with the process standardized by the [OCI Artifacts][artifacts] project. 30 | 31 | A Cloud Native Application Bundle is a collection of metadata and container images that are needed in order to successfully deploy an application - as such, it is not a _single_ new artifact, but represents a _collection of multiple artifacts_. 32 | 33 | An [OCI image index (or simply OCI index)][oci-index] represents a collection of container images stored in a repository - so rather than using a new artifact, this specification proposes reusing the OCI index to represent Cloud Native Application Bundles, and use it as a natural distribution mechanism for the bundle file, invocation image, and images used by the application. 34 | 35 | The approach of this specification is to reuse the existing distribution infrastructure for OCI artifacts when distributing CNAB bundles, to ensure compatibility with the CNAB Security specification, and to enable workflows such as the relocation of bundles and images. 36 | 37 | In the following sections, we will explore [the representation of bundles in OCI registries](201-representing-CNAB-in-OCI.md) and [on disk representations](210-on-disk-representation.md). 38 | 39 | [oci-org]: https://github.com/opencontainers/ 40 | [artifacts]: https://github.com/opencontainers/artifacts 41 | [oci-index]: https://github.com/opencontainers/image-spec/blob/master/image-index.md 42 | [oci-manifest]: https://github.com/opencontainers/image-spec/blob/master/manifest.md 43 | [docker-manifest]: https://docs.docker.com/registry/spec/manifest-v2-2/ 44 | -------------------------------------------------------------------------------- /810-well-known-custom-extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Well-known custom extensions 3 | weight: 810 4 | --- 5 | 6 | # Well-known custom extensions 7 | 8 | This section is non-normative, but is here to propose a common set of custom extensions a bundle MAY define, and that CNAB tools MAY implement. 9 | 10 | A bundle indicates that it uses a custom extension by including them in its custom section (as defined in [Custom Extensions](101-bundle-json.md#custom-extensions)). 11 | 12 | ## Dependencies 13 | 14 | A custom bundle extension, `io.cnab.dependencies`, MAY be defined that specifies 15 | that the bundle depends up on other bundles. See the [Dependencies 16 | Specification](500-CNAB-dependencies.md) for details. 17 | 18 | ## Parameter Sources 19 | 20 | A custom bundle extension, `io.cnab.parameter-sources`, MAY be defined that provides additional guidance for how a runtime MAY determine the default value of a [parameter][parameter]. The parameter source extension does not cover how the runtime should set the value of a parameter because each implementation may have different information to take into account when determining the final value of a parameter. 21 | 22 | The invocation image: 23 | - MAY use the the `io.cnab.parameter-sources` extension with outputs that are generated by actions other than the `install` action. It is the responsibility of the invocation image to handle when the output does not exist in the case when the action that generates the output has not been executed yet, for example by setting a default on the parameter or marking the parameter required. 24 | - MAY use the `io.cnab.parameter-sources` extension with outputs that are generated by `stateless: false` actions. 25 | 26 | A CNAB runtime implementing the `io.cnab.parameter-sources` extension: 27 | - SHOULD allow users to manually override parameter values. 28 | - SHOULD allow users to specify a parameter value when when a value cannot be determined using its parameter sources. 29 | 30 | In the example below, the [output][output] `tfstate` is initially generated during the install action, and the required parameter `tfstate` is used by the upgrade and uninstall actions. The parameter source specifies that the `tfstate` output can be used to set the `tfstate` parameter. This enables a bundle to pass data, in this case state, between actions. 31 | 32 | ```json 33 | { 34 | "custom": { 35 | "io.cnab.parameter-sources": { 36 | "tfstate": { 37 | "priority": ["output"], 38 | "sources": { 39 | "output": { 40 | "name": "tfstate" 41 | } 42 | } 43 | } 44 | } 45 | }, 46 | "outputs": { 47 | "tfstate": { 48 | "applyTo": [ "install", "upgrade", "uninstall" ], 49 | "definition": "tfstate", 50 | "path": "/cnab/app/outputs/tfstate" 51 | } 52 | }, 53 | "parameters": { 54 | "tfstate": { 55 | "applyTo": [ "upgrade", "uninstall" ], 56 | "definition": "tfstate", 57 | "required": true 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | - `io.cnab.parameter-sources`: Defines the parameter sources extension. 64 | - ``: The name of the destination parameter. 65 | - `priority`: Array of source types in the priority order that they should be used to populated the parameter. 66 | - `sources`: A map of key/value pairs of a source type and definition for the parameter value. 67 | 68 | ### Well-known Source Types 69 | 70 | #### Output 71 | 72 | The source of the parameter value comes from the output of a previous action. 73 | 74 | - `output`: Specifies that the source of the parameter is an output. 75 | - `name`: The name of the source output. 76 | 77 | [parameter]: 101-bundle-json.md#parameters 78 | [output]: 101-bundle-json.md#outputs 79 | -------------------------------------------------------------------------------- /801-declarative-images.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Declarative Invocation Images 3 | weight: 801 4 | --- 5 | 6 | # Declarative Invocation Images 7 | 8 | This section is non-normative. A CNAB implementation MAY implement this portion to be considered conforming. However, this section maps out what the authors consider a best practice. 9 | 10 | ## Declarative Infrastructure and Declarative Installers 11 | 12 | In declarative models, authors express the desired entities to be produced during an action. Declarative infrastructure, for example, is the practice of declaring which objects should exist in an infrastructure layer. Likewise, declarative installers provide authors with tools for describing what the installed program should look like. It is then left to the tooling to realize the authors' expression. 13 | 14 | This section of the documentation explains how CNAB can be used to create declarative CNAB bundles by leveraging _invocation image middleware_. 15 | 16 | ## Invocation Image Middleware 17 | 18 | As described in [the Invocation Image definition](102-invocation-image.md) and [the Bundle Runtime definition](103-bundle-runtime.md), the invocation image is responsible for executing an _action_ (install, upgrade, uninstall) inside of the image. 19 | 20 | The aforementioned documents show examples of building CNAB bundles from non-CNAB base images. This document introduces the idea of building CNAB bundles from _middleware invocation images_, which provide underlying CNAB functionality that can be used to simplify the construction of a CNAB bundle. 21 | 22 | Specifically, this section exhibits how a middleware package can provide the necessary tooling to make CNAB bundle construction a declarative matter. 23 | 24 | By providing a run tool (`/cnab/app/run`), a middleware image can remove the necessity to write the imperative portions of a CNAB bundle, essentially allowing construction of declarative CNAB bundles. In this model, the middleware image provides the tooling necessary for handling CNAB actions. Images layered on top of this middleware merely need to describe what entities are being installed, uninstalled, or upgraded. 25 | 26 | Here is an example of a declarative CNAB bundle that uses the Azure Resource Manager (ARM) templates to orchestrate an installation of an Azure application: 27 | 28 | ```text 29 | arm-aci 30 | └── cnab 31 | ├── build/Dockerfile 32 | ├── bundle.json 33 | └── app 34 | └── arm 35 | ├── parameters.json 36 | └── template.json 37 | ``` 38 | 39 | Note that this bundle is composed only of the following: 40 | 41 | - Dockerfile: The Dockerfile 42 | - bundle.json: the bundle file 43 | - template.json: an ARM template 44 | - parameters.json: an ARM parameters file 45 | 46 | The `Dockerfile` begins by importing a CNAB middleware image: 47 | 48 | ```Dockerfile 49 | FROM cnab/armbase:0.1.0 50 | 51 | COPY Dockerfile /cnab 52 | COPY cnab/app/arm /cnab/app/arm 53 | COPY bundle.json /cnab/bundle.json 54 | ``` 55 | 56 | The `cnab/armbase` middleware provides the tools necessary for executing Azure Resource Manager (ARM) templates. Consequently, the present CNAB bundle MAY have imperative components. 57 | 58 | The middleware image (`cnab/armbase`) contains tooling that looks in predefined locations for ARM templates, and understands how to install, upgrade, and uninstall those resources. 59 | 60 | ## Why Is This Non-normative? 61 | 62 | While declarative invocation images are considered the best practice, they are non-normative because CNAB does not require specific images to be used as base images. The CNAB definition is focused on describing the conditions under which a bundle MAY be correctly packaged and executed. We have chosen, however, to not prescribe the shape of the CNAB executable. 63 | 64 | Next section: [Credential Sets](802-credential-sets.md) 65 | -------------------------------------------------------------------------------- /804-well-known-custom-actions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Well known custom actions 3 | weight: 805 4 | --- 5 | 6 | # Well known custom actions 7 | 8 | This section is non-normative, but is here to propose a common set of optional actions a CNAB package MAY implement, and that CNAB tools can understand. 9 | 10 | A CNAB indicates that it supports those actions by including them in its custom action list (as defined in [the bundle definition](101-bundle-json.md)). 11 | - `io.cnab.dry-run` (with `stateless`: true and `modifies`: false): execute the installation in a dry-run mode, allowing to see what would happen with the given set of parameter values. 12 | - `io.cnab.help` (with `stateless`: true and `modifies`: false): print an help message to the standard output. Implementations MAY print different messages depending on the parameters values passed to the invocation image. 13 | - `io.cnab.log` (with `stateless`: false and `modifies`: false): print logs of the installed system to the standard output. 14 | - `io.cnab.status` (with `stateless`: false and `modifies`: false): print a human readable status message to the standard output. This action also produces an output file named `status` describing the detailed status as in the example below ([here](schema/status.schema.json) is the JSON schema for this): 15 | ```json 16 | { 17 | "components": { 18 | "backend": { 19 | "status": "Ready" 20 | }, 21 | "front-end": { 22 | "message": "Failed to pull Docker image mginx:latest", 23 | "status": "Failed" 24 | } 25 | }, 26 | "message": "Component front-end failed to deploy properly", 27 | "status": "Failed" 28 | } 29 | ``` 30 | Source: [804.01-status.json](examples/804.01-status.json) 31 | - `status` is a value indicating the deployment status of a component or of the whole bundle. Well known values are `Failed`, `Ready`, `Pending`. An invocation image MAY use other custom-values, but tools must at least understand those values. 32 | - `message` optional field giving details about the current bundle or component status. 33 | - `components`: map of components/statuses allowing a more detailed status. A component can itself have subcomponents, to enable scenarios like aggregating statuses in CNABs composed of other CNABs. e.g.: 34 | ```json 35 | { 36 | "components": { 37 | "gke": { 38 | "components": { 39 | "kube-api": { 40 | "status": "Ready" 41 | }, 42 | "network": { 43 | "status": "Ready" 44 | }, 45 | "scheduler": { 46 | "status": "Ready" 47 | } 48 | }, 49 | "status": "Ready" 50 | }, 51 | "wordpress": { 52 | "status": "Ready" 53 | } 54 | }, 55 | "status": "Ready" 56 | } 57 | ``` 58 | Source: [804.02-status.json](examples/804.02-status.json) 59 | - Additionaly to the fields defined above, an invocation image MAY add custom fields as they want. To avoid ambiguous naming, those fields names MAY be namespaced. An example of that could be a CNAB bundle deploying Kubernetes workloads and exposing scale details: 60 | ```json 61 | { 62 | "components": { 63 | "backend": { 64 | "com.example.scale": { 65 | "actual": 3, 66 | "desired": 3, 67 | "failed": 0, 68 | "pending": 0 69 | }, 70 | "status": "Ready" 71 | }, 72 | "front-end": { 73 | "com.example.scale": { 74 | "actual": 2, 75 | "desired": 3, 76 | "failed": 0, 77 | "pending": 1 78 | }, 79 | "status": "Pending" 80 | } 81 | }, 82 | "status": "Pending" 83 | } 84 | ``` 85 | Source: [804.03-status.json](examples/804.03-status.json) 86 | 87 | Next section: [Disconnected Scenarios](805-airgap.md) 88 | -------------------------------------------------------------------------------- /303-verification-workflows.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CNAB Security: Verification workflows 3 | weight: 303 4 | --- 5 | 6 | # Verification workflows 7 | 8 | - [Verification workflows](#verification-workflows) 9 | 10 | This document is a _prescriptive_ part of [CNAB Security](300-CNAB-security.md). 11 | 12 | The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). The use of these keywords in this document are statements about how a CNAB implementation may fulfill the CNAB Security specification _only_. 13 | 14 | ## Verification workflows 15 | 16 | This section describes the verification workflow that runtimes compliant with the CNAB Security Specifications MUST perform in order to verify the provenance and attestation of bundles. Regardless of [the bundle format][bundle-formats], the verification workflow is the same, with the signature of a thin bundle being the content digest of the `bundle.json` file, while the signature of a thick bundle being the content digest of the bundle archive. 17 | 18 | End-users SHOULD see no difference in their experience, unless an attack is caught. In that case, the installation of the bundle in question MUST be denied, and users SHOULD see an error message indicating why the verification workflow failed. 19 | 20 | In order to download and verify a bundle, a compliant runtime MUST take the following steps: 21 | 22 | 1. Use the [TUF workflow][tuf-workflow] to download trusted metadata about the desired bundle (e.g., `example.com/example-org/example-bundle:latest`) from the [metadata repository][metadata-repository]. A bundle runtime MUST use the rules outlined in [TAP 4](https://github.com/theupdateframework/taps/blob/master/tap4.md) to securely resolve bundles from different metadata repositories on different servers. 23 | 1. Download the (thin or thick) bundle itself. This operation can follow the [CNAB Registries Specification][registry-spec] for thin bundles and pull the bundle from an OCI registry, or use other protocols of obtaining it. 24 | 1. Compare the observed hashes of the downloaded bundle to the trusted hashes. If they are not equal, the runtime MUST discard the downloaded bundle, report the error, and stop execution. Otherwise, go to the next step. 25 | 1. If there is [associated in-toto metadata][metadata-repository] about the bundle, use the [TUF workflow][tuf-workflow] to download in-toto root layout, public keys for the root layout, and link metadata for the bundle, and go to the next step. Otherwise, halt, and return the verified bundle to the user. 26 | 1. Call the in-toto runtime to [verify and inspect](https://github.com/in-toto/docs/blob/e9806a000c32dea73f6044a140386f601c7d4e18/in-toto-spec.md#52-verifying-the-final-product) the bundle using the downloaded root layout, public keys for the root layout, link metadata, and the bundle itself. 27 | 1. The bundle runtime MAY run in-toto directly on the operating system, in a default container image provided by the bundle runtime, or in a custom container image provided by the user. In any case, note that the bundle runtime SHOULD ensure that in-toto inspections, which are arbitrary commands, run within bounded resources (e.g., space, time) so as not to to run into accidental [denial-of-service attacks](https://en.wikipedia.org/wiki/Denial-of-service_attack). It is also up to the bundle developers to ensure that in-toto inspections are as portable as possible. 28 | 1. If the verification and inspection of the bundle fails for any reason, the runtime MUST discard the downloaded bundle, report the error, and stop execution. Otherwise, if it passes, it MUST return the verified bundle to the user. 29 | 30 | Note that in-toto security guarantees depend on the public keys for the root layout being signed with offline keys in the TUF targets metadata. 31 | 32 | [bundle-formats]: 104-bundle-formats.md 33 | [metadata-repository]: 301-metadata-repositories.md 34 | [registry-spec]: 200-CNAB-registries.md 35 | [tuf-workflow]: https://github.com/theupdateframework/specification/blob/master/tuf-spec.md#5-detailed-workflows 36 | -------------------------------------------------------------------------------- /500-CNAB-dependencies.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dependencies 3 | weight: 500 4 | --- 5 | 6 | # CNAB Dependencies 1.0 7 | Draft, April. 2019 8 | 9 | This specification describes how bundles can define dependencies on other 10 | bundles. 11 | 12 | This specification, the CNAB Dependencies specification, is not part of the CNAB Core specification. An implementation of CNAB Core MAY NOT implement this specification, yet still claim compliance with CNAB Core. A CNAB implementation MUST NOT claim to be CNAB Dependencies-compliant unless it meets this specification. 13 | 14 | # Overview 15 | 16 | One way for a bundle author to manage the complexity of a large bundle and reuse 17 | common logic is to create bundles for discrete components, such as managing 18 | a database as a service on a cloud platform, and then use that bundle as a 19 | dependency. 20 | 21 | This specification defines [dependencies metadata](#dependencies-metadata) in the 22 | bundle.json for specifying dependencies but does not dictate that the metadata is 23 | specifically used at a particular time, or how the dependency graph is resolved. 24 | Each tool may choose to use the information differently. 25 | 26 | There are two cases for how a bundle may need to depend upon another bundle: 27 | 28 | 1. [Depend on a named bundle](#depend-on-a-named-bundle) 29 | 1. [Depend on a bundle that provides a capability](#depend-on-a-bundle-that-provides-a-capability) 30 | 31 | ## Depend on a named bundle 32 | 33 | The bundle depends on a specific named bundle that is known in advance. It is 34 | stored in the custom extensions section of the bundle. 35 | 36 | ```json 37 | { 38 | "custom": { 39 | "io.cnab.dependencies": { 40 | "sequence": ["storage", "mysql"], 41 | "requires": { 42 | "storage": { 43 | "bundle": "somecloud/blob-storage" 44 | }, 45 | "mysql": { 46 | "bundle": "somecloud/mysql", 47 | "version": { 48 | "prereleases": true, 49 | "ranges": ["5.7.x"] 50 | } 51 | } 52 | }, 53 | }, 54 | "name": "wordpress" 55 | } 56 | ``` 57 | 58 | ## Depend on a bundle that provides a capability 59 | 60 | This section is a placeholder and will be completed in a follow-up pull request. 61 | 62 | ## Dependencies Metadata 63 | 64 | This specification introduces a `io.cnab.dependencies` custom extension in the bundle.json 65 | that defines metadata necessary to specify a dependency. 66 | 67 | The entry `io.cnab.dependencies` in the custom extension object, `custom`, is reserved and 68 | MUST only be used for this CNAB Dependencies Specification. 69 | 70 | ### sequence 71 | 72 | The `sequence` array is a pre-defined sequencing of the dependant bundles. 73 | If the `sequence` array is non-empty, the runtime MUST execute each dependency according to this pre-defined order 74 | unless the runtime is capable of resolving the dependency graph to determine an execution order itself. 75 | The `sequence` for each occurrence is a way for the bundle to reference the order of a dependency. 76 | 77 | ### requires 78 | 79 | The `requires` map defines the criteria for the dependent bundle. The key for 80 | each dependency is a way for the bundle to reference the dependency. 81 | 82 | * `bundle`: A reference to a bundle in the format REGISTRY/NAME. 83 | * `version`: A set of criteria applied to the bundle version when selecting an 84 | acceptable version of the bundle. 85 | * `ranges`: An array of allowed version ranges. 86 | 87 | Versions are specified using semver, with or without a leading "v". 88 | An `x` in the place of the minor or patch place can be used to specify 89 | a wildcard. Ranges can be specified by separating the two versions with 90 | a dash, the dash must be surrounded by spaces in order to disambiguate 91 | from prerelease tags. 92 | 93 | Below are some example ranges: 94 | * `1.2.3` - Requires an exact version. 95 | * `1.2.x` - Restricts the major.minor to `1.2` and allows any patch. 96 | * `1.x` - Restricts the major to `1` and allows any minor. 97 | * `1.5.x - 3` - Allows a range of versions from `1.5` up to but not including 4. 98 | * `prereleases`: A boolean field, defaults to `false`, which specifies if 99 | prerelease versions of the bundle are allowed. 100 | -------------------------------------------------------------------------------- /104-bundle-formats.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bundle Formats 3 | weight: 104 4 | --- 5 | 6 | # Bundle Formats 7 | 8 | This section of the specification addresses how bundles are to be represented. The CNAB Core specification defines two representations (thick and thin), and any CNAB compliant bundle MUST be representable in both formats. CNAB runtimes MUST support thin bundles, and SHOULD support thick bundles. Various other CNAB tools MAY support only one or the other. For example, a bundle builder could support generating thick bundles, but not thin bundles, and yet still meet the requirements of the specification. 9 | 10 | Thick bundles do impose a slightly heavier burden on the runtime, and thus the specification allows that some runtimes might have pragmatic reasons for not supporting a thick bundle. Such runtimes MUST produce an error when given a thick bundle. 11 | 12 | ## Thick and Thin Bundles 13 | 14 | The thick and thin bundle formats refer to how much information must be transmitted in a package. Due to the nature of cloud technologies, it is sometimes possible (and desirable) to have a small artifact that references other external artifacts. In other cases, it is desirable to have one large self-contained artifact that depends on no external artifacts. CNAB represents the former as a thin bundle and the later as a thick bundle. 15 | 16 | A thin bundle contains only one object: The bundle descriptor. Thus, the format for a thin bundle is a JSON file. 17 | 18 | A thick bundle contains multiple objects: 19 | 20 | - The bundle descriptor (`bundle.json`) 21 | - One or more invocation images 22 | - Zero or more images 23 | 24 | As defined in this specification, objects of these three types are collected into a single archive file encoded as a gzipped tar archive. 25 | 26 | ## Differences in the Content of Bundle Descriptors 27 | 28 | Bundle descriptors have slightly different fields for thick and thin bundles. These are described in [the Bundle Descriptor description](101-bundle-json.md). 29 | 30 | ## Formatting and Transmitting Thin Bundles 31 | 32 | Thin bundles MUST be represented as Canonical JSON as specified in [the Bundle Descriptor description](101-bundle-json.md), and MUST conform to the schema provided in this specification. 33 | 34 | Thin bundles MAY then be stored and transmitted as JSON data. 35 | 36 | ## Formatting and Transmitting Thick Bundles 37 | 38 | Thick bundles MUST contain the bundle descriptor. In addition, a thick bundle MUST contain: 39 | 40 | - _All_ of the images referenced in the `invocationImages` section of the bundle descriptor 41 | - _All_ of the images referenced in the `images` section of the bundle descriptor 42 | 43 | ### File Format for Thick Bundles 44 | 45 | A thick bundle SHOULD be encoded as a gzipped TAR. This specification is neutral as to what compression ratio is used. 46 | 47 | The internal layout of the TAR SHOULD be as follows: 48 | ``` 49 | ├── artifacts 50 | │ └── layout 51 | │ └── ... 52 | └── bundle.json 53 | ``` 54 | 55 | The `bundle.json` MUST always be located at the root of the archive. 56 | 57 | CNAB implementations MAY create other directories at the root of the archive. 58 | 59 | All images MUST be located inside of the `artifacts/layout` directory, the contents of which SHOULD be an image layout conforming to the [OCI Image Layout Specification](https://github.com/opencontainers/image-spec/blob/master/image-layout.md), for example: 60 | ``` 61 | ├── artifacts 62 | │   └── layout 63 | │   ├── blobs 64 | │   │   └── sha256 65 | │   │   ├── 3588d02542238316759cbf24502f4344ffcc8a60c803870022f335d1390c13b4 66 | │   │   ├── 4b0bc1c4050b03c95ef2a8e36e25feac42fd31283e8c30b3ee5df6b043155d3c 67 | │   │   └── 7968321274dc6b6171697c33df7815310468e694ac5be0ec03ff053bb135e768 68 | │   ├── index.json 69 | │   └── oci-layout 70 | └── bundle.json 71 | ``` 72 | 73 | 74 | ### Transmitting Thick Bundles 75 | 76 | Thick bundles are represented as one large gzipped TAR file. As such, a thick bundle MAY be transmitted as a single unit. Some transmission implementations MAY decompose thick bundles for the purpose of efficiently transmitting them. In such cases, the transmission MUST NOT alter the contentDigest values of the artifacts, and MUST NOT alter the content of the `bundle.json`. Furthermore, they MUST use the same compression level when recomposing the bundle. To this end, the digest calculated on the source bundle file MUST be valid when the bundle is recovered. 77 | -------------------------------------------------------------------------------- /302-signing-workflows.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CNAB Security: Signing workflows 3 | weight: 302 4 | --- 5 | 6 | # Signing Workflows 7 | 8 | - [Signing workflow for the minimum viable product (MVP)](#signing-workflow-for-the-minimum-viable-product-mvp) 9 | 10 | This document is a _normative_ part of [CNAB Security](300-CNAB-security.md). 11 | 12 | The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). The use of these keywords in this document are statements about how a CNAB implementation may fulfill the CNAB Security specification _only_. 13 | 14 | ## Signing workflow for the minimum viable product (MVP) 15 | 16 | This subsection documents how a signing workflow MAY typically work for an [MVP metadata repository](301-metadata-repositories.md). To reiterate, this document is normative, and while a CNAB-Sec-compliant implementation may borrow ideas from here, it MAY also choose to implement different signing workflows, especially for security models different from those described in the MVP. 17 | 18 | ### Setup (one-time task) 19 | 20 | When bundle developers set up an MVP metadata repository for the first time, they SHOULD use one of the [known implementations of CNAB-Sec](806-security-known-implementations.md) to set up at least a complete set of the `root`, `timestamp`, `snapshot`, and `targets` TUF metadata. 21 | 22 | To do so, developers SHOULD begin by generating the private keys for the `root` and `targets` roles. There are two options for the `timestamp` and `snapshot` roles. One is that developers MAY also generate and manage their private keys. The other is that the metadata repository MAY automatically generate and manage their private keys, as is the default on [Docker Content Trust](https://docs.docker.com/engine/security/trust/trust_key_mng/). 23 | 24 | After these private keys have been generated, developers SHOULD generate the initial set of the TUF metadata. The `targets` metadata MAY list no bundles for the moment. Regardless of whether developers or the metadata repository manage the `timestamp` and `snapshot` keys, the `snapshot` metadata SHOULD point to this `targets` metadata file, and the `timestamp` metadata SHOULD point in turn to this `snapshot` metadata file. Finally, the `root` metadata SHOULD list the public keys for all top-level roles, including itself. 25 | 26 | If developers wish to use in-toto to [verify the provenance for their bundles](301-metadata-repositories.md#extending-the-MVP-to-verify-the-provenance-of-bundles), then they SHOULD also: 27 | 28 | 1. Generate the `targets/releases` key. 29 | 1. Generate and sign the `targets/releases` metadata. 30 | 1. Generate the private keys for the [in-toto root layout](https://github.com/in-toto/docs/blob/master/in-toto-spec.md#43-file-formats-layout). 31 | 1. Generate and sign the in-toto root layout. 32 | 1. Generate and sign the `targets` metadata, including signing the in-toto root layout as well as its associated public keys, and delegating in-toto links and all versions of bundles to the `targets/releases` role. 33 | 34 | The exactly details are out of the scope of this document. However, the interested reader MAY consult the [Datadog TUF and in-toto integration](https://www.datadoghq.com/blog/engineering/secure-publication-of-datadog-agent-integrations-with-tuf-and-in-toto/) for an example of how to do so. 35 | 36 | ### New or updated bundles (periodic task) 37 | 38 | Whenever developers wish to release a new version of a bundle, they SHOULD perform the following steps. 39 | 40 | First, they SHOULD sign new `targets` metadata that points to the new version of the bundle. If developers wish to use in-toto to provide provenance for their bundles, then, as discussed in [metadata repositories](301-metadata-repositories.md), they MAY also list in the custom targets metadata all of the root layout and link metadata required to verify this bundle. 41 | 42 | Second, regardless of whether developers or the metadata repository holds these signing keys, the `timestamp` and `snapshot` metadata SHOULD be updated to the point to the new bundle. 43 | 44 | If developers wish to use in-toto to [verify the provenance for their bundles](301-metadata-repositories.md#extending-the-MVP-to-verify-the-provenance-of-bundles), then they SHOULD also sign new `targets/releases`, instead of `targets` metadata as above, to point to new versions of bundles as well as associated in-toto link metadata. 45 | 46 | ### Recovering from a key compromise (exceptional task) 47 | 48 | If the private key for the `timestamp`, `snapshot`, `targets`, or `targets/releases` role has been compromised, then developers SHOULD rotate their keys using the `root` metadata. Furthermore, if the `targets` or `targets/releases` role key was compromised, developers SHOULD warn users about the possibility of having accidentally installed malicious versions of bundles. 49 | 50 | If less than a threshold of the `root` keys have been compromised, then developers SHOULD use at least a threshold of the `root` keys to rotate its own keys. Otherwise, if more than a threshold of the `root` keys have been compromised, then it is safest for developers to sign new `root` metadata using no threshold of previous `root` keys, which will require end-users to update `root` metadata using a safe out-of-band mechanism. 51 | -------------------------------------------------------------------------------- /805-airgap.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Disconnected Scenario 3 | weight: 805 4 | --- 5 | 6 | # Disconnected Scenarios 7 | 8 | This section is non-normative and describes the disconnected (air gapped) scenario mentioned in the introduction of [CNAB-Core](100-CNAB.md). 9 | 10 | ## Summary 11 | 12 | Using CNAB in a disconnected environment involves transferring a bundle and its images into the 13 | environment such that the bundle can be installed and the bundled software executed successfully. 14 | 15 | ## Internet Access 16 | 17 | A typical disconnected scenario will have limited, intermittent or no internet access, whether by design or by situation. 18 | To install a bundle directly in a disconnected environment, the bundle and its images need to be included in a [CNAB thick bundle](104-bundle-formats.md) 19 | and transferred into the disconnected environment, for instance on a USB stick. 20 | 21 | Alternatively, if a DMZ is available, it may be possible to read the bundle and/or its images from an external network 22 | and write the bundle and/or its images to a registry inside the disconnected environment. 23 | 24 | ## Private Registries 25 | 26 | Common cloud patterns today reference artifacts and code from multiple sources as described in 27 | [CNAB-Sec](300-CNAB-security.md). 28 | Industry best practices, particularly [NISTIR-8176](https://csrc.nist.gov/News/2017/NIST-Releases-NISTIR-8176), emphasise the importance of 29 | private registries and of avoiding the use of uncontrolled artifacts in production. CNAB aims to enable adherence to these best practices. 30 | 31 | A CNAB runtime is NOT required to provide registry functionality to a CNAB in a disconnected environment. 32 | It is assumed that an OCI compliant registry is available in the disconnected environment for hosting the bundle 33 | and/or its images. 34 | 35 | A private registry: 36 | * Can be hosted in a disconnected environment for security, auditability, or other reasons. 37 | * Provides complete control over when a bundle or image is updated or deleted: 38 | * This provides isolation from unwanted updates or deletion of the original bundle or image. 39 | * If the bundle or image becomes stale, for instance when it has known vulnerabilities, it can be deleted. 40 | 41 | ## CNAB Thick Bundles 42 | 43 | A CNAB thick bundle provides a convenient archive format for transferring a bundle and its images into a 44 | disconnected environment. But thick bundles have other benefits. 45 | 46 | A CNAB bundle may reference artifacts that are hosted in different repositories or registries. 47 | These remote artifacts may change over time without changing the references. 48 | If the digests of artifacts are provided in the bundle, the content of the artifacts cannot change without 49 | changing the digests, but even then the artifacts, or their repositories or registries, may be deleted. 50 | 51 | Archiving a CNAB and its images at a point of time as a CNAB thick bundle 52 | provides protection against modification or deletion of images and also provides a central location for code 53 | auditing and digital forensics of all code and references used in the CNAB. 54 | 55 | ## Image Relocation 56 | 57 | When the images of a CNAB are _relocated_ to (that is, stored in), a private registry, the images should be loaded from the private registry when they are run. 58 | This ensures that CNAB operations can function properly even if the original image repositories are unavailable. 59 | 60 | The runtime uses the relocated reference of the invocation image so that the image is loaded from the private registry. 61 | 62 | A [relocation mapping](103-bundle-runtime.md#relocation-mapping) is mounted so that the invocation 63 | image is aware of the original and new values of image references and can replace the original image references with their relocated counterparts. 64 | Thus the images referenced by the CNAB are also loaded from the private registry. 65 | 66 | ## CNAB Security 67 | 68 | When copying images from public registries to an airgapped environment, the user has at least two options. 69 | 70 | The first option is to use one of the [known 71 | implementations](806-security-known-implementations.md) of CNAB Security to 72 | verify signatures on bundles (and possibly images, although this is out of 73 | scope for CNAB Security) from public registries, but push unsigned bundles 74 | and images to their own private, airgapped registries. The pros here are that 75 | the user does verify the authenticity and integrity of public bundles, and does 76 | not need to maintain any private signing and verification infrastructure, but 77 | the con is that the user is not protected from internal attacks between 78 | registries and consumers. 79 | 80 | The second option is to use one of the known implementations of CNAB Security 81 | to not only verify signatures on bundles (and optionally images) from public 82 | registries, but also _independently_ sign and push bundles (and images) to 83 | their own private, airgapped registries. The signing keys are independent from 84 | the public, upstream registries, because: (1) the known implementation may not 85 | support copying signatures, (2) the original signatures may expire within the 86 | airgapped environment, and (3) there may be transformation of bundles (such as 87 | producing private thick bundles from public thin bundles). This option is 88 | more complicated than the first one, but does not suffer from the same cons. 89 | -------------------------------------------------------------------------------- /schema/definitions.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/v1/definitions.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "title": "Core schema meta-schema", 5 | "definitions": { 6 | "schemaArray": { 7 | "type": "array", 8 | "minItems": 1, 9 | "items": { "$ref": "#" } 10 | }, 11 | "nonNegativeInteger": { 12 | "type": "integer", 13 | "minimum": 0 14 | }, 15 | "nonNegativeIntegerDefault0": { 16 | "allOf": [ 17 | { "$ref": "#/definitions/nonNegativeInteger" }, 18 | { "default": 0 } 19 | ] 20 | }, 21 | "simpleTypes": { 22 | "enum": [ 23 | "array", 24 | "boolean", 25 | "integer", 26 | "null", 27 | "object", 28 | "string" 29 | ] 30 | }, 31 | "stringArray": { 32 | "type": "array", 33 | "items": { "type": "string" }, 34 | "uniqueItems": true, 35 | "default": [] 36 | } 37 | }, 38 | "type": ["object", "boolean"], 39 | "properties": { 40 | "$id": { 41 | "type": "string", 42 | "format": "uri-reference" 43 | }, 44 | "$schema": { 45 | "type": "string", 46 | "format": "uri" 47 | }, 48 | "$ref": { 49 | "type": "string", 50 | "format": "uri-reference" 51 | }, 52 | "$comment": { 53 | "type": "string" 54 | }, 55 | "title": { 56 | "type": "string" 57 | }, 58 | "description": { 59 | "type": "string" 60 | }, 61 | "default": true, 62 | "readOnly": { 63 | "type": "boolean", 64 | "default": false 65 | }, 66 | "examples": { 67 | "type": "array", 68 | "items": true 69 | }, 70 | "multipleOf": { 71 | "type": "integer", 72 | "exclusiveMinimum": 0 73 | }, 74 | "maximum": { 75 | "type": "integer" 76 | }, 77 | "exclusiveMaximum": { 78 | "type": "integer" 79 | }, 80 | "minimum": { 81 | "type": "integer" 82 | }, 83 | "exclusiveMinimum": { 84 | "type": "integer" 85 | }, 86 | "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, 87 | "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 88 | "pattern": { 89 | "type": "string", 90 | "format": "regex" 91 | }, 92 | "additionalItems": { "$ref": "#" }, 93 | "items": { 94 | "anyOf": [ 95 | { "$ref": "#" }, 96 | { "$ref": "#/definitions/schemaArray" } 97 | ], 98 | "default": true 99 | }, 100 | "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, 101 | "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 102 | "uniqueItems": { 103 | "type": "boolean", 104 | "default": false 105 | }, 106 | "contains": { "$ref": "#" }, 107 | "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, 108 | "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, 109 | "required": { "$ref": "#/definitions/stringArray" }, 110 | "additionalProperties": { "$ref": "#" }, 111 | "definitions": { 112 | "type": "object", 113 | "additionalProperties": { "$ref": "#" }, 114 | "default": {} 115 | }, 116 | "properties": { 117 | "type": "object", 118 | "additionalProperties": { "$ref": "#" }, 119 | "default": {} 120 | }, 121 | "patternProperties": { 122 | "type": "object", 123 | "additionalProperties": { "$ref": "#" }, 124 | "propertyNames": { "format": "regex" }, 125 | "default": {} 126 | }, 127 | "dependencies": { 128 | "type": "object", 129 | "additionalProperties": { 130 | "anyOf": [ 131 | { "$ref": "#" }, 132 | { "$ref": "#/definitions/stringArray" } 133 | ] 134 | } 135 | }, 136 | "propertyNames": { "$ref": "#" }, 137 | "const": true, 138 | "enum": { 139 | "type": "array", 140 | "items": true, 141 | "minItems": 1, 142 | "uniqueItems": true 143 | }, 144 | "type": { 145 | "anyOf": [ 146 | { "$ref": "#/definitions/simpleTypes" }, 147 | { 148 | "type": "array", 149 | "items": { "$ref": "#/definitions/simpleTypes" }, 150 | "minItems": 1, 151 | "uniqueItems": true 152 | } 153 | ] 154 | }, 155 | "format": { "type": "string" }, 156 | "contentMediaType": { "type": "string" }, 157 | "contentEncoding": { "type": "string" }, 158 | "if": { "$ref": "#" }, 159 | "then": { "$ref": "#" }, 160 | "else": { "$ref": "#" }, 161 | "allOf": { "$ref": "#/definitions/schemaArray" }, 162 | "anyOf": { "$ref": "#/definitions/schemaArray" }, 163 | "oneOf": { "$ref": "#/definitions/schemaArray" }, 164 | "not": { "$ref": "#" } 165 | }, 166 | "default": true 167 | } 168 | -------------------------------------------------------------------------------- /201-representing-CNAB-in-OCI.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Representing CNAB bundles in OCI Registries 3 | weight: 201 4 | --- 5 | 6 | # Representing CNAB bundles in OCI Registries 7 | 8 | This section describes how the bundle file and the information contained in the bundle MUST be represented when distributing bundles using OCI registries. 9 | 10 | A CNAB is made up of a descriptor file (`bundle.json`), one or more invocation images, and a list of zero or more component images. Each of these objects can be individually packaged into an [OCI manifest][oci-manifest] or [OCI index][oci-index]. An OCI index can then be used to combine these all into a single object. 11 | 12 | Runtimes and registries MAY choose to _relocate_ the images and invocation images referenced in the bundle to the registry where the bundle is pushed, but in its simplest form, a CNAB bundle MAY be represented in an OCI registry by the canonical `bundle.json` file descriptor, referenced in an OCI index (or manifest list). 13 | 14 | ![](https://i.imgur.com/PfTcKOm.png) 15 | 16 | ### `bundle.json` manifest 17 | 18 | The `bundle.json` MUST be serialized as canonical JSON and MUST be stored in the registry as a blob. This blob MUST be referenced by its digest in an OCI manifest. The manifest media type SHOULD be `application/vnd.cnab.bundle.config.v1+json` but MAY be a standard container image config type (`application/vnd.oci.image.config.v1+json`) if the target registry does not support the CNAB media type. 19 | 20 | ### Example 21 | 22 | The following non-canonical `bundle.json` file will be used as an example: 23 | 24 | ```json 25 | { 26 | "schemaVersion": "v1.0.0", 27 | "name": "helloworld", 28 | "version": "0.1.1", 29 | "description": "A short description of your bundle", 30 | "keywords": ["helloworld", "cnab", "tutorial"], 31 | "maintainers": [ 32 | { 33 | "name": "Jane Doe", 34 | "email": "jane.doe@example.com", 35 | "url": "https://example.com" 36 | } 37 | ], 38 | "invocationImages": [ 39 | { 40 | "imageType": "docker", 41 | "image": "cnab/helloworld:0.1.1", 42 | "size": 942, 43 | "contentDigest": "sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6", 44 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json" 45 | } 46 | ], 47 | "images": null, 48 | "parameters": null, 49 | "credentials": null 50 | } 51 | ``` 52 | 53 | The `bundle.json` is serialized into canonical JSON and is pushed as a blob to the registry. This blob is then referenced by its digest (for example `sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b`) as the object config. 54 | 55 | The bundle is encapsulated in an OCI manifest which is then referenced as part of an OCI index (or manifest list): 56 | 57 | ```json 58 | { 59 | "schemaVersion": 2, 60 | "config": { 61 | "mediaType": "application/vnd.cnab.bundle.config.v1+json", 62 | "digest": "sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b", 63 | "size": 498 64 | }, 65 | "layers": null 66 | } 67 | ``` 68 | 69 | The bundle file MUST be stored in the OCI registry, as it is the canonical representation of a CNAB bundle, and is the object that is used to compute the signature for a thin bundle. 70 | While implementations MAY choose to surface information from the bundle as top-level annotations in the OCI representations, they MUST store the unmodified bundle file as a blob. 71 | 72 | The bundle file and registry location MAY be used by implementations to generate relocation maps at runtime. 73 | 74 | ### Invocation images 75 | 76 | Each invocation image SHOULD be packaged as an OCI image but MAY be packaged as a Docker v2 image. If there are multiple invocation images, these SHOULD be referenced by an OCI index but MAY be referenced by a Docker manifest list. 77 | 78 | ### Component images 79 | 80 | Each component image SHOULD be packaged as an OCI image but MAY be packaged as a Docker v2 image. In the case that a component has multiple equivalent forms, such as a multiarch container image, each component MUST be packaged and then SHOULD be referenced by an OCI index but MAY be referenced by a Docker manifest list. 81 | 82 | ## Top-level representation 83 | 84 | The top-level representation of the CNAB SHOULD be an OCI index but may be a Docker manifest list. 85 | 86 | The `manifests` list MUST be filled in the following order: 87 | 88 | - A reference to packaged `bundle.json` manifest 89 | - References to the invocation images 90 | - References to the component images 91 | 92 | ### Example 93 | 94 | ```json 95 | { 96 | "schemaVersion": 2, 97 | "manifests": [ 98 | { 99 | "mediaType": "application/vnd.oci.image.manifest.v1+json", 100 | "digest": "sha256:6ec4fd695cace0e3d4305838fdf9fcd646798d3fea42b3abb28c117f903a6a5f", 101 | "size": 188, 102 | "annotations": { 103 | "io.cnab.manifest.type": "config" 104 | } 105 | }, 106 | { 107 | "mediaType": "application/vnd.docker.distribution.manifest.v2+json", 108 | "digest": "sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6", 109 | "size": 942, 110 | "annotations": { 111 | "io.cnab.manifest.type": "invocation" 112 | } 113 | } 114 | ], 115 | "annotations": { 116 | "io.cnab.keywords": "[\"helloworld\",\"cnab\",\"tutorial\"]", 117 | "io.cnab.runtime_version": "v1.0.0", 118 | "org.opencontainers.artifactType": "application/vnd.cnab.manifest.v1", 119 | "org.opencontainers.image.authors": "[{\"name\":\"Jane Doe\",\"email\":\"jane.doe@example.com\",\"url\":\"https://example.com\"}]", 120 | "org.opencontainers.image.description": "A short description of your bundle", 121 | "org.opencontainers.image.title": "helloworld", 122 | "org.opencontainers.image.version": "0.1.1" 123 | } 124 | } 125 | ``` 126 | 127 | [oci-index]: https://github.com/opencontainers/image-spec/blob/master/image-index.md 128 | [oci-manifest]: https://github.com/opencontainers/image-spec/blob/master/manifest.md 129 | -------------------------------------------------------------------------------- /102-invocation-image.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Invocation Images 3 | weight: 102 4 | --- 5 | 6 | # The Invocation Images 7 | 8 | The `invocationImages` section of a `bundle.json` MUST contain at least one image (the invocation image). This image MUST be formatted according to the specification laid out in the present document. 9 | The appropriate invocation image is selected by the CNAB runtime. 10 | 11 | When a bundle is executed, the invocation image will be retrieved (if necessary) and started. Credential, parameter and image map data is passed to it, and then its `run` tool is executed. (See [The Bundle Runtime](103-bundle-runtime.md) for details). 12 | 13 | This section describes the layout of an invocation image. 14 | 15 | ## Components of an Invocation Image 16 | 17 | An invocation image is composed of the following: 18 | 19 | - A file system hierarchy following a defined pattern (below) 20 | - A main entry point, called the _run tool_, which is an executable (often a script) responsible for translating action requests (`install`, `upgrade`,...) to a sequence of tasks 21 | - Runtime metadata (Helm charts, Terraform templates, etc) 22 | - The material necessary for reproducing the invocation image (`Dockerfile` and `packer.json` are two examples) 23 | 24 | Note that the bundle definition itself is not stored inside of the invocation image. 25 | 26 | ### The File System Layout 27 | 28 | The following exhibits the filesystem layout: 29 | 30 | ```yaml 31 | cnab/ # REQUIRED top-level directory 32 | └── build/ 33 | │ └──Dockerfile # OPTIONAL 34 | └── app # REQUIRED 35 | ├── run # REQUIRED: This is the main entrypoint, and MUST be executable 36 | ├── charts # Example: Helm charts might go here 37 | │ └── azure-voting-app 38 | │ ├── Chart.yaml 39 | │ ├── templates 40 | │ │ └── ... 41 | │ └── values.yaml 42 | └── sfmesh # Example: Service Fabric definitions might go here 43 | └── sfmesh-deploy.json 44 | ``` 45 | 46 | ### The `/cnab` Directory 47 | 48 | An invocation image MUST have a directory named `cnab` placed directly under the root of the file system hierarchy inside of an image. This directory MUST have a subdirectory named `app`. 49 | 50 | This `cnab` directory MAY have any of the following: 51 | 52 | - `build/`: A directory containing files used in the construction of this image 53 | - `Dockerfile`: A valid Dockerfile used for constructing this image 54 | - Files for the form `Dockerfile.$INFO`, where `$INFO` is a further specification of that Dockerfile (e.g. `Dockerfile.arm64`) 55 | - `packer.json`: A valid Packer configuration file 56 | - Other build-related files 57 | - `README.txt` or `README.md`: A text file containing information about this bundle 58 | - `LICENSE`: A text file containing the license(s) for this image 59 | 60 | This directory MUST NOT have any files or directories not explicitly named in the present document. The `/cnab` directory is considered a reserved namespace where future CNAB revisions MAY place new files or directories. 61 | 62 | ### The `/cnab/app` Directory 63 | 64 | The `app/` directory contains subdirectories, each of which stores configuration for a particular target environment. The `app/run` file _MUST be an executable file_ that will act as the "main" installer for this CNAB bundle. This is the only file that is REQUIRED in this directory. 65 | 66 | The contents beneath `/cnab/app/SUBDIRECTORY` are undefined by the spec. `run` is considered the only reserved word underneath `/cnab/app/` 67 | 68 | ### The OPTIONAL `/cnab/build` Directory 69 | 70 | The directory `/cnab/build` MAY be present within the CNAB hierarchy. This directory houses files used in the construction of the invocation image, and are provided to make it easier to rebuild the image during rewrite operations. If a `Dockerfile` was used to build the image, a `Dockerfile` SHOULD be included. Other files MAY be included. 71 | 72 | Examples: 73 | - `packer.json` 74 | - `Dockerfile.arm32` 75 | 76 | #### Dockerfiles for Constructing Invocation Images 77 | 78 | This subsection is non-normative. Images may be built using any suitable tooling, but this section describes the process using a `Dockerfile`. 79 | 80 | The `Dockerfile` used to build the invocation image MAY be stored inside of the invocation image. This is to ensure reproducibility, and in order to allow rename operations that require a rebuild. (Likewise, if a build tool like Packer is used, this tool's configuration MAY be placed in the bundle.) 81 | 82 | This is a normal Dockerfile, and MAY derive from any base image. 83 | 84 | Example: 85 | 86 | ```Dockerfile 87 | FROM ubuntu:latest 88 | 89 | COPY ./Dockerfile /cnab/Dockerfile 90 | COPY ./parameters.json /cnab/parameters.json 91 | 92 | RUN curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash 93 | RUN helm init --client-only 94 | RUN helm repo add example-stable https://repo.example.com/stable 95 | 96 | CMD /cnab/app/run 97 | ``` 98 | 99 | The above example installs and configures Helm inside of a base Ubuntu image. Note that there are no restrictions on what tools MAY be installed. 100 | 101 | ## The Run Tool 102 | 103 | The run tool MUST be located at the path `/cnab/app/run`. It MUST be executable. It MUST react to the `CNAB_ACTION` provided to it. 104 | 105 | The specification does not define what language(s) the tool must be written in, or any details about how it processes the information. However, the following is a non-normative example: 106 | 107 | ```bash 108 | #!/bin/sh 109 | 110 | action=$CNAB_ACTION 111 | name=$CNAB_INSTALLATION_NAME 112 | 113 | case $action in 114 | install) 115 | echo "Install action" 116 | ;; 117 | uninstall) 118 | echo "uninstall action" 119 | ;; 120 | upgrade) 121 | echo "Upgrade action" 122 | ;; 123 | *) 124 | echo "No action for $action" 125 | ;; 126 | esac 127 | echo "Action $action complete for $name" 128 | ``` 129 | 130 | The run tool above is implemented as a shell script, and merely reacts to each given `CNAB_ACTION` by printing a message. 131 | 132 | See [The Bundle Runtime](103-bundle-runtime.md) for a description on how this tool is used. 133 | 134 | Next section: [The Bundle Runtime](103-bundle-runtime.md) 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloud Native Application Bundle Specifications 2 | 3 | ## Abstract 4 | 5 | Cloud Native Application Bundles (CNAB) are a package format specification that describes a technology for bundling, installing, and managing distributed applications, that are by design, cloud agnostic. 6 | 7 | ## CNAB Core 1.2.0 (Final) 8 | 9 | The CNAB Working Group with the joint approval of the Executive Directors has approved the CNAB Core 1.2.0 specification for publication. CNAB Core 1.2.0 is complete. 10 | 11 | For more information on the approval process, see [the process documentation](901-process.md). Further changes to CNAB Core will be considered for CNAB Core 1.3. 12 | 13 | ### Branch/Tag Structure 14 | 15 | - [main](https://github.com/cnabio/cnab-spec) is the current working draft of all specs 16 | - [cnab-core-1.2.0](https://github.com/cnabio/cnab-spec/tree/cnab-core-1.2.0) is the CNAB Core 1.2.0 final draft 17 | - [cnab-core-1.1.0](https://github.com/cnabio/cnab-spec/tree/cnab-core-1.1.0) is the CNAB Core 1.1.0 final draft 18 | - [cnab-core-1.0.1](https://github.com/cnabio/cnab-spec/tree/cnab-core-1.0.1) is the CNAB Core 1.0.1 final draft 19 | - [cnab-core-1.0](https://github.com/cnabio/cnab-spec/tree/cnab-core-1.0) is the CNAB Core 1.0.0 final draft 20 | - [cnab-claims-1.0.0](https://github.com/cnabio/cnab-spec/tree/cnab-claims-1.0.0) is the CNAB Claims 1.0.0 final draft 21 | - [cnab-security-1.0.0](https://github.com/cnabio/cnab-spec/tree/cnab-security-1.0.0) is the CNAB Security 1.0.0 final draft 22 | 23 | ## Table of Contents 24 | 25 | - Chapter 1: [Cloud Native Application Bundle Core 1.0.0 (CNAB1)](100-CNAB.md) 26 | 1. [The bundle.json File](101-bundle-json.md) 27 | 1. [The Invocation Image Format](102-invocation-image.md) 28 | 1. [The Bundle Runtime](103-bundle-runtime.md) 29 | 1. [Bundle Formats (Thick and Thin)](104-bundle-formats.md) 30 | - Chapter 2: [Cloud Native Application Bundle Registry 1.0.0 (CNAB-Reg)](200-CNAB-registries.md) 31 | - Chapter 3: [Cloud Native Application Bundles Security (CNAB-Sec) 1.0.0 GA](300-CNAB-security.md) 32 | 1. [Cloud Native Application Bundles Security (CNAB-Sec): Metadata repositories 1.0.0 GA](301-metadata-repositories.md) 33 | 1. [Cloud Native Application Bundles Security (CNAB-Sec): Signing workflows 1.0.0 GA](302-signing-workflows.md) 34 | 1. [Cloud Native Application Bundles Security (CNAB-Sec): Verification workflows 1.0.0 GA](303-verification-workflows.md) 35 | - Chapter 4: [Cloud Native Application Bundle Claims 1.0.0 (CNAB-Claims1)](400-claims.md) 36 | - Chapter 5: [Cloud Native Application Bundle Dependencies 1.0.0 (CNAB-Deps)](500-CNAB-dependencies.md) 37 | - Chapter 8: Non-normative Content 38 | 1. [Declarative Invocation Images](801-declarative-images.md) 39 | 1. [Credential Sets](802-credential-sets.md) 40 | 1. [Repositories](803-repositories.md) 41 | 1. [Well known custom actions](804-well-known-custom-actions.md) 42 | 1. [Disconnected Scenarios](805-airgap.md) 43 | 1. [Known implementations for CNAB Security 1.0.0 (CNAB-Sec 1.0.0)](806-security-known-implementations.md) 44 | - Chapter 9: Appendix 45 | 1. [Appendix A: Preliminary Release Process](901-process.md) 46 | 47 | ## Contributing 48 | 49 | The specification is licensed under [OWF Contributor License Agreement 1.0 - Copyright and Patent](http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owf-contributor-license-agreement-1-0---copyright-and-patent) in the [LICENSE](./LICENSE) file. 50 | 51 | ## Communications 52 | 53 | ### Meetings 54 | * Community Meeting: Every other Wednesday at 09:00 AM US Pacific 55 | * https://zoom.us/j/653255416 56 | * [Meeting notes and Agenda](https://aka.ms/cnab/meeting). 57 | 58 | ### Slack Channel 59 | #cnab Slack channel for related discussion in 60 | [CNCF's Slack workspace](https://slack.cncf.io/). 61 | 62 | ### Mailing List 63 | 64 | We operate a [mailing list](https://lists.jointdevelopment.org/g/CNAB-Main) via the Joint Development Foundation. 65 | 66 | ## Versioned Schema URLs 67 | 68 | CNAB Spec schema defined in this repo (under `/schema`) will be hosted for all tagged versions. This way, implementations can require specific schema versions for validation and assert compatibility with the corresponding versions. 69 | 70 | Note that some tagged versions don't directly map to official schema versions. For instance, a Git SHA may be appended if the spec is still in a Draft state, e.g. `cnab-claim-1.0.0-DRAFT+abc1234`. Again, this facilitates the ability for implementations to pin to a certain tag whilst a spec is under heavy development with many breaking changes. 71 | 72 | The schema files are hosted via `https://cnab.io/schema//.schema.json`, e.g. https://cnab.io/schema/cnab-core-1.0/bundle.schema.json 73 | 74 | ## Notational Conventions 75 | 76 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC 2119][rfc2119]. 77 | 78 | The key words "unspecified", "undefined", and "implementation-defined" are to be interpreted as described in the [rationale for the C99 standard][c99-unspecified]. 79 | 80 | An implementation IS compliant if it satisfies all the MUST, REQUIRED, and SHALL requirements. 81 | 82 | An implementation IS NOT compliant if it fails to satisfy one or more of the MUST, REQUIRED, or SHALL requirements. 83 | 84 | [c99-unspecified]: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf#page=18 85 | [rfc2119]: http://tools.ietf.org/html/rfc2119 86 | 87 | ## Document Naming Conventions 88 | 89 | - The CNAB Core specification is contained in the 1xx documents. 90 | - The CNAB Registry specification is contained in the 2xx documents. 91 | - The CNAB Security specification reserves 3xx level documents. 92 | - The Claims specification reserves 4xx documents. 93 | - The CNAB Dependencies specification uses 5xx documents. 94 | - The 8xx-level documents are reserved for non-normative guidance. 95 | - The 9xx-level documents are reserved for process documents. 96 | 97 | The top-level description of each specification is found in the `x00` sections (e.g. 100 is the top of the CNAB Core specification, while 200 is the top of the CNAB Registry specification). Specifications may be broken down into subsections, which are numbered incrementally according to their section. Thus, the CNAB Core specification has a 100 (top-level) with four subsections (101, 102, 103, and 104). The 8XX section is entirely composed of non-normative documents, and each 8XX document stands alone. 98 | -------------------------------------------------------------------------------- /300-CNAB-security.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bundle Security 3 | weight: 300 4 | --- 5 | 6 | # Cloud Native Application Bundles Security (CNAB-Sec) 1.0.0 GA 7 | *[Working Group Approval](901-process.md), Jun. 2020* 8 | 9 | The CNAB Working Group has approved the CNAB Security 1.0 specification. This specification is now pending approval from the CNAB Foundation executive committee, after which it will be finalized as CNAB Security 1.0 (AD). For more information on the approval process, see [the process documentation](901-process.md). Further changes to CNAB Security will be considered for CNAB Security 1.1. 10 | 11 | 12 | * [Introduction](#introduction) 13 | * [Roadmap](#roadmap) 14 | * [Gradual security](#gradual-security) 15 | * [References](#references) 16 | 17 | ![cnab-security](https://user-images.githubusercontent.com/686194/61752644-54580b80-ad61-11e9-9518-608534d09bdd.png) 18 | 19 | This specification is distinct from the CNAB Core specification. An implementation may comply with the CNAB Core specification, and yet not comply with this specification. 20 | 21 | The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). The use of these keywords in this document are statements about how a CNAB implementation may fulfill the CNAB Security specification _only_. 22 | 23 | The reader is assumed to be thoroughly familiar with the [TUF][tuf-spec] and [in-toto][in-toto-spec] specifications, and some deployment models such as [PEP 458][pep-458], [PEP 480][pep-480], [ITE-4][ite-4], [ITE-5][ite-5], and [Datadog Agent integrations][datadog-agent-integrations]. 24 | 25 | The CNAB Security specification augments the [CNAB Core specification](100-CNAB.md) by standardizing on security mechanisms for signing, verifying, and attesting CNAB packages. It describes both a client/registry security model and a verification chain (provenance) model. 26 | 27 | This specification is distinct from the CNAB Core specification. An implementation may comply with the CNAB Core specification, and yet not comply with this specification. The use of terms such as MUST and SHOULD in this document are statements about how a CNAB implementation may fulfill the CNAB Security specification _only_. 28 | 29 | > An earlier version of the CNAB Core specification used OpenPGP-based mechanism to sign and verify bundles. This specification supersedes that portion of the specification. 30 | 31 | ## Introduction 32 | 33 | It is important to sign bundles and images so that attackers cannot tamper with them without being detected. To enable faster deployments, they are likely to be built and signed by continuous integration / continuous delivery (CI/CD) instead of developers. While CI/CD provides benefits such as safer handling of signing keys, it also has a severe drawback. Since signing keys must be kept _online_ so that CI/CD can build and sign new bundles or images on demand, attackers who compromise the infrastructure can also abuse these keys to build, sign, and distribute malicious versions. The goal of this document is to discuss how to achieve _compromise-resilience_ in this setting: that is, even if the infrastructure is compromised anywhere between developers and end-users, attackers should not be able to cause end-users to install malicious versions of bundles or images that were not released by developers. 34 | 35 | While SSL / TLS protects users from man-in-the-middle (MitM) attacks, it is _not_ compromise-resilient, because attackers who compromise the infrastructure can abuse the online SSL / TLS key to replace files signed in transit but not at rest. Likewise, other solutions, such as GPG or X.509, do not support necessary features such as in-band key revocation or rotation. 36 | 37 | We propose using [The Update Framework (TUF)](https://theupdateframework.com) and [in-toto](https://in-toto.io) to solve this problem. TUF is a framework that provides security between _registries_, or the servers used to distribute bundles or images, and end-users. Specifically, TUF ensures the authenticity and integrity of bundles and images downloaded from registries. in-toto is a framework that provides security between developers and registries. Specifically, in-toto ensures the end-to-end integrity of the _software supply chain_ used to build and sign bundles or images. By combining both, we get a system that provides compromise-resilience between developers and end-users. 38 | 39 | Signing and verifying [claims](400-claims.md) are out of the scope of this document. 40 | 41 | ## Roadmap 42 | 43 | The basic idea is to use TUF as a transport protocol that distributes several files in a compromise-resilient manner: 44 | 45 | * The root of trust for all bundles and images, as well as TUF and in-toto metadata. 46 | * The software supply chains defined using in-toto. 47 | * The public keys used to verify these supply chains. 48 | 49 | While images and bundles are published on registries, TUF and in-toto metadata about images and bundles are published on [_metadata repositories_](301-metadata-repositories.md). Metadata repositories are conceptually, but not necessarily physically, distinct from [CNAB](200-CNAB-registries.md) or [OCI](https://github.com/opencontainers/distribution-spec/blob/master/spec.md) registries. Furthermore, the key management models on metadata repositories are largely prescriptive. For these reasons and more, metadata repositories are out of the scope of this document. 50 | 51 | Unless specified otherwise, metadata are TUF and in-toto metadata, which may be produced using the suggested [signing workflows](302-signing-workflows.md). 52 | 53 | When a [bundle runtime](103-bundle-runtime.md) installs a bundle, it should first verify images and bundles using the suggested [verification workflows](303-verification-workflows.md). 54 | 55 | Considerations for airgapped environments are discussed [here](805-airgap.md#cnab-security). 56 | 57 | ### Gradual security 58 | 59 | Implementors / adopters MAY wish to implement security gradually instead of an all-or-nothing approach. 60 | 61 | Level 0: neither images nor bundles are signed. 62 | * Pros: no security to maintain. 63 | * Cons: completely lacking compromise-resilience. 64 | 65 | Level 1a: images are signed using TUF, but bundles are unsigned. 66 | * Pros: easiest level of security to achieve, e.g. using Docker Content Trust (metadata repository) and Notary (client). 67 | * Cons: only partly compromise-resilient. If a [CNAB registry](200-CNAB-registries.md) is compromised, a bundle could be easily replaced with malicious one. 68 | 69 | Level 1b: bundles are signed using TUF, but images are unsigned. 70 | * Pros: easiest level of security to achieve, e.g. using Docker Content Trust (metadata repository) and Notary (client). 71 | * Cons: only partly compromise-resilient. If an [OCI registry](https://github.com/opencontainers/distribution-spec/blob/master/spec.md) is compromised, a bundle runtime may be vulnerable to accidentally bundling malicious images. 72 | 73 | Level 2: both bundles and images are signed using TUF. 74 | * Pros: resilient against compromise of bundle and image registries. 75 | * Cons: not resilient against compromise of CI for bundles or images. 76 | 77 | Level 3a: images are signed using TUF and in-toto. Bundles are signed using TUF. 78 | * Pros: resilient against compromise of CI for images. 79 | * Cons: adds work to developers and administrators. Not resilient against compromise of CI for bundles. 80 | 81 | Level 3b: bundles are signed using TUF and in-toto. Images are signed using TUF. 82 | * Pros: resilient against compromise of CI for bundles. 83 | * Cons: adds work to developers and administrators. Not resilient against compromise of CI for images. 84 | 85 | Level 4: both bundles and images are signed using TUF and in-toto. 86 | * Pros: resilient against compromise of CI for bundles and images. 87 | * Cons: greatest amount of work in implementation and adoption. 88 | 89 | [tuf-spec]: https://github.com/theupdateframework/specification 90 | [in-toto-spec]: https://github.com/in-toto/docs 91 | [ite-4]: https://github.com/in-toto/ITE/pull/4 92 | [datadog-agent-integrations]: https://www.datadoghq.com/blog/engineering/secure-publication-of-datadog-agent-integrations-with-tuf-and-in-toto/ 93 | [ite-5]: https://github.com/in-toto/ITE/pull/5 94 | [pep-458]: https://www.python.org/dev/peps/pep-0458/ 95 | [pep-480]: https://www.python.org/dev/peps/pep-0480/ 96 | -------------------------------------------------------------------------------- /806-security-known-implementations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Known implementations for CNAB Security 3 | weight: 806 4 | --- 5 | 6 | The security implementation prescribes the structure and format that trust metadata SHOULD follow, but _not_ which specific implementations should be used. This document is non-normative, and presents known implementations of the CNAB security specification. As more projects implement the specification, they will be added here. 7 | 8 | ### [Signy][signy] 9 | 10 | [Signy][signy] is a project built to validate the feasability of integrating the [CNAB registry][cnab-reg] and CNAB security specifications by demonstrating the complete sign-push-verify workflow. 11 | 12 | The current document is based on [v0.0.3][signy-release] of Signy, which: 13 | 14 | - uses the [Notary][notary] client libraries, and communicates with a Notary server. 15 | - uses an [in-toto Go client][in-toto-golang] to perform structural verification for the in-toto layouts. 16 | - performs all in-toto verifications in a container based on a _verification image_ - this ensures the desired dependencies and their versions are available for all clients running the in-toto verifications, regardless of the operating system they are running. See [this project][in-toto-container] for an example of how verification images are used. 17 | - for thin CNAB bundles, signing will push the bundle to a compliant OCI registry, and verifying will pull the bundle from an OCI registry (the push and pull operations are performed using [`cnabio/cnab-to-oci`][cnab-to-oci]). 18 | - for thick CNAB bundles, Signy will pull the trust metadata from Notary, and will compare it against the content digest of a local file. 19 | 20 | > [Notary][notary] is an opinionated implementation of the TUF specification written in Go, which has wide adoption in container registries, through the Docker Content Trust model. 21 | 22 | > Because Signy uses Notary, its implementation is very closely related to the Docker Content Trust model. For an architecture of a service running Notary, [see this document][notary-architecture]. 23 | 24 | > Note that, at the time of writing (June 4th 2020), while Signy handles copying images from one registry to another, it does _not_ copy the associated Notary v1 signed metadata and private keys from the former to the latter, largely due to the limitations of Notary v1. However, users are free to use Signy to attach their own signatures on their own registry and [Notary services](https://docs.docker.com/notary/service_architecture/). 25 | 26 | #### Using Signy to perform TUF verifications 27 | 28 | - signing and pushing a bundle: 29 | 30 | ``` 31 | $ signy --tlscacert=$NOTARY_CA --server https://localhost:4443 sign testdata/cnab/bundle.json localhost:5000/thin-bundle:v1 32 | INFO[0000] Pushed trust data for localhost:5000/thin-bundle:v1: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 33 | INFO[0000] Starting to copy image cnab/helloworld:0.1.1 34 | INFO[0002] Completed image cnab/helloworld:0.1.1 copy 35 | INFO[0002] Generated relocation map: relocation.ImageRelocationMap{"cnab/helloworld:0.1.1":"localhost:5000/thin-bundle@sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6"} 36 | INFO[0002] Pushed successfully, with digest "sha256:b4936e42304c184bafc9b06dde9ea1f979129e09a021a8f40abc07f736de9268" 37 | ``` 38 | 39 | - pulling from an OCI registry and verifying the signature of a bundle: 40 | 41 | ``` 42 | $ signy --tlscacert=$NOTARY_CA --server https://localhost:4443 verify localhost:5000/thin-bundle:v1 43 | INFO[0000] Pulled trust data for localhost:5000/thin-bundle:v1, with role targets - SHA256: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 44 | INFO[0000] Pulling bundle from registry: localhost:5000/thin-bundle:v1 45 | INFO[0000] Computed SHA: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 46 | INFO[0000] The SHA sums are equal: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 47 | ``` 48 | 49 | #### Using Signy to also perform in-toto verifications 50 | 51 | - when signing a bundle, if in-toto metadata is available, it can be distributed according to the [CNAB Security specification][cnab-sec]: 52 | 53 | ``` 54 | $ signy --tlscacert=$NOTARY_CA --server https://localhost:4443 sign testdata/cnab/bundle.json localhost:5000/thin-intoto:v2 --in-toto --layout testdata/intoto/demo.layout.template --links testdata/intoto --layout-key testdata/intoto/alice.pub 55 | INFO[0000] Adding In-Toto layout and links metadata to TUF 56 | INFO[0000] Pushed trust data for localhost:5000/thin-intoto:v2: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 57 | INFO[0000] Starting to copy image cnab/helloworld:0.1.1 58 | INFO[0001] Completed image cnab/helloworld:0.1.1 copy 59 | INFO[0001] Generated relocation map: relocation.ImageRelocationMap{"cnab/helloworld:0.1.1":"localhost:5000/thin-intoto@sha256:a59a4e74d9cc89e4e75dfb2cc7ea5c108e4236ba6231b53081a9e2506d1197b6"} 60 | INFO[0001] Pushed successfully, with digest "sha256:b4936e42304c184bafc9b06dde9ea1f979129e09a021a8f40abc07f736de9268" 61 | ``` 62 | 63 | - if the TUF metadata associated with a bundle also contains in-toto metadata, Signy will validate all layouts and links, and perform the verifications inside a verification image: 64 | 65 | ``` 66 | $ signy --tlscacert=$NOTARY_CA --server https://localhost:4443 verify localhost:5000/thin-intoto:v2 --in-toto --target testdata/intoto/foo.tar.gz 67 | INFO[0000] Pulled trust data for localhost:5000/thin-intoto:v2, with role targets - SHA256: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 68 | INFO[0000] Pulling bundle from registry: localhost:5000/thin-intoto:v2 69 | INFO[0000] Computed SHA: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 70 | INFO[0000] The SHA sums are equal: c7e92bd51f059d60b15ad456edf194648997d739f60799b37e08edafd88a81b5 71 | INFO[0000] Writing In-Toto metadata files into /tmp/intoto-verification169227773 72 | INFO[0000] copying file /in-toto/layout.template in container for verification... 73 | INFO[0000] copying file /in-toto/key.pub in container for verification... 74 | INFO[0000] copying file in-toto/package.2f89b927.link in container for verification... 75 | INFO[0000] copying file in-toto/write-code.776a00e2.link in container for verification... 76 | INFO[0000] copying file in-toto/foo.tar.gz in container for verification... 77 | INFO[0000] Loading layout... 78 | INFO[0000] Loading layout key(s)... 79 | INFO[0000] Verifying layout signatures... 80 | INFO[0001] Verifying layout expiration... 81 | INFO[0001] Reading link metadata files... 82 | INFO[0001] Verifying link metadata signatures... 83 | INFO[0001] Verifying sublayouts... 84 | INFO[0001] Verifying alignment of reported commands... 85 | INFO[0001] Verifying command alignment for 'write-code.776a00e2.link'... 86 | INFO[0001] Verifying command alignment for 'package.2f89b927.link'... 87 | INFO[0001] Verifying threshold constraints... 88 | INFO[0001] Skipping threshold verification for step 'write-code' with threshold '1'... 89 | INFO[0001] Skipping threshold verification for step 'package' with threshold '1'... 90 | INFO[0001] Verifying Step rules... 91 | INFO[0001] Verifying material rules for 'write-code'... 92 | INFO[0001] Verifying product rules for 'write-code'... 93 | INFO[0001] Verifying 'ALLOW foo.py'... 94 | INFO[0001] Verifying material rules for 'package'... 95 | INFO[0001] Verifying 'MATCH foo.py WITH PRODUCTS FROM write-code'... 96 | INFO[0001] Verifying 'DISALLOW *'... 97 | INFO[0001] Verifying product rules for 'package'... 98 | INFO[0001] Verifying 'ALLOW foo.tar.gz'... 99 | INFO[0001] Verifying 'ALLOW foo.py'... 100 | INFO[0001] Executing Inspection commands... 101 | INFO[0001] Executing command for inspection 'untar'... 102 | INFO[0001] Running 'untar'... 103 | INFO[0001] Recording materials '.'... 104 | INFO[0001] Running command 'tar xfz foo.tar.gz'... 105 | INFO[0001] Recording products '.'... 106 | INFO[0001] Creating link metadata... 107 | INFO[0001] Verifying Inspection rules... 108 | INFO[0001] Verifying material rules for 'untar'... 109 | INFO[0001] Verifying 'MATCH foo.tar.gz WITH PRODUCTS FROM package'... 110 | INFO[0001] Verifying 'DISALLOW foo.tar.gz'... 111 | INFO[0001] Verifying product rules for 'untar'... 112 | INFO[0001] Verifying 'MATCH foo.py WITH PRODUCTS FROM write-code'... 113 | INFO[0001] Verifying 'DISALLOW foo.py'... 114 | INFO[0001] The software product passed all verification. 115 | ``` 116 | 117 | [signy]: https://github.com/cnabio/signy 118 | [signy-release]: https://github.com/cnabio/signy/releases/tag/0.0.3 119 | [cnab-reg]: ./200-CNAB-registries.md 120 | [cnab-sec]: /300-CNAB-security.md 121 | [notary]: https://github.com/theupdateframework/notary 122 | [in-toto-golang]: https://github.com/in-toto/in-toto-golang/ 123 | [in-toto-container]: https://github.com/engineerd/in-toto-container 124 | [notary-architecture]: https://github.com/theupdateframework/notary/blob/master/docs/service_architecture.md 125 | [cnab-to-oci]: https://github.com/cnabio/cnab-to-oci 126 | -------------------------------------------------------------------------------- /100-CNAB.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CNAB Core 3 | weight: 100 4 | --- 5 | 6 | # Cloud Native Application Bundle Core 1.2.0 (CNAB1) 7 | *[Final Approval, Published](901-process.md), May 2021* 8 | 9 | 10 | The Cloud Native Application Bundle (CNAB) is a _standard packaging format_ for multi-component distributed applications. It allows packages to target different runtimes and architectures. It empowers application distributors to package applications for deployment on a wide variety of cloud platforms, providers, and services. Furthermore, it provides necessary capabilities for delivering multi-container applications in disconnected (airgapped) environments. 11 | 12 | CNAB is not a platform-specific tool. While it uses *containers* for encapsulating installation logic, it remains un-opinionated about what cloud environment it runs in. CNAB developers can bundle applications targeting environments spanning IaaS (like OpenStack or Azure), container orchestrators (like Kubernetes or Nomad), container runtimes (like local Docker or ACI), and cloud platform services (like object storage or Database as a Service). 13 | 14 | CNAB can also be used for packaging other distributed applications, such as IoT or edge computing. 15 | 16 | ## Summary 17 | 18 | The CNAB format is a packaging format for a broad range of distributed applications. It specifies a pairing of a _bundle definition_ [(`bundle.json`)](101-bundle-json.md) to define the app, and an _invocation image_ to install the app. 19 | 20 | The _bundle definition_ is a single file that contains the following information: 21 | 22 | - Information about the bundle, such as name, bundle version, description, and keywords 23 | - Information about locating and running the _invocation image_ (the installer program) 24 | - A list of user-overridable parameters that this package recognizes 25 | - The list of executable images that this bundle will install 26 | - A list of credential paths or environment variables that this bundle requires to execute 27 | 28 | The canonical encoding of a bundle definition is a JSON-formatted file, which MUST be encoded as a [Canonical JSON](http://wiki.laptop.org/go/Canonical_JSON) object stored in a `bundle.json` file, as defined in [the bundle file definition](101-bundle-json.md). 29 | 30 | However, as a signed bundle definition represents an immutable bundle, all invocation images and images references must have a content digest. 31 | 32 | The bundle definition is mounted as a file within the image's runtime filesystem. Additionally, it can be stored on its own, or as part of a _packaged archive_, which is a CNAB bundle that includes the JSON file and exported images (including the [invocation image](102-invocation-image.md)). 33 | 34 | - A _thin bundle_ consists of just a bundle definition. 35 | - A _thick bundle_ consists of a packaged archive that contains both the bundle definition and an encoded representation of all of the invocation images and images. 36 | 37 | In either case, CNAB has the same schema, and this spec refers to this file as the "bundle definition" (or occasionally "bundle file"). 38 | 39 | When thin bundles are processed, the referenced content (such as invocation images and other images) are retrieved from their respective storage repositories and registries. A bundle is considered to be _well formed_ if its definition follows the CNAB schema and the images are in the correct formats. A bundle is considered _complete_ if it is packaged as a thick bundle, and all the components are present OR if it is packaged as a thin bundle and all of the references are resolvable. Completeness is thus in some cases contingent upon external factors such as network access. 40 | 41 | Bundles use cryptographic verification on multiple levels. Images (Docker, OCI, VM) are digested, and their cryptographic digest is then embedded into the `bundle.json`. The `bundle.json` is then signed using a public/private key system to ensure that it has not been tampered with. A bundle is considered _secure_ if the bundle definition contains the correct content digests for all images, and the bundle definition is cryptographically signed. 42 | 43 | Finally, this document describes a format for invocation images, including file system layout and a functional description of how an invocation image is installed. 44 | 45 | ## Approach 46 | 47 | The current distributed computing landscape involves a combination of executable units and supporting API-based services. Executable units include Virtual Machines (VMs), Containers (e.g. Docker and OCI) and Functions-as-a-Service (FaaS), as well as higher-level PaaS services. Along with these executable units, many managed cloud services (from load balancers to databases) are provisioned and interconnected via REST (and similar network-accessible) APIs. Our goal is to provide a packaging format that can enable application providers and developers with a way of installing a multi-component application into a distributed computing environment, supporting all of the above types. 48 | 49 | A bundle is comprised of a bundle definition and at least one _invocation image_. The invocation image's job is to install zero or more components into the host environment. Such components MAY include (but are not limited to) containers, functions, VMs, IaaS and PaaS layers, and service frameworks. 50 | 51 | At run time, the invocation image contains a standardized filesystem layout where metadata, installation data, and the bundle definition are stored in predictable places. A _run tool_ is the executable entry point into a CNAB bundle. Parameterization and credentialing allow injection of configuration data into the invocation image. The invocation image is described in detail in [the invocation image definition](102-invocation-image.md). 52 | 53 | _Actions_ are sent to the `run` command via environment variables. Actions determine whether a bundle is to be installed, upgraded, downgraded, or uninstalled. 54 | 55 | Invocation images allow limited configuration, as defined in two places in the bundle definition: 56 | 57 | - A bundle definition MAY declare zero or more configurable parameters. User-supplied parameters are injected into the invocation image. Parameters MAY be stored. 58 | - A bundle definition MAY declare zero or more credential requirements. This indicates which credentials MUST be passed into the invocation image in order for the invocation image to correctly authenticate to the services used by the bundle. Credentials are injected into the invocation image, but they MUST NOT be stored. 59 | 60 | ### Key Terms 61 | 62 | - Application: The functional unit composed by the components described in a bundle. This MAY be comprised of a mixture of containers, VMs, IaaS and PaaS definitions, and other services, as well as instructions for orchestrators and service frameworks. 63 | - Bundle: the collection of CNAB data and metadata necessary for installing an application on the designated cloud services. 64 | - Bundle definition: The information about a bundle, its parameters, credentials, images, and usage 65 | - `bundle.json`: The unsigned JSON-encoded representation of a bundle definition. 66 | - Image: Used generically, a container image (e.g. OCI images) or a VM image. 67 | - Invocation Image: The image that contains the bootstrapping and installation logic for the bundle 68 | - Registry: A storage and retrieval service for CNAB objects. 69 | 70 | Also, when referencing tooling, the following terms are used: 71 | 72 | - `CNAB runtime` or `runtime`: A program capable of reading a CNAB bundle and executing it 73 | - `CNAB builder` or `builder`: A program that can assemble a CNAB bundle 74 | - `bundle tooling`: Programs or tooling that generate CNAB bundle contents 75 | 76 | Individual tools may meet more than one of the definitions above, they have been separated in order to offer guidance such as: 77 | 78 | > A runtime MUST support the 'install', 'upgrade', and 'uninstall' actions, while bundle tooling MAY choose not to implement 'upgrade'. 79 | 80 | ### The Definitions 81 | 82 | The following subsections define the components of CNAB: 83 | 84 | - [The bundle.json File](101-bundle-json.md) 85 | - [The Invocation Image Format](102-invocation-image.md) 86 | - [The Bundle Runtime](103-bundle-runtime.md) 87 | 88 | The process for standardization is described in an appendix: 89 | 90 | - [Specification Process](901-process.md) 91 | 92 | ## History 93 | 94 | - The `bundle.cnab` is now the name of a signed `bundle.json`. 95 | - The `bundle.json` is now a stand-alone artifact, not part of the invocation image. 96 | - The initial draft of the spec included a `manifest.json`, a `ui.json` and a `parameters.json`. The `bundle.json` is now the only metadata file, containing what was formerly spread across those three. 97 | - The top-level `/cnab` directory was added to the bundle format due to conflicts with file hierarchy. 98 | - The signal handling method was discarded after early research showed its limitations. The replacement uses environment variables to trigger actions. 99 | - The `bundle.json` is now mounted in the invocation image at `/cnab/bundle.json`. 100 | - The generic action `run` has been replaced by specific actions: `install`, `uninstall`, `upgrade`. 101 | - The `status` action has been removed. 102 | - Registries, security, and claims have all be moved to separate specifications. 103 | 104 | Next section: [The bundle.json definition](101-bundle-json.md) 105 | -------------------------------------------------------------------------------- /901-process.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Standardization Process 3 | weight: 901 4 | --- 5 | 6 | # Appendix A: The Process of Developing the Specification 7 | 8 | This process governs the motion of a specification from rough draft through full standardization. A document is termed a _deliverable_ as it passes through various phases from _pre-draft deliverable_ to _accepted deliverable_, at which point it becomes a final specification. 9 | 10 | ## Versioning 11 | 12 | Specifications shall each track their own versions. A specification will call out a target version (such as 1.0), and then also include its phase (such as Pre-Draft, see below) to indicate progress towards the target version. 13 | 14 | For example, one of the specifications in this repository is the CNAB Core specification. The CNAB Core specification should be formally referenced as _Cloud Native Application Bundle Core 1.0.0_. The phase of the process MAY be appended as a stability marker: _Cloud Native Application Bundle Core 1.0.0-FA_. It MAY be abbreviated to _CNAB1_ or _CNAB1-FA_. 15 | 16 | Other specifications must follow the same nomenclature. 17 | 18 | Inspired by SemVer 2, CNAB follows a rigid versioning scheme. Versions are presented in the form `X.Y.Z[-S]`, where `X` is the major version, `Y` is the minor version, and `Z` is the patch version. The optional `-S` is a draft stability marker. 19 | 20 | - Major releases (`X`): Major releases contain breaking changes, including features, fixes, and reorganizations. Implementors should not assume that two major versions are compatible. For example, `1.9.9` is not to be considered compatible with `2.0.0`. 21 | - Minor releases (`Y`): Minor releases contain features and fixes only. A feature MUST NOT remove or modify existing pieces of the spec (including schemata and file system layouts), but MAY add new things. A minor release SHOULD be backward compatible, though certain security concerns may override this requirement. 22 | - Proposed changes to the spec SHOULD be rejected if accepting them will lead to changes in the interpretation of a bundle. (e.g. repurposing an existing field in the `bundle.json` is not allowed by these compatibility rules) 23 | - A runtime SHOULD be able to run bundles with the same major version and different minor versions. 24 | - A runtime MAY behave differently when processing a package of a newer minor release version, provided that difference is due to new features of the specification. 25 | - Bundle authors SHOULD strive to make bundles compatible with all minor versions of the same major version, though they MAY take advantage of additive features. 26 | - Patch releases (`Z`): Patch release contain fixes to the text of the specification. Patch releases MUST NOT change the behavior of the specification (except in cases where the specification was too vague and the patch clarifies). 27 | - Patch releases MUST be both forward and backward compatible to the minor version number 28 | - Patch releases MUST NOT make the schema harder to validate against (though they may relax the schema). 29 | - Our intention is that CNAB Runtimes SHOULD NOT have to specify behavior at the patch level, as all patch levels are compatible. 30 | 31 | Stability markers provide a way to indicate that a bundle or runtime is experimenting with features or fixes. _If an object is tagged with a stability marker, it MUST be treated as incompatible with any other version number. E.g. `1.0.0-GA` MUST be considered incompatible with `1.0.0`. Production artifacts SHOULD NOT use stability markers. 32 | 33 | A small number of stability markers are allowed, as determined by [the foundation's governance documents](https://github.com/cnabio/community/blob/main/governance.md): 34 | 35 | - `PD`: Pre-draft indicates that the version of the resource is an unstable testing version. 36 | - `DRAFT`: Draft indicates that the version is an unstable in-development version 37 | - `GA`: Working Group Approval indicates that this version has been accepted by the maintainers, but not accepted by the Executive Directors. In practice, this marker MUST only be used internally for testing, and never for production. 38 | - `AD`: Final approval by the Executive Directors. 39 | 40 | The tags `GA` and `AD` should never be used in a SemVer stability marker. `GA` is not a milestone that warrants publication, and `AD` is synonymous with the final release. 41 | 42 | The development process defines `Publication` and `Submission to Other Standards Bodies` as process steps. However, neither of these reflects a change in the specification. These are people processes. 43 | 44 | The stability markers `ALPHA`, `BETA`, and so on are _disallowed_ under this specification, and MUST NOT be used to express CNAB versions. 45 | 46 | Finally, certain small errata may be fixed on an existing release without incrementing the release version. The following changes are allowed as errata fixes: 47 | 48 | - Correcting spelling or typographical errors, where changing these does not alter the meaning of the specification. 49 | - Correcting minor grammatical mistakes. 50 | - Adding a revised link when a broken link appears. This should be done by appending the text `(Updated link: http://example.com...)`. The text may be corrected fully during the next version change. 51 | - In extenuating circumstances, the Executive Directors may approve retroactively editing text to meet legal requirements. In such cases, the directors will not approve changes that break the specification. Under such circumstances, the directors may issue a _retraction of a specification_ (removing a published specification), and publish a new specification version that meets the legal requirements. For example, an intellectual property infringement may only be correctable by a retraction. 52 | 53 | ## Git Release Flow 54 | 55 | This section deals with the practical considerations of versioning in Git, this repo's version control system. 56 | 57 | ### Patch releases 58 | 59 | When a patch release of a specification is required, the working group must approve the scope of commits proposed for inclusion. The patch commit(s) should be merged to the `main` branch when ready. Next, a new branch should be created for the designated patch. For example, if the previous most recent branch name of the specification is `cnab-core-1.0.0-ga`, the new branch would be created from `cnab-core-1.0.0-ga` and named `cnab-core-1.0.1-ga`. The patch commit(s) should then be cherry-picked into this new branch. 60 | 61 | When the final release is approved, a Git tag should also be pushed, which triggers schema artifact publishing. Extending the example above, a `cnab-core-1.0.1` tag should be created from the `cnab-core-1.0.1-ga` branch and pushed to origin. We drop the `-ga` suffix as branches and tags may not have the same name in Git. 62 | 63 | ### Minor releases 64 | 65 | When a minor release of a specification is required, the working group must approve the scope of commits proposed for inclusion. Likely this will be the `main` branch once the approved commit(s) are merged. Next, a new branch should be created from `main` and named `cnab-core-1.1.0-ga` (here assuming that the version immediately prior was `cnab-core-1.0.0`). 66 | 67 | When the final release is approved, a Git tag should also be pushed, which triggers schema artifact publishing. Extending the example above, a `cnab-core-1.1.0` tag should be created from the `cnab-core-1.1.0-ga` branch and pushed to origin. We drop the `-ga` suffix as branches and tags may not have the same name in Git. 68 | 69 | ### Major releases 70 | 71 | When a major release of a specification is required, the working group must approve the scope of commits proposed for inclusion. Likely this will be the `main` branch once the approved commit(s) are merged. Next, a new branch should be created from `main` and named `cnab-core-2.0.0-ga` (here assuming that the version immediately prior was `cnab-core-1.0.0`). 72 | 73 | When the final release is approved, a Git tag should also be pushed, which triggers schema artifact publishing. Extending the example above, a `cnab-core-2.0.0` tag should be created from the `cnab-core-2.0.0-ga` branch and pushed to origin. We drop the `-ga` suffix as branches and tags may not have the same name in Git. 74 | 75 | **Note:** For all of the patch, minor and major release protocols above, the convention is to also add the resulting tag to the `Branch/Tag Structure` section of this repo's [README](README.md#branchtag-structure). 76 | 77 | ### Ad hoc schema releases 78 | 79 | In addition to the scenarios above, if schemas at a certain commit need to be preserved in the form of artifacts and published, ad hoc versioning (i.e. not tied to a release branch) is permitted via the following Git tag flow. This is intended for specifications in `DRAFT` state which are perhaps under heavy development. 80 | 81 | To enable implementations to pin at a certain version prior to an official release, we can issue a Git tag and CI will handle publishing the schemas. For example, if the Claims specification is still in the `DRAFT` state but its schemas at a particular commit are needed for implementation verification, we can push an appropriate tag to origin. The tag form is: `cnab-claim-1.0.0-DRAFT+abc1234`, where `cnab-claim-1.0.0-DRAFT` is the current working version and `abc1234` is the short SHA of the commit the tag will be created from. 82 | 83 | ### Non-normative exclusions 84 | 85 | For non-normative documents (any in the `8xx` section), no formal release processes apply. As long as the associated working group approves of additions or modifications to documents in this section (say, via a pull request), no further action or process is needed. 86 | 87 | ## Development Process 88 | 89 | The specification will proceed through the following phases: 90 | 91 | - *Pre-Draft (PD):* Any working group participant or contributor may submit a proposed initial draft document as a candidate for this working group. The Working Group Chair may designate such a submission as a Pre-Draft Document. 92 | - *Draft (Draft):* A Pre-Draft may be approved by the working group, in which case it becomes an official draft under the auspices of the CNAB specification working group. The working group will continue to revise the draft until it is in a state the group sees as fit for standardization. 93 | - *Working Group Approval (GA):* When the working group believes a draft fit for standardization, the group formally approves the draft and submits it for final approval. 94 | - *Final Approval (AD):* The Executive Director (or a named proxy) may grant Final Approval to a draft with Working Group Approval. At this point, the work is now designated an Approved Deliverable and is no longer a draft. 95 | - *Publication:* When Final Approval is granted, the Approved Deliverable is made publicly available under the terms of the Open Web Foundation 1.0 license. 96 | - *Submission to Other Standards Bodies:* With the approval of the Working Group, the Executive Director may submit an Approved Deliverable to another standards body in collaboration with the JDF. 97 | 98 | Documents that have reached AD are considered complete. Errata may be captured in a separate section of the document, but the document itself is not changed except to correct typographical and formatting errors where necessary. 99 | 100 | When the content of a document needs changes that cannot be captured as errata, a new _version_ of the specification must be created, and must proceed through the stages outlined above. 101 | 102 | ## Changes 103 | 104 | An earlier "provisional" process was outlined here, based on W3's model. That provisional process has now been replaced with the process described herein. 105 | 106 | Anything previously marked "Working Draft" is now considered to be a Draft, as they have been accepted for work by the working group. 107 | 108 | The [CNAB Foundation's governance documents](https://github.com/cnabio/community/blob/main/governance.md) cover the acceptance process in more detail. -------------------------------------------------------------------------------- /schema/bundle.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://cnab.io/v1/bundle.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "additionalProperties": false, 5 | "definitions": { 6 | "credential": { 7 | "description": "Credential defines a particular credential, and where it should be placed in the invocation image", 8 | "properties": { 9 | "description": { 10 | "description": "A user-friendly description of this credential", 11 | "type": "string" 12 | }, 13 | "applyTo": { 14 | "description": "An optional exhaustive list of actions handling this credential", 15 | "items": { 16 | "type": "string" 17 | }, 18 | "type": "array" 19 | }, 20 | "env": { 21 | "description": "The environment variable name, such as MY_VALUE, into which the credential will be placed", 22 | "type": "string" 23 | }, 24 | "path": { 25 | "description": "The path inside of the invocation image where credentials will be mounted", 26 | "type": "string" 27 | }, 28 | "required": { 29 | "default": false, 30 | "description": "Indicates whether this credential must be supplied. By default, credentials are optional.", 31 | "type": "boolean" 32 | } 33 | }, 34 | "type": "object" 35 | }, 36 | "image": { 37 | "description": "An application image for this CNAB bundle", 38 | "properties": { 39 | "contentDigest": { 40 | "description": "A cryptographic hash digest of the contents of the image that can be used to validate the image. This may be interpreted differently based on imageType", 41 | "type": "string" 42 | }, 43 | "description": { 44 | "description": "A description of the purpose of this image", 45 | "type": "string" 46 | }, 47 | "image": { 48 | "description": "A resolvable reference to the image. This may be interpreted differently based on imageType, but the default is to treat this as an OCI image", 49 | "type": "string" 50 | }, 51 | "imageType": { 52 | "default": "oci", 53 | "description": "The type of image. If this is not specified, 'oci' is assumed", 54 | "type": "string" 55 | }, 56 | "labels": { 57 | "description": "Key/value pairs that used to specify identifying attributes of images", 58 | "type": "object", 59 | "additionalProperties": { 60 | "type": "string" 61 | } 62 | }, 63 | "mediaType": { 64 | "description": "The media type of the image", 65 | "type": "string" 66 | }, 67 | "size": { 68 | "description": "The image size in bytes", 69 | "type": "integer" 70 | } 71 | }, 72 | "required": [ 73 | "image" 74 | ], 75 | "type": "object" 76 | }, 77 | "invocationImage": { 78 | "description": "A bootstrapping image for the CNAB bundle.", 79 | "properties": { 80 | "contentDigest": { 81 | "description": "A cryptographic hash digest of the contents of the image that can be used to validate the image. This may be interpreted differently based on imageType", 82 | "type": "string" 83 | }, 84 | "image": { 85 | "description": "A resolvable reference to the image. This may be interpreted differently based on imageType, but the default is to treat this as an OCI image", 86 | "type": "string" 87 | }, 88 | "imageType": { 89 | "default": "oci", 90 | "description": "The type of image. If this is not specified, 'oci' is assumed", 91 | "type": "string" 92 | }, 93 | "labels": { 94 | "description": "Key/value pairs that used to specify identifying attributes of invocation images", 95 | "type": "object", 96 | "additionalProperties": { 97 | "type": "string" 98 | } 99 | }, 100 | "mediaType": { 101 | "description": "The media type of the image", 102 | "type": "string" 103 | }, 104 | "size": { 105 | "description": "The image size in bytes", 106 | "type": "integer" 107 | } 108 | }, 109 | "required": [ 110 | "image" 111 | ], 112 | "type": "object" 113 | }, 114 | "output": { 115 | "description": "A value that is produced by running an invocation image", 116 | "properties": { 117 | "applyTo": { 118 | "description": "An optional exhaustive list of actions producing this output", 119 | "items": { 120 | "type": "string" 121 | }, 122 | "type": "array" 123 | }, 124 | "definition": { 125 | "description": "The name of a definition that describes the schema structure of this output", 126 | "type": "string" 127 | }, 128 | "description": { 129 | "$ref": "http://json-schema.org/draft-07/schema#/properties/description" 130 | }, 131 | "path": { 132 | "description": "The path inside of the invocation image where output will be written", 133 | "pattern": "^/cnab/app/outputs/.+$", 134 | "type": "string" 135 | } 136 | }, 137 | "required": [ 138 | "definition", 139 | "path" 140 | ], 141 | "type": "object" 142 | }, 143 | "parameter": { 144 | "description": "A parameter that can be passed into the invocation image", 145 | "properties": { 146 | "applyTo": { 147 | "description": "An optional exhaustive list of actions handling this parameter", 148 | "items": { 149 | "type": "string" 150 | }, 151 | "type": "array" 152 | }, 153 | "definition": { 154 | "description": "The name of a definition that describes the schema structure of this parameter", 155 | "type": "string" 156 | }, 157 | "description": { 158 | "$ref": "http://json-schema.org/draft-07/schema#/properties/description" 159 | }, 160 | "destination": { 161 | "properties": { 162 | "env": { 163 | "description": "The environment variable name, such as MY_VALUE, in which the parameter value is stored", 164 | "type": "string" 165 | }, 166 | "path": { 167 | "description": "The path inside of the invocation image where parameter data is mounted", 168 | "type": "string" 169 | } 170 | }, 171 | "type": "object" 172 | }, 173 | "required": { 174 | "default": false, 175 | "description": "Indicates whether this parameter must be supplied. By default, parameters are optional.", 176 | "type": "boolean" 177 | } 178 | }, 179 | "required": [ 180 | "definition", 181 | "destination" 182 | ], 183 | "type": "object" 184 | } 185 | }, 186 | "description": "Cloud Native Application - Core version 1 - Bundle Descriptor", 187 | "properties": { 188 | "actions": { 189 | "additionalProperties": { 190 | "properties": { 191 | "description": { 192 | "description": "A description of the purpose of this action", 193 | "type": "string" 194 | }, 195 | "title": { 196 | "description": "A human-readable name for this action", 197 | "type": "string" 198 | }, 199 | "modifies": { 200 | "description": "Must be set to true if the action can change any resource managed by this bundle", 201 | "type": "boolean" 202 | }, 203 | "stateless": { 204 | "default": "false", 205 | "description": "Indicates that the action is purely informational, that credentials are not required, and that the runtime should not keep track of its invocation", 206 | "type": "boolean" 207 | } 208 | }, 209 | "type": "object" 210 | }, 211 | "description": "Custom actions that can be triggered on this bundle, action name should be namespaced and use reverse DNS notation", 212 | "type": "object" 213 | }, 214 | "credentials": { 215 | "additionalProperties": { 216 | "$ref": "#/definitions/credential" 217 | }, 218 | "description": "Credentials to be injected into the invocation image", 219 | "type": "object" 220 | }, 221 | "custom": { 222 | "$comment": "reserved for custom extensions", 223 | "type": "object", 224 | "additionalProperties": true 225 | }, 226 | "definitions": { 227 | "additionalProperties": { 228 | "$ref": "http://json-schema.org/draft-07/schema#" 229 | }, 230 | "type": "object" 231 | }, 232 | "description": { 233 | "description": "A description of this bundle, intended for users", 234 | "type": "string" 235 | }, 236 | "images": { 237 | "additionalProperties": { 238 | "$ref": "#/definitions/image" 239 | }, 240 | "description": "Images that are used by this bundle", 241 | "type": "object" 242 | }, 243 | "invocationImages": { 244 | "description": "The array of invocation image definitions for this bundle", 245 | "items": { 246 | "$ref": "#/definitions/invocationImage" 247 | }, 248 | "type": "array" 249 | }, 250 | "keywords": { 251 | "description": "A list of keywords describing the bundle, intended for users", 252 | "items": { 253 | "type": "string" 254 | }, 255 | "type": "array" 256 | }, 257 | "license": { 258 | "description": "The SPDX license code or proprietary license name for this bundle", 259 | "type": "string" 260 | }, 261 | "maintainers": { 262 | "description": "A list of parties responsible for this bundle, with contact info", 263 | "items": { 264 | "description": "An object that describes a maintainer", 265 | "properties": { 266 | "email": { 267 | "description": "Email address of responsible party", 268 | "type": "string" 269 | }, 270 | "name": { 271 | "description": "Name of party reponsible for this bundle", 272 | "type": "string" 273 | }, 274 | "url": { 275 | "description": "URL of the responsible party, perhaps containing additional contact info", 276 | "type": "string" 277 | } 278 | }, 279 | "required": [ 280 | "name" 281 | ], 282 | "type": "object" 283 | }, 284 | "type": "array" 285 | }, 286 | "name": { 287 | "description": "The name of this bundle", 288 | "type": "string" 289 | }, 290 | "outputs": { 291 | "additionalProperties": { 292 | "$ref": "#/definitions/output" 293 | }, 294 | "description": "Values that are produced by executing the invocation image", 295 | "type": "object" 296 | }, 297 | "parameters": { 298 | "additionalProperties": { 299 | "$ref": "#/definitions/parameter" 300 | }, 301 | "description": "Parameters that can be injected into the invocation image", 302 | "type": "object" 303 | }, 304 | "requiredExtensions": { 305 | "description": "A collection of extensions required for this bundle.", 306 | "type": "array", 307 | "additionalProperties": { 308 | "type": "string" 309 | } 310 | }, 311 | "schemaVersion": { 312 | "description": "The version of the CNAB specification. This should always be the string 'v1' for this schema version.", 313 | "type": "string" 314 | }, 315 | "version": { 316 | "description": "A SemVer2 version for this bundle", 317 | "pattern": "v?([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?", 318 | "type": "string" 319 | } 320 | }, 321 | "required": [ 322 | "invocationImages", 323 | "name", 324 | "schemaVersion", 325 | "version" 326 | ], 327 | "title": "CNAB1 Bundle Descriptor", 328 | "type": "object" 329 | } -------------------------------------------------------------------------------- /103-bundle-runtime.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Bundle Runtime 3 | weight: 103 4 | --- 5 | 6 | # The Bundle Runtime 7 | 8 | This section describes how the invocation image is executed, and how data is injected into the image. 9 | 10 | The [Invocation Image definition](102-invocation-image.md) specifies the layout of a CNAB invocation image. This section focuses on how the image is executed, with the goal of managing a cloud application. 11 | 12 | ## The Run Tool (Main Entry Point) 13 | 14 | The main entry point of a CNAB bundle MUST be located at `/cnab/app/run`. When a compliant CNAB runtime executes a bundle, it MUST execute the `/cnab/app/run` tool. In addition, images used as invocation images SHOULD also default to running `/cnab/app/run`. For example, a `Dockerfile`'s `exec` array SHOULD point to this entry point. 15 | 16 | > A fixed location for the `run` tool is mandated because not all image formats provide an equivalent method for starting an application. A client implementation of CNAB MAY access the image and directly execute the path `/cnab/app/run`. It is also permissible, given tooling constraints, to set the default entry point to a different path. 17 | 18 | The run tool MUST observe standard conventions for executing, exiting, and writing output. On POSIX-based systems, these are: 19 | 20 | - The execution mode bit (`x`) MUST be set on the run tool 21 | - Exit codes: Exit code 0 is reserved for the case where the run tool exits with no errors. Non-zero exit codes are considered to be error states. These are interpreted according to [the Open Base Specification](http://pubs.opengroup.org/onlinepubs/9699919799//utilities/V3_chap02.html#tag_18_08_02) 22 | - The special output stream STDERR should be used to write error text 23 | 24 | ### Bundle Definition 25 | 26 | The bundle definition is made accessible from inside the invocation image in order to allow the run tool to reference information in the file. The `bundle.json` MUST be mounted to `/cnab/bundle.json`. 27 | 28 | ### Injecting Data Into the Invocation Image 29 | 30 | CNAB allows injecting data into the invocation image in two ways: 31 | 32 | - Environment Variables: This is the preferred method. In this method, data is encoded as a string and placed into the the environment with an associated name. 33 | - Files: Additional files MAY be injected _at known points_ into the invocation image via credentials or parameters. 34 | 35 | The spec does not define or constrain any network interactions between the invocation image and external services or sources. 36 | 37 | ### Environment Variables 38 | 39 | When executing an invocation image, a CNAB runtime MUST provide the following three environment variables to `/cnab/app/run`: 40 | 41 | ``` 42 | CNAB_INSTALLATION_NAME=my_installation 43 | CNAB_BUNDLE_NAME=helloworld 44 | CNAB_ACTION=install 45 | ``` 46 | 47 | The _installation name_ is the name of the _instance of_ this application. The value of `CNAB_INSTALLATION_NAME` MUST be the installation name. Consider the situation where an application ("wordpress") is installed multiple times into the same cloud. Each installation MUST have a unique installation name, even though they will be installing the same CNAB bundle. Installation names MUST consist of Graph Unicode characters and MAY be user-readable. The Unicode Graphic characters include letters, marks, numbers, punctuation, symbols, and spaces, from categories L, M, N, P, S, Zs. 48 | 49 | The _bundle name_ is the name of the bundle (as represented in `bundle.json`'s `name` field). The specification of this field is in the [bundle definition](101-bundle-json.md). The value of `CNAB_BUNDLE_NAME` MUST be set to the bundle name. 50 | 51 | The _action_ is the action name. It MUST be either one of the built-in actions or one of the actions named in the `actions` portion of the bundle descriptor. `CNAB_ACTION` MUST be set to the action name. 52 | 53 | ### The CNAB Revision Variable 54 | 55 | A `CNAB_REVISION` SHOULD be passed into an install operation, and MUST be passed into `upgrade` and `uninstall`, where this is a _unique string_ indicating the current "version" of the _installation_. For example, if the `my_installation` installation is upgraded twice (changing only the parameters), three `CNAB_REVISIONS` should be generated (1. install, 2. upgrade, 3. upgrade). 56 | 57 | Revisions are regenerated on destructive operations so that one installation may be tracked over various revisions. CNAB Runtimes MUST generate a new `CNAB_REVISION` for every `install`, `upgrade`, or `uninstall` action. That is, if an application is installed once, upgraded twice, and then uninstalled, four revisions must be generated. For additionally defined targets, a new `CNAB_REVISION` MUST be generated if the target is labeled `"modifies": true`, and MUST NOT be generated if `"modifies": false`. 58 | 59 | A `CNAB_REVISION` SHOULD be a [ULID](https://github.com/ulid/spec). 60 | 61 | ### Parameters as Variables 62 | 63 | As specified in the `bundle.json`, some parameters MAY be injected into the environment as environment variables. 64 | 65 | A runtime MAY provide other `CNAB_`-prefixed variables. Parameters and credentials SHOULD NOT provide `CNAB_`-prefixed variables. 66 | 67 | ### Mounting Files 68 | 69 | Credentials and parameters MAY be mounted as files within the image's runtime filesystem. This definition does not specify how files are to be attached to an image. However, it specifies the conditions under which the files appear. 70 | 71 | Files MUST be attached to the invocation image before the image's `/cnab/app/run` tool is executed. Files MUST NOT be attached to the image when the image is built. That is, files MUST NOT be part of the image itself. This would cause a security violation. Files SHOULD be destroyed immediately following the exit of the invocation image, though secure at-rest encryption MAY be a viable alternative. 72 | 73 | ### Executing the Run Tool (CNAB Actions) 74 | 75 | The environment will provide the name of the current installation as `$CNAB_INSTALLATION_NAME` and the name of the action will be passed as `$CNAB_ACTION`. 76 | 77 | Example: 78 | 79 | ```bash 80 | #!/bin/bash 81 | action=$CNAB_ACTION 82 | 83 | if [[ action == "install" ]]; then 84 | helm install example-stable/wordpress -n $CNAB_INSTALLATION_NAME 85 | elif [[ action == "uninstall" ]]; then 86 | helm delete $CNAB_INSTALLATION_NAME 87 | fi 88 | ``` 89 | 90 | This simple example executes Helm, installing the WordPress chart with the default settings if `install` is sent, or deleting the installation if `uninstall` is sent. 91 | 92 | An implementation of a CNAB runtime MUST support sending the following actions to an invocation image: 93 | 94 | - `install` 95 | - `upgrade` 96 | - `uninstall` 97 | 98 | Invocation images SHOULD implement `install` and `uninstall`. If one of these REQUIRED actions is not implemented, an invocation image MUST NOT generate an error (though it MAY generate a warning). Implementations MAY map the same underlying operations to multiple actions (example: `install` and `upgrade` MAY perform the same action). The runtime MUST NOT perform a [bundle version](101-bundle-json.md#name-and-version-identifying-metadata) comparison when executing an action against an existing installation but the invocation image MAY return an error if the version transition is not supported. 99 | 100 | In addition to the default actions, CNAB runtimes MAY support custom actions (as defined in [the bundle definition](101-bundle-json.md)). Any invocation image whose accompanying bundle definition specifies custom actions SHOULD implement those custom actions. A CNAB runtime MAY exit with an error if a custom action is declared in the bundle definition, but cannot be executed by the invocation image. 101 | 102 | A bundle MUST exit with an error if the action is executed, but fails to run to completion. A CNAB runtime MUST issue an error if a bundle issues an error. And an error MUST NOT be issued if one of the three built-in actions is requested, but not present in the bundle. Errors are reserved for cases where something has gone wrong. 103 | 104 | In the event of an an error, the installation state MUST be considered as undefined. A subsequent execution of the same action or another action MAY resolve the installation state (example: a failed `install` action MAY be fixed by executing the `upgrade` action, a failed `upgrade` action MAY be fixed by executing the `upgrade` action again). A subsequent execution of the `uninstall` action SHOULD resolve the installation state. 105 | 106 | ## Setting Parameter Values 107 | 108 | A CNAB `bundle.json` file MAY specify zero or more parameters whose values MAY be specified by a user. 109 | 110 | If the `destination` field contains a key named `env`, values MUST be passed into the container as environment variables, where the value of the `env` field is the name of the environment variable. 111 | 112 | ```json 113 | { 114 | "definitions": { 115 | "greeting": { 116 | "default": "hello", 117 | "type": "string" 118 | } 119 | }, 120 | "parameters": { 121 | "greeting": { 122 | "definition": "greeting", 123 | "description": "this will be in $GREETING", 124 | "destination": { 125 | "env": "GREETING" 126 | } 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | By default (if no override value is provided by the CNAB runtime), the above will set `GREETING=hello`. If the runtime specifies a value `salutations`, then the environment variable would be set to `GREETING=salutations`. 133 | 134 | The parameter value is evaluated thus: 135 | 136 | - If the CNAB runtime provides a value, that value MAY be sanitized, then validated (as described below), then injected as the parameter value. In the event that sanitization or validation fail, the runtime SHOULD return an error and discontinue the action. 137 | - If the parameter is marked `required` and a value is not supplied, the CNAB Runtime MUST produce an error and discontinue action. 138 | - If the CNAB runtime does not provide a value, but `default` is set, then the default value MUST be used. 139 | - If the parameter is marked `required` and `default` is set, then the requirement is satisfied by the runtime-provided default. 140 | - If no value is provided and `default` is unset, the runtime MUST set the value to an empty string (""), regardless of type. 141 | - Non-string values SHOULD be converted to [JSON text](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). For example, boolean `true` (or `True` or `TRUE`) SHOULD be expressed as `true` and the object `{foo: 23}` SHOULD be expressed as `{"foo":23}`. String values SHOULD NOT be converted to JSON text. 142 | - The resolved content of the environment variable SHOULD use UTF-8 character encoding. 143 | 144 | > Setting the value of other types to a default value based on type, e.g. Boolean to `false` or integer to `0`, is considered _incorrect behavior_. Setting the value to `null`, `nil`, or a related character string is also considered incorrect. 145 | 146 | In the case where the `destination` object has a `path` field, the CNAB runtime MUST create a file at that path. The file MUST have sufficient permissions that the effective user ID of the image can read the contents of the file. The file's character encoding SHOULD be UTF-8 and new lines SHOULD be LF (line feed). And the contents of the file MUST be the parameter value (calculated according to the rules above). 147 | 148 | ```json 149 | { 150 | "definitions": { 151 | "greeting": { 152 | "default": "hello", 153 | "type": "string" 154 | } 155 | }, 156 | "parameters": { 157 | "greeting": { 158 | "definition": "greeting", 159 | "description": "this will be in /var/run/greeting.txt", 160 | "destination": { 161 | "path": "/var/run/greeting.txt" 162 | } 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | In the example above, the CNAB runtime creates a file at `/var/run/greeting.txt` whose content (if not overridden) is `hello`. If an empty string is provided as the parameter value, the file must still be created. 169 | 170 | A `path` MUST be absolute. But in the event that a CNAB runtime receives a relative path, it MUST treat the file as if the root path were prepended. Thus `var/run/greeting.txt` is treated (on Linux/UNIX) as `/var/run/greeting.txt`. In the cases where operating system pathing types differ, a CNAB runtime MAY freely translate between absolute pathing structures. `c:\foo.txt`, when passed to a Linux/UNIX system, MAY be translated to `/foo.txt`. In this way, multiple invocation images may share parameters regardless of the underlying OS. 171 | 172 | If `destination` contains both a `path` and an `env`, the CNAB runtime MUST provide both. 173 | 174 | ### Validating Parameters 175 | 176 | The validation of user-supplied values MUST happen outside of the CNAB bundle. Implementations of CNAB bundle tools MUST validate user-supplied parameter values against the named schema in the `definitions` section of a `bundle.json` before injecting them into the image. The outcome of successful validation MUST be the collection containing all parameters where either the user has supplied a value (that has been validated) or the name definition in the `definitions` section of `bundles.json` contains a `default`. 177 | 178 | The resulting calculated values are injected into the bundle before the bundle's `run` is executed (and also in such a way that the `run` has access to these variables.) This works analogously to `CNAB_ACTION` and `CNAB_INSTALLATION_NAME`. 179 | 180 | Resolution of conflicts in environment variable names is discussed in [the Bundle.json Description](101-bundle-json.md). 181 | 182 | ## Credential Files 183 | 184 | Credentials MAY be supplied as files on the file system. In such cases, the following rules obtain: 185 | 186 | - If a file is specified in the `bundle.json` credentials section, but is not present on the file system, the run tool MAY cause a fatal error 187 | - If a file is NOT specified in the `bundle.json`, and is not present, the run tool SHOULD NOT cause an error (though it MAY emit a warning) 188 | - If a file is present, but not correctly formatted, the run tool MAY cause a fatal error 189 | - If a file's permissions or metadata is incorrect, the run tool MAY try to remediate (e.g. run `chmod`), or MAY cause a fatal error 190 | - The run tool MAY modify credential files. Consequently, any runtime implementation MUST ensure that credentials changed inside of the invocation image will not result in modifications to the source. 191 | 192 | ## Image Relocation 193 | 194 | Images referenced by a CNAB bundle MAY be relocated, for example by copying them to a private registry. A _relocation mapping_ is a JSON map 195 | of original image references to relocated image references. The purpose of a relocation mapping is to enable an invocation image to substitute relocated image references for their original values. 196 | 197 | The relocation mapping MUST include in its keys all the image references defined by the CNAB bundle. 198 | 199 | Any image references defined by a CNAB bundle which are semantically equivalent MUST be included as separate entries in the map and MUST map to values which are semantically equivalent to each other. For example, "ubuntu" and "library/ubuntu" are semantically equivalent. On the other hand, image references which differ only by tag and/or digest are not semantically equivalent (even though they _could_ refer to the same image). 200 | 201 | At runtime a relocation mapping MAY be mounted in the invocation image's container as file `/cnab/app/relocation-mapping.json`. If the file is not mounted, this indicates that images have not been relocated. 202 | 203 | For example, if a CNAB bundle with an image `example/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687` and an invocation image `outside/helloworld:0.1.0` is relocated to a private registry `my.registry`, a mapping like the following would be mounted as the file `/cnab/app/relocation-mapping.json`: 204 | 205 | ```json 206 | { 207 | "example/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687": "my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687", 208 | "outside/helloworld:0.1.0": "my.registry/helloworld:0.1.0" 209 | } 210 | ``` 211 | 212 | Source: [103.01-relocation-mapping.json](examples/103.01-relocation-mapping.json) 213 | 214 | The run tool MAY use this file to modify its behavior. For example, a run tool MAY substitute image references using the mapping in this file. 215 | -------------------------------------------------------------------------------- /301-metadata-repositories.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CNAB Security: Metadata repositories 3 | weight: 301 4 | --- 5 | 6 | # Metadata repositories 7 | 8 | - [Metadata repositories](#metadata-repositories) 9 | - [Minimum viable product (MVP)](#minimum-viable-product-mvp) 10 | - [Security analysis of MVP](#security-analysis-of-mvp) 11 | - [Extending the MVP to verify the provenance of bundles](#extending-the-MVP-to-verify-the-provenance-of-bundles) 12 | - [Security analysis of extension](#security-analysis-of-extension) 13 | 14 | This document is a _normative_ part of [CNAB Security](300-CNAB-security.md). 15 | 16 | The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119). The use of these keywords in this document are statements about how a CNAB implementation may fulfill the CNAB Security specification _only_. 17 | 18 | ## Metadata repositories 19 | 20 | A _metadata repository_ is a service that hosts The Update Framework (TUF) and in-toto metadata about bundles. As discussed in [300](300-CNAB-security.md), TUF adds signed metadata that provides authenticity and integrity of bundles between [CNAB registries](200-CNAB-registries.md) and users, whereas in-toto adds signed metadata that provides authenticity and integrity of bundles between developers and CNAB registries. By combining both types of signed metadata, we get end-to-end authenticity and integrity of bundles between developers and users. 21 | 22 | Note that: 23 | 24 | - A metadata repository MAY be physically distinct from a CNAB registry, or not (e.g., bundles as well as TUF and/or in-toto metadata MAY live as [OCI Artifacts](https://stevelasker.blog/2019/08/25/oci-artifacts-and-a-view-of-the-future/) on a CNAB registry). 25 | - Both TUF and in-toto are frameworks that can be configured in a wide variety of ways to achieve varying degrees of security. However, we discuss one [minimum viable product (MVP)](#minimum-viable-product-mvp), which has been optimized in particular for [Docker Content Trust / Notary 0.6.1](https://github.com/theupdateframework/notary/releases/tag/v0.6.1), in this document. 26 | - Although we discuss only the signing and verification of bundles, exactly the same principles apply to images. 27 | - Trust for different metadata repositories with different roots of trust can be established out-of-band using an approach like [TAP 4](https://github.com/theupdateframework/taps/blob/master/tap4.md). For example, a bundle runtime could be shipped with known good copies of TUF `root` metadata for different repositories, or there could even be a meta metadata repository that distributes these `root` metadata. 28 | - How to establish whether a `root` metadata file is trustworthy is outside the scope of this document. Simply assuming a `root` metadata file is trustworthy (e.g., by "clicking through") voids all guarantees (of end-to-end authenticity and integrity of bundles) provided by the corresponding metadata repository. 29 | 30 | ## Minimum viable product (MVP) 31 | 32 | This subsection discusses the simplest way that developers MAY set up a metadata repository for their bundle. Every bundle MAY use a separate metadata repository on the same server, even if two or more bundles are maintained by the same group of developers. (This is similar to how images are signed using [Docker Content Trust](https://docs.docker.com/engine/security/trust/content_trust/). As noted earlier, the MVP has been optimized for [Docker Content Trust / Notary 0.6.1](https://github.com/theupdateframework/notary/releases/tag/v0.6.1). Nevertheless, note that developers MAY use _different_ servers to host metadata repositories for bundles. In [303](303-verification-workflows.md), we discuss how a bundle runtime MAY securely resolve different bundles from different metadata repositories on different servers.) Figure 1 illustrates our simple metadata repository for a bundle. 33 | 34 | ![Figure 1: An MVP metadata repository for a bundle](img/example-tuf-repository.png) 35 | **Figure 1**: An MVP metadata repository for a bundle. 36 | 37 | The metadata repository for a bundle SHOULD provide at least the TUF metadata for the four top-level roles: `root`, `timestamp`, `snapshot`, and `targets`. 38 | 39 | The `root` role distributes, revokes, and replaces the public keys for all four top-level roles. It SHOULD also use a [threshold](300-CNAB-security.md) (m, n) of [offline keys](300-CNAB-security.md), where n is the number of developers, and m is the number of quorum members that must agree on new `root` metadata. Its metadata SHOULD expire yearly, considering that an offline key ceremony is expensive in terms of time and resources. 40 | 41 | The `timestamp` role indicates concisely whether there is any new metadata or bundle on the metadata repository. 42 | 43 | The `snapshot` role prevents attackers from tampering with interdependencies between bundles signed by [delegations](300-CNAB-security.md), if any (none in the MVP). 44 | 45 | Since metadata for both the `timestamp` and `snapshot` roles could be updated whenever a new version of a bundle is produced at any time, they MAY each use a threshold (1, 1) of [online keys](300-CNAB-security.md). In fact, they MAY even share the same online key. The metadata for each role MAY expire daily, since it is expected to be updated relatively frequently (e.g., whenever a developer pushes a new version of a bundle, or a scheduled job runs to renew the metadata). 46 | 47 | The `targets` role lists the file sizes and cryptographic hashes of available versions of the bundle (e.g., `example.com/example-org/example-bundle:*`). Like the `root` role, it SHOULD also use a threshold (m, n) of offline keys, where n is the number of developers, and m is the number of quorum members that must agree on new `targets` metadata. Its metadata SHOULD expire monthly, or however often developers expect to produce new versions of the bundle. 48 | 49 | The following code listing is an example of the `targets` metadata for a bundle: 50 | 51 | ```json 52 | { 53 | "signatures": {...}, 54 | "signed": { 55 | ..., 56 | "delegations": {}, 57 | "targets": { 58 | "example.com/example-org/example-bundle:latest": { 59 | "hashes": { 60 | "sha256": "eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09" 61 | }, 62 | "length": 7206 63 | }, 64 | } 65 | ..., 66 | } 67 | } 68 | ``` 69 | 70 | For operational simplicity, bundles that share the same developers MAY share `root` and `targets` keys across different metadata repositories for these bundles. 71 | 72 | ### Security analysis of MVP 73 | 74 | With regard to key management, we assume that the `root` and `targets` keys are kept offline from the metadata repository, perhaps on private infrastructure controlled by the bundle developers. In contrast, we assume that `timestamp` and `snapshot` keys are kept online on the metadata repository. 75 | 76 | We assume that attackers can: 77 | 78 | 1. Compromise any combination of the aforementioned keys. 79 | 1. Serve new TUF metadata as well as bundles. This can be done either with a man-in-the-middle (MitM) attack, or by compromising the metadata repository and CNAB registry. 80 | 81 | If attackers compromise the metadata repository alone, then they have access to only the `timestamp` and `snapshot` keys. In this case, at worst, they can carry freeze and rollback attacks (limited by the expiration of `root` or `targets` metadata, whichever is earlier). 82 | 83 | However, if attackers also compromise, say, the private infrastructure controlled by the bundle developers, then they also have access to the `targets` key. This is because the `targets` key is very likely to be kept online _within_ this private infrastructure so that developers can use CI/CD to sign new versions of bundles on demand. In this case, at worst, attackers can sign off malicious versions of bundles. Developers can use the `root` key to rotate all other keys. 84 | 85 | Finally, if attackers somehow also compromise the `root` key, which SHOULD be kept securely on, say, a disconnected [hardware security module](https://en.wikipedia.org/wiki/Hardware_security_module) within the aforementioned private infrastructure, then attackers can not only sign off malicious versions of bundles, but also rotate all keys on the metadata repository. Nevertheless, developers can still use the `root` key to rotate all keys, including itself, for end-users who have not yet downloaded malicious versions of bundles. Other end-users will need to reset their systems, and update out-of-band. 86 | 87 | ## Extending the MVP to verify the provenance of bundles 88 | 89 | This subsection discusses how to extend the MVP to verify the provenance of bundles. We do this by transparently including in-toto metadata using TUF as a [compromise-resilient transport protocol](https://www.datadoghq.com/blog/engineering/secure-publication-of-datadog-agent-integrations-with-tuf-and-in-toto/). Figure 2 illustrates our simple metadata repository for a bundle. 90 | 91 | ![Figure 2: Extending the MVP metadata repository for a bundle](img/example-tuf-in-toto-repository.png) 92 | **Figure 2**: Extending the MVP metadata repository for a bundle. 93 | 94 | Instead of signing for new versions of a bundle itself, the `targets` role here SHOULD sign targets metadata about: 95 | 96 | 1. The in-toto [root layout](300-CNAB-security.md) for this collection of bundles (e.g., `example.com/example-org/example-bundle/in-toto-metadata/root.layout`). 97 | 1. All the public keys needed to verify this root layout in the first place (e.g., `example.com/example-org/example-bundle/in-toto-pubkeys/*.pub`). 98 | 99 | The `targets` role SHOULD delegate all versions of the bundle (e.g., `example.com/example-org/example-bundle:*`) as well as associated in-toto link metadata (e.g., `example.com/example-org/example-bundle/in-toto-metadata/*/*.link`) to the `targets/releases` role. Therefore, the `targets/releases` role SHOULD sign targets metadata about: 100 | 101 | 1. All available versions of the bundle (e.g., `example.com/example-org/example-bundle:*`). 102 | 1. All the in-toto [link metadata](300-CNAB-security.md) associated with available bundles (e.g., `example.com/example-org/example-bundle/in-toto-metadata/*/*.link`). 103 | 104 | With regard to the last bullet point, the `targets/releases` role SHOULD include [custom targets metadata](300-CNAB-security.md) about every version of a bundle such that: 105 | 106 | 1. Each root layout lists all of the public keys needed to verify it. 107 | 1. Each bundle lists all of the root layout and link metadata required to verify this bundle. 108 | 109 | A benefit of this approach is that different versions of bundles MAY be associated with completely different root layouts with different keys, allowing developers to update their software supply chains without breaking old bundles that are still popular. 110 | 111 | In order to prevent conflicts between link metadata for different versions of a bundle (because link metadata filenames are of the fixed form `$KEYID.$STEP.link`, which means that link metadata for different versions of a bundle will share the same base [basenames](https://en.wikipedia.org/wiki/Basename)), the complete set of link metadata for each bundle MAY be isolated in different directories. The simplest way to do this is to use a separate directory to contain the in-toto link metadata for each bundle that is named after the digest (e.g., SHA-256) for that bundle. 112 | 113 | The following code listing is an example of the `targets` metadata for a bundle: 114 | 115 | ```json 116 | { 117 | "signatures": {...}, 118 | "signed": { 119 | ..., 120 | "delegations": { 121 | "keys": {...}, 122 | "roles": [ 123 | { 124 | "keyids": [...], 125 | "name": "targets/releases", 126 | "paths": [ 127 | "example.com/example-org/example-bundle:*", 128 | "example.com/example-org/example-bundle/in-toto-metadata/*/*.link" 129 | ], 130 | "threshold": 1 131 | } 132 | ] 133 | }, 134 | "targets": { 135 | "example.com/example-org/example-bundle/in-toto-metadata/root.layout": { 136 | "custom": { 137 | "in-toto": [ 138 | "example.com/example-org/example-bundle/in-toto-pubkeys/298f37401f0b526a708967b7f708bc9c938fe0ad4bfe50d66837c20a57084e84.pub", 139 | "example.com/example-org/example-bundle/in-toto-pubkeys/3e82bcdc71b29999340ceaadf3dc4193f8b06572d1c20612e9acdd7b52fa4b90.pub", 140 | "example.com/example-org/example-bundle/in-toto-pubkeys/e847f58ca5e83fc48d1d2388ddd8f1a168b205a3fe7978ad015dee3ae7b2ecf7.pub" 141 | ] 142 | }, 143 | "hashes": { 144 | "sha256": "930c48fa182d14835febd6a7f9129e34b83246f74238b9747fef7fc12147184d" 145 | }, 146 | "length": 101047 147 | }, 148 | "example.com/example-org/example-bundle/in-toto-pubkeys/298f37401f0b526a708967b7f708bc9c938fe0ad4bfe50d66837c20a57084e84.pub": { 149 | "hashes": { 150 | "sha256": "a19b11a130b35fb205e8cf8ab2f2488f387332be56857968785ce9899a521b05" 151 | }, 152 | "length": 799 153 | }, 154 | "example.com/example-org/example-bundle/in-toto-pubkeys/3e82bcdc71b29999340ceaadf3dc4193f8b06572d1c20612e9acdd7b52fa4b90.pub": { 155 | "hashes": { 156 | "sha256": "3560de9da223ac51b5cdbf25acf9f8e8f9f7b699eeda912c7a26a68c5f01ce12" 157 | }, 158 | "length": 799 159 | }, 160 | "example.com/example-org/example-bundle/in-toto-pubkeys/e847f58ca5e83fc48d1d2388ddd8f1a168b205a3fe7978ad015dee3ae7b2ecf7.pub": { 161 | "hashes": { 162 | "sha256": "8cb4a254ae123a8bd91b1c9abdd99e719aa8349ff7eafd168988ce8a935d51a1" 163 | }, 164 | "length": 799 165 | } 166 | } 167 | ..., 168 | } 169 | } 170 | ``` 171 | 172 | The following code listing is an example of the `targets/releases` metadata for a bundle: 173 | 174 | ```json 175 | { 176 | "signatures": {...}, 177 | "signed": { 178 | ..., 179 | "delegations": {}, 180 | "targets": { 181 | "example.com/example-org/example-bundle:latest": { 182 | "custom": { 183 | "in-toto": [ 184 | "example.com/example-org/example-bundle/in-toto-metadata/root.layout", 185 | "example.com/example-org/example-bundle/in-toto-metadata/eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09/step1.87d52666.link", 186 | "example.com/example-org/example-bundle/in-toto-metadata/eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09/step2.20585de1.link" 187 | ] 188 | }, 189 | "hashes": { 190 | "sha256": "eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09" 191 | }, 192 | "length": 7206 193 | }, 194 | "example.com/example-org/example-bundle/in-toto-metadata/eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09/step1.87d52666.link": { 195 | "hashes": { 196 | "sha256": "0a33cbf67b70f315c0b7a83923bcef35308e986140169950e609e3be38585289" 197 | }, 198 | "length": 132251 199 | }, 200 | "example.com/example-org/example-bundle/in-toto-metadata/eb4189fc29d97463822ecd6409677e9a4fcb9d66d9bee392e9f9aece0917fc09/step2.20585de1.link": { 201 | "hashes": { 202 | "sha256": "e5076f59e2096fb64deae6b13384575d3d63c1c4f7a42f48d0a238097a8823eb" 203 | }, 204 | "length": 57495 205 | } 206 | } 207 | ..., 208 | } 209 | } 210 | ``` 211 | 212 | ### Security analysis of extension 213 | 214 | The biggest difference between this extension and the MVP itself is that there is now compromise-resilient distribution of the software supply chain for the bundle using the in-toto root layout. In addition to the assurances of the MVP, this provides additional assurance that attackers cannot change the rules of the software supply chain governing how this bundle was produced. 215 | 216 | Assuming that the `targets/releases` key is kept on private infrastructure controlled by the bundle developers, and even if attackers compromise this key, then they cannot change the rules governing the software supply chain. 217 | 218 | However, we _cannot_ analyze the security provided by the software supply chain itself. This is because different bundles are very likely to have different software supply chains. For example, if all the keys used to sign all the steps (and therefore in-toto link metadata) in the software supply chain are kept online on the aforementioned private infrastructure, then this is _not_ as secure a setup as the case where source code must be signed off by developers using keys protected using hardware security keys. Known [implementations](806-security-known-implementations.md) SHOULD provide usable software supply chains out of the box that provide sufficiently strong security guarantees without hampering usability by developers. 219 | 220 | For an example of how to design a secure software supply chain, please see the following [example](https://www.datadoghq.com/blog/engineering/secure-publication-of-datadog-agent-integrations-with-tuf-and-in-toto/). 221 | -------------------------------------------------------------------------------- /400-claims.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Claims System 3 | weight: 400 4 | --- 5 | 6 | # CNAB Claims 1.0.0 (CNAB-Claims1) 7 | *[Final Approval, Published](901-process.md), Oct. 2020* 8 | 9 | This specification describes the CNAB Claims system. This is not part of the [CNAB Core](100-CNAB.md) specification, but is a specification describing how records of CNAB installations may be formatted for storage. Implementations of CNAB Core can meet the CNAB Core specification without implementing this specification. Implementations that support claims MAY state that they comply with CNAB Claims 1.0.0. 10 | 11 | In CNAB, an _installation_ is a particular installed instance of a CNAB bundle. For example, if a bundle named `myApp` is installed in two places, we say there are _two installations of `myApp`_. 12 | 13 | An installation MAY change over time, as a particular bundle is installed, then upgraded. In CNAB, each time a mutating (destructive) operation is performed (Such as `install`, `upgrade`, or custom operations that are not read-only), the installation gets a new _revision_. A revision is a unique identifier that identifies the combination of an _installation_ and a modification of that installation. For example, when a CNAB bundle is installed, the initial installation will have an initial revision ID. When that installation is upgraded, it will have a new revision ID. How revisions are used is outside of the scope of this document, but it is safe to assume that if a revision ID has changed, one or more artifacts owned by the installation has also been changed. 14 | 15 | A _claim_ is a record of an action against a CNAB _installation_. This specification defines the format of a claim, and describes certain necessary behaviors of a system that implements claims. 16 | 17 | A _host environment_ is an environment, possibly shared between multiple CNAB runtimes, that provides persistence to a CNAB configuration. For example, a filesystem may contain a record of claims that two different CNAB clients share. Or a database may contain the environment that is shared by multiple tools at different locations in the network. Each of these is a CNAB host environment. 18 | 19 | The word _claim_ was chosen because it represents the relationship between a certain CNAB host environment and the resources that were created by a CNAB runtime in that host environment. In this sense, an environment takes responsibility for those resources if and only if it can _claim_ them. A claim is an _external assertion of ownership_. That is, the claim itself is not "installed" into the host environment. It is stored separately, possibly in an entirely different location. 20 | 21 | The claims system is designed to satisfy the requirements of the [Bundle Runtime specification](103-bundle-runtime.md) regarding the tracking of `CNAB_REVISION`. It also provides a description of how the state of a bundle installation may be represented. 22 | 23 | This specification uses the terminology defined in [the CNAB Core specification](100-CNAB.md). 24 | 25 | ## Managing State 26 | 27 | Fundamentally, package managers provide a state management layer to keep records of what was installed. For example, [homebrew](http://homebrew.sh), a popular macOS package manager, stores records for all installed software in `/usr/local/Cellar`. Helm, the package manager for Kubernetes, stores state records in Kubernetes ConfigMaps located in the system namespace. The Debian Apt system stores state in `/var/run`. In all of these cases, the stored state allows the package managing system to be able to answer (quickly) the question of whether a given package is installed. 28 | 29 | Example from Homebrew: 30 | 31 | ```console 32 | $ brew info cscope 33 | cscope: stable 15.8b (bottled) 34 | Tool for browsing source code 35 | https://cscope.sourceforge.io/ 36 | /usr/local/Cellar/cscope/15.8b (10 files, 714.2KB) * 37 | Poured from bottle on 2017-05-15 at 09:24:58 38 | From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/cscope.rb 39 | ``` 40 | 41 | The CNAB Claims specification does not define where or how records are stored, nor how these records MAY be used by CNAB tooling. However, this specification describes how a CNAB-based system MUST emit the record to an invocation image, and provides some guidance on maintaining integrity of the system. 42 | 43 | This is done so that implementors can standardize on a way of relating an installation claim (the record of an installation) to operations like `install`, `upgrade`, or `uninstall`. This, in turn, is necessary if CNAB bundles are expected to be executable by different implementations. 44 | 45 | ### Anatomy of a Claim 46 | 47 | While implementors are not REQUIRED to implement claims, this is the standard format for claims-supporting systems. 48 | 49 | The CNAB claim is defined as a JSON document. The specification currently does not require that claims be formatted as Canonical JSON. Claims are immutable and are not modified after creation. Before an action is executed against an installation, a new claim is created to represent the operation. 50 | 51 | The claim for the last modifying action MUST be retained. Previous claims, and/or claims for non-modifying actions, MAY be retained to provide a history of actions performed on an installation. 52 | 53 | ```json 54 | { 55 | "action": "install", 56 | "bundle": { 57 | "credentials": {}, 58 | "images": {}, 59 | "invocationImages": [ 60 | { 61 | "image": "example/helloworld:0.1.0", 62 | "imageType": "docker" 63 | } 64 | ], 65 | "name": "helloworld", 66 | "outputs": { 67 | "clientCert": { 68 | "definition": "x509Certificate", 69 | "path": "/cnab/app/outputs/clientCert" 70 | }, 71 | "hostName": { 72 | "applyTo": [ 73 | "install" 74 | ], 75 | "definition": "string", 76 | "description": "the hostname produced installing the bundle", 77 | "path": "/cnab/app/outputs/hostname" 78 | }, 79 | "port": { 80 | "definition": "port", 81 | "path": "/cnab/app/outputs/port" 82 | } 83 | }, 84 | "parameters": {}, 85 | "schemaVersion": "v1.0.0", 86 | "version": "0.1.2" 87 | }, 88 | "bundleReference": "hub.example.com/technosophos/hellohelm@sha256:eec03d9da1bad36b3e3a4526cc774153f7024a94f25df8d2dc3ca5602fc5273d", 89 | "created": "2018-08-30T20:39:55.549002887-06:00", 90 | "custom": {}, 91 | "id": "01E5G8ZYP714JVM8NHTJQ4FH15", 92 | "installation": "technosophos.helloworld", 93 | "parameters": {}, 94 | "revision": "01CP6XM0KVB9V1BQDZ9NK8VP29" 95 | } 96 | ``` 97 | Source: [400.01-claim.json](examples/400.01-claim.json) 98 | 99 | The fields above are defined as follows: 100 | 101 | - `action` (REQUIRED): The name of the action executed against the installation. This may be any of the built-in actions (`install`, `upgrade`, `uninstall`) as well as any custom actions as defined in the bundle descriptor. The special name `unknown` MAY be used in the case where the CNAB Runtime cannot determine the action name of a claim. 102 | - `bundle` (REQUIRED): The bundle, as defined in [the Bundle Definition](101-bundle-json.md). 103 | - `created` (REQUIRED): A timestamp indicating when this _claim_ was created. 104 | - `bundleReference` (OPTIONAL): A canonical reference to the bundle used in the action. This bundle reference SHOULD be digested to identify a specific version of the referenced bundle. 105 | - `custom` (OPTIONAL): A section for custom extension data applicable to a given runtime. 106 | - `id` (REQUIRED): The claim id. A [ULID](https://github.com/ulid/spec) that MUST change with each new claim, so that every claim associated with an installation has a unique id. This is used to associate the the claim with its result(s). 107 | - `installation` (REQUIRED): The name of the _installation_. This can be automatically generated, though humans may need to interact with it. It MUST be unique within the installation environment, though that constraint MUST be imposed externally. Elsewhere, this field is referenced as the _installation name_. The format of this field must follow the same format used for the `installation` field in the [bundle.json file specification](101-bundle-json.md#the-bundlejson-file). 108 | - `parameters` (OPTIONAL): Key/value pairs that were passed in during the operation. These are stored so that the operation can be re-run. See the [Parameters](#parameters) section below for more details. 109 | - `revision` (REQUIRED): The _installation_ revision. A [ULID](https://github.com/ulid/spec) that MUST change each time the installation is modified. It MUST NOT change when a [non-modifying operation](https://github.com/cnabio/cnab-spec/blob/main/101-bundle-json.md#custom-actions) is performed on the installation. 110 | 111 | > Note that credential data is _never_ stored in a claim. For this reason, a claim is not considered _trivially repeatable_. Credentials MUST be supplied on each action. Implementations of the claims specification are expected to retrieve the credential requirements from the `bundle` field. 112 | 113 | Timestamps in JSON are defined in the [ECMAScript specification](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-date-time-string-format), which matches the [ISO-8601 Extended Format](https://www.iso.org/iso-8601-date-and-time-format.html). 114 | 115 | ### ULIDs 116 | 117 | ULIDs have two properties that are desirable: 118 | 119 | - High probability of [uniqueness](https://github.com/ulid/javascript) 120 | - Sortable by time. The first 48 bits contain a timestamp 121 | 122 | Compared to a monotonic increment, ULID has strong advantages when it cannot be assumed that only one actor will be acting upon the CNAB claim record. While other unique IDs are not meaningfully sortable, ULIDs are. Thus, even unordered claim storage records can be sorted. 123 | 124 | ### Parameters 125 | 126 | If parameters are passed in during the operation, they MUST be stored on the claim. The parameter data stored in a claim is _the resolved key/value pairs_ that result from the following transformation: 127 | 128 | - The values supplied by the user are validated by the rules specified in the `bundle.json` file 129 | - The output of this operation is a set of key/value pairs in which: 130 | - Valid user-supplied values are presented 131 | - Default values are supplied for all parameters where `default` is provided and no user-supplied value overrides this 132 | 133 | ### Claim Results 134 | 135 | The result of executing an operation is defined separately in a claim result document. Claim results are immutable and are not modified after creation. A claim can have multiple results. Only the final status, such as `succeeded` or `failed` MUST be recorded for a claim, though an implemenation MAY choose to persist results for intermediate status transitions. For example, a claim may have a result for `starting` and another for `succeeded`, or have multiple results when the operation was canceled and then retried. 136 | 137 | The last result associated with a retained claim MUST also be retained. Previous results MAY also be retained to provide a more detailed history of the operation's progress. 138 | 139 | ```json 140 | { 141 | "claimId": "01E5G8ZYP714JVM8NHTJQ4FH15", 142 | "custom": {}, 143 | "id": "01E2ZZ2FKSE0V41DCXFCSW5D1M", 144 | "created": "2018-08-30T20:39:55.549002887-06:00", 145 | "message": "", 146 | "outputs": { 147 | "clientCert": { 148 | "contentDigest":"sha256:aaa..." 149 | }, 150 | "hostName": { 151 | "contentDigest":"sha256:bbb..." 152 | }, 153 | "port": { 154 | "contentDigest":"sha256:ccc..." 155 | } 156 | }, 157 | "status": "succeeded" 158 | } 159 | ``` 160 | Source: [400.01-claim-result.json](examples/400.01-claim-result.json) 161 | 162 | The fields above are defined as follows: 163 | 164 | - `claimId` (REQUIRED): ID of the claim that generated this result. 165 | - `custom` (OPTIONAL): A section for custom extension data applicable to a given runtime. 166 | - `id` (REQUIRED): A [ULID](https://github.com/ulid/spec) identifier for the result. 167 | - `created` (REQUIRED): A timestamp indicating when this result was created. 168 | - `message` (OPTIONAL): A human-readable string that communicates the outcome. Error messages MAY be included in `failed` conditions. 169 | - `outputs` (OPTIONAL): Outputs generated by the operation. It is a map from the output names to metadata about the output. The output value is not stored in the claim result. See the [Outputs](#outputs) section for details. If this field is not present, it may be assumed that no outputs were generated as a result of the operation. 170 | - `contentDigest` (OPTIONAL): The `contentDigest` field contains a digest, in [OCI format](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests), which can be used to compute the integrity of the output. 171 | - `status` (REQUIRED): Indicates the status of the last phase transition. Valid statuses are: 172 | - `canceled`: The operation was canceled, potentially during the operation's execution. This is an error condition. 173 | - `failed`: Failed before completion. 174 | - `pending`: Execution has been requested and has not begun. This should be considered a temporary status, and the runtime SHOULD work to resolve this to either `failed` or `succeeded`. 175 | - `running`: Execution is in progress and has not completed. This should be considered a temporary status, and the runtime SHOULD work to resolve this to either `failed` or `succeeded`. 176 | - `unknown`: The state is unknown. This is an error condition. 177 | - `succeeded`: Completed successfully. 178 | 179 | #### Outputs 180 | 181 | The claim result provides metadata about the outputs generated by the operation. A tool may choose to request the contents of any outputs to persist them but is not required to do so. 182 | 183 | The output name, as defined in the bundle, is used to request the content of the file located at the path defined for that output. 184 | 185 | Below, you can see an example of a claim result that includes an entry for the output `clientCert`. 186 | 187 | ```json 188 | { 189 | "claimId": "01E5G8ZYP714JVM8NHTJQ4FH15", 190 | "id": "01E2ZZ2FKSE0V41DCXFCSW5D1M", 191 | "created": "2018-08-30T20:39:55.549002887-06:00", 192 | "message": "", 193 | "outputs": { 194 | "clientCert": { 195 | "contentDigest":"sha256:aaa..." 196 | }, 197 | "hostName": { 198 | "contentDigest":"sha256:bbb..." 199 | }, 200 | "port": { 201 | "contentDigest":"sha256:ccc..." 202 | } 203 | }, 204 | "status": "succeeded", 205 | } 206 | ``` 207 | 208 | The tool may request the contents of the output, retrieved from `/cnab/app/outputs/clientCert`, and persist it for later use. 209 | 210 | ``` 211 | -----BEGIN CERTIFICATE----- 212 | MIIDBzCCAe+gAwIBAgIJAL2nOwEePOPvMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV 213 | BAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xOTA3MTUxNjQwMzdaFw0yOTA3MTIxNjQw 214 | MzdaMBoxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEB 215 | BQADggEPADCCAQoCggEBAJ7779ImmmvEt+ywP8GjfpzgM57n1WZ26fBTVy+ZibiH 216 | hKCJuzU5vynu5M0eYCufRFQ7LG/xet1GvpBIch0U6ilZVnNDrsNUtQ03Hpen144g 217 | li5ldPh5Sm88ibDbi4yEIgti2JBKIuVE+iEdkIejF8DZps008TbLLoENM1VpHpUT 218 | CIJY657t+Xhz9GOhp1w3bVoKEoF/6psvc6IFHK8bUMq+4003VGDZe2BMlgazZPHc 219 | 3o5CqNviajnRoo9QnLUH1qOljNMR+mkewNOkL2PRGvkCuHJrEk0qmUU7lX3iVN1J 220 | C7y1fax53ePXajLD+5/sQNeszVg1cIIUlXBy2Bx/F7UCAwEAAaNQME4wHQYDVR0O 221 | BBYEFAZ1+cZNMujQhCrtCRKfPm+NLDg0MB8GA1UdIwQYMBaAFAZ1+cZNMujQhCrt 222 | CRKfPm+NLDg0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAuyyYER 223 | T5+E+KuFbDL/COmxRWZhF/u7wIW9cC2o5LlKCUp5rRQfAVRNJlqasldM/G4Bg/uQ 224 | 5khSYe2XNe2C3iajVkR2RlqXBvCdQuCtudhZVd4jSGWi/yI7Ub2/HyOTjZ5eG82o 225 | F6e3pNRCQwTw0y0orQmdh0s+UmHEVjIe8PfbdRymfeQO70EXTxncBJ5elZx8s0E9 226 | TPPdbl2knZmKJhwnZFKCaa4DmDA6CDa2GPz+2++DQl1rCIB3mIcxpg6wSdMA6C6l 227 | nBJBX5Wnxckldp8G0FNXTa1DYqjPZ5U84tkh46pFSLsbSse45xNhrMPeZDWlgHsp 228 | WfK01YbCWioNVGk= 229 | -----END CERTIFICATE----- 230 | ``` 231 | 232 | ### How the Claim is Used 233 | 234 | The claim is used to inform any CNAB tooling about how to address a particular installation. For example, given the claim record, a runtime that implements the CNAB Claims spec should be able to: 235 | 236 | - List the _names_ of the installations, given a _bundle name_ 237 | - Given an installation's _name_, return the _bundle info_ that is installed under that name 238 | - Given an installation _name_ and a _bundle_, generate a _bundle info_. 239 | - This is accompanied by running the `install` path in the bundle 240 | - Given an installation's _name_, replace the _bundle info_ with updated _bundle info_, and update the revision with a new ULID, and the modified timestamp with the current time. This is an upgrade operation. 241 | - This is accompanied by running the `upgrade` path in the bundle 242 | - Given an installation's name, mark the installation as deleted. 243 | - This is accompanied by running the `uninstall` path in the bundle 244 | - Given an installation's name, return the name(s) of the generated outputs. 245 | 246 | To satisfy these requirements, implementations of a CNAB runtime are expected to be able to store and retrieve state information. However, note that nothing in the CNAB Claims specification tells _how or where_ this state information is to be stored. It is _not a requirement_ to store that state information inside of the invocation image. (In fact, this is discouraged.) 247 | 248 | ## Injecting Claim Data into an Invocation Image 249 | 250 | Compliant CNAB Claims Spec implementations MUST conform to this section. 251 | 252 | ### Environment Variables 253 | 254 | The claim is produced outside of the CNAB package. The following claim data is injected 255 | into the invocation container at runtime: 256 | 257 | - `$CNAB_INSTALLATION_NAME`: The value of `claim.installation`. 258 | - `$CNAB_BUNDLE_NAME`: The name of the present bundle. 259 | - `$CNAB_ACTION`: The action to be performed (install, upgrade, ...) 260 | - `$CNAB_REVISION`: The ULID for the present revision. (On upgrade, this is the _new_ revision) 261 | - `$CNAB_CLAIMS_VERSION`: The version of this specification (currently `CNAB-Claims-1.0.0`) 262 | 263 | > Credential data, which is also injected into the invocation image, is _not_ managed by the claim system. Rules for injecting credentials are found in [the bundle runtime definition](103-bundle-runtime.md). 264 | 265 | Parameters declared with an `env` key in the `destination` object MUST have their values injected as environment variables according to the name specified. Likewise, files MUST be injected if `path` is set on `destination`. 266 | 267 | ### Files 268 | 269 | The invocation image may benefit from accessing the claim. Consequently, a claim MUST be attached to the invocation image when the invocation image is started. 270 | 271 | The claim MUST be mounted at the path `/cnab/claim.json` inside of the bundle. The version of this claim that is to be mounted is the _version representing the current operation_. In other words, when a bundle is installed, the runtime creates the original installation claim and passes this in. On the first upgrade, the claim describing the _upgrade_ operation is located at `/cnab/claim.json`. 272 | 273 | Note: Systems may be compliant with the core specification but not support the claims specification. If `$CNAB_CLAIMS_VERSION` is not present, a runtime SHOULD assume that the claims specification is not implemented. Bundle authors may therefore have to take care when relying upon `/cnab/claim.json`, accommodating the case where the runtime does not support claims. 274 | 275 | ## Calculating the Result 276 | 277 | The `result` object is populated by the result of the invocation image's action. For example, consider the case where an invocation image executes an installation action. The action is represented by the following shell script, and `$CNAB_INSTALLATION_NAME` is set to `my_first_install`: 278 | 279 | ```bash 280 | #!/bin/bash 281 | 282 | set -eo pipefail 283 | 284 | helm install stable/wordpress -n $CNAB_INSTALLATION_NAME > /dev/null 285 | kubectl create pod $CNAB_INSTALLATION_NAME > /dev/null 286 | echo "yay!" 287 | ``` 288 | 289 | (Note that the above script redirects data to `/dev/null` just to make the example easier. A production CNAB bundle might choose to include more verbose output.) 290 | 291 | If both commands exit with code `0`, then the claim result will look like this: 292 | 293 | ``` 294 | { 295 | "claimId": "01E5G8ZYP714JVM8NHTJQ4FH15", 296 | "id": "ULID", 297 | "message": "yay!", // From STDOUT (echo) 298 | "created": "TIMESTAMP", 299 | "status": "succeeded" // succeeded (exit == 0), failed (exit > 0), or unknown (connection terminated before exit code was received) 300 | } 301 | ``` 302 | 303 | Tools that implement claims MAY then present this result info to end users to show the result of running an invocation image. 304 | 305 | ## Credentials, Parameters, and Claims 306 | 307 | The claims specification makes two assumptions about security: 308 | 309 | - Claims are safe for writing sensitive information 310 | - Claims are not a means for proxying identity 311 | 312 | Parameters may contain sensitive information, such as database passwords. And this specification assumes that storing such information (un-redacted) can be done securely. However, the precise security mechanisms applied at the storage layer are beyond the scope of this specification. 313 | 314 | Parameter values MUST be stored in claims. Implementations of the claims spec MUST NOT provide methods to block certain parameters from having their values written to the claim. 315 | 316 | Credentials, on the other hand, are proxies for user identity, including potentially both authentication information and authorization information. Claims are not intended to provide a proxy for identity. In other words, a claim record should allow two users with identical permissions the ability to install the same configuration, it should not provide a way for one user to _gain_ the permissions of another user. 317 | 318 | Credential data MUST NOT be stored in claims. Credentials are identity-specific, while claims are identity-neutral. 319 | --------------------------------------------------------------------------------