metropolis: reduce usage of identity.NodeID

Eventually, we want to be able to rotate node keypairs. To allow this,
the node ID needs to become independent of the public key. This change
is a refactoring which starts this work by reducing the usage of
identity.NodeID, the function which derives a node ID from a public key.

Change-Id: I5231ed0a7be37c23327fec93481b00c74374af07
Reviewed-on: https://review.monogon.dev/c/monogon/+/3445
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/node/core/consensus/BUILD.bazel b/metropolis/node/core/consensus/BUILD.bazel
index ac048d6..5c58c2a 100644
--- a/metropolis/node/core/consensus/BUILD.bazel
+++ b/metropolis/node/core/consensus/BUILD.bazel
@@ -15,7 +15,6 @@
         "//go/logging",
         "//metropolis/node",
         "//metropolis/node/core/consensus/client",
-        "//metropolis/node/core/identity",
         "//metropolis/node/core/localstorage",
         "//osbase/event",
         "//osbase/event/memory",
diff --git a/metropolis/node/core/consensus/configuration.go b/metropolis/node/core/consensus/configuration.go
index bb3db82..071f5f5 100644
--- a/metropolis/node/core/consensus/configuration.go
+++ b/metropolis/node/core/consensus/configuration.go
@@ -13,7 +13,6 @@
 	"go.etcd.io/etcd/server/v3/embed"
 
 	"source.monogon.dev/metropolis/node"
-	"source.monogon.dev/metropolis/node/core/identity"
 	"source.monogon.dev/metropolis/node/core/localstorage"
 	"source.monogon.dev/osbase/pki"
 )
@@ -31,8 +30,11 @@
 	// If that data is not present, a new cluster will be bootstrapped.
 	JoinCluster *JoinCluster
 
+	// NodeID is the node ID, which is also used to identify consensus nodes.
+	NodeID string
+
 	// NodePrivateKey is the node's main private key which is also used for
-	// Metropolis PKI. The same key will be used to identify consensus nodes, but
+	// Metropolis PKI. The same key will be used for consensus nodes, but
 	// different certificates will be used.
 	NodePrivateKey ed25519.PrivateKey
 
@@ -85,12 +87,11 @@
 // over TLS. This requires TLS credentials to be present on disk, and will be
 // disabled for bootstrapping the instance.
 func (c *Config) build(enablePeers bool) *embed.Config {
-	nodeID := identity.NodeID(c.nodePublicKey())
 	port := int(node.ConsensusPort)
 	if p := c.testOverrides.externalPort; p != 0 {
 		port = p
 	}
-	host := nodeID
+	host := c.NodeID
 	if c.testOverrides.externalAddress != "" {
 		host = c.testOverrides.externalAddress
 	}
@@ -101,7 +102,7 @@
 
 	cfg := embed.NewConfig()
 
-	cfg.Name = nodeID
+	cfg.Name = c.NodeID
 	cfg.ClusterState = "existing"
 	cfg.InitialClusterToken = "METROPOLIS"
 	cfg.Logger = "zap"
@@ -147,7 +148,7 @@
 		}}
 	}
 
