Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions .github/workflows/sconify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
name: Build, Test and Push Docker Image

on:
workflow_call:
inputs:
docker-registry:
description: "Docker registry of docker image to sconify"
default: "docker.io"
type: string
docker-username:
description: "Docker registry username"
type: string
required: true
image-name:
description: "Name of docker image to sconify"
type: string
required: true
image-tag:
description: "Tag of docker image to sconify"
type: string
required: true
scontain-username:
description: "Scontain registry username"
type: string
required: true
sconify-version:
description: "Version of the sconify image to use"
type: string
required: true
binary:
description: "Path of the binary to use"
type: string
required: true
command:
description: "Command to execute (default: ENTRYPOINT + CMD of native image)"
type: string
binary-fs:
description: "Embed the file system into the binary via Scone binary file system (default: false)"
type: boolean
default: false
fs-dir:
description: "Path of directories to add to the binary file system (use multiline to add multiple directories)"
type: string
fs-file:
description: "Path of files to add to the binary file system (use multiline to add multiple files)"
type: string
host-path:
description: "Host path, served directly from the host file system (use multiline to add multiple path)"
type: string
heap:
description: "Enclave heap size (default 1G)"
type: string
default: "1G"
mprotect:
description: "Scone mprotect mode (0:disable; 1:enable; default 0)"
type: number
default: 0
dlopen:
description: "Scone dlopen mode (0:disable; 1:enable; default 0)"
type: number
default: 0
sconify-debug:
description: "Create Scone debug image (default true)"
type: boolean
default: true
sconify-prod:
description: "Create Scone production image (default true)"
type: boolean
default: true
runner:
description: "Runner to use (overrides `runs-on`) ⚠️ the specified runner must feature Ubuntu OS and docker CE"
type: string
default: "ubuntu-latest"
secrets:
docker-password:
description: "Docker Registry Password or Token"
required: true
scontain-password:
description: "Scontain Registry Password or Token"
required: true
scone-signing-key:
description: "Signing Key for Scone Production (not required with `sconify-prod: false`)"
required: false
outputs:
debug-image:
description: "Debug Sconified Image"
value: ${{ jobs.build.outputs.debug-image }}
debug-mrenclave:
description: "Debug Sconified Image MrEnclave Fingerprint"
value: ${{ jobs.build.outputs.debug-mrenclave }}
debug-checksum:
description: "Debug Sconified Image Checksum"
value: ${{ jobs.build.outputs.debug-checksum }}
prod-image:
description: "Prod Sconified Image"
value: ${{ jobs.build.outputs.prod-image }}
prod-mrenclave:
description: "Prod Sconified Image MrEnclave Fingerprint"
value: ${{ jobs.build.outputs.prod-mrenclave }}
prod-checksum:
description: "Prod Sconified Image Checksum"
value: ${{ jobs.build.outputs.prod-checksum }}

jobs:
build:
runs-on: ${{ inputs.runner }}
outputs:
debug-image: ${{ steps.push-debug.outputs.image }}
debug-mrenclave: ${{ steps.push-debug.outputs.mrenclave }}
debug-checksum: ${{ steps.push-debug.outputs.checksum }}
prod-image: ${{ steps.push-prod.outputs.image }}
prod-mrenclave: ${{ steps.push-prod.outputs.mrenclave }}
prod-checksum: ${{ steps.push-prod.outputs.checksum }}
steps:
- name: Create Temporary Directory
run: mkdir -p ${{github.workspace}}/tmp

