blob: f92c0084463a1c1af8a6e62b05107dc7dae5b134 [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
28 "git.monogon.dev/source/nexantic.git/core/generated/api"
29 "git.monogon.dev/source/nexantic.git/core/internal/common"
30 "google.golang.org/grpc"
31 "google.golang.org/grpc/credentials"
32)
33
34// Agent specifices the interface which every integrity agent needs to fulfill
35type Agent interface {
36 // Initialize needs to be called once and initializes the systems required to maintain integrity
37 // on the given platform.
38 // nodeCert is a X.509 DER certificate which identifies the node once it's unlocked. This is
39 // required to bind the node certificate (which is only available when the node is unlocked) to
40 // the integrity subsystem used to attest said node.
41 // Initialize returns the cryptographic identity that it's bound to.
42 Initialize(newNode api.NewNodeInfo, enrolment api.EnrolmentConfig) (string, error)
43
44 // Unlock performs all required actions to assure the integrity of the platform and retrieves
45 // the unlock key in a secure manner
46 Unlock(enrolment api.EnrolmentConfig) ([]byte, error)
47}
48
49// DialNMS creates a secure GRPC connection to the NodeManagementService
50func DialNMS(enrolment api.EnrolmentConfig) (*grpc.ClientConn, error) {
51 var targets []string
52 for _, target := range enrolment.MasterIps {
53 targets = append(targets, fmt.Sprintf("%v:%v", net.IP(target), common.MasterServicePort))
54 }
55 cert, err := x509.ParseCertificate(enrolment.MastersCert)
56 if err != nil {
57 return nil, err
58 }
59 mastersPool := x509.NewCertPool()
60 mastersPool.AddCert(cert)
61
62 secureTransport := &tls.Config{
63 InsecureSkipVerify: true,
64 // Critical function, please review any changes with care
65 // TODO(lorenz): Actively check that this actually provides the security guarantees that we need
66 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
67 for _, cert := range rawCerts {
68 // X.509 certificates in DER can be compared like this since DER has a unique representation
69 // for each certificate.
70 if bytes.Equal(cert, enrolment.MastersCert) {
71 return nil
72 }
73 }
74 return errors.New("failed to find authorized NMS certificate")
75 },
76 MinVersion: tls.VersionTLS13,
77 }
78 secureTransportCreds := credentials.NewTLS(secureTransport)
79
80 return grpc.Dial(strings.Join(targets, ","), grpc.WithTransportCredentials(secureTransportCreds))
81}