Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpcserver: Adds ability to allow alternative dns names for TLS. #1476

Merged
merged 2 commits into from
Oct 11, 2018
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: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ var (
defaultRPCKeyFile = filepath.Join(defaultHomeDir, "rpc.key")
defaultRPCCertFile = filepath.Join(defaultHomeDir, "rpc.cert")
defaultLogDir = filepath.Join(defaultHomeDir, defaultLogDirname)
defaultAltDNSNames = []string{}
)

// runServiceCommand is only set to a real function on Windows. It is used
Expand Down Expand Up @@ -165,6 +166,7 @@ type config struct {
PipeRx uint `long:"piperx" description:"File descriptor of read end pipe to enable parent -> child process communication"`
PipeTx uint `long:"pipetx" description:"File descriptor of write end pipe to enable parent <- child process communication"`
LifetimeEvents bool `long:"lifetimeevents" description:"Send lifetime notifications over the TX pipe"`
AltDNSNames []string `long:"altdnsnames" description:"Specify additional dns names to use when generating the rpc server certificate" env:"DCRD_ALT_DNSNAMES" env-delim:","`
onionlookup func(string) ([]net.IP, error)
lookup func(string) ([]net.IP, error)
oniondial func(string, string) (net.Conn, error)
Expand Down Expand Up @@ -456,6 +458,7 @@ func loadConfig() (*config, []string, error) {
AllowOldVotes: defaultAllowOldVotes,
NoExistsAddrIndex: defaultNoExistsAddrIndex,
NoCFilters: defaultNoCFilters,
AltDNSNames: defaultAltDNSNames,
}

// Service options which are only added on Windows.
Expand Down
68 changes: 68 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package main
davecgh marked this conversation as resolved.
Show resolved Hide resolved

import (
"flag"
"io/ioutil"
"os"
"strings"
"testing"
)

// in order to test command line arguments and environment variables
// you will need to append the flags to the os.Args variable like so
// os.Args = append(os.Args, "--altdnsnames=\"hostname1,hostname2\"")
// For environment variables you can use the
// os.Setenv("DCRD_ALT_DNSNAMES", "hostname1,hostname2") to set the variable
// before loadConfig() is called
// These args and env variables will then get parsed by loadConfig()

func setup() {
// Temp config file is used to ensure there are no external influences
// from previously set env variables or default config files.
file, _ := ioutil.TempFile("", "dcrd_test_file.cfg")
defer os.Remove(file.Name())

// Parse the -test.* flags before removing them from the command line
davecgh marked this conversation as resolved.
Show resolved Hide resolved
// arguments list, which we do to allow go-flags to succeed.
flag.Parse()
os.Args = os.Args[:1]
}

func TestLoadConfig(t *testing.T) {
_, _, err := loadConfig()
if err != nil {
t.Errorf("Failed to load dcrd config: %s\n", err.Error())
}
}

func TestDefaultAltDNSNames(t *testing.T) {
cfg, _, _ := loadConfig()
if len(cfg.AltDNSNames) != 0 {
t.Errorf("Invalid default value for altdnsnames: %s\n", cfg.AltDNSNames)
}
}

func TestAltDNSNamesWithEnv(t *testing.T) {
os.Setenv("DCRD_ALT_DNSNAMES", "hostname1,hostname2")
cfg, _, _ := loadConfig()
hostnames := strings.Join(cfg.AltDNSNames, ",")
if hostnames != "hostname1,hostname2" {
t.Errorf("altDNSNames should be %s but was %s", "hostname1,hostname2", hostnames)
}
}

func TestAltDNSNamesWithArg(t *testing.T) {
setup()
old := os.Args
os.Args = append(os.Args, "--altdnsnames=\"hostname1,hostname2\"")
cfg, _, _ := loadConfig()
hostnames := strings.Join(cfg.AltDNSNames, ",")
if hostnames != "hostname1,hostname2" {
t.Errorf("altDNSNames should be %s but was %s", "hostname1,hostname2", hostnames)
}
os.Args = old
}
6 changes: 3 additions & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6333,13 +6333,13 @@ func (s *rpcServer) Start() {
}

// genCertPair generates a key/cert pair to the paths provided.
func genCertPair(certFile, keyFile string) error {
func genCertPair(certFile, keyFile string, altDNSNames []string) error {
rpcsLog.Infof("Generating TLS certificates...")

org := "dcrd autogenerated cert"
validUntil := time.Now().Add(10 * 365 * 24 * time.Hour)
cert, key, err := certgen.NewTLSCertPair(elliptic.P521(), org,
validUntil, nil)
validUntil, altDNSNames)
if err != nil {
return err
}
Expand Down Expand Up @@ -6391,7 +6391,7 @@ func newRPCServer(listenAddrs []string, generator *BlkTmplGenerator, s *server)
// Generate the TLS cert and key file if both don't already
// exist.
if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) {
err := genCertPair(cfg.RPCCert, cfg.RPCKey)
err := genCertPair(cfg.RPCCert, cfg.RPCKey, cfg.AltDNSNames)
if err != nil {
return nil, err
}
Expand Down
61 changes: 61 additions & 0 deletions rpcserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ package main

import (
"bytes"
"crypto/x509"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"os"
"runtime/debug"
"testing"
Expand Down Expand Up @@ -106,6 +110,12 @@ var primaryHarness *rpctest.Harness
func TestMain(m *testing.M) {
var err error

// Parse the -test.* flags before removing them from the command line
// arguments list, which we do to allow go-flags to succeed.
// See config_test.go for more info
flag.Parse()
os.Args = os.Args[:1]

// In order to properly test scenarios on as if we were on mainnet,
// ensure that non-standard transactions aren't accepted into the
// mempool or relayed.
Expand Down Expand Up @@ -165,3 +175,54 @@ func TestRpcServer(t *testing.T) {
currentTestNum++
}
}

func TestCertCreationWithHosts(t *testing.T) {
certfile, err := ioutil.TempFile("", "certfile")
if err != nil {
t.Fatalf("Unable to create temp certfile: %s", err)
}
keyfile, err := ioutil.TempFile("", "keyfile")
if err != nil {
t.Fatalf("Unable to create temp keyfile: %s", err)
}
hostnames := []string{"hostname1", "hostname2"}
defer os.Remove(keyfile.Name())
defer os.Remove(certfile.Name())
err = genCertPair(certfile.Name(), keyfile.Name(), hostnames)
if err != nil {
t.Fatalf("certifcate was not created correctly: %s", err)
}
certBytes, err := ioutil.ReadFile(certfile.Name())
if err != nil {
t.Fatalf("Unable to read the certfile: %s", err)
}
pemCert, _ := pem.Decode(certBytes)
x509Cert, err := x509.ParseCertificate(pemCert.Bytes)
if err != nil {
t.Fatalf("Unable to parse the certificate: %s", err)
}
// Ensure the specified extra hosts are present.
for _, host := range hostnames {
err := x509Cert.VerifyHostname(host)
if err != nil {
t.Fatalf("failed to verify extra host '%s'", host)
davecgh marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

func TestCertCreationWithOutHosts(t *testing.T) {
certfile, err := ioutil.TempFile("", "certfile")
if err != nil {
t.Fatalf("Unable to create temp certfile: %s", err)
}
keyfile, err := ioutil.TempFile("", "keyfile")
if err != nil {
t.Fatalf("Unable to create temp keyfile: %s", err)
}
defer os.Remove(keyfile.Name())
defer os.Remove(certfile.Name())
err = genCertPair(certfile.Name(), keyfile.Name(), []string{})
if err != nil {
t.Fatalf("certifcate was not created correctly: %s", err)
}
}