Deploy Duo on Google Confidential Space
This tutorial deploys the Duo backend to your own Google Cloud project: two public Confidential
Space VMs, a private Postgres instance, and optionally policy-svc with the policy admin dashboard
on Cloud Run. At the end you point the demo mobile app at it and create a wallet.
What you will build
A {2, 2} threshold wallet. During Distributed Key Generation the wallet's public key and its two
secret shares are computed independently by the phone and the cloud node, so the full private key
never exists.
| Service | Port | Reachable from | Runs on |
|---|---|---|---|
duo-server | 8080 | Anywhere | Confidential Space VM |
auth-svc | 9090 | Anywhere | Confidential Space VM |
policy-svc | 5002 | auth-svc only | Confidential Space VM |
| Policy admin dashboard | 443 | Anywhere | Cloud Run |
| Postgres | 5432 | Inside the VPC | Cloud SQL |
With debug_mode = true, port 22 is also open to Google's IAP range (35.235.240.0/20) for SSH.
policy-svc and the policy admin dashboard are off by default. See
Enable the policy engine.
Actors
Mobile: a party in the threshold signature scheme. It holds one keyshare, signs DKG and DSG
requests with the phone's secure enclave, and authenticates to auth-svc through Auth0. We ship one
as the React Native Boilerplate demo
app.
duo-server: the cloud node, holding the other keyshare in encrypted form. Its port is public,
but before starting a session it calls an auth-svc validation hook, which rejects any device that
is not registered.
auth-svc: verifies the user's Auth0 JWT, registers devices, and answers duo-server's hook
callbacks. State lives in Cloud SQL. We ship an example implementation in Python. See the
auth-svc documentation.
policy-svc: decides whether a requested operation is allowed under the configured policy.
Reachable only by auth-svc.
KMS: holds the key that encrypts keyshares before they reach storage, and releases it only to
duo-server running in the TEE with a valid attestation token.
Secret Manager: holds the database passwords and service signing keys. OpenTofu creates the secrets empty and a script fills them in, so their values never reach OpenTofu state. Vendor credentials are variables instead. See Third-party credentials.
Security model
The workloads run inside a Trusted Execution Environment, so their memory cannot be read or altered even by someone with root on the host. Confidential Space provides this on Google Cloud and produces a proof that a workload really is running inside the TEE. KMS releases keys only against such a proof. The assertion is set in Set up Secret Guardian resources.
The Secret Guardian hosts KMS and Secret Manager, and does not host the services. KMS must reside on a separate project, not accessible to the potentially malicious Admin of the service VMs. Otherwise that Admin can reconfigure it and decrypt keyshares outside the TEE.
A single project works for a trial deployment; split them for production.
Attestation currently runs between the VMs and KMS only. Mobile does not verify the backend's attestation yet.
Architecture
MPC execution flow
Mobile signs a setup message with its device key. duo-server verifies that signature, then asks
auth-svc whether the session is allowed before running anything.
Distributed Key Generation
The device must already be registered. Afterwards duo-server reports the new key-id, binding the
wallet to the user.
Distributed Signature Generation
Two additions: auth-svc checks the requested key belongs to the requesting user, and with the
policy engine enabled, asks policy-svc to evaluate the transaction first.
What you need from us
You depend on a reply from us at three points, so send the first request before you start:
- Credentials for Auth0, FaceTec and Helius, unless you bring your own. See Third-party credentials.
- Registry access for the service account created in Set up backend resources, and with the policy engine, for the Cloud Run service agent. Nothing can pull its image before this.
- Dashboard sign-in, only with the policy engine enabled. The dashboard deploys and serves, but signing in fails until we add its URL to our Auth0 application. See Enable the policy engine.
Send each to [email protected].