This document provides guidance on setting up a remote signing system utilizing Optimism's op-signer service backed by Cloud KMS.
Cryptographic key management is a critical safeguard for blockchain operators, particularly for protecting high-risk components like batcher, proposer, and challenger hot wallets. If these addresses are compromised, the system can be exploited in addition to funds lost:
- Compromised batcher address can cause L2 reorgs or sequencer outages.
- Compromised proposer address could propose invalid state proposals that can be used to execute invalid withdrawals after 7 days.
- Compromised challenger could invalidate valid state proposals and fail to challenge invalid state proposals.
Storing keys in environment variables or passing them via CLI flags exposes systems to significant risks, such as accidental leakage or unauthorized access. Cloud Key Management Service (KMS) paired with Hardware Security Modules (HSMs) provides a robust solution by:
- Generating and managing cryptographic keys in FIPS-compliant, tamper-resistant hardware.
- Ensuring keys never leave secure, isolated environments, even during signing operations.
The op-signer service acts as a secure bridge between your application and KMS. It enables transaction signing via a hardened workflow:
- Clients submit transaction requests through an RPC endpoint.
- Mutual TLS (mTLS) authentication validates both client and server identities, preventing unauthorized access.
- The op-signer forwards requests to KMS, where signing occurs within the HSM’s secure boundary.
This end-to-end protection ensures sensitive keys remain shielded from exposure, even during transaction processing.
The following steps prepare you to configure GCP KMS.
It is recommended to create a standalone Service Account to access the KMS. To do this, go to the Service account page, select your project (e.g., Signing Test), or create a new project for the KMS.
Next, click CREATE SERVICE ACCOUNT. Give a name to the service account (e.g., swc-signer-sa). Take note of the email address of the account, which will be used as principals to grant permissions for the KMS (e.g., swc-signer-sa@signing-test-450710.iam.gserviceaccount.com).
Enter the service account detail page and click the Keys tab.
- Click
Add Key / Create new key. - For Key type, select the JSON key option, then click Create. The file will automatically download to your computer.
- Place the *.json file you just downloaded in a directory of your choice for later use.
The following steps prepare you to create a GCP KMS key.
If not done yet, go to https://console.cloud.google.com/flows/enableapi?apiid=cloudkms.googleapis.com, confirm that the project is correct, and enable the Cloud Key Management Service (KMS) API.
A key ring organizes keys in a specific Google Cloud location and allows you to manage access control on groups of keys. Go to https://console.cloud.google.com/security/kms, where you may see key rings already created. Optionally, you can create new key rings by clicking the CREATE KEY RING button and assigning the following values as an example:
key-ring-name: op-signer
Multi-region: Global
Then click CREATE.
Choose a key ring on the page https://console.cloud.google.com/security/kms and click CREATE KEY. In the Create Key page, assign values as in the following example and click CONTINUE for each step:
key name: op-challenger (Depending on the consumer of the key)
Protection Level: HSM (Protection level must be set to HSM to use secp256k1)
Key material: HSM-generated key
Purpose: Asymmetric sign
Algorithm: Elliptic Curve secp256k1 - SHA256 Digest (Ethereum compatible)
Default expiration: 30 days
Then click CREATE.
Click the Permissions tab of the Key detail page and click Grant Access.
- Add the email address of the service account as principals from this step and assign proper roles. For example:
Add principals: swc-signer-sa@signing-test-450710.iam.gserviceaccount.com
Assign roles: roles/cloudkms.signerVerifier
Note that only IAM principals with Owner (roles/owner) or Cloud KMS Admin (roles/cloudkms.admin) roles can grant or revoke access to Cloud KMS resources.
Click on the created item and in the Versions tab of the key detail page, click the button under Actions and select Copy resource name.
For example:
projects/swc-signer/locations/global/keyRings/op-signer/cryptoKeys/op-challenger/cryptoKeyVersions/1
You will add this value as key in op-signer/config.yaml in the infra repo.
Log on to the server where the op-signer service will be deployed.
git clone https://github.com/QuarkChain/infra.git
cd infra/op-signer
makeWhile in infra/op-signer, run the following command to generate TLS:
# Replace op-signer.testnet.quarkchain.io with the real DNS name of the server that the client uses to connect.
./tls.sh server op-signer.testnet.quarkchain.ioYou will receive several CA and TLS-related files in tls-server folder:
tls-server
├── ca.crt
├── ca.key
├── ca.srl
├── tls.crt
├── tls.csr
└── tls.key
⚠️ Note: If the CA key is compromised, an attacker can issue any certificate using it, which will be recognized as legitimate by the system.
Add the following line to op-signer/.envrc in the infra repo:
export GOOGLE_APPLICATION_CREDENTIALS="<PATH_TO_SERVICE_ACCOUNT_JSON_FILE>"Replace <PATH_TO_SERVICE_ACCOUNT_JSON_FILE> with the path of the service account JSON file downloaded in this step.
You can also change other configurations like port and log level in the .envrc file.
Modify the config.yaml file to connect op-signer with your cloud KMS:
- name: DNS name of the client connecting to op-signer.
- key: key resource name from Cloud KMS obtained from this step.
For example:
# Replace `test.local` with the real DNS name of the client.
auth:
- name: test.local
key: projects/signing-test-450710/locations/global/keyRings/op-signer/cryptoKeys/op-challenger/cryptoKeyVersions/1You can add multiple entries for different clients connecting with op-signer.
For each key, there is a corresponding address that needs to be used as from when drafting Ethereum transactions. Execute this command to retrieve addresses from keys:
./bin/op-signer addressOutput example:
0: projects/signing-test-450710/locations/global/keyRings/op-signer/cryptoKeys/op-challenger-1/cryptoKeyVersions/1 => 0x74D3b2A1c7cD4Aea7AF3Ce8C08Cf5132ECBA64ED
In the op-signer folder, execute this command:
./bin/op-signer \
--tls.cert=./tls-server/tls.crt \
--tls.ca=./tls-server/ca.crt \
--tls.key=./tls-server/tls.key Log on to the server where your op-signer service deployed in this step, and go to op-signer directory.
Now, generate TLS using the following command:
# Replace `test.local` with the real DNS name of the client.
./tls.sh client test.localNote that the client DNS should match one of those specified in your server's auth configuration.
You should now have these files in your tls folder:
tls
├── ca.crt
├── tls.csr
└── tls.keyRun the following command to verify the op-signer service is correctly configured (working with client DNS test.local):
./bin/op-signer client transaction --endpoint https://localhost:8080 0x02f85d82053980010182753094000000000000000000000000000000000000aaaa0180f838f794000000000000000000000000000000000000aaaae1a00000000000000000000000000000000000000000000000000000000000000000808080Expected result:
{"type":"0x2","chainId":"0x539","nonce":"0x0","to":"0x000000000000000000000000000000000000aaaa","gas":"0x7530","gasPrice":null,"maxPriorityFeePerGas":"0x1","maxFeePerGas":"0x1","value":"0x1","input":"0x","accessList":[{"address":"0x000000000000000000000000000000000000aaaa","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"v":"0x1","r":"0x8d9540ce394ec0c270ce9f44a28859a2f58a7adff5c7663fc4888ea0c78ae953","s":"0x1c220902ba8c86fe31fc9ea4be1d1b452fb7e6a9b249b942be5986a5de16bf24","yParity":"0x1","hash":"0x3f0c90e8bc05d77f22b0df71aaad5c6ca87db673944ba6de823c992e8e8ef16e"}Now, you can move the folder to the client where the signer service is being called from.
Use these remote signing flags on your client side when calling signer service, replacing flags like --private-key, or others as needed:
--signer.address # Address for signing requests
--signer.endpoint # Signer endpoint for client connection
--signer.header # Headers passed to remote signer; format: key=value
--signer.tls.enabled # Enable or disable TLS client authentication (default: true)
--signer.tls.ca value # Path for TLS CA cert (default: "tls/ca.crt")
--signer.tls.cert value # Path for TLS cert (default: "tls/tls.crt")
--signer.tls.key value # Path for TLS key (default: "tls/tls.key") For example, use op-challenger to perform an action against a specific game:
./bin/op-challenger move --attack --claim \
--l1-eth-rpc http://5.9.87.214:8545 \
--game-address 0xAa0ef55777C8783602d3E0024ea640546b2ee124 \
--signer.endpoint=https://op-signer.testnet.quarkchain.io:8080 \
--signer.address=0x74D3b2A1c7cD4Aea7AF3Ce8C08Cf5132ECBA64ED \
--signer.tls.cert=./tls/tls.crt \
--signer.tls.ca=./tls/ca.crt \
--signer.tls.key=./tls/tls.key This manual provides comprehensive instructions on setting up Cloud Key Management Service (Cloud KMS) with Optimism's remote signing system using op-signer. By following these steps, users can securely manage cryptographic keys and facilitate secure transaction signing through RPC with mTLS authentication.