Skip to content
Open
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
8 changes: 5 additions & 3 deletions docker-compose-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ services:
volumes:
- ./federator-env/${FED_ENV}/:/app/federator/config
- ./federator-env/${FED_ENV}/db:/app/federator/db
# environment:
# - PM2_PUBLIC_KEY=${PM2_PUBLIC_KEY}
# - PM2_SECRET_KEY=${PM2_SECRET_KEY}
environment:
- NODEREAL_BSC_FEDERATOR_API=${NODEREAL_BSC_FEDERATOR_API:-}
- NODEREAL_ETH_FEDERATOR_API=${NODEREAL_ETH_FEDERATOR_API:-}
# - PM2_PUBLIC_KEY=${PM2_PUBLIC_KEY}
# - PM2_SECRET_KEY=${PM2_SECRET_KEY}

datadog:
container_name: datadog-agent
Expand Down
2 changes: 1 addition & 1 deletion federator-env/mainnet-BSC-RSK/bmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"multiSig": "0xec3fabc3517e64e07669dd1d2d673f466f93a328",
"allowTokens": "0xc4b5178Cc086E764568AdfB2dacCBB0d973e8132",
"erc777Converter": "0x9D46B33171eA7124aEE472bFe61B5B7084B55069",
"host": "https://bsc.sovryn.app/mainnet",
"host": "https://bsc-mainnet.nodereal.io/v1/${NODEREAL_BSC_FEDERATOR_API}",
"fromBlock": 52652645
}
22 changes: 19 additions & 3 deletions federator-env/mainnet-BSC-RSK/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
const fs = require('fs');

function expandEnv(value) {
return value.replace(/\$\{([A-Z0-9_]+)\}/g, (_, name) => {
const envValue = process.env[name];
if (!envValue) throw new Error(`Missing required environment variable ${name}`);
return envValue;
});
}

function loadChainConfig(path) {
const chainConfig = require(path);
return {
...chainConfig,
host: expandEnv(chainConfig.host),
};
}

let telegramToken;
try {
telegramToken = fs.readFileSync(`${__dirname}/telegram.key`, 'utf8').trim();
Expand All @@ -7,8 +24,8 @@ try {
telegramToken = '';
}
module.exports = {
mainchain: require('./rskmainnet.json'), //the json containing the smart contract addresses in rsk
sidechain: require('./bmainnet.json'), //the json containing the smart contract addresses in eth
mainchain: loadChainConfig('./rskmainnet.json'), //the json containing the smart contract addresses in rsk
sidechain: loadChainConfig('./bmainnet.json'), //the json containing the smart contract addresses in bsc
runEvery: 2, // In minutes,
confirmations: 120, // Number of blocks before processing it, if working with ganache set as 0
privateKey: fs.readFileSync(`${__dirname}/federator.key`, 'utf8').trim(),
Expand Down Expand Up @@ -1235,4 +1252,3 @@ module.exports = {
}
}
}

22 changes: 19 additions & 3 deletions federator-env/mainnet-ETH-RSK/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
const fs = require('fs');

function expandEnv(value) {
return value.replace(/\$\{([A-Z0-9_]+)\}/g, (_, name) => {
const envValue = process.env[name];
if (!envValue) throw new Error(`Missing required environment variable ${name}`);
return envValue;
});
}

function loadChainConfig(path) {
const chainConfig = require(path);
return {
...chainConfig,
host: expandEnv(chainConfig.host),
};
}

let telegramToken;
try {
telegramToken = fs.readFileSync(`${__dirname}/telegram.key`, 'utf8').trim();
Expand All @@ -15,8 +32,8 @@ try {
}

module.exports = {
mainchain: require('./rskmainnet.json'), //the json containing the smart contract addresses in rsk
sidechain: require('./mainnet.json'), //the json containing the smart contract addresses in eth
mainchain: loadChainConfig('./rskmainnet.json'), //the json containing the smart contract addresses in rsk
sidechain: loadChainConfig('./mainnet.json'), //the json containing the smart contract addresses in eth
runEvery: 2, // In minutes,
gasApiRunEvery: 5, // In Seconds,
avgGasRunEvery: 10, // In Seconds,
Expand Down Expand Up @@ -753,4 +770,3 @@ module.exports = {
}
}
}

2 changes: 1 addition & 1 deletion federator-env/mainnet-ETH-RSK/mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"multiSig": "0x062c74f9d27b1178bb76186c1756128ccb3ccd2e",
"allowTokens": "0xf9A59a649859A27d664C8bDb51fA53bCb268545C",
"erc777Converter": "0xC0b2A9E31f69e4F0bC24584C678C582714a4fA1b",
"host": "https://eth.sovryn.app/mainnet",
"host": "https://eth-mainnet.nodereal.io/v1/${NODEREAL_ETH_FEDERATOR_API}",
"fromBlock": 22838052
}
238 changes: 238 additions & 0 deletions ops/docs/federator-nodereal-secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Federator NodeReal Secrets

