About the contract
When you create a virtual server instance by using the HPVS image, you must specify a contract as part of the user input (user-data).
This procedure is intended for users with the role system admin or app developer or ISV.
What is a contract?
The contract is a definition file in the YAML format that is specific to the IBM Hyper Protect Virtual Servers instance. You must create this file as a prerequisite for creating an instance, and after this file is created, it must be passed as an input when the IBM Hyper Protect Virtual Servers instance is created. You cannot create an IBM Hyper Protect Virtual Servers instance without a valid contract. If you create an IBM Hyper Protect Virtual Servers instance without a contract, the deployment starts and then fails, and the instance goes into a shutdown state. The contract is specific to creating an IBM Hyper Protect Virtual Servers instance and is an extension of the IBM Secure Execution technology by Hyper Protect.
Contract sections
A contract file can have the following four valid high-level
sections, of which the workload and env
sections are mandatory:
-
workloadis a mandatory section. -
envis a mandatory section. -
attestationPublicKeyis an optional section. You can provide a public RSA key as part of the contract, which is used to encrypt the attestation document and the attribute must be named asattestationPublicKey. -
envWorkloadSignatureis an optional section and contains the signature of the other sections of the contract.
The two primary sections in a contract are the
workload and env sections. These two
sections are needed because the information that is added into the
contract comes from two different personas, namely the "workload"
and the "deployer" persona.
The workload persona provides information about the container (or workload) that needs to be brought up on the IBM Hyper Protect Virtual Servers instance. It includes information about the name of the container, the container registry where it resides, credentials of the container registry, the image digest, the notary server information (required for image validation), the environment variables that need to be passed to the container, and the docker compose file or Pod descriptors with the container information.
The deployer persona works closely with workload persona. This
persona receives the workload information (encrypted workload
section) from the workload persona. The deployer then creates the
env section of the contract. This section typically
contains configuration details that are outside the Workload
persona's domain of knowledge or responsibility. For instance, it
may include logging configurations such as customer-specific
rsyslog server details.
The workload section
One of the most important sections of the contract, the
workload section can have multiple subsections and the
purpose of the subsections is to provide information that is
required for bringing up the workload. The workload
section is the parent section that can have the following
subsections:
- type: workload. This subsection is mandatory.
-
auths. This subsection is optional. -
compose(for single container) orplay(for single container or multiple containers). They are mutually exclusive; one of the sections must exist. -
images. This subsection is optional. -
volumes. This subsection is optional.
The following snippet shows a high-level sample of the workload section of the contract. The minimum that a workload section needs is the compose section. The other sections can be added based on the requirement.
type: workload
auths:
<registry url>:
password: <password>
username: <user name>
<registry url>:
password: <password>
username: <user name>
compose:
archive: <base64 encoded of tgz of docker-compose.yaml>
images:
dct:
<docker image name (without the tag, an example is docker.io/redbookuser/s390x:)>:
notary: "<notary URL>"
publicKey: <docker content trust signed public key>
<docker image name>:
notary: "<notary URL>"
publicKey: <docker content trust signed public key>
volumes:
<volume key>:
mount: "<data volume mount path>"
seed: "<Passphrase of the luks encryption>"
filesystem: "ext4"
The auths subsection
The auths section consists of information about the
container's registry. If a public image is used in the contract,
then you do not need the auths section because no
credentials are required. The auths subsection is
required only if the container images are private. This subsection
does not have any image information, as shown in the following
sample. This subsection needs to contain the name of the image
registry and the credentials such as username-password for the
same. The key must be the hostname of the container registry or the
string 'https://index.docker.io/v1/' for
the default docker registry.
The following snippet shows an example for IBM Cloud Registry. For more information about using the API key, see Using client software to authenticate in automation.
auths:
us.icr.io:
password: <apikey>
username: iamapikey
The compose
subsection
It consists of an archive subsection. The archive subsection
contains the Base64 encoded TGZ file archive of the
docker-compose.yaml file. As the Hyper Protect
Container Runtime image uses Docker Engine and Docker Compose to
start the container, the information about the container must first
be created by using a standard docker-compose file. This file is
then archived and base64 encoded and the output of this is provided
as the value to the archive subsection, within the compose section.
For more information, see Overview of Docker
Compose.
You must use an image that you built and update information
about the image in the image section of the
docker-compose.yaml file. You cannot use Docker
Compose to build the image.
The mount points that are specified under the volumes section of
the docker-compose.yaml file must be aligned with the
volumes "mount" field that is specified in the workload section of
the contract. For example, the value for volumes that are shown in
the example of a docker compose file is
/mnt/data/var/www/html:/var/www/html, where
/mnt/data must match the mount point that is specified
in the mount field (mount: /mnt/data) in the
volumes section of the workload.
build section.Both "yaml" and "yml" formats are supported for docker-compose file. The following is an example of a docker-compose file.
services:
nc:
image: nextcloud:apache
restart: always
ports:
- 80:80
volumes:
- /mnt/data/var/www/html:/var/www/html
environment:
- REDIS_HOST=redis
- MYSQL_HOST=db
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=nextcloud
Tip: There are use cases where the registry is not known when the workload section is pre-encrypted. For example, when the workload provider wants to allow the deployer to use a registry mirror or a secure private container registry. In such a case, it's possible to dynamically override the registry as well as the pull credentials. This is a coordinated effort between the workload provider and the deployer. For more information, see Using a dynamic registry reference.
Use the following command to get the base64 encoded archive file. The base64 output is available in the compose.b64 file.
tar czvf - -C <COMPOSE_FOLDER> . | base64 -w0 > compose.b64
Copy the content of compose.b64 as a value of compose -> archive.
compose:
archive: <paste the content of compose.b64 >
For this example, you would see a response similar to the following output:
compose:
archive: H4sIAKOFmGIAA+2RTW6DMBBGs84pRuyB8Q8k+DIRwZOGtmBkkyrcvhgnLVVV1EWkqhJv4ZHt8ednWZvqhWxcmaYzjpKhed08HETMpQRfd3k2VeRhPpEJCUxymTPkIuOALBOIG8DHq3zn4vrSjiqdLY/nsv+xb2w7nRZywlPgo/4THNm3uiKntgCWdO1aowmZnwLUTflECpwo8Jpu9NyZ2zvQgdADFEudoXyQzSu+fPPzseSvedo6qjV7mDa2anZbdH8totL6somtUlvX8K4SJshDsFKU2NmFvAZuMc9U37wceeys+Y6BI8Fi6+6vxK5RS+YFDh6RNu//tuVlZWVJd4BcjKckQAIAAA=
The play subsection
In the play subsection, you can define the workload
via Pod
descriptors. Each pod can contain one or more container
definitions. Descriptors can be provided in one of the following
ways:
-
In plain YAML format in the
resourcessubsection ofplay. This section is an array of descriptors and supports two types of descriptors: Pods and ConfigMaps.The following example illustrates how to use the
resourcessection:workload: | type: workload play: resources: - apiVersion: v1 kind: Pod metadata: name: busybox spec: containers: - name: main image: ... command: - printenv envFrom: - configMapRef: name: contract.config.map optional: false restartPolicy: Never -
In the
archivesubsection ofplay, the archive is a base64 encoded, gzipped tar file. The Pods or ConfigMaps are represented as YAML files, at the top level in this tar file. The file may also contain extra files and all files will be extracted to the host file system before starting the Pods. The current working directory is the directory in which the files have been extracted, so it's possible to use a volume mount with a relative path to mount files or directories from the yaml file.
Example:
workload: |
type: workload
play:
archive: ${COMPOSE_VALUE}
auths:
us.icr.io:
username: iamapikey
password: Eqx0TS....
volumes:
test-volume:
mount: /var/hyperprotect
seed: workload phrase
filesystem: ext4
-
In a template format in the
templatessubsection ofplay. This section is an array of descriptors in the YAML format. Pods or ConfigMaps may have points of variability (POV) that are not known at the time of writing the descriptors. These POVs may be represented as templates and the values will be filled in at deployment time from information in the contract. We use go templates as the templating syntax, which is the same as used for helm charts, so templates can easily be exchanged with k8s. We support the followingBuilt-Inobjects:-
Environment: this object contains the environment
variables as merged between the workload and the environment
section. The object is available as
{{ .Env }}.
Example:
workload: | type: workload auths: docker.io: password: <password> username: test play: templates: - apiVersion: v1 kind: Pod metadata: name: busybox spec: containers: - name: main image: "{{ .Env.REGISTRY }}/hpse-docker-busybox-s390x@sha256:732efa374f1e6c964caeacab0bcb370385ee386041a14d4a32176462e3f75c7b" command: - printenv envFrom: - configMapRef: name: contract.config.map optional: false restartPolicy: Never env: | type: env logging: logDNA: ingestionKey: <ingestion Key of the Log Analysis instance> hostname: <host name of the Log Analysis instance> port: 6514 env: REGISTRY: docker-io/testThe
{{ .Env REGISTRY }}expression references theREGISTRYenvironment variable that in this example is defined in theenvsection of the contract.The templates need to be valid YAML, so a replacement expression must be escaped if it appears as the first part of a string. Otherwise it collides with the block mapping syntax. This is different to helm templates where expressions are applied to the textual representation of the document instead of the model representation.
-
Environment: this object contains the environment
variables as merged between the workload and the environment
section. The object is available as
Environment Variables
In the contract, you can define environment variables in the
workload and env sections. Both sets of
variables are merged together with workload taking
precedence. Pods use the concept of a ConfigMap
to define configuration, so HPVS represents the merged environment
sections as a special ConfigMap
named contract.config.map. The following example
mounts all environment variables from the contract into the
container:
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: main
image: ...
command:
- printenv
envFrom:
- configMapRef:
name: contract.config.map
optional: false
restartPolicy: Never
Pod communication
-
Container to container
Containers inside one Pod communicate to each other via
localhost. Each container needs to listen on a different port, because - per design - they share the same IP address. -
Pod to host
Usually a Pod needs to expose at least one of its containers to the host, so that container is accessible via the IP address on the host via a mapped port. For this use case, make use of the
hostPortfeature on a container. Note that this is not best practice in the Kubernetes world, in which a service would be used instead.Specify both
hostPortandcontainerPortexplicitly. If you specify onlycontainerPort, ports are not bound.Example:
apiVersion: v1 kind: Pod metadata: name: nginx-with-busybox spec: containers: - image: ... name: frontend ports: - containerPort: 80 hostPort: 80 volumeMounts: - mountPath: /etc/nginx name: local-frontend readOnly: true - command: - httpd - -vv - -f - -p - "8080" - -h - /www image: ... name: backend volumeMounts: - mountPath: /www name: local-backend readOnly: true volumes: - hostPath: path: ./www type: Directory name: local-backend - hostPath: path: ./nginx type: Directory name: local-frontend -
Pod to Pod
To reach from one Pod to another, expose a
hostPorton the target Pod. The source Pod can then make a request to the host on the exposed port to get to the target Pod.The source Pod can find the IP address of the host via the following command:
ip route | awk '/default/ { print $3 }'
Volumes
For HPVS instance, volumes are managed by the volumes section in the contract. Based on this information, HPVS will encrypt and mount external block devices on the host. To mount these volumes into the pod, use the hostPath mount option on the volume.
Example:
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: main
image: ...
volumeMounts:
- name: test-volume
readOnly: true
mountPath: /fromHost
volumes:
- name: test-volume
hostPath:
path: /var/hyperprotect
type: Directory
restartPolicy: Never
volumes field here
defines the data on the host to be mounted into the pod. It's
different from volumes in the HPVS contract.The images
subsection
Images described by docker compose
The container image that is listed in the docker-compose file can be signed or not signed by using Docker Content Trust (DCT).
The following example shows an image URL:
<container registry>/<username or namespace>/<image name>
eg- us.icr.io/mynamespace/my-haproxy:
The following shows an example of a notary URL:
notary: "https://notary.us.icr.io"
The publicKey is the corresponding public key by
which the image is signed by using DCT. Use the following command
to get the public key:
cat ~/.docker/trust/tuf/us.icr.io/<username>/<imagename>/metadata/root.json
The following snippet is an example:
images:
dct:
us.icr.io/mynamespace/my-haproxy:
notary: "https://notary.us.icr.io"
publicKey: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpRENDQVM2Z0F3SUJBZ0lSQUxCMXBPYlpEQlRRc09GSFlxazMzaWd3Q2dZSUtvWkl6ajBFQXdJd0tqRW8KTUNZR0ExVUVBeE1mZFhNdWFXTnlMbWx2TDNCeVlXSm9ZWFF4TWpNdmJYa3RhR0Z3Y205NGVUQWVGdzB5TWpBMApNVE14TURFd01ETmFGdzB6TWpBME1UQXhNREV3TUROYU1Db3hLREFtQmdOVkJBTVRIM1Z6TG1samNpNXBieTl3CmNtRmlhR0YwTVRJekwyMTVMV2hoY0hKdmVIa3dXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBU1AKWGsrelE2MlFZNjI3MWQ1cTBMZHY3SGc3QzZkMGZOUlRsQmJXekhOWWFDZzlpU0piYnVNdjVBY0JmMjlqQi83eApqYzhzVitxMksyemtkTHV4QWxGWm96VXdNekFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJCkt3WUJCUVVIQXdNd0RBWURWUjBUQVFIL0JBSXdBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUIzd0JTa0IxaXAKZHZZYlBMbFBmS3RZT0hsYnZzUllKa0FZM2hnY0xuNWhwQUloQUt6cmhsU3p4K1I5bmdtMTBlZVkyaFNCRmgrawpMWHp6SFkwaktTVzhyM1FhCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
For an image that is not signed, no entry is required in the images subsection. However, for unsigned images a digest is required. Complete the following steps to get the digest:
- Log in to the container registry dashboard.
- Open the image.
- Click Tag, and then click Digest.
After you get the digest, add this digest in the
docker-compose.yaml file. The following is an
example:
services:
<imagename>:
image: s390x/redis@sha256:db467ab5c53bdeef65762a7534e26fecb94a0f218bd38afd2eaba1a670c472b1
Images described by Pod descriptors
Container images described by Pod descriptors can be validated by RedHat Simple Signing.
If the image is referenced by a digest, the service allows its usage without additional checks.
Images without a digest need a GPG key to be validated. The key is transferred in base64 encoded binary format that can be created. For example:
gpg -a --export ${KEY_ID}|base64 -w0
This key is conveyed via the rhs subsection of the
images section. This section is a map with the image
identifier as the key and the GPG key in the publicKey
field:
Example:
images:
rhs:
OCI-image-identifier:
publicKey: abcdef
The workload -
volumes subsection
The volumes section needs to be provided in the
contract only if a data volume is attached to the instance at the
time of creation. The information provided in this section is used
to mount the attached data volume (provided by the user) and is
later encrypted using the "seeds" provided in the
workload and env sections. You can
provide any path of your choice for the "mount" field. The path
provided by the user is used internally to mount the data volume.
The mount path provided in the contract must match the path
provided under the volumes section of the
docker-compose.yaml file, so that all the data
associated with the container workload is stored in this data
volume.
The volumes subsection has support for auto
encryption of the data volume with user-provided seeds. If a data
volume is attached to the IBM Hyper Protect Virtual Servers
instance, it is encrypted automatically with the seeds that are
provided through the "seed" field in the volumes
subsections of the contract. Thus two seeds must be provided, one
through the workload section (by the workload persona)
and the other through the env section (by the deployer
persona). These two seeds are internally converted to UTF8
sequences and then concatenated. Later, the hash (SHA256) of the
concatenated sequence is computed as a hexdigest, which is used as
the LUKS passphrase to encrypt the data volume.
You can use the following command to validate the hexdigest:
echo -n "seed1seed2" | sha256sum
Here you can learn how the 'seed' can be provided in the
workload section of the contract. For more information about how
the "seed" input can be provided through the env
section, see The env
section. It is mandatory to provide both the seeds for
encryption. Encryption fails (the instance shuts down) if only one
of the seeds is provided.
volumes:
test:
filesystem: ext4
mount: /mnt/data
seed: workload phrase
Note: Starting from Hyper Protect Virtual Servers 2.1.4, for new instances, the data volume is partitioned into two parts. The first partition (100Mib) is reserved for internal metadata; the second partition remains as the data volume for workload. Only new volumes are partitioned, and you can't use the partitioned volume with an older version of the HPVS image. Provisioning with an existing encrypted volume also works. The difference is that the existing volume does not get partitioned, and you can also go back to an older image with this volume.
Note: Starting with IBM Hyper Protect Servers
Version 2.1.6, the deployer and
provider can use the "rolling of seeds" feature. An option is
provided to roll or rotate the seeds to increase the security
posture, or if the seed is compromised. When the deployer or the
provider or both of them want to roll the seeds, the current seed
information must be specified in the previousSeed
parameter, and the new seed information must be specified in the
seed parameter. See the snippet shown below for an
example.
The following snippet is an example for the volumes section:
volumes:
test:
filesystem: "ext4"
mount: "/mnt/data"
seed: "workload phrase1"
previousSeed: "workload phrase"
Note: Starting with IBM Hyper Protect Servers
Version 2.1.7, and 2.1.6.1, you can attach multiple
volumes when you bring up the virtual server instance. Volumes
attached when the instance is running are ignored. Ensure that you
clearly map the disk to the volumes in the contract file, and
update the volume key in the contract file by using the
serial parameter in the disk section of
the hpvs.xml file. For an example, see this.
The following snippet is an example for the volumes section:
volumes:
test1:
filesystem: "ext4"
mount: "/mnt/data"
seed: "seed1"
test2:
filesystem: "ext4"
mount: "/mnt/test"
seed: "seed2"
The env section
The env section is also one of the most important
sections in a contract. The env section of a contract
deals with information that is specific to the cloud environment
and is not known to the workload persona. This section is created
by the deployer persona.
The subsections for the env section are:
- type: env. This subsection is mandatory.
-
logging. This subsection is mandatory. -
volumes. This subsection must be used only when a data volume is attached. -
signingKey. This subsection must be used only when you want to use a contract signature. -
env. This subsection is used to specify values forenvvariables if they are defined by the workload provider. -
crypto-pt. This subsection is used to specify Crypto Passthrough details. This is an optional subsection. -
host-attestation. This subsection is used to specify one or more host key documents. This is an optional subsection.
Note: To configure crypto domains, you must
provide crypto-pt and host-attestation
subsections. To configure the same, see Crypto Passthrough.
The logging
subsection
LogDNA
The minimum subsection that is required for this section is the logDNA subsection. For more information, see Logging for IBM Hyper Protect Virtual Servers.
The following snippet is an example of the logDNA
subsection:
logging:
logDNA:
hostname: <host name of the Log Analysis instance>
ingestionKey: <ingestion Key of the Log Analysis instance>
port: <port default-6514>
tags: ["custom tag name 1", "custom tag name 2"]
ICL
The minimum subsection that is required for this section is logRouter. For more information, see Logging for IBM Hyper Protect Virtual Servers.
The following snippet is an example of the ICL
subsection:
env:
logging:
logRouter:
hostname: <host name of the service instance> /
iamApiKey: <iamApiKey of the service instance> / xxxx
port: <port of the service instance(443)>
The env -
volumes subsection
Read the workload - volumes
subsection of the workload section before you continue with this
section. As already mentioned, for auto disk encryption of the
attached data volume, you must provide two customer seeds, one in
the workload - volumes subsection, and
the other in the env- volumes subsection.
The seeds can be any random text of your choice.
This is an example of the env -
volumes subsection:
volumes:
test:
seed: env phrase
The following snippet as an example when you use the "rolling of seeds" feature:
volumes:
test:
seed: env phrase1
previousSeed: env phrase
The following snippet is an example when you use multiple volumes:
env:
logging:
logDNA:
hostname: syslog-a.eu-gb.logging.cloud.ibm.com
ingestionKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
port: 6514
volumes:
test1:
seed: "seed1"
test2:
seed: "seed2"
As mentioned, you can integrate with Hyper Protect Crypto Services to generate a third seed and wrap it with your root key. See the following example. For more information, See Securing your data.
volumes:
test:
kms:
- apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
type: "public"
- apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx:key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
type: "private"
seed: "seed1"
kmsTimeout: 10
signingKey
subsection
For information about how to use the signingKey,
see Contract signature.
env subsection
-
If Pod descriptors are used in the
workloadsection:See the example for the template format in the
playsubsection. -
If a docker compose file is used in the
workloadsection:If the docker compose file has an environment section, you can use the following snippet as an example:
environment: KEY1: "${Value1}" KEY2: "${Value2}"When the docker compose file has an environment section, as shown in the example above, then you can pass the values in the
envsection of the deployer. The following example shows how to specify the values for theenvvariables:env: value1: "abc" value2: "xyz"
Contract encryption
You can encrypt the contents of a contract. Although you can use the contract without encryption, it is recommended that you encrypt the contract. It is also recommended that you initially try to use a non-encrypted contract for testing purposes, and after it works as expected, you can use an encrypted contract for your production environment.
You can decide which sections of the contract need encryption.
For example, you can choose to encrypt only the
workload section, or encrypt only the env
section.
When the IBM Hyper Protect Virtual Servers instance boots, the bootloader decrypts the contract. It takes the value of each of the sections in the contract and decrypts it if it is encrypted. If it finds that a section is not encrypted, it considers it as it is without any decryption. You must use the public key to encrypt the contract before you pass it as an input.
The encryption and attestation certificates are signed by the IBM intermediate certificate and this is signed by the IBM Digicert intermediate cert (which in turn is signed by DigiCert Trusted Root G4). For more information about the certificates, see DigiCert Trusted Root Authority Certificates.
Downloading the encryption certificate and extracting the public key
- Get the certificate that is used to encrypt the contract from the image TAR file.
- Validate the encryption certificate by following the instructions from Validating the certificates.
Creating the encrypted
workload section of a contract
The value of any section in a contract can be in plain text or encrypted. Complete the following steps to encrypt the workload section used in a contract:
-
Create the
docker-compose.yamlfile based on your workload requirements. For example,services: redisnode01: image: s390x/redis@sha256:db467ab5c53bdeef65762a7534e26fecb94a0f218bd38afd2eaba1a670c472b1 ports: - "6379:6379"For more information, see Overview of Docker Compose.
-
Create the workload section of the contract and add the contents in the
workload.yamlfile. -
Export the complete path of the
workload.yamlfile andibm-hyper-protect-container-runtime-25.1.0-encrypt.crt:WORKLOAD="<PATH to workload.yaml>" CONTRACT_KEY="<PATH to ibm-hyper-protect-container-runtime-25.1.0-encrypt.crt>" -
Use the following command to create a random password (the contract is encrypted via symmetric AES with a random PASSWORD):
PASSWORD="$(openssl rand 32 | base64 -w0)" -
Use the following command to encrypt password with
ibm-hyper-protect-container-runtime-25.1.0-encrypt.crt:ENCRYPTED_PASSWORD="$(echo -n "$PASSWORD" | base64 -d | openssl rsautl -encrypt -inkey $CONTRACT_KEY -certin | base64 -w0 )" -
Use the following command to encrypt the
workload.yamlfile with a random password:ENCRYPTED_WORKLOAD="$(echo -n "$PASSWORD" | base64 -d | openssl enc -aes-256-cbc -pbkdf2 -pass stdin -in "$WORKLOAD" | base64 -w0)" -
Use the following command to get the encrypted section of the contract:
echo "hyper-protect-basic.${ENCRYPTED_PASSWORD}.${ENCRYPTED_WORKLOAD}" -
Get the output from step 7 and add it to the
user-data.yamlfile.workload: hyper-protect-basic.js7TGt77EQ5bgTIKk5C0pViFTRHqWtn..............Note: The prefixhyper-protect-basicis mandatory.
Creating the encrypted
env section of a contract
Complete the following steps to encrypt the env
section used in a contract:
-
Create the env section of the contract and add the contents in the
env.yamlfile. -
Export the complete path of the
env.yamlfile andibm-hyper-protect-container-runtime-25.1.0-encrypt.crt:ENV="<PATH to env.yaml>" CONTRACT_KEY="<PATH to ibm-hyper-protect-container-runtime-25.1.0-encrypt.crt>" -
Use the following command to create a random password:
PASSWORD="$(openssl rand 32 | base64 -w0)" -
Use the following command to encrypt password with
ibm-hyper-protect-container-runtime-25.1.0-encrypt.crt:ENCRYPTED_PASSWORD="$(echo -n "$PASSWORD" | base64 -d | openssl rsautl -encrypt -inkey $CONTRACT_KEY -certin | base64 -w0)" -
Use the following command to encrypt env.yaml with a random password:
ENCRYPTED_ENV="$(echo -n "$PASSWORD" | base64 -d | openssl enc -aes-256-cbc -pbkdf2 -pass stdin -in "$ENV" | base64 -w0)" -
Use the following command to get the encrypted section of the contract:
echo "hyper-protect-basic.${ENCRYPTED_PASSWORD}.${ENCRYPTED_ENV}" -
To encrypt the workload section, see Creating the encrypted
workloadsection of a contract. -
Get the output from step 6 and add it to the
user-data.yamlfile.env: hyper-protect-basic.VWg/5/SWE+9jLfhr8q4i.........
Contract signature
Contract signature is an optional feature that can be used with
the contract. You can choose to sign a contract before it is passed
as input. Contracts that are in plain text or encrypted can be
signed. Validation of the contract signature is done by the IBM
Hyper Protect Virtual Servers image. The purpose of this signature
feature is to ensure that the workload and
env sections are always used together and are not
tampered with by a third party. Another optional feature in
contract signature is setting expiry of contract using signing
certificate. Using this feature, if an instance is booted after the
certificate has expired, the instance will fail to boot. The
signature of the workload and the env
sections are added as the value to the
envWorkloadSignature section.
Following are the two sections in a contract that are relevant while creating and adding a contract signature:
-
envWorkloadSignature: This is section where the signature of the other sections of the contract is added. This section is not required for a contract that is not signed. -
signingKey: This is a subsection that must be added to theenvsection of the contract. This holds the value to the user-generated public key or certificate, whose corresponding private key was used to create the contract signature. Public key or certificate can also be parsed as base64 string.
Complete the following steps to create the contract signature:
-
Use the following command to generate key pair to sign the contract:
openssl genrsa -aes128 -passout pass:test1234 -out private.pem 4096 openssl rsa -in private.pem -passin pass:test1234 -pubout -out public.pemNote: "test1234" is the passphrase to generate keys; however, you can use your own. -
Utilise the following command as an example to get the signing key:
key=$(awk -vRS="\n" -vORS="\\\n" '1' public.pem) echo ${key%\\n} -
If you want to pass the signing key as base64 optionally, use the following command:
key=$(cat public.pem | base64 -w 0) echo $key -
If you want to enable contract expiry optionally, refer to the steps mentioned in the following:
a. Use the following command to generate a certificate request:
openssl req -new -key private.pem -passin pass:test1234 -out csr.pemb. The command generates the certificate. Utilise the following steps to generate the certificate:
-
Use the following command to generate private key for CA:
openssl genrsa -out personal_ca.key 2048 -
Generate a self-signed CA certificate using the following command:
openssl req -new -x509 -key ca.key -out personal_ca.crt -
Use the following command to sign CSR with self signed CA certificate along with number of days for the certificate to be valid. The end date of the generated certificate is the contract expiry date.
openssl x509 -req -in csr.pem -CA personal_ca.crt -CAkey personal_ca.key -CAcreateserial -out certificate.pem -days 365
c. Use the following command as an example to get the certificate:
certificate=$(awk -vRS="\n" -vORS="\\\n" '1' certificate.pem) echo ${certificate%\\n}d. Use the following command as an example to optionally get the certificate in base64 format:
certificate=$(cat certificate.pem | base64 -w 0) echo $certificate -
-
Create the
env.yamlfile. For that, use the following as an example:a. If
signingkeyis a public key:env: | type: env logging: logDNA: hostname: syslog-a.eu-gb.logging.cloud.ibm.com ingestionKey: cfae1522876e860e58f5844a33bdcaa8 port: 6514 volumes: test: seed: hogwarts signingKey: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvLaeSA8Nc3p99HNUMwon\n5lMMALAsIxRRpWUaEZ5IcUky2sgCi/rSmxU2sm6FK/BmCftk33f5W2BsYHdY9R/0\nELZ9A4POQcJsPF3ronU2QHwnRjcqYuUFXmf1VqfPPLpELriFNoCb2FN2zCa+VUmu\n+fGhroZ3Fr9kBPwJhGr917E5jeCQ+MzsGkulcTvr0SfvThiZQQ/KlU0R35ThamF3\n8C0F5IQBpqDUwDFmWvD5lF2SmprpluDBFEj8LLfLxvW9M2Qwku6nGUnnFReg3vNH\n7IF0SRr1K1AdO5hEmevCdyG9hgTdUY6dXcjntiN/kbqXErILknvzDnb4jyPZZRdK\ndrOzVt8hjbdmkS396SrMFtA++QrV3GNZl5zCscpn6d8S7BEA8mDzroo2UAbrypVP\n9l9AmzUnmnPCpZQySUUHoY0xG2vgMSA50CWH7Uwjmpixr02Td4/LU8fE7NWCO6ci\nx4++ANSaxu+uuZ2Pe1OjjgV98r06ZUs38eaxptLZqLpn3N6w8WAJxGwSLapZwNtP\ng2spUXu2Eh/TN5t4/ly5iXOsyIy8IPtTrUPX7rpaaqFZ72P6BJLj3WLEvOG/eF/8\nBTjrsZAjb8YjkO1uGk10IPa63sniZWe5vlm9w9UKy2uGuy6RhWxwoVHRRbfhboQF\nsO20dsVwgTZn8c46HMD2PoMCAwEAAQ==\n-----END PUBLIC KEY----"b. If
signingkeyis a certificate:env: | type: env logging: logDNA: hostname: syslog-a.eu-gb.logging.cloud.ibm.com ingestionKey: cfae1522876e860e58f5844a33bdcaa8 port: 6514 volumes: test: seed: hogwarts signingKey: "-----BEGIN CERTIFICATE-----\nMIIFETCCAvkCFBAMxyO6Cl7BNKBGxtlAzHpI2oiNMA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwMTMwMDM1ODMzWhcNMjQwNTA5MDM1\nODMzWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE\nCgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOC\nAg8AMIICCgKCAgEAv5h6i7Fn1DMUM+3AnPPZUNMe1ss3KL/AmUmptwlAPErVoH1k\naiqTUsSNjXctj+nk95I+e2nugw/HlaVT1eRgEtvjssheXKboFn+zW/i31Nq9USgQ\nZA325VtchYlgJLXMPaH/ukBUr0UI4LnjC/dNdAQzKwWPNF2Jlv5wKX8OBVOQO9Df\nExVmcEkKDoh0nZk5eOA8vzJGhfr8TvQx9FQFsP4OXTwQgcdZV26mLm0bMkqEt3o5\n8OSpisqNGY1XnMHjOWNqSbErkpbIKEFAQSnWmzEvJdHsQX+7eTF7CisHJREseT4s\nUSuIFBZKXbS3qq6EL/EYviu0EGnY/rkJJcIRb8hycqHRgoITT2bWT7PSMUyXoX3G\nVKfp/xKFhkYzoRDSb5S0lh8sugmoRkioAkw6G56CP2hablPZRUMmUKceFfOG/k4L\nei8qJtbfQJ9BlCNRPpjqY3sGSdeXI4zefyQ8xxcus9Sl5wXZV86lz2lO/fz3Cvpd\n0eKvfv5uXyvF3O36lrlEERmSukaZYaEJECjxOUeafc7E1DVyIaMpc2SOum1crwMG\nRKhnU1JShDON0yClnKOlACfjFIpdpEMpE4lLps1x+PXV+x21zGBMUvXYa4xpbyWR\nK1gfMWmuvGOivl9y0mPSIeyJ9R/7bSRAbcYJR4N99TrtWxZU1yQi7HSRV5cCAwEA\nATANBgkqhkiG9w0BAQsFAAOCAgEAg006zJ4ZKwT8moOOl3PdThFUcf8rrIlec9Iy\nqPcWSqt5UTeYLIe58oGhhQmcaIRUOQaib5JqH2ukzqpo+gsJ3zZb3eIn4FB7cKef\nLqaiemOveEe1/qSwAGqMZyZELssiOflhnJdzuYSRWO8DO6Q6JMqQthDcw20budjO\nzP4nhXQqT+s8ljzqSJW77hDbrNAezTz/0SJFDtaMBs5UweX//7/4sXtJ8kBIBSxd\n7y4w8tuuxUaXOtYMjNrJAYLwFVeeO8CFURpbEuv7ABT0k8U4E8C6j4U4Jysx4XVP\nZj36rIAtvctchh0yAhHz8whXe1tvaFw9wzRDATnThFAuJG4Z07K2/rlDP9kO9wmn\ng8hHxKeqQMJDp29e0sGkz8oDi6Mz24k9CqFJJ0CUz1ntz7rrDkA3QwQbFRzk938y\n3rSfePO5qXlUQ9mm05hYr1EKKceTLEowc4XOouNLlUWGiRshRR1szMw5C29prFJ2\nyYuV9tBaFYkq7dnh8JnmrreEvAnsKyyECxMmtV/W701OSUYBcThwgAo+hkEeOJ+/\nwrOS7yoJqDF1y+5LLQJmUlrLCPXem3ZTa4UMe1p2g7ge7Dg6Zud9NDBcMigdHByt\nJP/i9PcJSEWrccWJ1ajToUCZ0wqfJ3Z4KqoEd0fadQhb32AuDUbu7E12EUFNPGIH\n8rQKbDU=\n-----END CERTIFICATE-----"c. If
singingkeyis a base64 encoded signing key or certificate:env: | type: env logging: logDNA: hostname: syslog-a.eu-gb.logging.cloud.ibm.com ingestionKey: cfae1522876e860e58f5844a33bdcaa8 port: 6514 volumes: test: seed: hogwarts signingKey: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlGRVRDQ0F2a0NGQkFNeHlPNkNsN0JOS0JHeHRsQXpIcEkyb2lOTUEwR0NTcUdTSWIzRFFFQkN3VUFNRVV4XG5DekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1TRXdId1lEVlFRS0RCaEpiblJsXG5jbTVsZENCWGFXUm5hWFJ6SUZCMGVTQk1kR1F3SGhjTk1qUXdNVE13TURNMU9ETXpXaGNOTWpRd05UQTVNRE0xXG5PRE16V2pCRk1Rc3dDUVlEVlFRR0V3SkJWVEVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFaE1COEdBMVVFXG5DZ3dZU1c1MFpYSnVaWFFnVjJsa1oybDBjeUJRZEhrZ1RIUmtNSUlDSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DXG5BZzhBTUlJQ0NnS0NBZ0VBdjVoNmk3Rm4xRE1VTSszQW5QUFpVTk1lMXNzM0tML0FtVW1wdHdsQVBFclZvSDFrXG5haXFUVXNTTmpYY3RqK25rOTVJK2UybnVndy9IbGFWVDFlUmdFdHZqc3NoZVhLYm9Gbit6Vy9pMzFOcTlVU2dRXG5aQTMyNVZ0Y2hZbGdKTFhNUGFIL3VrQlVyMFVJNExuakMvZE5kQVF6S3dXUE5GMkpsdjV3S1g4T0JWT1FPOURmXG5FeFZtY0VrS0RvaDBuWms1ZU9BOHZ6SkdoZnI4VHZReDlGUUZzUDRPWFR3UWdjZFpWMjZtTG0wYk1rcUV0M281XG44T1NwaXNxTkdZMVhuTUhqT1dOcVNiRXJrcGJJS0VGQVFTbldtekV2SmRIc1FYKzdlVEY3Q2lzSEpSRXNlVDRzXG5VU3VJRkJaS1hiUzNxcTZFTC9FWXZpdTBFR25ZL3JrSkpjSVJiOGh5Y3FIUmdvSVRUMmJXVDdQU01VeVhvWDNHXG5WS2ZwL3hLRmhrWXpvUkRTYjVTMGxoOHN1Z21vUmtpb0FrdzZHNTZDUDJoYWJsUFpSVU1tVUtjZUZmT0cvazRMXG5laThxSnRiZlFKOUJsQ05SUHBqcVkzc0dTZGVYSTR6ZWZ5UTh4eGN1czlTbDV3WFpWODZsejJsTy9mejNDdnBkXG4wZUt2ZnY1dVh5dkYzTzM2bHJsRUVSbVN1a2FaWWFFSkVDanhPVWVhZmM3RTFEVnlJYU1wYzJTT3VtMWNyd01HXG5SS2huVTFKU2hET04weUNsbktPbEFDZmpGSXBkcEVNcEU0bExwczF4K1BYVit4MjF6R0JNVXZYWWE0eHBieVdSXG5LMWdmTVdtdXZHT2l2bDl5MG1QU0lleUo5Ui83YlNSQWJjWUpSNE45OVRydFd4WlUxeVFpN0hTUlY1Y0NBd0VBXG5BVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBZ0VBZzAwNnpKNFpLd1Q4bW9PT2wzUGRUaEZVY2Y4cnJJbGVjOUl5XG5xUGNXU3F0NVVUZVlMSWU1OG9HaGhRbWNhSVJVT1FhaWI1SnFIMnVrenFwbytnc0ozelpiM2VJbjRGQjdjS2VmXG5McWFpZW1PdmVFZTEvcVN3QUdxTVp5WkVMc3NpT2ZsaG5KZHp1WVNSV084RE82UTZKTXFRdGhEY3cyMGJ1ZGpPXG56UDRuaFhRcVQrczhsanpxU0pXNzdoRGJyTkFlelR6LzBTSkZEdGFNQnM1VXdlWC8vNy80c1h0SjhrQklCU3hkXG43eTR3OHR1dXhVYVhPdFlNak5ySkFZTHdGVmVlTzhDRlVScGJFdXY3QUJUMGs4VTRFOEM2ajRVNEp5c3g0WFZQXG5aajM2cklBdHZjdGNoaDB5QWhIejh3aFhlMXR2YUZ3OXd6UkRBVG5UaEZBdUpHNFowN0syL3JsRFA5a085d21uXG5nOGhIeEtlcVFNSkRwMjllMHNHa3o4b0RpNk16MjRrOUNxRkpKMENVejFudHo3cnJEa0EzUXdRYkZSems5Mzh5XG4zclNmZVBPNXFYbFVROW1tMDVoWXIxRUtLY2VUTEVvd2M0WE9vdU5MbFVXR2lSc2hSUjFzek13NUMyOXByRkoyXG55WXVWOXRCYUZZa3E3ZG5oOEpubXJyZUV2QW5zS3l5RUN4TW10Vi9XNzAxT1NVWUJjVGh3Z0FvK2hrRWVPSisvXG53ck9TN3lvSnFERjF5KzVMTFFKbVVsckxDUFhlbTNaVGE0VU1lMXAyZzdnZTdEZzZadWQ5TkRCY01pZ2RIQnl0XG5KUC9pOVBjSlNFV3JjY1dKMWFqVG9VQ1owd3FmSjNaNEtxb0VkMGZhZFFoYjMyQXVEVWJ1N0UxMkVVRk5QR0lIXG44clFLYkRVPVxuLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVxu
A notification about the expiry of the contract will be sent to your logging service. Timelines for the notification are as follows:
- On the first of every month
- Everyday for 30 days before the expiry
- Once in every 4 hours if the contract is about to expire in 7 days
- Each and every hour if the contract has already expired or is about to expire in a day
Preparing the Attestation section
Attestation is the optional feature that can be used with
contract. The attestationPublicKey is the user
provided public key used to encrypt the attestation document. This
can be provided as a public RSA key or base64 encoded of the public
RSA key as a part of the contract.
-
If you use the plain text public RSA key for the
attestationPublicKeyin the yaml file, use the following example:attestationPublicKey: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvLaeSA8Nc3p99HNUMwon\n5lMMALAsIxRRpWUaEZ5IcUky2sgCi/rSmxU2sm6FK/BmCftk33f5W2BsYHdY9R/0\nELZ9A4POQcJsPF3ronU2QHwnRjcqYuUFXmf1VqfPPLpELriFNoCb2FN2zCa+VUmu\n+fGhroZ3Fr9kBPwJhGr917E5jeCQ+MzsGkulcTvr0SfvThiZQQ/KlU0R35ThamF3\n8C0F5IQBpqDUwDFmWvD5lF2SmprpluDBFEj8LLfLxvW9M2Qwku6nGUnnFReg3vNH\n7IF0SRr1K1AdO5hEmevCdyG9hgTdUY6dXcjntiN/kbqXErILknvzDnb4jyPZZRdK\ndrOzVt8hjbdmkS396SrMFtA++QrV3GNZl5zCscpn6d8S7BEA8mDzroo2UAbrypVP\n9l9AmzUnmnPCpZQySUUHoY0xG2vgMSA50CWH7Uwjmpixr02Td4/LU8fE7NWCO6ci\nx4++ANSaxu+uuZ2Pe1OjjgV98r06ZUs38eaxptLZqLpn3N6w8WAJxGwSLapZwNtP\ng2spUXu2Eh/TN5t4/ly5iXOsyIy8IPtTrUPX7rpaaqFZ72P6BJLj3WLEvOG/eF/8\nBTjrsZAjb8YjkO1uGk10IPa63sniZWe5vlm9w9UKy2uGuy6RhWxwoVHRRbfhboQF\nsO20dsVwgTZn8c46HMD2PoMCAwEAAQ==\n-----END PUBLIC KEY----" -
If you use the base64 encoded signing key for the
attestationPublicKeyin the yaml file, use the following command and example.base64 -w0 <public RSA key file>You need to replace
<public RSA key file>with the path to your actual public RSA key file.attestationPublicKey: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpRENDQVM2Z0F3SUJBZ0lSQUxCMXBPYlpEQlRRc09GSFlxazMzaWd3Q2dZSUtvWkl6ajBFQXdJd0tqRW8KTUNZR0ExVUVBeE1mZFhNdWFXTnlMbWx2TDNCeVlXSm9ZWFF4TWpNdmJYa3RhR0Z3Y205NGVUQWVGdzB5TWpBMApNVE14TURFd01ETmFGdzB6TWpBME1UQXhNREV3TUROYU1Db3hLREFtQmdOVkJBTVRIM1Z6TG1samNpNXBieTl3CmNtRmlhR0YwTVRJekwyMTVMV2hoY0hKdmVIa3dXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBU1AKWGsrelE2MlFZNjI3MWQ1cTBMZHY3SGc3QzZkMGZOUlRsQmJXekhOWWFDZzlpU0piYnVNdjVBY0JmMjlqQi83eApqYzhzVitxMksyemtkTHV4QWxGWm96VXdNekFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJCkt3WUJCUVVIQXdNd0RBWURWUjBUQVFIL0JBSXdBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUIzd0JTa0IxaXAKZHZZYlBMbFBmS3RZT0hsYnZzUllKa0FZM2hnY0xuNWhwQUloQUt6cmhsU3p4K1I5bmdtMTBlZVkyaFNCRmgrawpMWHp6SFkwaktTVzhyM1FhCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K -
If you encrypt the
attestationPublicKeyin the yaml file, you must follow the same steps as mentioned in the Contract encryption.To decrypt the attestation document, follow the instructions in the Decrypting the attestation document.
Preparing the signature
Complete the following steps to prepare the signature:
If you have got the encrypted user-data.yaml from
the Creating the
encrypted workload section of a contract, and
Creating encrypted
env section of a contract sections, skip to step
3.
-
Get the encrypted
workload.yamland encryptedenv.yamlfiles. -
Add them into the
user-data.yamlfile.workload: hyper-protect-basic.js7TGt77EQ5bgTIKk5C0pViFTRHqWtn.............. env: hyper-protect-basic.VWg/5/SWE+9jLfhr8q4i......... -
Create the
contract.txtfile. Add the value ofworkloadfirst then add the value ofenvfrom theuser-data.yamlfile. Ensure that there is no space or new line afterworkloadand beforeenv. Also, ensure that there is no new line or space at the end of the file. It is recommended to cross-check the binary content of thecontract.txtfile with tools such ashexdump. In the binary file dump, make sure that you do not see the0aASCII value as the last entry.hyper-protect-basic.js7TGt77EQ5bgTIKk5C0pViFTRHqWtn..............hyper-protect-basic.VWg/5/SWE+9jLfhr8q4i......... -
Use the following command to generate the signature:
echo $( cat contract.txt | tr -d "\n\r" | openssl dgst -sha256 -sign private.pem | openssl enc -base64) | tr -d ' ' -
Add the signature into the
user-data.yamlfile:workload: hyper-protect-basic.js7TGt77EQ5bgTIKk5C0pViFTRHqWtn.............. env: hyper-protect-basic.VWg/5/SWE+9jLfhr8q4i......... envWorkloadSignature: Icbm1D/CVpLNYkWRC9e .....
Getting started with a simple IBM Hyper Protect Virtual Servers contract
1. Get the details of your logging instance
You can configure logging with IBM Log Analysis or a generic syslog backend. This example uses IBM Log Analysis. There are different plans that you can choose from. To understand how you get the required details including the hostname and the ingestion key, see Logging for IBM Hyper Protect Virtual Servers.
2. Create the env section
Ensure that you do not miss the pipe symbol '|' if you are using a plain text contract. It is not required if you are planning to encrypt the section.
env: |
type: env
logging:
logDNA:
hostname: syslog-a.au-syd.logging.cloud.ibm.com
ingestionKey: XXXXXXXXXX
port: 6514
3. Prepare the docker-compose file
Assuming that you have the logging details, find a simple docker compose file. The following example has a public NGINX container. Create a docker-compose.yaml by using the example.
services:
nginx:
image: docker.io/library/nginx@sha256:b1306efee704017b0e02efadc011d374063a4e9c47b86bdc57744fc3f0666383
ports:
- 80:80
- 443:443
4. Get the base64 encoded version of the docker-compose file
tar -czvf compose.tgz docker-compose.yaml
base64 -i compose.tgz > compose.b64
5. Create the compose section with it
compose:
archive: H4sIADXNg2IAA+3W326CMBQGcK59it555XbanraMq70KlOLIJhjqzPb2q6g3S9xiIi7T75eQlj+hDYcP6vvVuo/hMZsQJc6YsU2+t2NfsmKyyhHLjKRUSmbCTDmpo/e4KQchsqHvNz9d99v5f8of6l/3/jUMi8Puw+fq7XJj7ApsmU/WX2m7r7/j9Abs6s/W2kzQ5aZw2p3XfxuG2PZdIeZ6Poth2LY+xGImRLdsu49dR4h2VS5DsT/yHF9KZWxRSU02NCGkxJJ0FQVSoSlrn8Jba5eyrEsOT55dlduq9sY55sbrhlJpda7HG6/7YRP3YyxETkVOhz6zLtI2++unc/tO5b+84AfgrPyn4JM0pDXyfw3I/32bMvdH5+SfOK0TpdZWIv/XgPzftynX/Udn/f/NmH+l8P+/CuQfAAAAAAAAAAAAAOD2fAEPQbuiACgAAA==
6. Populate the workload section. Ensure that you do not miss the pipe symbol (|) if you are using a plain text contract
workload: |
type: workload
compose:
archive: H4sIADXNg2IAA+3W326CMBQGcK59it555XbanraMq70KlOLIJhjqzPb2q6g3S9xiIi7T75eQlj+hDYcP6vvVuo/hMZsQJc6YsU2+t2NfsmKyyhHLjKRUSmbCTDmpo/e4KQchsqHvNz9d99v5f8of6l/3/jUMi8Puw+fq7XJj7ApsmU/WX2m7r7/j9Abs6s/W2kzQ5aZw2p3XfxuG2PZdIeZ6Poth2LY+xGImRLdsu49dR4h2VS5DsT/yHF9KZWxRSU02NCGkxJJ0FQVSoSlrn8Jba5eyrEsOT55dlduq9sY55sbrhlJpda7HG6/7YRP3YyxETkVOhz6zLtI2++unc/tO5b+84AfgrPyn4JM0pDXyfw3I/32bMvdH5+SfOK0TpdZWIv/XgPzftynX/Udn/f/NmH+l8P+/CuQfAAAAAAAAAAAAAOD2fAEPQbuiACgAAA==
7. Your simple contract is ready
env: |
type: env
logging:
logDNA:
hostname: syslog-a.au-syd.logging.cloud.ibm.com
ingestionKey: xxxxxxxxxx
port: 6514
workload: |
type: workload
compose:
archive: H4sIADXNg2IAA+3W326CMBQGcK59it555XbanraMq70KlOLIJhjqzPb2q6g3S9xiIi7T75eQlj+hDYcP6vvVuo/hMZsQJc6YsU2+t2NfsmKyyhHLjKRUSmbCTDmpo/e4KQchsqHvNz9d99v5f8of6l/3/jUMi8Puw+fq7XJj7ApsmU/WX2m7r7/j9Abs6s/W2kzQ5aZw2p3XfxuG2PZdIeZ6Poth2LY+xGImRLdsu49dR4h2VS5DsT/yHF9KZWxRSU02NCGkxJJ0FQVSoSlrn8Jba5eyrEsOT55dlduq9sY55sbrhlJpda7HG6/7YRP3YyxETkVOhz6zLtI2++unc/tO5b+84AfgrPyn4JM0pDXyfw3I/32bMvdH5+SfOK0TpdZWIv/XgPzftynX/Udn/f/NmH+l8P+/CuQfAAAAAAAAAAAAAOD2fAEPQbuiACgAAA==