- name: Prepare Sconify Command
id: prepare-command
run: |
FROM_IMAGE=${{ inputs.docker-registry }}/${{ inputs.image-name }}:${{ inputs.image-tag }}
DEBUG_IMAGE=$FROM_IMAGE-scone-debug-${{ inputs.sconify-version }}
echo "debug-image=$DEBUG_IMAGE"
echo "debug-image=$DEBUG_IMAGE" >> "$GITHUB_OUTPUT"
PROD_IMAGE=$FROM_IMAGE-scone-prod-${{ inputs.sconify-version }}
echo "prod-image=$PROD_IMAGE"
echo "prod-image=$PROD_IMAGE" >> "$GITHUB_OUTPUT"
SCONIFY_CMD="sconify_iexec"
# REQUIRED:
# --from
SCONIFY_CMD+=" --from=$FROM_IMAGE"
# --to will be added later on
# --binary
SCONIFY_CMD+=" --binary=${{ inputs.binary }}"
# OPTIONAL:
# --command option
[[ -n '${{ inputs.command }}' ]] && SCONIFY_CMD+=" --command=${{ inputs.command }}"
# --host-path variadic option
while IFS= read -r line; do [[ -n "$line" ]] && SCONIFY_CMD+=" --host-path=$line" ; done <<< '${{ inputs.host-path }}'
# BINARY FILE SYSTEM (binary fs):
# --binary-fs option
if ${{ inputs.binary-fs }}; then SCONIFY_CMD+=" --binary-fs"; fi
# --fs-dir variadic option
while IFS= read -r line; do [[ -n "$line" ]] && SCONIFY_CMD+=" --fs-dir=$line" ; done <<< '${{ inputs.fs-dir }}'
# --fs-file variadic option
while IFS= read -r line; do [[ -n "$line" ]] && SCONIFY_CMD+=" --file=$line" ; done <<< '${{ inputs.fs-file }}'
# SCONE ENV VARS:
# --heap option
[[ -n '${{ inputs.heap }}' ]] && SCONIFY_CMD+=" --heap=${{ inputs.heap }}"
# --dlopen option
[[ -n '${{ inputs.dlopen }}' ]] && SCONIFY_CMD+=" --dlopen=${{ inputs.dlopen }}"
# --mprotect option
[[ -n '${{ inputs.mprotect }}' ]] && SCONIFY_CMD+=" --mprotect=${{ inputs.mprotect }}"
# DEBUG
# --verbose --no-color options
SCONIFY_CMD+=" --verbose --no-color"
echo "sconify-base-command=$SCONIFY_CMD"
echo "sconify-base-command=$SCONIFY_CMD" >> "$GITHUB_OUTPUT"

- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ${{ inputs.docker-registry }}
username: ${{ inputs.docker-username }}
password: ${{ secrets.docker-password }}

- name: Login to Scontain Docker Registry
uses: docker/login-action@v3
with:
registry: "registry.scontain.com"
username: ${{ inputs.scontain-username }}
password: ${{ secrets.scontain-password }}

- name: Pull Image to Sconify
run: docker pull ${{ inputs.docker-registry }}/${{ inputs.image-name }}:${{ inputs.image-tag }}

- name: Pull Sconify Image
run: docker pull registry.scontain.com/scone-production/iexec-sconify-image:${{ inputs.sconify-version }}

- name: Sconify Image Debug
if: ${{ inputs.sconify-debug }}
run: |
docker run \
--rm \
-v /var/run/docker.sock:/var/run/docker.sock \
registry.scontain.com/scone-production/iexec-sconify-image:${{ inputs.sconify-version }} \
${{ steps.prepare-command.outputs.sconify-base-command }} \
--to=${{ steps.prepare-command.outputs.debug-image }}

- name: Push Debug Image
if: ${{ inputs.sconify-debug }}
id: push-debug
run: |
docker push ${{ steps.prepare-command.outputs.debug-image }}
echo "image=${{ steps.prepare-command.outputs.debug-image }}" >> "$GITHUB_OUTPUT"
echo "checksum=0x$(docker image inspect ${{ steps.prepare-command.outputs.debug-image }} | jq .[0].RepoDigests[0] | sed 's/"//g' | awk -F '@sha256:' '{print $2}')" >> "$GITHUB_OUTPUT"
echo "mrenclave=$(docker run --rm -e SCONE_HASH=1 ${{ steps.prepare-command.outputs.debug-image }})" >> "$GITHUB_OUTPUT"

- name: Sconify Image Prod
if: ${{ inputs.sconify-prod }}
run: |
mkdir -p ${{github.workspace}}/tmp/sig
echo "${{ secrets.scone-signing-key }}" > ${{github.workspace}}/tmp/sig/enclave-key.pem
docker run \
--rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ${{github.workspace}}/tmp/sig/enclave-key.pem:/sig/enclave-key.pem \
registry.scontain.com/scone-production/iexec-sconify-image:${{ inputs.sconify-version }} \
${{ steps.prepare-command.outputs.sconify-base-command }} \
--to=${{ steps.prepare-command.outputs.prod-image }} \
--scone-signer=/sig/enclave-key.pem

