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
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@ inputs:
description: Maximum disk space used by the IPFS node datastore (experimental).
required: false
default: "50GB"
mithril_ipfs_ipns_signing_key:
description: IPNS signing key in PEM PKCS#8 format used by the IPFS node to publish the immutable archives directory (optional, the aggregator does not publish to IPNS if empty, experimental).
required: false
default: ""
ipfs_node_version:
description: IPFS node version (experimental).
required: false
default: "v0.38.1"
default: "v0.43.1"
ipfs_node_docker_registry:
description: IPFS node Docker image registry (experimental).
required: false
Expand Down Expand Up @@ -282,6 +286,9 @@ runs:
MITHRIL_AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS_BASE64=$(echo '${{ inputs.mithril_aggregator_ancillary_signer_gcp_kms_credentials }}' | base64 -w 0)
echo "::add-mask::$MITHRIL_AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS_BASE64"
echo "mithril_aggregator_ancillary_signer_gcp_kms_credentials_base64=$MITHRIL_AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS_BASE64" >> $GITHUB_OUTPUT
MITHRIL_IPFS_IPNS_SIGNING_KEY_BASE64=$(printf '%s' '${{ inputs.mithril_ipfs_ipns_signing_key }}' | base64 -w 0)
echo "::add-mask::$MITHRIL_IPFS_IPNS_SIGNING_KEY_BASE64"
echo "mithril_ipfs_ipns_signing_key_base64=$MITHRIL_IPFS_IPNS_SIGNING_KEY_BASE64" >> $GITHUB_OUTPUT

- name: Prepare terraform variables
shell: bash
Expand Down Expand Up @@ -316,6 +323,7 @@ runs:
mithril_p2p_signer_relay_signature_registration_mode = "${{ inputs.mithril_p2p_signer_relay_signature_registration_mode }}"
mithril_ipfs_enabled = "${{ inputs.mithril_ipfs_enabled }}"
mithril_ipfs_storage_max = "${{ inputs.mithril_ipfs_storage_max }}"
mithril_ipfs_ipns_signing_key = "${{ steps.prepare.outputs.mithril_ipfs_ipns_signing_key_base64 }}"
mithril_api_domain = "${{ inputs.mithril_api_domain }}"
mithril_image_id = "${{ inputs.mithril_image_id }}"
mithril_genesis_verification_key_url = "${{ inputs.mithril_genesis_verification_key_url }}"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,7 @@ jobs:
mithril_aggregator_ancillary_signer_secret_key: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_SECRET_KEY }}
mithril_aggregator_ancillary_signer_gcp_kms_resource_name: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_RESOURCE_NAME }}
mithril_aggregator_ancillary_signer_gcp_kms_credentials: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS }}
mithril_ipfs_ipns_signing_key: ${{ secrets.IPFS_IPNS_SIGNING_KEY }}
mithril_aggregator_custom_origin_tag_white_list: ${{ vars.AGGREGATOR_CUSTOM_ORIGIN_TAG_WHITE_LIST }}
prometheus_auth_username: ${{ secrets.PROMETHEUS_AUTH_USERNAME }}
prometheus_auth_password: ${{ secrets.PROMETHEUS_AUTH_PASSWORD }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ jobs:
mithril_aggregator_ancillary_signer_secret_key: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_SECRET_KEY }}
mithril_aggregator_ancillary_signer_gcp_kms_resource_name: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_RESOURCE_NAME }}
mithril_aggregator_ancillary_signer_gcp_kms_credentials: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS }}
mithril_ipfs_ipns_signing_key: ${{ secrets.IPFS_IPNS_SIGNING_KEY }}
mithril_aggregator_custom_origin_tag_white_list: ${{ vars.AGGREGATOR_CUSTOM_ORIGIN_TAG_WHITE_LIST }}
prometheus_auth_username: ${{ secrets.PROMETHEUS_AUTH_USERNAME }}
prometheus_auth_password: ${{ secrets.PROMETHEUS_AUTH_PASSWORD }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ jobs:
mithril_aggregator_ancillary_signer_secret_key: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_SECRET_KEY }}
mithril_aggregator_ancillary_signer_gcp_kms_resource_name: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_RESOURCE_NAME }}
mithril_aggregator_ancillary_signer_gcp_kms_credentials: ${{ secrets.AGGREGATOR_ANCILLARY_SIGNER_GCP_KMS_CREDENTIALS }}
mithril_ipfs_ipns_signing_key: ${{ secrets.IPFS_IPNS_SIGNING_KEY }}
mithril_aggregator_custom_origin_tag_white_list: ${{ vars.AGGREGATOR_CUSTOM_ORIGIN_TAG_WHITE_LIST }}
prometheus_auth_username: ${{ secrets.PROMETHEUS_AUTH_USERNAME }}
prometheus_auth_password: ${{ secrets.PROMETHEUS_AUTH_PASSWORD }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-deploy-network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ jobs:
mithril_p2p_network_bootstrap_peer: ${{ matrix.mithril_p2p_network_bootstrap_peer }}
mithril_ipfs_enabled: ${{ matrix.mithril_ipfs_enabled }}
mithril_ipfs_storage_max: ${{ vars.IPFS_STORAGE_MAX }}
mithril_ipfs_ipns_signing_key: ${{ secrets.IPFS_IPNS_SIGNING_KEY }}
ipfs_node_version: ${{ vars.IPFS_NODE_VERSION }}
ipfs_node_docker_registry: ${{ vars.IPFS_NODE_DOCKER_REGISTRY }}
mithril_api_domain: ${{ matrix.mithril_api_domain }}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ As a minor extension, we have adopted a slightly different versioning convention
- Reworked the Mithril aggregator's file archiver to produce byte-stable archives across systems.
- Existing archives must be regenerated by the Mithril aggregator to ensure byte stability.
- Preliminary support for uploading immutable files to IPFS with the Mithril aggregator and downloading them from IPFS with the Mithril client library and CLI.
- Support for the IPNS signing key of the IPFS node of the Mithril aggregator in the infrastructure, and upgrade of the default Kubo version to `v0.43.1`.
- Support for the circuit verification key registry, a genesis-signed whitelist (with revocations) of the circuit verification keys trusted for SNARK certificates.
- The registry lives in the new `mithril-circuit-key-registry` crate, holding one entry per circuit verification key, either allowed over an epoch range or revoked for every epoch.
- The registry is retrieved over HTTPS and the last verified registry is kept when a refresh fails.
Expand Down
1 change: 1 addition & 0 deletions docs/runbook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ This page gathers the available guides to operate a Mithril network.
| **Update circuits verification keys** | [update-circuit-keys](./update-circuit-keys/README.md) | Update the circuits verification keys after an intentional circuit modification. |
| **GitHub self-hosted runner** | [self-hosted-runner](./self-hosted-runner/README.md) | Set up a GCP virtual machine as a GitHub self-hosted runner for heavy workloads. |
| **Circuit verification key registry** | [circuit-key-registry](./circuit-key-registry/README.md) | Publish, rotate and revoke circuit verification keys in the genesis-signed registry. |
| **IPNS signing key** | [ipns-signing-key](./ipns-signing-key/README.md) | Create, register and rotate the IPNS signing key of the aggregator IPFS node. |
76 changes: 76 additions & 0 deletions docs/runbook/ipns-signing-key/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# IPNS signing key

## Introduction

When IPFS is enabled on a Mithril network, the aggregator publishes the IPFS directory of the immutable archives under an IPNS name. This IPNS name is derived from an Ed25519 signing key that the IPFS node of the aggregator imports at startup.

The signing key is provided to the deployment by the `IPFS_IPNS_SIGNING_KEY` secret of the GitHub environment of the network, in PEM PKCS#8 format. This secret is optional (e.g. for a follower aggregator): the aggregator does not publish to IPNS when it is empty.

> [!IMPORTANT]
> :fire: The IPNS signing key is a private key: anyone who holds it can publish content under the IPNS name of the network. It must not be stored anywhere other than the GitHub environment secret and the IPFS node of the aggregator.

All the commands below are run locally and require [Docker](https://docs.docker.com/get-docker/).

## Pre-requisites

Export the environment variables:

```bash
export IPFS_IMAGE=ipfs/kubo:v0.43.1
export KEY_DIRECTORY=$(mktemp -d)
```

## Create a signing key

Generate a new Ed25519 signing key and export it in PEM PKCS#8 format:

```bash
docker run --rm -u $(id -u):$(id -g) -e IPFS_PATH=/tmp/ipfs -v $KEY_DIRECTORY:/keys --entrypoint sh $IPFS_IMAGE -c '
ipfs init > /dev/null
ipfs key gen --type=ed25519 ipns-signing-key > /dev/null
ipfs key export ipns-signing-key --format=pem-pkcs8-cleartext -o /keys/ipns-signing-key.pem
'
```

Display the signing key:

```bash
cat $KEY_DIRECTORY/ipns-signing-key.pem
```

## Compute the IPNS name

Compute the IPNS name associated to the signing key:

```bash
docker run --rm -u $(id -u):$(id -g) -e IPFS_PATH=/tmp/ipfs -v $KEY_DIRECTORY:/keys:ro --entrypoint sh $IPFS_IMAGE -c '
ipfs init > /dev/null
ipfs key import --format=pem-pkcs8-cleartext ipns-signing-key /keys/ipns-signing-key.pem
'
```

The command outputs the IPNS name, and the immutable archives directory is then available at `/ipns/<IPNS_NAME>`.

> [!TIP]
> The same command computes the IPNS name of an existing signing key, provided it is first stored in `$KEY_DIRECTORY/ipns-signing-key.pem`.

## Register the signing key

Copy the full content of `$KEY_DIRECTORY/ipns-signing-key.pem` (including the `BEGIN` and `END` lines) into the `IPFS_IPNS_SIGNING_KEY` secret of the GitHub environment of the network.

The key is imported by the IPFS node at the next deployment of the network.

Then delete the local copy of the signing key:

```bash
rm -rf $KEY_DIRECTORY
```

## Rotate the signing key

The IPFS node replaces its signing key with the one of the deployment at each start.

> [!WARNING]
> Rotating the signing key changes the IPNS name of the network. The third-party nodes that follow the previous IPNS name must be informed of the new one before the rotation is deployed. This coordination is planned to be handled with DNSLink, which is not implemented yet.

To rotate the signing key, create a new signing key, compute its IPNS name and register it as described above.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ services:
- IPFS_SWARM_PORT=${AGGREGATOR_IPFS_SWARM_PORT}
- IPFS_PUBLIC_ADDRESS=${AGGREGATOR_IPFS_PUBLIC_ADDRESS}
- IPFS_STORAGE_MAX=${AGGREGATOR_IPFS_STORAGE_MAX}
- IPFS_IPNS_SIGNING_KEY_NAME=${AGGREGATOR_IPFS_IPNS_SIGNING_KEY_NAME}
- IPFS_IPNS_SIGNING_KEY=${AGGREGATOR_IPFS_IPNS_SIGNING_KEY}
volumes:
- ../data/${NETWORK}/mithril-aggregator/ipfs:/data/ipfs
- ./ipfs/container-init.d:/container-init.d:ro
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,18 @@ ipfs config --json Swarm.ResourceMgr.MaxFileDescriptors 1024
ipfs config Datastore.StorageMax "${IPFS_STORAGE_MAX}"
ipfs config --json Experimental.FilestoreEnabled true
ipfs config Plugins.Plugins.telemetry.Config.Mode off

# The IPNS signing key is replaced at each start so that a rotated key from the deployment is applied
if [ -n "${IPFS_IPNS_SIGNING_KEY:-}" ]; then
if [ -z "${IPFS_IPNS_SIGNING_KEY_NAME:-}" ]; then
echo "configure-node: IPFS_IPNS_SIGNING_KEY_NAME is not set" >&2
exit 1
fi
if ipfs key list | grep -qx "${IPFS_IPNS_SIGNING_KEY_NAME}"; then
ipfs key rm "${IPFS_IPNS_SIGNING_KEY_NAME}" > /dev/null
Comment thread
jpraynaud marked this conversation as resolved.
fi
ipns_key_file=$(mktemp)
trap 'rm -f "$ipns_key_file"' EXIT
printf '%s\n' "${IPFS_IPNS_SIGNING_KEY}" > "$ipns_key_file"
ipfs key import --format=pem-pkcs8-cleartext "${IPFS_IPNS_SIGNING_KEY_NAME}" "$ipns_key_file" > /dev/null
fi
2 changes: 1 addition & 1 deletion mithril-infra/assets/infra.version
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0.5.28
0.5.29

7 changes: 6 additions & 1 deletion mithril-infra/mithril.aggregator.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ locals {
mithril_aggregator_dmq_port = 6161
mithril_aggregator_ipfs_swarm_port = 4001
mithril_aggregator_ipfs_rpc_api_url = "http://ipfs-node-aggregator:5001/"
mithril_aggregator_ipfs_ipns_signing_key = base64decode(var.mithril_ipfs_ipns_signing_key)
mithril_aggregator_ancillary_signer_gcp_kms_credentials = base64decode(var.mithril_aggregator_ancillary_signer_gcp_kms_credentials)
}

Expand Down Expand Up @@ -34,6 +35,8 @@ resource "null_resource" "mithril_aggregator" {
mithril_p2p_dmq_dense_topology = var.mithril_p2p_dmq_dense_topology,
mithril_ipfs_enabled = var.mithril_ipfs_enabled,
mithril_ipfs_storage_max = var.mithril_ipfs_storage_max,
mithril_ipfs_ipns_signing_key = sha256(var.mithril_ipfs_ipns_signing_key),
mithril_ipfs_ipns_signing_key_name = var.mithril_ipfs_ipns_signing_key_name,
ipfs_image_id = var.ipfs_image_id,
ipfs_image_registry = var.ipfs_image_registry,
}
Expand Down Expand Up @@ -191,7 +194,7 @@ EOT
,
<<-EOT
if [ "${var.mithril_ipfs_enabled}" = "true" ]; then
export IPFS_RPC_SERVER_CONFIG=$(jq -nc --arg url '${local.mithril_aggregator_ipfs_rpc_api_url}' '{"url": $url}')
export IPFS_RPC_SERVER_CONFIG=$(jq -nc --arg url '${local.mithril_aggregator_ipfs_rpc_api_url}' --arg ipns_key_name '${var.mithril_ipfs_ipns_signing_key_name}' --arg ipns_enabled '${var.mithril_ipfs_ipns_signing_key != ""}' '{"url": $url} + if $ipns_enabled == "true" then {"ipns_key_name": $ipns_key_name} else {} end')
Comment thread
jpraynaud marked this conversation as resolved.
fi
EOT
,
Expand Down Expand Up @@ -220,6 +223,8 @@ EOT
"export AGGREGATOR_IPFS_SWARM_PORT='${local.mithril_aggregator_ipfs_swarm_port}'",
"export AGGREGATOR_IPFS_PUBLIC_ADDRESS='${google_compute_address.mithril-external-address.address}'",
"export AGGREGATOR_IPFS_STORAGE_MAX='${var.mithril_ipfs_storage_max}'",
"export AGGREGATOR_IPFS_IPNS_SIGNING_KEY_NAME='${var.mithril_ipfs_ipns_signing_key_name}'",
"export AGGREGATOR_IPFS_IPNS_SIGNING_KEY='${local.mithril_aggregator_ipfs_ipns_signing_key}'",
"export P2P_BOOTSTRAP_PEER='${var.mithril_p2p_network_bootstrap_peer}'",
"export ENABLE_METRICS_SERVER=true",
"export METRICS_SERVER_IP=0.0.0.0",
Expand Down
15 changes: 14 additions & 1 deletion mithril-infra/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,23 @@ variable "mithril_ipfs_storage_max" {
default = "50GB"
}

variable "mithril_ipfs_ipns_signing_key" {
type = string
description = "The IPNS signing key in PEM PKCS#8 format base64 encoded, imported by the IPFS node to publish the immutable archives directory (optional, the aggregator does not publish to IPNS if empty)"
sensitive = true
default = ""
}

variable "mithril_ipfs_ipns_signing_key_name" {
type = string
description = "The name under which the IPNS signing key is imported by the IPFS node and used by the aggregator"
default = "mithril-snapshot"
}

variable "ipfs_image_id" {
type = string
description = "The IPFS image tag of service to deploy"
default = "v0.38.1"
default = "v0.43.1"
}

variable "ipfs_image_registry" {
Expand Down
Loading