blob: 6850a127ed530e682ac16f27332628e71fa75b48 [file] [log] [blame]
Lorenz Brunaa6b7342019-12-12 02:55:02 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package integrity
18
19import (
20 "bytes"
21 "crypto/tls"
22 "crypto/x509"
23 "errors"
24 "fmt"
25 "net"
26 "strings"
27
Lorenz Brunaa6b7342019-12-12 02:55:02 +010028 "google.golang.org/grpc"
29 "google.golang.org/grpc/credentials"
Hendrik Hofstadt8efe51e2020-02-28 12:53:41 +010030
31 "git.monogon.dev/source/nexantic.git/core/generated/api"
32 "git.monogon.dev/source/nexantic.git/core/internal/common"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010033)
34
35// Agent specifices the interface which every integrity agent needs to fulfill
36type Agent interface {
37 // Initialize needs to be called once and initializes the systems required to maintain integrity
38 // on the given platform.
39 // nodeCert is a X.509 DER certificate which identifies the node once it's unlocked. This is
40 // required to bind the node certificate (which is only available when the node is unlocked) to
41 // the integrity subsystem used to attest said node.
42 // Initialize returns the cryptographic identity that it's bound to.
43 Initialize(newNode api.NewNodeInfo, enrolment api.EnrolmentConfig) (string, error)
44
45 // Unlock performs all required actions to assure the integrity of the platform and retrieves
46 // the unlock key in a secure manner
47 Unlock(enrolment api.EnrolmentConfig) ([]byte, error)
48}
49
50// DialNMS creates a secure GRPC connection to the NodeManagementService
51func DialNMS(enrolment api.EnrolmentConfig) (*grpc.ClientConn, error) {
52 var targets []string
53 for _, target := range enrolment.MasterIps {
54 targets = append(targets, fmt.Sprintf("%v:%v", net.IP(target), common.MasterServicePort))
55 }
56 cert, err := x509.ParseCertificate(enrolment.MastersCert)
57 if err != nil {
58 return nil, err
59 }
60 mastersPool := x509.NewCertPool()
61 mastersPool.AddCert(cert)
62
63 secureTransport := &tls.Config{
64 InsecureSkipVerify: true,
65 // Critical function, please review any changes with care
66 // TODO(lorenz): Actively check that this actually provides the security guarantees that we need
67 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
68 for _, cert := range rawCerts {
69 // X.509 certificates in DER can be compared like this since DER has a unique representation
70 // for each certificate.
71 if bytes.Equal(cert, enrolment.MastersCert) {
72 return nil
73 }
74 }
75 return errors.New("failed to find authorized NMS certificate")
76 },
77 MinVersion: tls.VersionTLS13,
78 }
79 secureTransportCreds := credentials.NewTLS(secureTransport)
80
81 return grpc.Dial(strings.Join(targets, ","), grpc.WithTransportCredentials(secureTransportCreds))
82}