/tls/validate and /tls/configure -- do checks on private key, add more fields to certificate status, do keypair check last.
This commit is contained in:
parent
81bb4aea78
commit
cb97c221fd
18
config.go
18
config.go
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
|
||||
|
@ -73,9 +74,20 @@ type tlsConfigSettings struct {
|
|||
|
||||
// field ordering is not important -- these are for API and are recalculated on each run
|
||||
type tlsConfigStatus struct {
|
||||
NotAfter string `yaml:"-" json:"not_after,omitempty"`
|
||||
StatusCertificate string `yaml:"-" json:"status_cert,omitempty"`
|
||||
StatusKey string `yaml:"-" json:"status_key,omitempty"`
|
||||
// certificate status
|
||||
ValidChain bool `yaml:"-" json:"valid_chain"`
|
||||
Subject string `yaml:"-" json:"subject,omitempty"`
|
||||
Issuer string `yaml:"-" json:"issuer,omitempty"`
|
||||
NotBefore time.Time `yaml:"-" json:"not_before,omitempty"`
|
||||
NotAfter time.Time `yaml:"-" json:"not_after,omitempty"`
|
||||
DNSNames []string `yaml:"-" json:"dns_names"`
|
||||
StatusCertificate string `yaml:"-" json:"status_cert,omitempty"`
|
||||
|
||||
// key status
|
||||
ValidKey bool `yaml:"-" json:"valid_key"`
|
||||
KeyType string `yaml:"-" json:"key_type,omitempty"`
|
||||
|
||||
// warnings
|
||||
Warning string `yaml:"-" json:"warning,omitempty"`
|
||||
WarningValidation string `yaml:"-" json:"warning_validation,omitempty"`
|
||||
}
|
||||
|
|
92
control.go
92
control.go
|
@ -3,11 +3,15 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
@ -1097,16 +1101,13 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) {
|
|||
func validateCertificates(data tlsConfig) (tlsConfig, error) {
|
||||
var err error
|
||||
|
||||
// clear out status for certificates
|
||||
data.tlsConfigStatus = tlsConfigStatus{}
|
||||
|
||||
// check only public certificate separetely from the key
|
||||
if data.CertificateChain != "" {
|
||||
log.Printf("got certificate: %s", data.CertificateChain)
|
||||
|
||||
if data.PrivateKey != "" {
|
||||
_, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey))
|
||||
if err != nil {
|
||||
return data, errorx.Decorate(err, "Invalid certificate or key")
|
||||
}
|
||||
}
|
||||
|
||||
// now do a more extended validation
|
||||
var certs []*pem.Block // PEM-encoded certificates
|
||||
var skippedBytes []string // skipped bytes
|
||||
|
@ -1165,13 +1166,20 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) {
|
|||
if err != nil {
|
||||
// let self-signed certs through
|
||||
data.WarningValidation = fmt.Sprintf("Your certificate does not verify: %s", err)
|
||||
} else {
|
||||
data.ValidChain = true
|
||||
}
|
||||
// spew.Dump(chains)
|
||||
|
||||
// update status
|
||||
if mainCert != nil {
|
||||
notAfter := mainCert.NotAfter
|
||||
data.NotAfter = notAfter.Format(time.RFC3339)
|
||||
data.Subject = mainCert.Subject.String()
|
||||
data.Issuer = mainCert.Issuer.String()
|
||||
data.NotAfter = notAfter
|
||||
data.NotBefore = mainCert.NotBefore
|
||||
data.DNSNames = mainCert.DNSNames
|
||||
|
||||
data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName)
|
||||
if len(mainCert.DNSNames) == 1 {
|
||||
data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0])
|
||||
|
@ -1192,9 +1200,77 @@ func validateCertificates(data tlsConfig) (tlsConfig, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// validate private key (right now the only validation possible is just parsing it)
|
||||
if data.PrivateKey != "" {
|
||||
// now do a more extended validation
|
||||
var key *pem.Block // PEM-encoded certificates
|
||||
var skippedBytes []string // skipped bytes
|
||||
|
||||
// go through all pem blocks, but take first valid pem block and drop the rest
|
||||
pemblock := []byte(data.PrivateKey)
|
||||
for {
|
||||
var decoded *pem.Block
|
||||
decoded, pemblock = pem.Decode(pemblock)
|
||||
if decoded == nil {
|
||||
break
|
||||
}
|
||||
if decoded.Type == "PRIVATE KEY" || strings.HasSuffix(decoded.Type, " PRIVATE KEY") {
|
||||
key = decoded
|
||||
break
|
||||
} else {
|
||||
skippedBytes = append(skippedBytes, decoded.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if key == nil {
|
||||
return data, fmt.Errorf("No valid keys were found")
|
||||
}
|
||||
|
||||
// parse the decoded key
|
||||
_, keytype, err := parsePrivateKey(key.Bytes)
|
||||
if err != nil {
|
||||
return data, errorx.Decorate(err, "Failed to parse private key")
|
||||
}
|
||||
|
||||
data.ValidKey = true
|
||||
data.KeyType = keytype
|
||||
}
|
||||
|
||||
// if both are set, validate both in unison
|
||||
if data.PrivateKey != "" && data.CertificateChain != "" {
|
||||
_, err = tls.X509KeyPair([]byte(data.CertificateChain), []byte(data.PrivateKey))
|
||||
if err != nil {
|
||||
return data, errorx.Decorate(err, "Invalid certificate or key")
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
|
||||
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
|
||||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
|
||||
func parsePrivateKey(der []byte) (crypto.PrivateKey, string, error) {
|
||||
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
||||
return key, "RSA", nil
|
||||
}
|
||||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return key, "RSA", nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return key, "ECDSA", nil
|
||||
default:
|
||||
return nil, "", errors.New("tls: found unknown private key type in PKCS#8 wrapping")
|
||||
}
|
||||
}
|
||||
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
||||
return key, "ECDSA", nil
|
||||
}
|
||||
|
||||
return nil, "", errors.New("tls: failed to parse private key")
|
||||
}
|
||||
|
||||
// unmarshalTLS handles base64-encoded certificates transparently
|
||||
func unmarshalTLS(r *http.Request) (tlsConfig, error) {
|
||||
data := tlsConfig{}
|
||||
|
|
Loading…
Reference in New Issue