Attestation & Identity & Global Unlock & Enrolment
This changes the node startup sequence significantly. Now the following three startup procedures replace the old setup/join mechanic:
* If no enrolment config is present, automatically bootstrap a new cluster and become master for it.
* If an enrolment config with an enrolment token is present, register with the NodeManagementService.
* If an enrolment config without an enrolment token is present, attempt a normal cluster unlock.
It also completely revamps the GRPC management services:
* NodeManagementService is a master-only service that deals with other nodes and has a cluster-wide identity
* NodeService is only available in unlocked state and keyed with the node identity
* ClusterManagement is now a master-only service that's been spun out of the main NMS since they have very different authentication models and also deals with EnrolmentConfigs
The TPM support library has also been extended by:
* Lots of integrity attestation and verification functions
* Built-in AK management
* Some advanced policy-based authentication stuff
Also contains various enhancements to the network service to make everything work in a proper multi-node environment.
Lots of old code has been thrown out.
Test Plan: Passed a full manual test of all three startup modes (bootstrap, enrolment and normal unlock) including automated EnrolmentConfig generation and consumption in a dual-node configuration on swtpm / OVMF.
Bug: T499
X-Origin-Diff: phab/D291
GitOrigin-RevId: d53755c828218b1df83a1d7ad252c7b3231abca8
diff --git a/core/internal/network/main.go b/core/internal/network/main.go
index 0888de7..04ab159 100644
--- a/core/internal/network/main.go
+++ b/core/internal/network/main.go
@@ -21,9 +21,12 @@
"fmt"
"net"
"os"
+ "sync"
+ "time"
"git.monogon.dev/source/nexantic.git/core/internal/common/service"
+ "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/vishvananda/netlink"
"go.uber.org/zap"
@@ -39,6 +42,9 @@
*service.BaseService
config Config
dhcp4Client *nclient4.Client
+ ip *net.IP
+ ipNotify chan struct{}
+ lock sync.Mutex
}
type Config struct {
@@ -46,7 +52,8 @@
func NewNetworkService(config Config, logger *zap.Logger) (*Service, error) {
s := &Service{
- config: config,
+ config: config,
+ ipNotify: make(chan struct{}),
}
s.BaseService = service.NewBaseService("network", logger, s)
return s, nil
@@ -105,20 +112,60 @@
if err != nil {
panic(err)
}
- _, ack, err := client.Request(context.Background())
- if err != nil {
- panic(err)
+ var ack *dhcpv4.DHCPv4
+ for {
+ dhcpCtx, dhcpCtxCancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer dhcpCtxCancel()
+ _, ack, err = client.Request(dhcpCtx)
+ if err == nil {
+ break
+ }
+ s.Logger.Info("DHCP request failed", zap.Error(err))
}
s.Logger.Info("Network service got IP", zap.String("ip", ack.YourIPAddr.String()))
if err := setResolvconf(ack.DNS(), []string{}); err != nil {
s.Logger.Warn("Failed to set resolvconf", zap.Error(err))
}
+
+ s.lock.Lock()
+ s.ip = &ack.YourIPAddr
+ s.lock.Unlock()
+loop:
+ for {
+ select {
+ case s.ipNotify <- struct{}{}:
+ default:
+ break loop
+ }
+ }
+
if err := addNetworkRoutes(iface, net.IPNet{IP: ack.YourIPAddr, Mask: ack.SubnetMask()}, ack.GatewayIPAddr); err != nil {
s.Logger.Warn("Failed to add routes", zap.Error(err))
}
return nil
}
+// GetIP returns the current IP (and optionally waits for one to be assigned)
+func (s *Service) GetIP(wait bool) *net.IP {
+ s.lock.Lock()
+ if !wait {
+ ip := s.ip
+ s.lock.Unlock()
+ return ip
+ }
+
+ for {
+ if s.ip != nil {
+ ip := s.ip
+ s.lock.Unlock()
+ return ip
+ }
+ s.lock.Unlock()
+ <-s.ipNotify
+ s.lock.Lock()
+ }
+}
+
func (s *Service) OnStart() error {
s.Logger.Info("Starting network service")
links, err := netlink.LinkList()