evcc-io/util/cloud/client.go

86 lines
1.6 KiB
Go

package cloud
import (
"bytes"
"crypto/tls"
"crypto/x509"
_ "embed"
"errors"
"fmt"
"net"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
var Host = "cloud.evcc.io:8080"
var (
conn *grpc.ClientConn
)
//go:embed ca-cert.pem
var caCert []byte
func caPEM() []byte {
copy := bytes.NewBuffer(caCert)
return copy.Bytes()
}
func loadTLSCredentials() (*tls.Config, error) {
certPool, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
if !certPool.AppendCertsFromPEM(caPEM()) {
return nil, fmt.Errorf("failed to add CA certificate")
}
// create the credentials and return it
config := &tls.Config{
RootCAs: certPool,
}
return config, nil
}
func verifyConnection(host string) func(conn tls.ConnectionState) error {
return func(conn tls.ConnectionState) error {
if len(conn.PeerCertificates) > 0 {
peer := conn.PeerCertificates[0]
return peer.VerifyHostname(host)
}
return errors.New("missing host certificate")
}
}
func Connection(hostPort string) (*grpc.ClientConn, error) {
var err error
if conn == nil {
creds := insecure.NewCredentials()
if !strings.HasPrefix(hostPort, "localhost") {
host, _, err := net.SplitHostPort(hostPort)
if err != nil {
return nil, err
}
var tlsConfig *tls.Config
if tlsConfig, err = loadTLSCredentials(); err != nil {
return nil, err
}
// make sure it matches the hostname
tlsConfig.VerifyConnection = verifyConnection(host)
creds = credentials.NewTLS(tlsConfig)
}
conn, err = grpc.Dial(hostPort, grpc.WithTransportCredentials(creds))
}
return conn, err
}