mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-11-24 22:15:45 +03:00
+ config: add certificate_path, private_key_path
* POST /control/tls/configure: support certificate_path and private_key_path
This commit is contained in:
parent
c847df9976
commit
24bb708b21
6 changed files with 142 additions and 14 deletions
|
@ -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
|
||||
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -119,8 +119,8 @@ func TestDotServer(t *testing.T) {
|
|||
|
||||
s.conf.TLSConfig = TLSConfig{
|
||||
TLSListenAddr: &net.TCPAddr{Port: 0},
|
||||
CertificateChain: string(certPem),
|
||||
PrivateKey: string(keyPem),
|
||||
CertificateChainData: certPem,
|
||||
PrivateKeyData: keyPem,
|
||||
}
|
||||
|
||||
// Starting the server
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 != "" {
|
||||
|
@ -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
|
||||
|
|
14
home/home.go
14
home/home.go
|
@ -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)
|
||||
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue