From 25fc95a7d45e18da993bf6e1dc726ade1815030c Mon Sep 17 00:00:00 2001 From: andig Date: Tue, 5 Mar 2024 22:40:13 +0100 Subject: [PATCH] Revert "chore: remove certificate pinning (before expiry) (#12670)" This reverts commit 041427928b9797b4efcebb7a2442c39c6c792e0c. --- util/cloud/ca-cert.pem | 28 +++++++++++++++ util/cloud/client.go | 77 +++++++++++++++++++++++++++++++++++++----- util/sponsor/auth.go | 4 ++- vehicle/cloud.go | 7 ++-- 4 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 util/cloud/ca-cert.pem diff --git a/util/cloud/ca-cert.pem b/util/cloud/ca-cert.pem new file mode 100644 index 000000000..c8bb6225f --- /dev/null +++ b/util/cloud/ca-cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE1DCCArwCCQC/TbpAPhcOQTANBgkqhkiG9w0BAQsFADAsMQswCQYDVQQGEwJE +RTEdMBsGCSqGSIb3DQEJARYOY3B1aWRsZUBnbXguZGUwHhcNMjEwMzMxMDc1ODAz +WhcNMjIwMzMxMDc1ODAzWjAsMQswCQYDVQQGEwJERTEdMBsGCSqGSIb3DQEJARYO +Y3B1aWRsZUBnbXguZGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC+ +noDTXbidh/5RFfsLlMJbfMn4Vu91oJb4MM41RQM8UOr3SRU/ZzcoLfWJe2ePpEDp +ZL4piobs8EIjFcWU2C/8La0rygbcDDqUJkW/nViH0Drs5ctgJl/uYeiDLnZiSZO+ +PBoOA0trXxqlJPxuEseJbFaLvVE81DsLBnltDPqhJZEUJPEUIjtfStOhQM0f5YoO +Greewk7P5LUkTAuTIz+xk6uKnC1YF89SKmdPMH26Rcy6q1IqhEQG3tjUliJZiKS9 +eQlnp9gskqNGop9gEsmFIMnhlrTUsVU2q38zLDFcmNDbNkRvv4Tf+7jHo4tLs02m +/fMTikdMNCRMOcJKbdobcwXq4Ghvc85xeM4/8wIQHEDFbLzmxi/4ibFfJjOfQOe9 +VpK+TbeSBTxBw42CH9V0TUOz/LELYkMrJ+zfwMCvTV4Eodmc0gjwiy9aIYdpZXKF +ueqGGljhVCq2XqkaSATFu5CsdPZrfUszLiAVqpuVTMjy3VQ9ICzz1Bd5ICBRFt6l +fDsvfB6SgmB9r1bC+yr/tqe/unzUPajL5mZwn7Jev0uOl6mBNmQpcIEne+9cKedf +vPCv/tUQpwpz6blGacEtTxuM+fWDk+CLpPnId/4PEnIQWRFE2aXQ4nrJSwu3O5+k +vuZNWety8MOSkuQr0toH//p3VNHOtT7L4JWSlDfTZQIDAQABMA0GCSqGSIb3DQEB +CwUAA4ICAQANoLbh4Di1A485ggnKfp9ykfVvg+NduGm+eqr27be033N6IJ7fuKmF +7Ki1E+sp0IJDMdyikbqoHdbroHu2chwp/1GzFxhx3Vo7kswS/ehSxWpHjhtrGjAF +tSDUFR9q4C+km2A3k1ZNyd9C1w+R9Lr16j9lBQoGWmUgFsRf8ED16FMMSK/1mCC0 +1VSydYnYhKmPZxMByTozGTV97wihA0XqXtadbkQoyvcnEuarBvEX3mPA3effqcEG +rreeDp1yzYtQRW85ZASaqgF5CYKhe9NekurZ5Jd+2mQPYRWPFpIFsSHoOLqbfcN1 +/S0Si6LZWq0Mi8cbSi/zq+Eh7Q0w+EXP6Goh0A7M4e1dKt+cfGQpeyeg3cF49Cda +RrTkSv2Sl2jbrShqAsG/HNhyBaI/gtLGi9tig9wAoV2zjGw1Ehs0GcwFq2g/f6Zv +W7rdZIBZT1RIQLczuwsEhv6cFrSM5OU2f5fuKnmeI6uvyz0jRqiMncYRjwI6LotB +sWmdTWi6xsctYirJ3Yip5Tqm01asyUuIeiT+eQ6I9CfHLiGBXEzxlK5PrFqfD7gK +LfB1gCN7SAlxKGepmFMwfF+fDrsurL2T2ePxaaNBLjRAzmpSpH0NedMd+eanuHXV +a/iOL2XmVis7iuFyk5M2XtLFfYBff+mxSPA8d9u5kCpoel0tx3iLmw== +-----END CERTIFICATE----- diff --git a/util/cloud/client.go b/util/cloud/client.go index ce89d1138..82ac9ab8e 100644 --- a/util/cloud/client.go +++ b/util/cloud/client.go @@ -1,25 +1,84 @@ package cloud import ( + "bytes" + "crypto/tls" + "crypto/x509" _ "embed" + "errors" + "fmt" + "net" + "strings" - "github.com/evcc-io/evcc/util" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) -var ( - host = util.Getenv("GRPC_URI", "sponsor.evcc.io:8080") +var Host = "sponsor.evcc.io:8080" - conn *grpc.ClientConn -) +var conn *grpc.ClientConn -func Connection() (*grpc.ClientConn, error) { - if conn != nil { - return conn, nil +//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 - conn, err = grpc.Dial(host) + 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 } diff --git a/util/sponsor/auth.go b/util/sponsor/auth.go index a2f4b14f5..acef59bd0 100644 --- a/util/sponsor/auth.go +++ b/util/sponsor/auth.go @@ -6,6 +6,7 @@ import ( "time" "github.com/evcc-io/evcc/api/proto/pb" + "github.com/evcc-io/evcc/util" "github.com/evcc-io/evcc/util/cloud" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -35,7 +36,8 @@ func ConfigureSponsorship(token string) error { } } - conn, err := cloud.Connection() + host := util.Getenv("GRPC_URI", cloud.Host) + conn, err := cloud.Connection(host) if err != nil { return err } diff --git a/vehicle/cloud.go b/vehicle/cloud.go index 97e7d800d..f5ed9ddab 100644 --- a/vehicle/cloud.go +++ b/vehicle/cloud.go @@ -48,7 +48,8 @@ func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) { return nil, api.ErrSponsorRequired } - conn, err := cloud.Connection() + host := util.Getenv("GRPC_URI", cloud.Host) + conn, err := cloud.Connection(host) if err != nil { return nil, err } @@ -61,7 +62,9 @@ func NewCloudFromConfig(other map[string]interface{}) (api.Vehicle, error) { client: pb.NewVehicleClient(conn), } - err = v.prepareVehicle() + if err == nil { + err = v.prepareVehicle() + } v.chargeStateG = provider.Cached(v.chargeState, cc.Cache)