- name: Push Prod Image
if: ${{ inputs.sconify-prod }}
id: push-prod
run: |
docker push ${{ steps.prepare-command.outputs.prod-image }}
echo "image=${{ steps.prepare-command.outputs.prod-image }}" >> "$GITHUB_OUTPUT"
echo "checksum=0x$(docker image inspect ${{ steps.prepare-command.outputs.prod-image }} | jq .[0].RepoDigests[0] | sed 's/"//g' | awk -F '@sha256:' '{print $2}')" >> "$GITHUB_OUTPUT"
echo "mrenclave=$(docker run --rm -e SCONE_HASH=1 ${{ steps.prepare-command.outputs.prod-image }})" >> "$GITHUB_OUTPUT"

- name: Clean Temporary Directory
if: always()
run: rm -rf ${{github.workspace}}/tmp
157 changes: 157 additions & 0 deletions sconify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Sconify - Reusable Workflow Documentation 🚀

## Overview 🌟

This reusable GitHub Actions workflow automates the process of sconifying a Docker image. It is configurable via inputs for the Sconification options and secrets for docker registries credentials and production enclave signing key.

The workflow performs the following actions:

- **Create Temporary Directory**
- **Prepare Sconify Command** and args
- **Login to Docker Registry**
- **Login to Scontain Docker Registry**
- **Pull Image to Sconify** from Docker Registry
- **Pull Sconify Image** from Scontain Docker Registry
- [unless input `sconify-debug: false`]
- **Sconify Image Debug**
- **Push Debug Image** to Docker Registry and prepare outputs (`debug-image`,`debug-mrenclave`,`debug-checksum`)
- [unless input `sconify-prod: false`]
- **Sconify Image Prod** using scone-signing-key stored in the Temporary Directory
- **Push Prod Image** to Docker Registry and prepare outputs (`prod-image`,`prod-mrenclave`,`prod-checksum`)
- **Clean Temporary Directory** always

## Workflow Inputs 🛠️

| **Input** | **Description** | **Required** | **Default** |
| --------------------- | -------------------------------------------------------------------------------------------------------- | ------------ | -------------------------------- |
| **docker-registry** | Docker registry of docker image to sconify | No | docker.io |
| **docker-username** | Docker registry username | Yes | - |
| **image-name** | Name of docker image to sconify | Yes | - |
| **image-tag** | Tag of docker image to sconify | Yes | - |
| **scontain-username** | Scontain registry username | Yes | - |
| **sconify-version** | Version of the sconify image to use | Yes | - |
| **binary** | [SCONE] Path of the binary to use | Yes | - |
| **command** | [SCONE] Command to execute | No | ENTRYPOINT + CMD of native image |
| **binary-fs** | [SCONE] Embed the file system into the binary via Scone binary file system | No | false |
| **fs-dir** | [SCONE] Path of directories to add to the binary file system (use multiline to add multiple directories) | No | - |
| **fs-file** | [SCONE] Path of files to add to the binary file system (use multiline to add multiple files) | No | - |
| **host-path** | [SCONE] Host path, served directly from the host file system (use multiline to add multiple path) | No | - |
| **heap** | [SCONE] Enclave heap size | No | 1G |
| **dlopen** | [SCONE] Scone dlopen mode (0:disable; 1:enable) | No | 0 |
| **mprotect** | [SCONE] Scone mprotect mode (0:disable; 1:enable) | No | 0 |

| **sconify-debug** | Create Scone debug image | No | true |
| **sconify-prod** | Create Scone production image | No | true |
| **runner** | Runner to use (overrides `runs-on`) ⚠️ the specified runner must feature Ubuntu OS and docker CE | No | ubuntu-latest |

