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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ node0
node1
node2
config.yaml
.vscode
.vscode
.vagrant
8 changes: 4 additions & 4 deletions cmd/mpcium-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ func main() {
Action: registerPeers,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "input",
Aliases: []string{"i"},
Usage: "Input peers JSON file path (default: peers.json)",
Value: peersFileName,
Name: "peers",
Aliases: []string{"p"},
Usage: "Path to peers.json file (defaults to ./peers.json)",
Required: false,
},
&cli.StringFlag{
Name: "environment",
Expand Down
28 changes: 26 additions & 2 deletions cmd/mpcium-cli/register-peers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ import (
)

func registerPeers(ctx context.Context, c *cli.Command) error {
inputPath := c.String("input")
inputPath := c.String("peers")
environment := c.String("environment")

// If no peers path specified, check for peers.json in current directory
if inputPath == "" {
inputPath = "peers.json"
}

// Hardcoded prefix for MPC peers in Consul
prefix := "mpc_peers/"

Expand All @@ -28,6 +33,9 @@ func registerPeers(ctx context.Context, c *cli.Command) error {

// Check if input file exists
if _, err := os.Stat(inputPath); os.IsNotExist(err) {
if inputPath == "peers.json" {
return fmt.Errorf("peers.json not found in current directory. Please specify the path using --peers flag or create peers.json in the current directory")
}
return fmt.Errorf("input file %s does not exist", inputPath)
}

Expand Down Expand Up @@ -58,10 +66,26 @@ func registerPeers(ctx context.Context, c *cli.Command) error {
// Register peers in Consul
for nodeName, nodeID := range peerMap {
key := prefix + nodeName

// Check if the key already exists
existing, _, err := kv.Get(key, nil)
if err != nil {
return fmt.Errorf("failed to check existing key %s: %w", key, err)
}

if existing != nil {
existingID := string(existing.Value)
if existingID != nodeID {
return fmt.Errorf("conflict detected: peer %s already exists with ID %s, but trying to register with different ID %s", nodeName, existingID, nodeID)
}
fmt.Printf("Peer %s already registered with same ID %s, skipping\n", nodeName, nodeID)
continue
}

p := &api.KVPair{Key: key, Value: []byte(nodeID)}

// Store the key-value pair
_, err := kv.Put(p, nil)
_, err = kv.Put(p, nil)
if err != nil {
return fmt.Errorf("failed to store key %s: %w", key, err)
}
Expand Down
35 changes: 20 additions & 15 deletions cmd/mpcium/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ func runNode(ctx context.Context, c *cli.Command) error {

viper.SetDefault("backup_enabled", true)
config.InitViperConfig(configPath)
environment := viper.GetString("environment")

appConfig := config.LoadConfig()
environment := appConfig.Environment
logger.Init(environment, debug)

// Handle password file if provided
Expand All @@ -127,15 +129,15 @@ func runNode(ctx context.Context, c *cli.Command) error {
promptForSensitiveCredentials()
} else {
// Validate the config values
checkRequiredConfigValues()
checkRequiredConfigValues(appConfig)
}

consulClient := infra.GetConsulClient(environment)
keyinfoStore := keyinfo.NewStore(consulClient.KV())
peers := LoadPeersFromConsul(consulClient)
nodeID := GetIDFromName(nodeName, peers)

badgerKV := NewBadgerKV(nodeName, nodeID)
badgerKV := NewBadgerKV(nodeName, nodeID, appConfig)
defer badgerKV.Close()

// Start background backup job
Expand All @@ -151,7 +153,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
logger.Fatal("Failed to create identity store", err)
}

natsConn, err := GetNATSConnection(environment)
natsConn, err := GetNATSConnection(environment, appConfig)
if err != nil {
logger.Fatal("Failed to connect to NATS", err)
}
Expand Down Expand Up @@ -388,9 +390,9 @@ func maskString(s string) string {
}

// Check required configuration values are present
func checkRequiredConfigValues() {
func checkRequiredConfigValues(appConfig *config.AppConfig) {
// Show warning if we're using file-based config but no password is set
if viper.GetString("badger_password") == "" {
if appConfig.BadgerPassword == "" {
logger.Fatal("Badger password is required", nil)
}

Expand Down Expand Up @@ -441,7 +443,7 @@ func GetIDFromName(name string, peers []config.Peer) string {
return nodeID
}

func NewBadgerKV(nodeName, nodeID string) *kvstore.BadgerKVStore {
func NewBadgerKV(nodeName, nodeID string, appConfig *config.AppConfig) *kvstore.BadgerKVStore {
// Badger KV DB
// Use configured db_path or default to current directory + "db"
basePath := viper.GetString("db_path")
Expand All @@ -459,8 +461,8 @@ func NewBadgerKV(nodeName, nodeID string) *kvstore.BadgerKVStore {
// Create BadgerConfig struct
config := kvstore.BadgerConfig{
NodeID: nodeName,
EncryptionKey: []byte(viper.GetString("badger_password")),
BackupEncryptionKey: []byte(viper.GetString("badger_password")), // Using same key for backup encryption
EncryptionKey: []byte(appConfig.BadgerPassword),
BackupEncryptionKey: []byte(appConfig.BadgerPassword), // Using same key for backup encryption
BackupDir: backupDir,
DBPath: dbPath,
}
Expand Down Expand Up @@ -499,8 +501,8 @@ func StartPeriodicBackup(ctx context.Context, badgerKV *kvstore.BadgerKVStore, p
return backupCancel
}

func GetNATSConnection(environment string) (*nats.Conn, error) {
url := viper.GetString("nats.url")
func GetNATSConnection(environment string, appConfig *config.AppConfig) (*nats.Conn, error) {
url := appConfig.NATs.URL
opts := []nats.Option{
nats.MaxReconnects(-1), // retry forever
nats.ReconnectWait(2 * time.Second),
Expand All @@ -517,9 +519,12 @@ func GetNATSConnection(environment string) (*nats.Conn, error) {

if environment == constant.EnvProduction {
// Load TLS config from configuration
clientCert := viper.GetString("nats.tls.client_cert")
clientKey := viper.GetString("nats.tls.client_key")
caCert := viper.GetString("nats.tls.ca_cert")
var clientCert, clientKey, caCert string
if appConfig.NATs.TLS != nil {
clientCert = appConfig.NATs.TLS.ClientCert
clientKey = appConfig.NATs.TLS.ClientKey
caCert = appConfig.NATs.TLS.CACert
}

// Fallback to default paths if not configured
if clientCert == "" {
Expand All @@ -535,7 +540,7 @@ func GetNATSConnection(environment string) (*nats.Conn, error) {
opts = append(opts,
nats.ClientCert(clientCert, clientKey),
nats.RootCAs(caCert),
nats.UserInfo(viper.GetString("nats.username"), viper.GetString("nats.password")),
nats.UserInfo(appConfig.NATs.Username, appConfig.NATs.Password),
)
}

Expand Down
25 changes: 0 additions & 25 deletions deployments/systemd/setup-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,29 +109,6 @@ check_root() {
fi
}

# Get environment from config.yaml
get_environment_from_config() {
log_step "Reading environment from config.yaml..."

local config_file="/etc/mpcium/config.yaml"

if [[ ! -f "$config_file" ]]; then
log_error "Config file not found at $config_file"
exit 1
fi

# Extract environment from config.yaml
local environment=$(grep "^environment:" "$config_file" | sed 's/environment: *//g' | sed 's/"//g' | sed "s/'//g")

if [[ -z "$environment" ]]; then
environment="production" # default
log_warn "Environment not specified in config.yaml, defaulting to production"
fi

# Store the environment for later use
MPCIUM_ENVIRONMENT="$environment"
log_info "Environment from config.yaml: $MPCIUM_ENVIRONMENT"
}

# Prompt for MPCIUM_NODE_NAME if not provided
setup_node_name() {
Expand Down Expand Up @@ -559,7 +536,6 @@ setup_environment_file() {
# Mpcium Environment Variables
# Generated on $(date)
# Note: All credentials are now configured in /etc/mpcium/config.yaml
ENVIRONMENT=${MPCIUM_ENVIRONMENT}
MPCIUM_NODE_NAME=${MPCIUM_NODE_NAME}
EOF

Expand Down Expand Up @@ -734,7 +710,6 @@ main() {
log_info "Starting Mpcium configuration setup..."

check_root
get_environment_from_config
setup_node_name
check_binaries
ensure_configuration
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
module github.com/fystack/mpcium

go 1.23.0
go 1.23.8

toolchain go1.23.5
toolchain go1.24.7

require (
filippo.io/age v1.2.1
github.com/avast/retry-go v3.0.0+incompatible
github.com/aws/aws-sdk-go-v2/config v1.31.4
github.com/aws/aws-sdk-go-v2/credentials v1.18.8
github.com/aws/aws-sdk-go-v2/service/kms v1.45.0
github.com/bnb-chain/tss-lib/v2 v2.0.2
github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3
github.com/dgraph-io/badger/v4 v4.7.0
github.com/google/uuid v1.6.0
github.com/hashicorp/consul/api v1.26.1
github.com/hashicorp/consul/api v1.32.1
github.com/mitchellh/mapstructure v1.5.0
github.com/nats-io/nats.go v1.31.0
github.com/rs/zerolog v1.31.0
Expand All @@ -29,7 +30,6 @@ require (
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.38.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.5 // indirect
Expand Down Expand Up @@ -96,7 +96,7 @@ require (
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.24.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=
github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -406,6 +409,8 @@ golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand Down
5 changes: 0 additions & 5 deletions peers.json

This file was deleted.

62 changes: 58 additions & 4 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ func TestAppConfig_MarshalJSONMask(t *testing.T) {
masked := config.MarshalJSONMask()

// Verify that sensitive data is masked
assert.Contains(t, masked, "localhost:8500") // Address should not be masked
assert.Contains(t, masked, "admin") // Username should not be masked
assert.Contains(t, masked, "nats_user") // Username should not be masked
assert.Contains(t, masked, "localhost:8500") // Address should not be masked
assert.Contains(t, masked, "admin") // Username should not be masked
assert.Contains(t, masked, "nats_user") // Username should not be masked
assert.Contains(t, masked, "nats://localhost:4222") // URL should not be masked

// Verify that passwords are masked
Expand Down Expand Up @@ -120,4 +120,58 @@ func TestAppConfig_PartialConfig(t *testing.T) {
assert.Contains(t, masked, "localhost:8500")
assert.NotContains(t, masked, "test")
assert.Contains(t, masked, "****") // masked badger password
}
}

func TestValidateEnvironment(t *testing.T) {
tests := []struct {
name string
environment string
wantErr bool
}{
{
name: "valid production environment",
environment: "production",
wantErr: false,
},
{
name: "valid development environment",
environment: "development",
wantErr: false,
},
{
name: "invalid environment",
environment: "staging",
wantErr: true,
},
{
name: "empty environment",
environment: "",
wantErr: true,
},
{
name: "case sensitive - Production",
environment: "Production",
wantErr: true,
},
{
name: "case sensitive - PRODUCTION",
environment: "PRODUCTION",
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateEnvironment(tt.environment)
if tt.wantErr {
assert.Error(t, err)
if err != nil {
assert.Contains(t, err.Error(), "invalid environment")
assert.Contains(t, err.Error(), "production, development")
}
} else {
assert.NoError(t, err)
}
})
}
}
Loading
Loading