-	cfg.InitialCluster = cfg.InitialClusterFromName(nodeID)
+	cfg.InitialCluster = cfg.InitialClusterFromName(c.NodeID)
 	if c.JoinCluster != nil {
 		for _, n := range c.JoinCluster.ExistingNodes {
 			cfg.InitialCluster += "," + n.connectionString()
diff --git a/metropolis/node/core/consensus/consensus.go b/metropolis/node/core/consensus/consensus.go
index 8da53d6..f8861a7 100644
--- a/metropolis/node/core/consensus/consensus.go
+++ b/metropolis/node/core/consensus/consensus.go
@@ -87,7 +87,6 @@
 
 import (
 	"context"
-	"crypto/ed25519"
 	"crypto/x509"
 	"crypto/x509/pkix"
 	"errors"
@@ -102,7 +101,6 @@
 	"go.etcd.io/etcd/server/v3/embed"
 
 	"source.monogon.dev/metropolis/node/core/consensus/client"
-	"source.monogon.dev/metropolis/node/core/identity"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/event/memory"
 	"source.monogon.dev/osbase/logtree/unraw"
@@ -131,16 +129,16 @@
 	}
 }
 
-func pkiPeerCertificate(pubkey ed25519.PublicKey, extraNames []string) x509.Certificate {
+func pkiPeerCertificate(nodeID string, extraNames []string) x509.Certificate {
 	return x509.Certificate{
 		Subject: pkix.Name{
-			CommonName: identity.NodeID(pubkey),
+			CommonName: nodeID,
 		},
 		KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
 		ExtKeyUsage: []x509.ExtKeyUsage{
 			x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth,
 		},
-		DNSNames: append(extraNames, identity.NodeID(pubkey)),
+		DNSNames: append(extraNames, nodeID),
 	}
 }
 
@@ -451,10 +449,10 @@
 		extraNames = []string{external}
 	}
 	memberTemplate := pki.Certificate{
-		Name:      identity.NodeID(s.config.nodePublicKey()),
+		Name:      s.config.NodeID,
 		Namespace: &pkiNamespace,
 		Issuer:    s.ca,
-		Template:  pkiPeerCertificate(s.config.nodePublicKey(), extraNames),
+		Template:  pkiPeerCertificate(s.config.NodeID, extraNames),
 		Mode:      pki.CertificateExternal,
 		PublicKey: s.config.nodePublicKey(),
 	}
diff --git a/metropolis/node/core/consensus/consensus_test.go b/metropolis/node/core/consensus/consensus_test.go
index 85df62e..64dd0b3 100644
--- a/metropolis/node/core/consensus/consensus_test.go
+++ b/metropolis/node/core/consensus/consensus_test.go
@@ -84,6 +84,7 @@
 	etcd := New(Config{
 		Data:           &b.root.Data.Etcd,
 		Ephemeral:      &b.root.Ephemeral.Consensus,
+		NodeID:         "node1",
 		NodePrivateKey: b.privkey,
 		testOverrides: testOverrides{
 			externalPort:    2345,
@@ -132,6 +133,7 @@
 	etcd := New(Config{
 		Data:           &b.root.Data.Etcd,
 		Ephemeral:      &b.root.Ephemeral.Consensus,
+		NodeID:         "node1",
 		NodePrivateKey: b.privkey,
 		testOverrides: testOverrides{
 			externalPort: 1234,
@@ -168,6 +170,7 @@
 	etcd := New(Config{
 		Data:           &b.root.Data.Etcd,
 		Ephemeral:      &b.root.Ephemeral.Consensus,
+		NodeID:         "node1",
 		NodePrivateKey: b.privkey,
 		testOverrides: testOverrides{
 			externalPort: 1235,
@@ -216,6 +219,7 @@
 	etcd = New(Config{
 		Data:           &b.root.Data.Etcd,
 		Ephemeral:      &b.root.Ephemeral.Consensus,
+		NodeID:         "node1",
 		NodePrivateKey: b.privkey,
 		testOverrides: testOverrides{
 			externalPort: 1235,
@@ -262,6 +266,7 @@
 	etcd := New(Config{
 		Data:           &b.root.Data.Etcd,
 		Ephemeral:      &b.root.Ephemeral.Consensus,
+		NodeID:         "node1",
 		NodePrivateKey: b.privkey,
 		testOverrides: testOverrides{
 			externalPort:    3000,
@@ -290,7 +295,7 @@
 	b2 := prep(t)
 	defer b2.close()
 
-	join, err := st.AddNode(b.ctx, b2.privkey.Public().(ed25519.PublicKey), &AddNodeOption{
+	join, err := st.AddNode(b.ctx, "node2", b2.privkey.Public().(ed25519.PublicKey), &AddNodeOption{
 		externalAddress: "localhost",
 		externalPort:    3001,
 	})
@@ -301,6 +306,7 @@
 	etcd2 := New(Config{
 		Data:           &b2.root.Data.Etcd,
 		Ephemeral:      &b2.root.Ephemeral.Consensus,
+		NodeID:         "node2",
 		NodePrivateKey: b2.privkey,
 		JoinCluster:    join,
 		testOverrides: testOverrides{
diff --git a/metropolis/node/core/consensus/status.go b/metropolis/node/core/consensus/status.go
index ee3efbc..40988e5 100644
--- a/metropolis/node/core/consensus/status.go
+++ b/metropolis/node/core/consensus/status.go
@@ -12,7 +12,6 @@
 
 	"source.monogon.dev/metropolis/node"
 	"source.monogon.dev/metropolis/node/core/consensus/client"
-	"source.monogon.dev/metropolis/node/core/identity"
 	"source.monogon.dev/osbase/event"
 	"source.monogon.dev/osbase/pki"
 )
@@ -87,19 +86,18 @@
 	return s.cl
 }
 
-// AddNode creates a new consensus member corresponding to a given Ed25519 node
-// public key if one does not yet exist. The member will at first be marked as a
+// AddNode creates a new consensus member corresponding to a given node ID
+// if one does not yet exist. The member will at first be marked as a
 // Learner, ensuring it does not take part in quorum until it has finished
 // catching up to the state of the etcd store. As it does, the autopromoter will
 // turn it into a 'full' node and it will start taking part in the quorum and be
 // able to perform all etcd operations.
-func (s *Status) AddNode(ctx context.Context, pk ed25519.PublicKey, opts ...*AddNodeOption) (*JoinCluster, error) {
+func (s *Status) AddNode(ctx context.Context, nodeID string, pk ed25519.PublicKey, opts ...*AddNodeOption) (*JoinCluster, error) {
 	clPKI, err := s.pkiClient()
 	if err != nil {
 		return nil, err
 	}
 
-	nodeID := identity.NodeID(pk)
 	var extraNames []string
 	name := nodeID
 	port := int(node.ConsensusPort)
@@ -117,7 +115,7 @@
 		Name:      nodeID,
 		Namespace: &pkiNamespace,
 		Issuer:    s.ca,
-		Template:  pkiPeerCertificate(pk, extraNames),
+		Template:  pkiPeerCertificate(nodeID, extraNames),
 		Mode:      pki.CertificateExternal,
 		PublicKey: pk,
 	}