2017-01-20 19:32:10 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-03-15 15:55:30 +00:00
|
|
|
"context"
|
2017-01-20 19:32:10 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/pem"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Certificate returns the PEM encoded certificate chain for osqueryd TLS termination.
|
2021-06-01 00:07:51 +00:00
|
|
|
func (svc *Service) CertificateChain(ctx context.Context) ([]byte, error) {
|
2017-01-20 19:32:10 +00:00
|
|
|
config, err := svc.AppConfig(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:27:41 +00:00
|
|
|
u, err := url.Parse(config.ServerSettings.ServerURL)
|
2017-01-20 19:32:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "parsing serverURL")
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := connectTLS(u)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return chain(conn.ConnectionState(), u.Hostname())
|
|
|
|
}
|
|
|
|
|
|
|
|
func connectTLS(serverURL *url.URL) (*tls.Conn, error) {
|
|
|
|
var hostport string
|
|
|
|
if serverURL.Port() == "" {
|
|
|
|
hostport = net.JoinHostPort(serverURL.Host, "443")
|
|
|
|
} else {
|
|
|
|
hostport = serverURL.Host
|
|
|
|
}
|
|
|
|
|
|
|
|
// attempt dialing twice, first with a secure conn, and then
|
|
|
|
// if that fails, use insecure
|
|
|
|
dial := func(insecure bool) (*tls.Conn, error) {
|
|
|
|
conn, err := tls.Dial("tcp", hostport, &tls.Config{
|
|
|
|
InsecureSkipVerify: insecure})
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "dial tls")
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
return conn, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
conn *tls.Conn
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
conn, err = dial(false)
|
|
|
|
if err == nil {
|
|
|
|
return conn, nil
|
|
|
|
}
|
|
|
|
conn, err = dial(true)
|
|
|
|
return conn, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// chain builds a PEM encoded certificate chain using the PeerCertificates
|
|
|
|
// in tls.ConnectionState. chain uses the hostname to omit the Leaf certificate
|
|
|
|
// from the chain.
|
|
|
|
func chain(cs tls.ConnectionState, hostname string) ([]byte, error) {
|
|
|
|
buf := bytes.NewBuffer([]byte(""))
|
|
|
|
|
|
|
|
verifyEncode := func(chain []*x509.Certificate) error {
|
|
|
|
for _, cert := range chain {
|
|
|
|
if len(chain) > 1 {
|
|
|
|
// drop the leaf certificate from the chain. osqueryd does not
|
|
|
|
// need it to establish a secure connection
|
|
|
|
if err := cert.VerifyHostname(hostname); err == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := encodePEMCertificate(buf, cert); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// use verified chains if available(which adds the root CA), otherwise
|
|
|
|
// use the certificate chain offered by the server (if terminated with
|
|
|
|
// self-signed certs)
|
|
|
|
if len(cs.VerifiedChains) != 0 {
|
|
|
|
for _, chain := range cs.VerifiedChains {
|
|
|
|
if err := verifyEncode(chain); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "encode verified chains pem")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := verifyEncode(cs.PeerCertificates); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "encode peer certificates pem")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodePEMCertificate(buf io.Writer, cert *x509.Certificate) error {
|
|
|
|
block := &pem.Block{
|
|
|
|
Type: "CERTIFICATE",
|
|
|
|
Bytes: cert.Raw,
|
|
|
|
}
|
|
|
|
return pem.Encode(buf, block)
|
|
|
|
}
|