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
21 changes: 21 additions & 0 deletions .github/workflows/reusable-api-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,27 @@ jobs:
fi
shell: bash

- name: Check configuration files on server
run: |
ssh -o StrictHostKeyChecking=no \
-i ~/.ssh/ghrunnerci \
${{ secrets.host }} << 'EOF'
cd /opt/iapp-api
missing=0
if [ ! -f .env.app ]; then
echo ".env.app file not found on remote server"
missing=1
fi
if [ ! -f sig/enclave-key.pem ]; then
echo "sig/enclave-key.pem not found on remote server"
missing=1
fi
if [ "$missing" -ne 0 ]; then
exit 1
fi
EOF
shell: bash

- name: Prepare .env for Compose
run: |
printf "IMAGE_NAME=%s\nIMAGE_TAG=%s\n" "${{ env.IMAGE_NAME }}" "${{ inputs.tag }}"> .env
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

node_modules
api/.env
api/sig
.tags

cli/dist
15 changes: 14 additions & 1 deletion api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ The API is composed of:
- Node 20
- docker installed locally with support for linux/amd64 architecture (either
native or emulated)
- Scontain account whit pull access to docker repository
- Scontain account with pull access to docker repository
`registry.scontain.com/scone-production/iexec-sconify-image`
- An enclave signing key to sign Scone production images

Create a `.env` file see [`.env.template`](.env.template)

