Audit Service
This page is a tutorial on how how to set up Blockdaemon's external audit server, and how to configure the MPC nodes in a Builder Vault instance to post audit logs to the audit service.
This tutorial assumes that your Builder Vault has three MPC nodes, but it should be easy to adapt to Builder Vault instances with any other number of MPC nodes.
Demo
In addition to this guide, you can also find a full, working example of a Builder Vault instance configured with audit logging in our demo repository.
Log in to Blockdaemon's docker registry
Start by login into Blockdaemon's private docker registry using the credentials you were provided:
docker login bv.sepior.net
Create Audit Server Configuration
The MPC nodes in Builder Vault will communicate with the Audit Server on port 3000, the logs can be queried on port 8000 and the Audit server will communicate with a MongoDB instance (or Amazon DocumentDB) on port 27017.
Start by generating a private/public key pair for the Audit Server:
openssl ecparam -name P-256 -genkey -param_enc named_curve -outform DER -out audit_private.key
openssl ec -inform DER -in audit_private.key -pubout -outform DER -out audit_public.key
Next, generate a new set of private/public keys for each of the MPC nodes used just for logging:
openssl ecparam -name P-256 -genkey -param_enc named_curve -outform DER -out audit_private0.key
openssl ec -inform DER -in audit_private0.key -pubout -outform DER -out audit_public0.key
openssl ecparam -name P-256 -genkey -param_enc named_curve -outform DER -out audit_private1.key
openssl ec -inform DER -in audit_private1.key -pubout -outform DER -out audit_public1.key
openssl ecparam -name P-256 -genkey -param_enc named_curve -outform DER -out audit_private2.key
openssl ec -inform DER -in audit_private2.key -pubout -outform DER -out audit_public2.key
Get the Audit Server private key in base64:
openssl base64 -A -in audit_private.key; echo
Then get the all the nodes' base64 encoded public keys:
openssl base64 -A -in audit_public0.key; echo
openssl base64 -A -in audit_public1.key; echo
openssl base64 -A -in audit_public2.key; echo
And lastly, a random password for accessing the Audit Server:
LC_ALL=C tr -cd "[:alnum:]" < /dev/urandom | fold -w24 | head -n1
Finally, fill all of the above into the template below and save it to a file called config.toml
[Database]
Host = "<mongodb hostname>"
Username = "<mongodb username>"
Password = "<mongodb password>"
[LogServer]
Port = 3000
PrivateKey = "<audit server private key base64>"
[LogServer.HTTPResponseHeaders] # Optional
<key = value>
[QueryServer]
Port = 8000
CertificateFile = "" # HTTPS disabled
CertificateKeyFile = "" # HTTPS disabled
[QueryServer.HTTPResponseHeaders] # Optional
<key = value>
[TSM.demo]
Password = "<random password>"
PublicKeys = [
"<node0 audit public key as base64>",
"<node1 audit public key as base64>",
"<node2 audit public key as base64>"
]
Host Configuration
The Host field can be used for setting any configuration option, which the mongo-go-driver will accept. For instance ssl can be added, by specifying
[Database]
Host = "hostname:27017/?ssl=true&tlsCAFile=ca-bundle.pem"
Username = "<mongodb username>"
Password = "<mongodb password>"
When running via Docker, you can mount the folder containing the .pem file, and point to the absolute file path in Host, remember to URI encode the path.
Further inspiration can be found here:
https://github.com/mongodb/mongo-go-driver/blob/master/mongo/client_examples_test.go
Update the MPC Node Configuration
We need to add an [Audit]
section to the MPC node configuration files we already have.
For each of the three MPC nodes, we need to first get the audit server public key as base64:
openssl base64 -A -in audit_public.key; echo
the MPC node audit private key as base64:
openssl base64 -A -in audit_private0.key; echo
and a random signing seed (each node should use its own seed):
openssl rand -base64 32
That is all plugged in to the template bellow, and inserted in to the MPC node configuration file that we made earlier.
[Audit]
ReceiverURL = "https://<audit server hostname>:<audit server logging port>/log"
ReceiverPublicKey = "<audit server public key>"
ClientPrivateKey = "<MPC node audit private key>"
LogEntrySigningKeySeed = "<random signing seed>"
MaxBatchSize = 50
MinWaitTime = "5s"
MaxWaitTime = "2m"
Start the Audit Server
Add the following to the docker-compose.yml
file we made earlier, insert mongodb username and password, and restart:
mongo:
image: mongo
command: --noscripting
restart: always
volumes:
- ./audit_server/db/:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: <mongo username>
MONGO_INITDB_ROOT_PASSWORD: <mongo password>
audit-server:
image: bv.sepior.net/audit-server
ports:
- 8000:8000/tcp
- 3000:3000/tcp
restart: always
volumes:
- ./config.toml:/config/config.toml
Querying
To query the server basic authentication is required. The username is the name of the TSM in the audit configuration file, i.e., demo
in the above example, and the password is the <random password>
in the [TSM.demo]
section.
A query is on the form
https://HOST:QUERY_PORT/logs?filter={FILTER}&options={OPTIONS}
Where FILTER is replaced by a MongoDB search filter, and OPTIONS is replaced by MongoDB find options (see https://docs.mongodb.com/manual/reference/command/find/). Remember to URL encode values.
Examples
Retrieve two log entries with a session id, showing the sessionID
, timestamp
, and keyID
:
https://HOST:QUERY_PORT/logs?filter={"sessionID": {"$neq": null}}&options={"limit": 2, "projection": {"_id": 0, "sessionID": 1, "timestamp": 1, "keyID": 1 }}
Retrieve all logs without a session id, showing the userID
, and the operation
:
https://HOST:QUERY_PORT/logs?filter={"sessionID": {"$eq": null}}&options={"projection": {"_id": 0, "userID": 1, "operation": 1}}
In the next example we use cURL to retrieve two log entries, and parse them with jq
. We assume that the audit server has it's query endpoint at http://localhost:8000/logs
and we use the URL-encoded parameters filter={}&options={"limit": 2}
. The basic auth in the audit server config file is `[TSM.demo] Password = "demo_password"
curl -s -u demo:demo_password "http://localhost:8000/logs?filter=%7B%7D&options=%7B%22limit%22%3A%202%7D" | jq
This should result in JSON output like this:
[
{
"_id": "67d97714a292eb1b22a8b7de",
"algorithm": "ECDSA",
"keyID": "1hC9DhuUqMGieUMNtQSutGhqumvV",
"operation": "Keygen",
"parameters": {
"curve": "secp256k1"
},
"player": 0,
"sessionID": "MXa9LrKvkLq49v5DpgtVZ_30tf8QbMtXy8iMbpORXZY",
"signature": "Vw16bm/nwnMKl2w1rkqsesyLvfMvJSsLNJztx1UWqtDMIEXib8EYC5SNGZx2zUznNfrDtrSrmyXTmkFFJSXfDA==",
"timestamp": 1742302418310,
"userID": "b4zVds2jyvvJ2wXgSfNuHyryJUWa"
},
{
"_id": "67d97714a292eb1b22a8b7df",
"algorithm": "ECDSA",
"keyID": "1hC9DhuUqMGieUMNtQSutGhqumvV",
"operation": "PresigGen",
"parameters": {
"Count": 3
},
"player": 0,
"sessionID": "ttSsU5W5kEhrCc0MDRGg3i0DBabQ8LMs-vTa_kH8ZAk",
"signature": "xbe7bZT1g4mKzEtCGs0xrUOB5/vqzOtOofa0mkt5XNK3E2z2+v3fMf7dbue1bHblsP22z6kYZKLnZfHNT7AOCA==",
"timestamp": 1742302418334,
"userID": "b4zVds2jyvvJ2wXgSfNuHyryJUWa"
}
]
Updated about 1 month ago