> ℹ️ for more details about [SCONE] options see [Scone's documentation](https://sconedocs.github.io/ee_sconify_image/#all-supported-options)

### Secrets 🔐

| **Secret** | **Description** | **Required** |
| --------------------- | ----------------------------------------------- | --------------------------------------- |
| **docker-password** | Docker Registry Password or Token | Yes |
| **scontain-password** | Scontain Registry Password or Token | Yes |
| **scone-signing-key** | Signing Key for Scone Production (PEM RSA-3072) | Yes unless `inputs.sconify-prod: false` |

### Outputs 📤

| **Output** | **Description** |
| ------------------- | ---------------------------------------------------------------------------------- |
| **debug-image** | Debug Sconified Image (unless `inputs.sconify-debug: false`) |
| **debug-mrenclave** | Debug Sconified Image MrEnclave Fingerprint (unless `inputs.sconify-debug: false`) |
| **debug-checksum** | Debug Sconified Image Checksum (unless `inputs.sconify-debug: false`) |
| **prod-image** | Prod Sconified Image (unless `inputs.sconify-prod: false`) |
| **prod-mrenclave** | Prod Sconified Image MrEnclave Fingerprint (unless `inputs.sconify-prod: false`) |
| **prod-checksum** | Prod Sconified Image Checksum (unless `inputs.sconify-prod: false`) |

## How to Use This Reusable Workflow 🔄

1. **Save the Workflow File**
This workflow is already saved as `.github/workflows/sconify.yml` in the repository. 💾

2. **Call the Reusable Workflow**
In another workflow file (e.g., triggered manually or by a release), invoke this reusable workflow like so:

```yaml
name: Sconify iApp

on:
workflow_dispatch:
inputs:
image-name:
required: true
type: string
image-tag:
required: true
type: string
sconify-debug:
type: boolean
default: true
sconify-prod:
type: boolean
default: true

jobs:
sconify:
uses: iExecBlockchainComputing/github-actions-workflows/.github/workflows/sconify.yml@feat/sconify
with:
# runner: your-runner-here ⚠️ control the runner used in the workflow to match your requirements
image-name: ${{ inputs.image-name }}
image-tag: ${{ inputs.image-tag }}
sconify-debug: ${{ inputs.sconify-debug }}
sconify-prod: ${{ inputs.sconify-prod }}
docker-registry: docker.io
sconify-version: 5.9.0-v15
binary: /usr/local/bin/node
command: node /app/src/app.js
host-path: |
/etc/hosts
/etc/resolv.conf
binary-fs: true
fs-dir: /app
heap: 1G
dlopen: 1
mprotect: 1
docker-username: ${{ vars.DOCKER_USERNAME }}
scontain-username: ${{ vars.SCONTAIN_USERNAME }}
secrets:
docker-password: ${{ secrets.DOCKER_TOKEN }}
scontain-password: ${{ secrets.SCONTAIN_TOKEN }}
scone-signing-key: ${{ secrets.SCONE_SIGNING_KEY }}

use-sconify-output:
# usually you want to deploy the sconified image as an iExec app using the sconify job outputs
runs-on: ubuntu-latest
needs: sconify
steps:
- run: |
echo "DEBUG IMAGE INFO: image=${{ needs.sconify.outputs.debug-image }} | checksum=${{ needs.sconify.outputs.debug-checksum }} | mrenclave=${{ needs.sconify.outputs.debug-mrenclave }}"
echo "PROD IMAGE INFO: image=${{ needs.sconify.outputs.prod-image }} | checksum=${{ needs.sconify.outputs.prod-checksum }} | mrenclave=${{ needs.sconify.outputs.prod-mrenclave }}"
```

3. **Configure Variables**
Ensure that the following variables are added to your repository's settings:

- `DOCKER_USERNAME`: Your Docker Registry username
- `SCONTAIN_USERNAME`: Your Scontain username

NB: Beware if you choose to use secrets to store registries usernames;
registries usernames can appear in sconified image names outputted as `outputs.debug-image` and `outputs.prod-image`, in such a case GitHub Actions blanks the outputs with this waring:

> Skip output 'prod-image' since it may contain secret.

> Skip output 'debug-image' since it may contain secret.

4. **Configure Secrets**
Ensure that the following secrets are added to your repository's settings:
- `DOCKER_PASSWORD`: Your Docker Registry password or access token
- `SCONTAIN_PASSWORD`: Your Scontain password or access token
- `SCONE_SIGNING_KEY`: The key to use for signing Scone Prod applications

## Prerequisites 📋

1. **Read/Write access to the image to sconify**

2. **Read access to Scontain's `iexec-sconify-image` image**:
- You must have a Scontain account with access to the `scone-production/iexec-sconify-image` image.