metropolis: implement cluster configuration

This adds a cluster configuration to Metropolis. We'll be keeping any
non-node-specific options there. The config is stored in etcd by the
curator.

An initial cluster configuration can be specified when bootstrapping a
cluster. By design the configuration is then immutable by default, but
we might add some purpose-specific management API calls to change some
values if needed.

We initialize the cluster configuration with a setting for node TPM
policy, 'TPMMode'. It's currently populated on cluster bootstrap, but
not used otherwise. That will come in a follow-up CR.

Change-Id: I44ddcd099c9ae68c20519c77e3fa77c894cf5a20
Reviewed-on: https://review.monogon.dev/c/monogon/+/1494
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/proto/api/configuration.proto b/metropolis/proto/api/configuration.proto
index 3a4a5cd..8c6c454 100644
--- a/metropolis/proto/api/configuration.proto
+++ b/metropolis/proto/api/configuration.proto
@@ -37,6 +37,10 @@
         // key can be used to prove ownership of the cluster and retrieve
         // management credentials for the cluster via an AAA.Escrow call.
         bytes owner_public_key = 1;
+
+        // If not set, defaults to:
+        //   - TPM mode: REQUIRED
+        common.ClusterConfiguration initial_cluster_configuration = 3;
     }
     // ClusterRegister configures the node to attempt to register into an
     // existing cluster, ie. contact an existing running cluster and become
diff --git a/metropolis/proto/api/management.proto b/metropolis/proto/api/management.proto
index 5b66311..5560455 100644
--- a/metropolis/proto/api/management.proto
+++ b/metropolis/proto/api/management.proto
@@ -90,6 +90,8 @@
 
     // ca_certificate is the x509 DER encoded CA certificate of the cluster.
     bytes ca_certificate = 2;
+
+    metropolis.proto.common.ClusterConfiguration cluster_configuration = 3;
 }
 
 message GetNodesRequest {
diff --git a/metropolis/proto/common/common.proto b/metropolis/proto/common/common.proto
index a884dff..27dfa5a 100644
--- a/metropolis/proto/common/common.proto
+++ b/metropolis/proto/common/common.proto
@@ -263,3 +263,42 @@
     }
 }
 
+// ClusterConfiguration contains the entirety of the user-configurable behaviour
+// of the cluster that is scoped to the entirety of the cluster (vs. per-node
+// configuration, which is kept alongside Node).
+//
+// It can be set initially when a cluster is being bootstrapped (in
+// NodeParamaters.ClusterBootstrap), and then can be partially managed by
+// management calls to the curator.
+message ClusterConfiguration {
+    // tpm_mode defines the TPM usage policy for cluster nodes. When nodes
+    // register into the cluster (and then join into it) they will report their
+    // TPM availability, and in return the cluster will respond whether they
+    // should use that TPM or not.
+    //
+    // If a node is instructed to use its TPM, it will use it to encrypt its part
+    // of the disk encryption key when saving it to the EFI system partition.
+    // That means that the node will only be able to re-join the cluster if its
+    // secure boot configuration doesn't change.
+    //
+    // If a node is instructed to not use its TPM, it will save its part of the
+    // disk encryption key straight onto the EFI system partition without any
+    // further encryption. It still needs to connect to a working cluster to
+    // retrieve the other part of the key. This means that the configuration is
+    // secure vs. offline disk decryption attempts, but not secure if an
+    // attacker can connect to a cluster and impersonate the node in order to
+    // retrieve the other part of its key.
+    enum TPMMode {
+        TPM_MODE_INVALID = 0;
+        // Nodes need to join with a TPM2.0 device and will be instructed to
+        // use it.
+        TPM_MODE_REQUIRED = 1;
+        // Nodes will be allowed to join regardless of TPM2.0 presence, and will
+        // be instructed to use it if they have one.
+        TPM_MODE_BEST_EFFORT = 2;
+        // Regardless of the node's local TPM presence it will be instructed to
+        // not use it.
+        TPM_MODE_DISABLED = 3;
+    }
+    TPMMode tpm_mode = 1;
+}