Skip to content

Commit

Permalink
+ config: add certificate_path, private_key_path
Browse files Browse the repository at this point in the history
* POST /control/tls/configure: support certificate_path and private_key_path
  • Loading branch information
szolin committed Aug 30, 2019
1 parent c847df9 commit 24bb708
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 14 deletions.
63 changes: 63 additions & 0 deletions AGHTechDoc.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Contents:
* Updating
* Get version command
* Update command
* TLS
* API: Get TLS configuration
* API: Set TLS configuration
* Device Names and Per-client Settings
* Per-client settings
* Get list of clients
Expand Down Expand Up @@ -515,6 +518,66 @@ Response:
200 OK


## TLS


### API: Get TLS configuration

Request:

GET /control/tls/status

Response:

200 OK

{
"enabled":true,
"server_name":"...",
"port_https":443,
"port_dns_over_tls":853,
"certificate_chain":"...",
"private_key":"...",
"certificate_path":"...",
"private_key_path":"..."

"subject":"CN=...",
"issuer":"CN=...",
"not_before":"2019-03-19T08:23:45Z",
"not_after":"2029-03-16T08:23:45Z",
"dns_names":null,
"key_type":"RSA",
"valid_cert":true,
"valid_key":true,
"valid_chain":false,
"valid_pair":true,
"warning_validation":"Your certificate does not verify: x509: certificate signed by unknown authority"
}


### API: Set TLS configuration

Request:

POST /control/tls/configure

{
"enabled":true,
"server_name":"hostname",
"force_https":false,
"port_https":443,
"port_dns_over_tls":853,
"certificate_chain":"...",
"private_key":"...",
"certificate_path":"...", // if set, certificate_chain must be empty
"private_key_path":"..." // if set, private_key must be empty
}

Response:

200 OK


## Device Names and Per-client Settings

When a client requests information from DNS server, he's identified by IP address.
Expand Down
10 changes: 8 additions & 2 deletions dnsforward/dnsforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ type TLSConfig struct {
TLSListenAddr *net.TCPAddr `yaml:"-" json:"-"`
CertificateChain string `yaml:"certificate_chain" json:"certificate_chain"` // PEM-encoded certificates chain
PrivateKey string `yaml:"private_key" json:"private_key"` // PEM-encoded private key

CertificatePath string `yaml:"certificate_path" json:"certificate_path"` // certificate file name
PrivateKeyPath string `yaml:"private_key_path" json:"private_key_path"` // private key file name

CertificateChainData []byte `yaml:"-" json:"-"`
PrivateKeyData []byte `yaml:"-" json:"-"`
}

// ServerConfig represents server configuration.
Expand Down Expand Up @@ -216,9 +222,9 @@ func (s *Server) startInternal(config *ServerConfig) error {

convertArrayToMap(&s.BlockedHosts, s.conf.BlockedHosts)

if s.conf.TLSListenAddr != nil && s.conf.CertificateChain != "" && s.conf.PrivateKey != "" {
if s.conf.TLSListenAddr != nil && len(s.conf.CertificateChainData) != 0 && len(s.conf.PrivateKeyData) != 0 {
proxyConfig.TLSListenAddr = s.conf.TLSListenAddr
keypair, err := tls.X509KeyPair([]byte(s.conf.CertificateChain), []byte(s.conf.PrivateKey))
keypair, err := tls.X509KeyPair(s.conf.CertificateChainData, s.conf.PrivateKeyData)
if err != nil {
return errorx.Decorate(err, "Failed to parse TLS keypair")
}
Expand Down
6 changes: 3 additions & 3 deletions dnsforward/dnsforward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ func TestDotServer(t *testing.T) {
defer removeDataDir(t)

s.conf.TLSConfig = TLSConfig{
TLSListenAddr: &net.TCPAddr{Port: 0},
CertificateChain: string(certPem),
PrivateKey: string(keyPem),
TLSListenAddr: &net.TCPAddr{Port: 0},
CertificateChainData: certPem,
PrivateKeyData: keyPem,
}

// Starting the server
Expand Down
6 changes: 6 additions & 0 deletions home/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ func parseConfig() error {
}
config.Clients = nil

status := tlsConfigStatus{}
if !tlsLoadConfig(&config.TLS, &status) {
log.Error("%s", status.WarningValidation)
return err
}

// Deduplicate filters
deduplicateFilters()

Expand Down
57 changes: 55 additions & 2 deletions home/control_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
Expand All @@ -23,6 +24,41 @@ import (
"github.com/joomcode/errorx"
)

// Set certificate and private key data
func tlsLoadConfig(tls *tlsConfig, status *tlsConfigStatus) bool {
tls.CertificateChainData = []byte(tls.CertificateChain)
tls.PrivateKeyData = []byte(tls.PrivateKey)

var err error
if tls.CertificatePath != "" {
if tls.CertificateChain != "" {
status.WarningValidation = "certificate data and file can't be set together"
return false
}
tls.CertificateChainData, err = ioutil.ReadFile(tls.CertificatePath)
if err != nil {
status.WarningValidation = err.Error()
return false
}
status.ValidCert = true
}

if tls.PrivateKeyPath != "" {
if tls.PrivateKey != "" {
status.WarningValidation = "private key data and file can't be set together"
return false
}
tls.PrivateKeyData, err = ioutil.ReadFile(tls.PrivateKeyPath)
if err != nil {
status.WarningValidation = err.Error()
return false
}
status.ValidKey = true
}

return true
}

// RegisterTLSHandlers registers HTTP handlers for TLS configuration
func RegisterTLSHandlers() {
httpRegister(http.MethodGet, "/control/tls/status", handleTLSStatus)
Expand Down Expand Up @@ -55,7 +91,12 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) {
}
}

data.tlsConfigStatus = validateCertificates(data.CertificateChain, data.PrivateKey, data.ServerName)
status := tlsConfigStatus{}
if tlsLoadConfig(&data, &status) {
status = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName)
}
data.tlsConfigStatus = status

marshalTLS(w, data)
}

Expand All @@ -80,8 +121,14 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
}
}

status := tlsConfigStatus{}
if !tlsLoadConfig(&data, &status) {
data.tlsConfigStatus = status
marshalTLS(w, data)
return
}
data.tlsConfigStatus = validateCertificates(string(data.CertificateChainData), string(data.PrivateKeyData), data.ServerName)
restartHTTPS := false
data.tlsConfigStatus = validateCertificates(data.CertificateChain, data.PrivateKey, data.ServerName)
if !reflect.DeepEqual(config.TLS.tlsConfigSettings, data.tlsConfigSettings) {
log.Printf("tls config settings have changed, will restart HTTPS server")
restartHTTPS = true
Expand Down Expand Up @@ -300,6 +347,9 @@ func unmarshalTLS(r *http.Request) (tlsConfig, error) {
return data, errorx.Decorate(err, "Failed to base64-decode certificate chain")
}
data.CertificateChain = string(certPEM)
if data.CertificatePath != "" {
return data, fmt.Errorf("certificate data and file can't be set together")
}
}

if data.PrivateKey != "" {
Expand All @@ -309,6 +359,9 @@ func unmarshalTLS(r *http.Request) (tlsConfig, error) {
}

data.PrivateKey = string(keyPEM)
if data.PrivateKeyPath != "" {
return data, fmt.Errorf("private key data and file can't be set together")
}
}

return data, nil
Expand Down
14 changes: 7 additions & 7 deletions home/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,13 @@ func httpServerLoop() {
// this mechanism doesn't let us through until all conditions are met
for config.TLS.Enabled == false ||
config.TLS.PortHTTPS == 0 ||
config.TLS.PrivateKey == "" ||
config.TLS.CertificateChain == "" { // sleep until necessary data is supplied
len(config.TLS.PrivateKeyData) == 0 ||
len(config.TLS.CertificateChainData) == 0 { // sleep until necessary data is supplied
config.httpsServer.cond.Wait()
}
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS))
// validate current TLS config and update warnings (it could have been loaded from file)
data := validateCertificates(config.TLS.CertificateChain, config.TLS.PrivateKey, config.TLS.ServerName)
data := validateCertificates(string(config.TLS.CertificateChainData), string(config.TLS.PrivateKeyData), config.TLS.ServerName)
if !data.ValidPair {
cleanupAlways()
log.Fatal(data.WarningValidation)
Expand All @@ -235,10 +235,10 @@ func httpServerLoop() {

// prepare certs for HTTPS server
// important -- they have to be copies, otherwise changing the contents in config.TLS will break encryption for in-flight requests
certchain := make([]byte, len(config.TLS.CertificateChain))
copy(certchain, []byte(config.TLS.CertificateChain))
privatekey := make([]byte, len(config.TLS.PrivateKey))
copy(privatekey, []byte(config.TLS.PrivateKey))
certchain := make([]byte, len(config.TLS.CertificateChainData))
copy(certchain, config.TLS.CertificateChainData)
privatekey := make([]byte, len(config.TLS.PrivateKeyData))
copy(privatekey, config.TLS.PrivateKeyData)
cert, err := tls.X509KeyPair(certchain, privatekey)
if err != nil {
cleanupAlways()
Expand Down

0 comments on commit 24bb708

Please sign in to comment.