This runbook describes how to configure private NodeReal RPC keys for federator nodes without committing keys to git.

The federator configs use these environment variables:

- `NODEREAL_BSC_FEDERATOR_API`
- `NODEREAL_ETH_FEDERATOR_API`

The public JSON files contain placeholders such as:

```json
"host": "https://bsc-mainnet.nodereal.io/v1/${NODEREAL_BSC_FEDERATOR_API}"
```

At runtime, `config.js` expands the placeholder from the environment. If the required variable is missing, the federator fails fast instead of falling back to the public overloaded endpoint.

## Recommended Emergency Setup: Local `.env`

Docker Compose automatically reads a `.env` file from the same directory where `docker-compose-prod.yml` is executed.

On each federator node:

```sh
cd /home/ubuntu/Bridge-SC
```

Create or update `.env`:

```sh
cat > .env <<'EOF'
NODEREAL_BSC_FEDERATOR_API=<BSC_NODE_REAL_API_KEY>
NODEREAL_ETH_FEDERATOR_API=<ETH_NODE_REAL_API_KEY>
EOF
```

Lock down permissions:

```sh
chmod 600 .env
```

Make sure `.env` is not committed:

```sh
git status --short .env
```

If it appears in git status, add it to the local exclude file:

```sh
echo .env >> .git/info/exclude
```

## Restart BSC Federator

Restart only the federator container:

```sh
cd /home/ubuntu/Bridge-SC
export FED_ENV=mainnet-BSC-RSK
docker-compose -f docker-compose-prod.yml up -d --force-recreate federation
```

Do not use `start.sh` for a simple hot restart unless you intentionally want its full reset behavior. It calls `reset.sh`, which deletes federator DB progress and performs git hard resets.

## Restart ETH Federator

```sh
cd /home/ubuntu/Bridge-SC
export FED_ENV=mainnet-ETH-RSK
docker-compose -f docker-compose-prod.yml up -d --force-recreate federation
```

## Verify The Container Has The Variables

Do not print the actual values. Check only whether they are present:

```sh
docker-compose -f docker-compose-prod.yml exec federation sh -lc \
'test -n "$NODEREAL_BSC_FEDERATOR_API" && echo BSC key present || true'
```

```sh
docker-compose -f docker-compose-prod.yml exec federation sh -lc \
'test -n "$NODEREAL_ETH_FEDERATOR_API" && echo ETH key present || true'
```

For a BSC-only node, only the BSC variable needs to be present. For an ETH-only node, only the ETH variable needs to be present.

## Verify RPC Access From The Host

BSC:

```sh
. ./.env

curl -sS "https://bsc-mainnet.nodereal.io/v1/$NODEREAL_BSC_FEDERATOR_API" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' | jq

unset NODEREAL_BSC_FEDERATOR_API NODEREAL_ETH_FEDERATOR_API
```

Expected BSC mainnet result:

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x38"
}
```

ETH:

```sh
. ./.env

curl -sS "https://eth-mainnet.nodereal.io/v1/$NODEREAL_ETH_FEDERATOR_API" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}' | jq

unset NODEREAL_BSC_FEDERATOR_API NODEREAL_ETH_FEDERATOR_API
```

Expected ETH mainnet result:

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x1"
}
```

## Check Federator Logs

```sh
docker-compose -f docker-compose-prod.yml logs --tail=100 -f federation
```

The app currently logs the sidechain host as `ETH Host` even when it is BSC. The URL should show the NodeReal domain, not `bsc.sovryn.app` or `eth.sovryn.app`.

## Optional Hardening: AWS Secrets Manager

AWS Secrets Manager is useful for long-term operations, auditability, and controlled rotation. It is not required for emergency recovery.

`start.sh` can load these secrets from AWS Secrets Manager if the environment variables are not already set:

- Secret name: `NODEREAL_BSC_FEDERATOR_API`
- Secret value: raw BSC NodeReal API key
- Secret name: `NODEREAL_ETH_FEDERATOR_API`
- Secret value: raw ETH NodeReal API key

Each AWS account that runs federator nodes must create its own secrets and IAM permissions.

Create secrets:

```sh
aws secretsmanager create-secret \
--region us-east-2 \
--name NODEREAL_BSC_FEDERATOR_API \
--secret-string '<BSC_NODE_REAL_API_KEY>'
```

```sh
aws secretsmanager create-secret \
--region us-east-2 \
--name NODEREAL_ETH_FEDERATOR_API \
--secret-string '<ETH_NODE_REAL_API_KEY>'
```

If the secret already exists, update it:

```sh
aws secretsmanager put-secret-value \
--region us-east-2 \
--secret-id NODEREAL_BSC_FEDERATOR_API \
--secret-string '<BSC_NODE_REAL_API_KEY>'
```

```sh
aws secretsmanager put-secret-value \
--region us-east-2 \
--secret-id NODEREAL_ETH_FEDERATOR_API \
--secret-string '<ETH_NODE_REAL_API_KEY>'
```

Attach this inline policy to the federator EC2 instance role. Replace `<ACCOUNT_ID>`:

```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadFederatorNodeRealSecrets",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:secretsmanager:us-east-2:<ACCOUNT_ID>:secret:NODEREAL_BSC_FEDERATOR_API-*",
"arn:aws:secretsmanager:us-east-2:<ACCOUNT_ID>:secret:NODEREAL_ETH_FEDERATOR_API-*"
]
}
]
}
```

The trailing `-*` is required because Secrets Manager appends a random suffix to secret ARNs.

Verify from the instance:

```sh
aws secretsmanager get-secret-value \
--region us-east-2 \
--secret-id NODEREAL_BSC_FEDERATOR_API \
--query SecretString \
--output text >/dev/null
```

```sh
aws secretsmanager get-secret-value \
--region us-east-2 \
--secret-id NODEREAL_ETH_FEDERATOR_API \
--query SecretString \
--output text >/dev/null
```

Both commands should exit with status `0`.

## Operational Notes

- Keep `.env` local to each federator node and never commit it.
- Use `chmod 600 .env`.
- Revoke the old leaked/public NodeReal key after federators are confirmed healthy on the new keys.
- Monitor NodeReal CU usage per key after rollout.
- Restrict NodeReal keys by source IP if NodeReal supports it for the account/plan.
- Longer term, protect `https://bsc.sovryn.app/mainnet` at the proxy layer with rate limits, auth, IP allowlists, or a capped public fallback.
36 changes: 35 additions & 1 deletion start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ export FED_ID=$2
# Otherwise the errors can get uncaught and this wastes development time.
set -e

load_aws_secret_if_unset() {
ENV_NAME="$1"
DEFAULT_SECRET_ID="$2"
eval CURRENT_VALUE="\${$ENV_NAME:-}"
if [ -n "$CURRENT_VALUE" ]
then
return
fi

eval SECRET_ID_OVERRIDE="\${${ENV_NAME}_SECRET_ID:-}"
SECRET_ID="${SECRET_ID_OVERRIDE:-$DEFAULT_SECRET_ID}"

echo "loading $ENV_NAME from AWS Secrets Manager secret: $SECRET_ID"
SECRET_STRING=`aws secretsmanager get-secret-value --secret-id "$SECRET_ID" --region us-east-2 | jq -r .SecretString`
SECRET_VALUE=`printf '%s' "$SECRET_STRING" | jq -r --arg key "$ENV_NAME" 'try (fromjson | .[$key] // empty) catch empty'`
if [ -z "$SECRET_VALUE" ]
then
SECRET_VALUE="$SECRET_STRING"
fi

export "$ENV_NAME=$SECRET_VALUE"
}

if [ -z "$FED_ENV" ]
then
echo "ERROR: please choose the federator env config as first cmd arg."
Expand Down Expand Up @@ -58,9 +81,20 @@ echo using key named: $FED_KEY_NAME
cat << EOF > /home/ubuntu/Bridge-SC/federator-env/$FED_ENV/federator.key
$FED_KEY
EOF

if [ "$FED_ENV" = "mainnet-BSC-RSK" ]
then
load_aws_secret_if_unset NODEREAL_BSC_FEDERATOR_API NODEREAL_BSC_FEDERATOR_API
fi

if [ "$FED_ENV" = "mainnet-ETH-RSK" ]
then
load_aws_secret_if_unset NODEREAL_ETH_FEDERATOR_API NODEREAL_ETH_FEDERATOR_API
fi

echo "starting federator please wait... this takes"
nohup 2>&1 docker-compose -f docker-compose-prod.yml up > federator.log &
sleep 90
echo "federator logs: /home/ubuntu/Bridge-SC/federator.log"
rm -rf /home/ubuntu/Bridge-SC/federator-env/$FED_ENV/federator.key
tail -f /home/ubuntu/Bridge-SC/federator.log
tail -f /home/ubuntu/Bridge-SC/federator.log