Enable TLS support for btcd websocket connections.
This adds an additional config option, -cafile, to specify the root certificates checked when verifying a btcd TLC connection. btcd will now automatically generate certs in ~/.btcd/data/{main,test}net/rpc.cert, and this file should be copied to ~/.btcwallet/cert.pem. The -btcdport option is also gone now, and replaced with -connect (or -c), to specify both the hostname/ip and port of the server running btcd.
This commit is contained in:
parent
474106a757
commit
5dbf69d23e
6 changed files with 54 additions and 15 deletions
|
@ -96,7 +96,7 @@ messages they originated from.
|
|||
## TODO
|
||||
|
||||
- Require authentication before wallet functionality can be accessed
|
||||
- Serve websocket connections over TLS
|
||||
- Serve frontend websocket connections over TLS
|
||||
- Rescan the blockchain for missed transactions
|
||||
- Documentation (specifically the websocket API additions)
|
||||
- Code cleanup
|
||||
|
|
10
cmd.go
10
cmd.go
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/conformal/btcwallet/wallet"
|
||||
"github.com/conformal/btcwire"
|
||||
"github.com/conformal/btcws"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
@ -312,6 +313,13 @@ func main() {
|
|||
log.Errorf("cannot open wallet: %v", err)
|
||||
}
|
||||
|
||||
// Read CA file to verify a btcd TLS connection.
|
||||
cafile, err := ioutil.ReadFile(cfg.CAFile)
|
||||
if err != nil {
|
||||
log.Errorf("cannot open CA file: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Start account disk syncer goroutine.
|
||||
go DirtyAccountSyncer()
|
||||
|
||||
|
@ -334,7 +342,7 @@ func main() {
|
|||
replies := make(chan error)
|
||||
done := make(chan int)
|
||||
go func() {
|
||||
BtcdConnect(replies)
|
||||
BtcdConnect(cafile, replies)
|
||||
close(done)
|
||||
}()
|
||||
selectLoop:
|
||||
|
|
26
config.go
26
config.go
|
@ -21,12 +21,14 @@ import (
|
|||
"github.com/conformal/btcutil"
|
||||
"github.com/conformal/btcwire"
|
||||
"github.com/conformal/go-flags"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCAFilename = "cert.pem"
|
||||
defaultConfigFilename = "btcwallet.conf"
|
||||
defaultDataDirname = "data"
|
||||
defaultBtcNet = btcwire.TestNet3
|
||||
|
@ -35,13 +37,15 @@ const (
|
|||
|
||||
var (
|
||||
btcwalletHomeDir = btcutil.AppDataDir("btcwallet", false)
|
||||
defaultCAFile = filepath.Join(btcwalletHomeDir, defaultCAFilename)
|
||||
defaultConfigFile = filepath.Join(btcwalletHomeDir, defaultConfigFilename)
|
||||
defaultDataDir = btcwalletHomeDir
|
||||
)
|
||||
|
||||
type config struct {
|
||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||
BtcdPort string `short:"b" long:"btcdport" description:"Port to connect to btcd on (default: 18334, mainnet: 18332)"`
|
||||
CAFile string `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with btcd"`
|
||||
Connect string `short:"c" long:"connect" description:"Server and port of btcd instance to connect to"`
|
||||
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
|
||||
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
|
||||
SvrPort string `short:"p" long:"serverport" description:"Port to serve frontend websocket connections on (default: 18332, mainnet: 8332)"`
|
||||
|
@ -56,8 +60,8 @@ type config struct {
|
|||
// object are the default so options specified by the user on the command line
|
||||
// are not overridden.
|
||||
func updateConfigWithActiveParams(cfg *config) {
|
||||
if cfg.BtcdPort == netParams(defaultBtcNet).btcdPort {
|
||||
cfg.BtcdPort = activeNetParams.btcdPort
|
||||
if cfg.Connect == netParams(defaultBtcNet).connect {
|
||||
cfg.Connect = activeNetParams.connect
|
||||
}
|
||||
|
||||
if cfg.SvrPort == netParams(defaultBtcNet).svrPort {
|
||||
|
@ -75,6 +79,16 @@ func fileExists(name string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// normalizeAddress returns addr with the passed default port appended if
|
||||
// there is not already a port specified.
|
||||
func normalizeAddress(addr, defaultPort string) string {
|
||||
_, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return net.JoinHostPort(addr, defaultPort)
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// loadConfig initializes and parses the config using a config file and command
|
||||
// line options.
|
||||
//
|
||||
|
@ -91,8 +105,9 @@ func loadConfig() (*config, []string, error) {
|
|||
// Default config.
|
||||
cfg := config{
|
||||
DebugLevel: defaultLogLevel,
|
||||
CAFile: defaultCAFile,
|
||||
ConfigFile: defaultConfigFile,
|
||||
BtcdPort: netParams(defaultBtcNet).btcdPort,
|
||||
Connect: netParams(defaultBtcNet).connect,
|
||||
SvrPort: netParams(defaultBtcNet).svrPort,
|
||||
DataDir: defaultDataDir,
|
||||
}
|
||||
|
@ -161,6 +176,9 @@ func loadConfig() (*config, []string, error) {
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Add default port to connect flag if missing.
|
||||
cfg.Connect = normalizeAddress(cfg.Connect, activeNetParams.btcdPort)
|
||||
|
||||
return &cfg, remainingArgs, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ var activeNetParams = netParams(defaultBtcNet)
|
|||
// params is used to group parameters for various networks such as the main
|
||||
// network and test networks.
|
||||
type params struct {
|
||||
connect string
|
||||
btcdPort string
|
||||
svrPort string
|
||||
}
|
||||
|
@ -32,6 +33,7 @@ type params struct {
|
|||
// mainNetParams contains parameters specific running btcwallet and
|
||||
// btcd on the main network (btcwire.MainNet).
|
||||
var mainNetParams = params{
|
||||
connect: "localhost:8334",
|
||||
btcdPort: "8334",
|
||||
svrPort: "8332",
|
||||
}
|
||||
|
@ -39,6 +41,7 @@ var mainNetParams = params{
|
|||
// testNet3Params contains parameters specific running btcwallet and
|
||||
// btcd on the test network (version 3) (btcwire.TestNet3).
|
||||
var testNet3Params = params{
|
||||
connect: "localhost:18334",
|
||||
btcdPort: "18334",
|
||||
svrPort: "18332",
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
; Network settings
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; The port used for btcd websocket connections.
|
||||
; btcdport=18334
|
||||
; The server and port used for btcd websocket connections.
|
||||
; connect=localhost:18334
|
||||
|
||||
; Username and password to authenticate to btcd RPC/websocket HTTP server.
|
||||
; username=
|
||||
|
|
24
sockets.go
24
sockets.go
|
@ -18,6 +18,8 @@ package main
|
|||
|
||||
import (
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
@ -577,22 +579,30 @@ func (s *server) Start() {
|
|||
// BtcdConnect connects to a running btcd instance over a websocket
|
||||
// for sending and receiving chain-related messages, failing if the
|
||||
// connection cannot be established or is lost.
|
||||
func BtcdConnect(reply chan error) {
|
||||
// btcd requires basic authorization, so we use a custom config with
|
||||
// the Authorization header set.
|
||||
server := fmt.Sprintf("ws://%s/wallet", net.JoinHostPort("localhost", cfg.BtcdPort))
|
||||
login := cfg.Username + ":" + cfg.Password
|
||||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
config, err := websocket.NewConfig(server, "http://localhost/")
|
||||
func BtcdConnect(certificates []byte, reply chan error) {
|
||||
url := fmt.Sprintf("wss://%s/wallet", cfg.Connect)
|
||||
config, err := websocket.NewConfig(url, "https://localhost/")
|
||||
if err != nil {
|
||||
reply <- ErrConnRefused
|
||||
return
|
||||
}
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(certificates)
|
||||
config.TlsConfig = &tls.Config{
|
||||
RootCAs: pool,
|
||||
}
|
||||
|
||||
// btcd requires basic authorization, so we use a custom config with
|
||||
// the Authorization header set.
|
||||
login := cfg.Username + ":" + cfg.Password
|
||||
auth := "Basic" + base64.StdEncoding.EncodeToString([]byte(login))
|
||||
config.Header.Add("Authorization", auth)
|
||||
|
||||
// Attempt to connect to running btcd instance. Bail if it fails.
|
||||
btcdws, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
reply <- ErrConnRefused
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue