blob: f92c0084463a1c1af8a6e62b05107dc7dae5b134 [file] [log] [blame]
// Copyright 2020 The Monogon Project Authors.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package integrity
import (
"bytes"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"strings"
"git.monogon.dev/source/nexantic.git/core/generated/api"
"git.monogon.dev/source/nexantic.git/core/internal/common"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
// Agent specifices the interface which every integrity agent needs to fulfill
type Agent interface {
// Initialize needs to be called once and initializes the systems required to maintain integrity
// on the given platform.
// nodeCert is a X.509 DER certificate which identifies the node once it's unlocked. This is
// required to bind the node certificate (which is only available when the node is unlocked) to
// the integrity subsystem used to attest said node.
// Initialize returns the cryptographic identity that it's bound to.
Initialize(newNode api.NewNodeInfo, enrolment api.EnrolmentConfig) (string, error)
// Unlock performs all required actions to assure the integrity of the platform and retrieves
// the unlock key in a secure manner
Unlock(enrolment api.EnrolmentConfig) ([]byte, error)
}
// DialNMS creates a secure GRPC connection to the NodeManagementService
func DialNMS(enrolment api.EnrolmentConfig) (*grpc.ClientConn, error) {
var targets []string
for _, target := range enrolment.MasterIps {
targets = append(targets, fmt.Sprintf("%v:%v", net.IP(target), common.MasterServicePort))
}
cert, err := x509.ParseCertificate(enrolment.MastersCert)
if err != nil {
return nil, err
}
mastersPool := x509.NewCertPool()
mastersPool.AddCert(cert)
secureTransport := &tls.Config{
InsecureSkipVerify: true,
// Critical function, please review any changes with care
// TODO(lorenz): Actively check that this actually provides the security guarantees that we need
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, cert := range rawCerts {
// X.509 certificates in DER can be compared like this since DER has a unique representation
// for each certificate.
if bytes.Equal(cert, enrolment.MastersCert) {
return nil
}
}
return errors.New("failed to find authorized NMS certificate")
},
MinVersion: tls.VersionTLS13,
}
secureTransportCreds := credentials.NewTLS(secureTransport)
return grpc.Dial(strings.Join(targets, ","), grpc.WithTransportCredentials(secureTransportCreds))
}