Expand All @@ -39,6 +40,18 @@ cp .env.template .env
# fill in the .env file
```

Create or provide your own enclave signing key in `sig/enclave-key.pem` to sign
Scone production images

> The enclave signing key should be a PEM formatted RSA key 3072 bits
>
> A valid signing key can be generated with openssl by running
> `openssl genrsa -3 3072`

```sh
npm run ensure-signing-key
```

## run locally

```sh
Expand Down
4 changes: 3 additions & 1 deletion api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ services:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# .env.app already on the server
- ./.env.app:/app/.env
- ./.env.app:/app/.env:ro
# enclave key already on the server in sig/enclave-key.pem
- ./sig/:/app/sig/:ro
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
interval: 30s
Expand Down
7 changes: 4 additions & 3 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"main": "index.js",
"type": "module",
"scripts": {
"start": "tsx --env-file=.env ./src/index.js",
"dev": "tsx --env-file=.env --watch ./src/index.js",
"dev:pretty": "tsx --env-file=.env --watch ./src/index.js | pino-pretty -tc",
"ensure-signing-key": "[ -e 'sig/enclave-key.pem' ] && echo 'using existing signing key' || (mkdir -p sig && openssl genrsa -3 -out sig/enclave-key.pem 3072 && echo 'generated new signing key')",
"start": "npm run ensure-signing-key && tsx --env-file=.env ./src/index.js",
"dev": "npm run ensure-signing-key && tsx --env-file=.env --watch ./src/index.js",
"dev:pretty": "npm run ensure-signing-key && tsx --env-file=.env --watch ./src/index.js | pino-pretty -tc",
"check-format": "prettier --check .",
"check-types": "tsc --project tsconfig.json",
"format": "prettier --write .",
Expand Down
1 change: 0 additions & 1 deletion api/src/sconify/deprecated_sconify.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ export async function deprecated_sconify({
sconifiedImageId = await sconifyImage({
fromImage: dockerImageToSconify,
sconifyVersion,
entrypoint: appEntrypoint,
binary: configTemplate.binary,
});
logger.info({ sconifiedImageId }, 'Sconified successfully');
Expand Down
7 changes: 7 additions & 0 deletions api/src/sconify/sconifyBuild.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const bodySchema = z.object({
.enum(Object.keys(TEMPLATE_CONFIG) as [TemplateName])
.default('JavaScript'),
sconeVersion: z.enum(['v5', 'v5.9']).default('v5'),
sconeProd: z.boolean().default(false),
});

async function handleSconifyRequest(requestObj: object) {
Expand All @@ -37,13 +38,15 @@ async function handleSconifyRequest(requestObj: object) {
let dockerhubPushToken: string;
let sconeVersion: SconeVersion;
let template: TemplateName;
let sconeProd: boolean;
try {
({
yourWalletPublicAddress,
dockerhubImageToSconify,
dockerhubPushToken,
sconeVersion,
template,
sconeProd,
} = bodySchema.parse(requestObj));
} catch (error) {
throw fromError(error, {
Expand All @@ -58,6 +61,9 @@ async function handleSconifyRequest(requestObj: object) {
if (sconeVersion === 'v5') {
logger.warn('Deprecated feature hit: sconeVersion === "v5"');
}
if (sconeProd === false) {
logger.warn('Deprecated feature hit: sconeProd === false');
}

const { dockerImage, dockerImageDigest, fingerprint, entrypoint } =
await sconify({
Expand All @@ -66,6 +72,7 @@ async function handleSconifyRequest(requestObj: object) {
userWalletPublicAddress: yourWalletPublicAddress,
sconeVersion,
templateLanguage: template,
sconeProd,
});
return {
dockerImage,
Expand Down
7 changes: 5 additions & 2 deletions api/src/sconify/sconifyBuild.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export async function sconify({
pushToken,
sconeVersion,
templateLanguage,
sconeProd = false,
}: {
/**
* Examples of valid dockerImageToSconify:
Expand All @@ -39,6 +40,7 @@ export async function sconify({
pushToken: string;
templateLanguage: TemplateName;
sconeVersion: SconeVersion;
sconeProd?: boolean;
}): Promise<{
dockerImage: string;
dockerImageDigest: string;
Expand All @@ -58,6 +60,7 @@ export async function sconify({
templateLanguage,
userWalletPublicAddress,
wsEnabled,
sconeProd,
},
'New sconify request'
);
Expand Down Expand Up @@ -140,8 +143,8 @@ export async function sconify({
sconifiedImageId = await sconifyImage({
fromImage: dockerImageToSconify,
sconifyVersion,
entrypoint: appEntrypoint,
binary: configTemplate.binary,
prod: sconeProd,
});
logger.info({ sconifiedImageId }, 'Sconified successfully');
} finally {
Expand All @@ -168,7 +171,7 @@ export async function sconify({

const imageRepo = `${dockerUserName}/${imageName}`;
const sconifiedImageShortId = sconifiedImageId.substring(7, 7 + 12); // extract 12 first chars after the leading "sha256:"
const sconifiedImageTag = `${imageTag}-tee-scone-${sconifyVersion}-debug-${sconifiedImageShortId}`; // add digest in tag to avoid replacing previous build
const sconifiedImageTag = `${imageTag}-tee-scone-${sconifyVersion}-${sconeProd ? 'prod' : 'debug'}-${sconifiedImageShortId}`; // add digest in tag to avoid replacing previous build
const sconifiedImage = `${imageRepo}:${sconifiedImageTag}`;

let pushed;
Expand Down
72 changes: 50 additions & 22 deletions api/src/singleFunction/sconifyImage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { join } from 'node:path';
import { access, constants } from 'node:fs/promises';
import Docker from 'dockerode';
import { SCONIFY_IMAGE_NAME } from '../constants/constants.js';
import { logger } from '../utils/logger.js';
Expand All @@ -8,14 +10,16 @@ import { removeContainer } from './removeContainer.js';

const docker = new Docker();

const ENCLAVE_KEY_PATH = join(process.cwd(), 'sig/enclave-key.pem');

/**
* Sconifies an iapp docker image
*/
export async function sconifyImage({
fromImage,
sconifyVersion,
entrypoint,
binary,
prod = false,
}: {
/**
* image to sconify
Expand All @@ -25,42 +29,66 @@ export async function sconifyImage({
* sconifier version
*/
sconifyVersion: string;
/**
* command to run the app (whitelisted)
*/
entrypoint: string;
/**
* whitelisted binary
*/
binary: string;
/**
* sconify production flag
*/
prod?: boolean;
}): Promise<string> {
logger.info({ fromImage, entrypoint }, 'Running sconify command...');
logger.info(
{ fromImage },
`Running sconify command in ${prod ? 'prod' : 'debug'} mode...`
);
const sconifierImage = `${SCONIFY_IMAGE_NAME}:${sconifyVersion}`;

logger.info({ sconifierImage }, 'Pulling sconifier image...');
await pullSconeImage(sconifierImage);

if (prod) {
// check signing key can be read on host
try {
await access(ENCLAVE_KEY_PATH, constants.R_OK);
} catch (error) {
logger.error(
{ error, path: ENCLAVE_KEY_PATH },
'Cannot read enclave key from host'
);
throw new Error('Cannot read enclave key from host');
}
}

const toImage = `${fromImage}-tmp-sconified-${Date.now()}`; // create an unique temporary identifier for the target image
logger.info({ fromImage, toImage }, 'Sconifying...');

const sconifyBaseCmd = [
'sconify_iexec',
`--from=${fromImage}`,
`--to=${toImage}`,
'--binary-fs',
'--fs-dir=/app',
'--host-path=/etc/hosts',
'--host-path=/etc/resolv.conf',
`--binary=${binary}`,
'--heap=1G',
'--dlopen=1',
'--no-color',
'--verbose',
];

const baseBinds = ['/var/run/docker.sock:/var/run/docker.sock'];

const sconifyContainer = await docker.createContainer({
Image: sconifierImage,
Cmd: [
'sconify_iexec',
`--from=${fromImage}`,
`--to=${toImage}`,
'--binary-fs',
'--fs-dir=/app',
'--host-path=/etc/hosts',
'--host-path=/etc/resolv.conf',
`--binary=${binary}`,
'--heap=1G',
'--dlopen=1',
'--no-color',
'--verbose',
`--command=${entrypoint}`,
],
Cmd: prod
? sconifyBaseCmd.concat('--scone-signer=/sig/enclave-key.pem')
: sconifyBaseCmd,
HostConfig: {
Binds: ['/var/run/docker.sock:/var/run/docker.sock'],
Binds: prod
? baseBinds.concat(`${ENCLAVE_KEY_PATH}:/sig/enclave-key.pem:ro`) // mount signing key
: baseBinds,
},
});

Expand Down
4 changes: 2 additions & 2 deletions cli/src/cmd/debug.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from 'ethers';
import { askForWallet } from '../cli-helpers/askForWallet.js';
import { getIExecDebug } from '../utils/iexec.js';
import { getIExec } from '../utils/iexec.js';
import { getSpinner } from '../cli-helpers/spinner.js';
import * as color from '../cli-helpers/color.js';
import { handleCliError } from '../cli-helpers/handleCliError.js';
Expand All @@ -26,7 +26,7 @@ export async function debug({
const chainConfig = getChainConfig(chainName);
spinner.info(`Using chain ${chainName}`);
const signer = await askForWallet({ spinner });
const iexec = getIExecDebug({
const iexec = getIExec({
...chainConfig,
signer,
});
Expand Down
4 changes: 2 additions & 2 deletions cli/src/cmd/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { handleCliError } from '../cli-helpers/handleCliError.js';
import { getSpinner } from '../cli-helpers/spinner.js';
import { askForAppSecret } from '../cli-helpers/askForAppSecret.js';
import { askForWallet } from '../cli-helpers/askForWallet.js';
import { getIExecDebug } from '../utils/iexec.js';
import { getIExec } from '../utils/iexec.js';
import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
import * as color from '../cli-helpers/color.js';
import { hintBox } from '../cli-helpers/box.js';
Expand Down Expand Up @@ -43,7 +43,7 @@ export async function deploy({ chain }: { chain?: string }) {
if (useTdx) {
iexec = getIExecTdx({ ...chainConfig, signer });
} else {
iexec = getIExecDebug({ ...chainConfig, signer });
iexec = getIExec({ ...chainConfig, signer });
}

await ensureBalances({ spinner, iexec });
Expand Down
6 changes: 3 additions & 3 deletions cli/src/cmd/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { addRunData } from '../utils/cacheExecutions.js';
import { getSpinner, type Spinner } from '../cli-helpers/spinner.js';
import { handleCliError } from '../cli-helpers/handleCliError.js';
import { getIExecDebug } from '../utils/iexec.js';
import { getIExec } from '../utils/iexec.js';
import { extractZipToFolder } from '../utils/extractZipToFolder.js';
import { askShowResult } from '../cli-helpers/askShowResult.js';
import { goToProjectRoot } from '../cli-helpers/goToProjectRoot.js';
Expand Down Expand Up @@ -104,7 +104,7 @@ export async function runInDebug({
if (useTdx) {
iexec = getIExecTdx({ ...chainConfig, signer });
} else {
iexec = getIExecDebug({
iexec = getIExec({
...chainConfig,
signer,
});
Expand Down Expand Up @@ -151,7 +151,7 @@ export async function runInDebug({
// Workerpool Order
spinner.start('Fetching workerpool order...');
const workerpoolOrderbook = await iexec.orderbook.fetchWorkerpoolOrderbook({
workerpool: useTdx ? WORKERPOOL_TDX : chainConfig.workerpoolDebug,
workerpool: useTdx ? WORKERPOOL_TDX : chainConfig.workerpool,
app: iAppAddress,
dataset: protectedData || ethers.ZeroAddress,
minTag: SCONE_TAG,
Expand Down
12 changes: 4 additions & 8 deletions cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,36 +70,32 @@ export const WS_RECONNECTION_MAX_ATTEMPTS = Math.floor(

type ChainConfig = {
rpcHostUrl: string;
smsDebugUrl: string;
ipfsGatewayUrl: string;
iexecExplorerUrl: string;
workerpoolDebug: string;
workerpool: string;
};

export const DEFAULT_CHAIN = 'bellecour';

export const CHAINS_CONFIGURATIONS: Record<string, ChainConfig> = {
bellecour: {
rpcHostUrl: 'https://bellecour.iex.ec',
smsDebugUrl: 'https://sms.scone-debug.v8-bellecour.iex.ec',
ipfsGatewayUrl: 'https://ipfs-gateway.v8-bellecour.iex.ec',
iexecExplorerUrl: 'https://explorer.iex.ec/bellecour',
workerpoolDebug: 'debug-v8-learn.main.pools.iexec.eth',
workerpool: 'prod-v8-learn.main.pools.iexec.eth',
},
'arbitrum-mainnet': {
rpcHostUrl: 'https://arb1.arbitrum.io/rpc',
smsDebugUrl: 'https://sms-debug.arbitrum-mainnet.iex.ec',
ipfsGatewayUrl: 'https://ipfs-gateway.arbitrum-mainnet.iex.ec',
iexecExplorerUrl: 'https://explorer.iex.ec/arbitrum-mainnet',
workerpoolDebug: '0xAaA90d37034fD1ea27D5eF2879f217fB6fD7F7Ca',
workerpool: '0x2c06263943180cc024daffeee15612db6e5fd248',
},
...(useExperimentalNetworks && {
'arbitrum-sepolia-testnet': {
rpcHostUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
smsDebugUrl: 'https://sms.arbitrum-sepolia-testnet.iex.ec',
ipfsGatewayUrl: 'https://ipfs-gateway.arbitrum-sepolia-testnet.iex.ec',
iexecExplorerUrl: 'https://explorer.iex.ec/arbitrum-sepolia-testnet',
workerpoolDebug: '0xB967057a21dc6A66A29721d96b8Aa7454B7c383F',
workerpool: '0xB967057a21dc6A66A29721d96b8Aa7454B7c383F',
},
}),
};
Expand Down
Loading