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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
db/
tmp/
bin/
identity/
event_initiator.identity.json
event_initiator.key
event_initiator.key.age
Expand Down
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all build clean mpcium mpc test test-verbose test-coverage e2e-test e2e-clean cleanup-test-env
.PHONY: all build clean mpcium mpc install test test-verbose test-coverage e2e-test e2e-clean cleanup-test-env

BIN_DIR := bin

Expand All @@ -16,6 +16,16 @@ mpcium:
mpc:
go install ./cmd/mpcium-cli

# Install binaries to /usr/local/bin (auto-detects architecture)
install:
@echo "Building and installing mpcium binaries for Linux..."
GOOS=linux go build -o /tmp/mpcium ./cmd/mpcium
GOOS=linux go build -o /tmp/mpcium-cli ./cmd/mpcium-cli
sudo install -m 755 /tmp/mpcium /usr/local/bin/
sudo install -m 755 /tmp/mpcium-cli /usr/local/bin/
rm -f /tmp/mpcium /tmp/mpcium-cli
@echo "Successfully installed mpcium and mpcium-cli to /usr/local/bin/"

# Run all tests
test:
go test ./...
Expand Down
7 changes: 7 additions & 0 deletions cmd/mpcium-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ func main() {
cmd := &cli.Command{
Name: "mpcium",
Usage: "Fystack MPC node management tools",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Path to configuration file",
},
},
Commands: []*cli.Command{
{
Name: "generate-peers",
Expand Down
2 changes: 1 addition & 1 deletion cmd/mpcium-cli/register-peers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func registerPeers(ctx context.Context, c *cli.Command) error {
}

// Initialize config and logger
config.InitViperConfig()
config.InitViperConfig(c.String("config"))
logger.Init(environment, true)

// Connect to Consul
Expand Down
101 changes: 75 additions & 26 deletions cmd/mpcium/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
Expand All @@ -21,6 +22,7 @@ import (
"github.com/fystack/mpcium/pkg/logger"
"github.com/fystack/mpcium/pkg/messaging"
"github.com/fystack/mpcium/pkg/mpc"
"github.com/fystack/mpcium/pkg/security"
"github.com/hashicorp/consul/api"
"github.com/nats-io/nats.go"
"github.com/spf13/viper"
Expand Down Expand Up @@ -49,6 +51,11 @@ func main() {
Usage: "Node name",
Required: true,
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Path to configuration file",
},
&cli.BoolFlag{
Name: "decrypt-private-key",
Aliases: []string{"d"},
Expand All @@ -60,6 +67,16 @@ func main() {
Aliases: []string{"p"},
Usage: "Prompt for sensitive parameters",
},
&cli.StringFlag{
Name: "password-file",
Aliases: []string{"f"},
Usage: "Path to file containing BadgerDB password",
},
&cli.StringFlag{
Name: "age-password-file",
Aliases: []string{"k"},
Usage: "Path to file containing password for decrypting .age encrypted node private key",
},
&cli.BoolFlag{
Name: "debug",
Usage: "Enable debug logging",
Expand Down Expand Up @@ -87,15 +104,24 @@ func main() {

func runNode(ctx context.Context, c *cli.Command) error {
nodeName := c.String("name")
configPath := c.String("config")
decryptPrivateKey := c.Bool("decrypt-private-key")
usePrompts := c.Bool("prompt-credentials")
passwordFile := c.String("password-file")
agePasswordFile := c.String("age-password-file")
debug := c.Bool("debug")

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

// Handle password file if provided
if passwordFile != "" {
if err := loadPasswordFromFile(passwordFile); err != nil {
return fmt.Errorf("failed to load password from file: %w", err)
}
}
// Handle configuration based on prompt flag
if usePrompts {
promptForSensitiveCredentials()
Expand All @@ -120,7 +146,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
defer stopBackup()
}

identityStore, err := identity.NewFileStore("identity", nodeName, decryptPrivateKey)
identityStore, err := identity.NewFileStore("identity", nodeName, decryptPrivateKey, agePasswordFile)
if err != nil {
logger.Fatal("Failed to create identity store", err)
}
Expand Down Expand Up @@ -272,6 +298,28 @@ func runNode(ctx context.Context, c *cli.Command) error {
return nil
}

// loadPasswordFromFile reads the BadgerDB password from a file
func loadPasswordFromFile(filePath string) error {
passwordBytes, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("failed to read password file %s: %w", filePath, err)
}

// Trim whitespace/newlines without altering content
password := strings.TrimSpace(string(passwordBytes))

if password == "" {
security.ZeroBytes(passwordBytes)
return fmt.Errorf("password file %s is empty", filePath)
}

viper.Set("badger_password", password)
security.ZeroBytes(passwordBytes)
security.ZeroString(&password)

return nil
}

// Prompt user for sensitive configuration values
func promptForSensitiveCredentials() {
fmt.Println("WARNING: Please back up your Badger DB password in a secure location.")
Expand All @@ -282,6 +330,12 @@ func promptForSensitiveCredentials() {
var confirmPass []byte
var err error

// Ensure sensitive buffers are zeroed on exit
defer func() {
security.ZeroBytes(badgerPass)
security.ZeroBytes(confirmPass)
}()

for {
fmt.Print("Enter Badger DB password: ")
badgerPass, err = term.ReadPassword(int(syscall.Stdin))
Expand Down Expand Up @@ -311,28 +365,11 @@ func promptForSensitiveCredentials() {
}

// Show masked password for confirmation
maskedPassword := maskString(string(badgerPass))
passwordStr := string(badgerPass)
maskedPassword := maskString(passwordStr)
fmt.Printf("Password set: %s\n", maskedPassword)

viper.Set("badger_password", string(badgerPass))

// Prompt for initiator public key (using regular input since it's not as sensitive)
var initiatorKey string
fmt.Print("Enter event initiator public key (hex): ")
if _, err := fmt.Scanln(&initiatorKey); err != nil {
logger.Fatal("Failed to read initiator key", err)
}

if initiatorKey == "" {
logger.Fatal("Initiator public key cannot be empty", nil)
}

// Show masked key for confirmation
maskedKey := maskString(initiatorKey)
fmt.Printf("Event initiator public key set: %s\n", maskedKey)

viper.Set("event_initiator_pubkey", initiatorKey)
fmt.Println("\n✓ Configuration complete!")
viper.Set("badger_password", passwordStr)
security.ZeroString(&passwordStr)
}

// maskString shows the first and last character of a string, replacing the middle with asterisks
Expand Down Expand Up @@ -479,9 +516,21 @@ func GetNATSConnection(environment string) (*nats.Conn, error) {
}

if environment == constant.EnvProduction {
clientCert := filepath.Join(".", "certs", "client-cert.pem")
clientKey := filepath.Join(".", "certs", "client-key.pem")
caCert := filepath.Join(".", "certs", "rootCA.pem")
// 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")

// Fallback to default paths if not configured
if clientCert == "" {
clientCert = filepath.Join(".", "certs", "client-cert.pem")
}
if clientKey == "" {
clientKey = filepath.Join(".", "certs", "client-key.pem")
}
if caCert == "" {
caCert = filepath.Join(".", "certs", "rootCA.pem")
}

opts = append(opts,
nats.ClientCert(clientCert, clientKey),
Expand Down
6 changes: 5 additions & 1 deletion config.prod.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ nats:
url: tls://127.0.0.1:4222 # Please use TLS for production
username: ""
password: ""
tls:
client_cert: "/etc/mpcium/certs/client-cert.pem"
client_key: "/etc/mpcium/certs/client-key.pem"
ca_cert: "/etc/mpcium/certs/rootCA.pem"

consul:
address: https://consul.example.com # Use HTTPS for production
address: https://consul.example.com
username: username
token: ""
password: ""
Expand Down
2 changes: 1 addition & 1 deletion examples/generate/kms/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func main() {

flag.Parse()

config.InitViperConfig()
config.InitViperConfig("")
logger.Init(environment, false)

// KMS signer only supports P256
Expand Down
2 changes: 1 addition & 1 deletion examples/generate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func main() {

flag.Parse()

config.InitViperConfig()
config.InitViperConfig("")
logger.Init(environment, false)

algorithm := viper.GetString("event_initiator_algorithm")
Expand Down
2 changes: 1 addition & 1 deletion examples/reshare/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

func main() {
const environment = "dev"
config.InitViperConfig()
config.InitViperConfig("")
logger.Init(environment, true)

algorithm := viper.GetString("event_initiator_algorithm")
Expand Down
2 changes: 1 addition & 1 deletion examples/sign/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

func main() {
const environment = "dev"
config.InitViperConfig()
config.InitViperConfig("")
logger.Init(environment, true)

algorithm := viper.GetString("event_initiator_algorithm")
Expand Down
30 changes: 23 additions & 7 deletions pkg/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,31 @@ type ConsulConfig struct {
}

type NATsConfig struct {
URL string `mapstructure:"url"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
URL string `mapstructure:"url"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
TLS *TLSConfig `mapstructure:"tls"`
}

func InitViperConfig() {
viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath(".") // optionally look for config in the working directory
type TLSConfig struct {
ClientCert string `mapstructure:"client_cert"`
ClientKey string `mapstructure:"client_key"`
CACert string `mapstructure:"ca_cert"`
}

func InitViperConfig(configPath string) {
if configPath != "" {
// Use specific config file path
viper.SetConfigFile(configPath)
} else {
// Use default behavior - search for config.yaml in common locations
viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath(".") // optionally look for config in the working directory
viper.AddConfigPath("/etc/mpcium/") // look for config in /etc/mpcium/
viper.AddConfigPath("$HOME/.mpcium/") // look for config in home directory
}

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
err := viper.ReadInConfig() // Find and read the config file
Expand Down
Loading
Loading