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
|
## TODO
|
||||||
|
|
||||||
- Require authentication before wallet functionality can be accessed
|
- 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
|
- Rescan the blockchain for missed transactions
|
||||||
- Documentation (specifically the websocket API additions)
|
- Documentation (specifically the websocket API additions)
|
||||||
- Code cleanup
|
- Code cleanup
|
||||||
|
|
10
cmd.go
10
cmd.go
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/conformal/btcwallet/wallet"
|
"github.com/conformal/btcwallet/wallet"
|
||||||
"github.com/conformal/btcwire"
|
"github.com/conformal/btcwire"
|
||||||
"github.com/conformal/btcws"
|
"github.com/conformal/btcws"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -312,6 +313,13 @@ func main() {
|
||||||
log.Errorf("cannot open wallet: %v", err)
|
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.
|
// Start account disk syncer goroutine.
|
||||||
go DirtyAccountSyncer()
|
go DirtyAccountSyncer()
|
||||||
|
|
||||||
|
@ -334,7 +342,7 @@ func main() {
|
||||||
replies := make(chan error)
|
replies := make(chan error)
|
||||||
done := make(chan int)
|
done := make(chan int)
|
||||||
go func() {
|
go func() {
|
||||||
BtcdConnect(replies)
|
BtcdConnect(cafile, replies)
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
selectLoop:
|
selectLoop:
|
||||||
|
|
26
config.go
26
config.go
|
@ -21,12 +21,14 @@ import (
|
||||||
"github.com/conformal/btcutil"
|
"github.com/conformal/btcutil"
|
||||||
"github.com/conformal/btcwire"
|
"github.com/conformal/btcwire"
|
||||||
"github.com/conformal/go-flags"
|
"github.com/conformal/go-flags"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
defaultCAFilename = "cert.pem"
|
||||||
defaultConfigFilename = "btcwallet.conf"
|
defaultConfigFilename = "btcwallet.conf"
|
||||||
defaultDataDirname = "data"
|
defaultDataDirname = "data"
|
||||||
defaultBtcNet = btcwire.TestNet3
|
defaultBtcNet = btcwire.TestNet3
|
||||||
|
@ -35,13 +37,15 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
btcwalletHomeDir = btcutil.AppDataDir("btcwallet", false)
|
btcwalletHomeDir = btcutil.AppDataDir("btcwallet", false)
|
||||||
|
defaultCAFile = filepath.Join(btcwalletHomeDir, defaultCAFilename)
|
||||||
defaultConfigFile = filepath.Join(btcwalletHomeDir, defaultConfigFilename)
|
defaultConfigFile = filepath.Join(btcwalletHomeDir, defaultConfigFilename)
|
||||||
defaultDataDir = btcwalletHomeDir
|
defaultDataDir = btcwalletHomeDir
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
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}"`
|
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"`
|
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)"`
|
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
|
// object are the default so options specified by the user on the command line
|
||||||
// are not overridden.
|
// are not overridden.
|
||||||
func updateConfigWithActiveParams(cfg *config) {
|
func updateConfigWithActiveParams(cfg *config) {
|
||||||
if cfg.BtcdPort == netParams(defaultBtcNet).btcdPort {
|
if cfg.Connect == netParams(defaultBtcNet).connect {
|
||||||
cfg.BtcdPort = activeNetParams.btcdPort
|
cfg.Connect = activeNetParams.connect
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.SvrPort == netParams(defaultBtcNet).svrPort {
|
if cfg.SvrPort == netParams(defaultBtcNet).svrPort {
|
||||||
|
@ -75,6 +79,16 @@ func fileExists(name string) bool {
|
||||||
return true
|
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
|
// loadConfig initializes and parses the config using a config file and command
|
||||||
// line options.
|
// line options.
|
||||||
//
|
//
|
||||||
|
@ -91,8 +105,9 @@ func loadConfig() (*config, []string, error) {
|
||||||
// Default config.
|
// Default config.
|
||||||
cfg := config{
|
cfg := config{
|
||||||
DebugLevel: defaultLogLevel,
|
DebugLevel: defaultLogLevel,
|
||||||
|
CAFile: defaultCAFile,
|
||||||
ConfigFile: defaultConfigFile,
|
ConfigFile: defaultConfigFile,
|
||||||
BtcdPort: netParams(defaultBtcNet).btcdPort,
|
Connect: netParams(defaultBtcNet).connect,
|
||||||
SvrPort: netParams(defaultBtcNet).svrPort,
|
SvrPort: netParams(defaultBtcNet).svrPort,
|
||||||
DataDir: defaultDataDir,
|
DataDir: defaultDataDir,
|
||||||
}
|
}
|
||||||
|
@ -161,6 +176,9 @@ func loadConfig() (*config, []string, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add default port to connect flag if missing.
|
||||||
|
cfg.Connect = normalizeAddress(cfg.Connect, activeNetParams.btcdPort)
|
||||||
|
|
||||||
return &cfg, remainingArgs, nil
|
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
|
// params is used to group parameters for various networks such as the main
|
||||||
// network and test networks.
|
// network and test networks.
|
||||||
type params struct {
|
type params struct {
|
||||||
|
connect string
|
||||||
btcdPort string
|
btcdPort string
|
||||||
svrPort string
|
svrPort string
|
||||||
}
|
}
|
||||||
|
@ -32,6 +33,7 @@ type params struct {
|
||||||
// mainNetParams contains parameters specific running btcwallet and
|
// mainNetParams contains parameters specific running btcwallet and
|
||||||
// btcd on the main network (btcwire.MainNet).
|
// btcd on the main network (btcwire.MainNet).
|
||||||
var mainNetParams = params{
|
var mainNetParams = params{
|
||||||
|
connect: "localhost:8334",
|
||||||
btcdPort: "8334",
|
btcdPort: "8334",
|
||||||
svrPort: "8332",
|
svrPort: "8332",
|
||||||
}
|
}
|
||||||
|
@ -39,6 +41,7 @@ var mainNetParams = params{
|
||||||
// testNet3Params contains parameters specific running btcwallet and
|
// testNet3Params contains parameters specific running btcwallet and
|
||||||
// btcd on the test network (version 3) (btcwire.TestNet3).
|
// btcd on the test network (version 3) (btcwire.TestNet3).
|
||||||
var testNet3Params = params{
|
var testNet3Params = params{
|
||||||
|
connect: "localhost:18334",
|
||||||
btcdPort: "18334",
|
btcdPort: "18334",
|
||||||
svrPort: "18332",
|
svrPort: "18332",
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
; Network settings
|
; Network settings
|
||||||
; ------------------------------------------------------------------------------
|
; ------------------------------------------------------------------------------
|
||||||
|
|
||||||
; The port used for btcd websocket connections.
|
; The server and port used for btcd websocket connections.
|
||||||
; btcdport=18334
|
; connect=localhost:18334
|
||||||
|
|
||||||
; Username and password to authenticate to btcd RPC/websocket HTTP server.
|
; Username and password to authenticate to btcd RPC/websocket HTTP server.
|
||||||
; username=
|
; username=
|
||||||
|
|
24
sockets.go
24
sockets.go
|
@ -18,6 +18,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.net/websocket"
|
"code.google.com/p/go.net/websocket"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -577,22 +579,30 @@ func (s *server) Start() {
|
||||||
// BtcdConnect connects to a running btcd instance over a websocket
|
// BtcdConnect connects to a running btcd instance over a websocket
|
||||||
// for sending and receiving chain-related messages, failing if the
|
// for sending and receiving chain-related messages, failing if the
|
||||||
// connection cannot be established or is lost.
|
// connection cannot be established or is lost.
|
||||||
func BtcdConnect(reply chan error) {
|
func BtcdConnect(certificates []byte, reply chan error) {
|
||||||
// btcd requires basic authorization, so we use a custom config with
|
url := fmt.Sprintf("wss://%s/wallet", cfg.Connect)
|
||||||
// the Authorization header set.
|
config, err := websocket.NewConfig(url, "https://localhost/")
|
||||||
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/")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reply <- ErrConnRefused
|
reply <- ErrConnRefused
|
||||||
return
|
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)
|
config.Header.Add("Authorization", auth)
|
||||||
|
|
||||||
// Attempt to connect to running btcd instance. Bail if it fails.
|
// Attempt to connect to running btcd instance. Bail if it fails.
|
||||||
btcdws, err := websocket.DialConfig(config)
|
btcdws, err := websocket.DialConfig(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
reply <- ErrConnRefused
|
reply <- ErrConnRefused
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue