Added bootstrap CA

This adds a self-contained CA for bootstrapping and securing etcd
using certificates of infinite duration and a CRL for near-instant
revocation.

The bootstrapping problem is addressed by first
generating the CA and issuing initial certificates and then
injecting them once the consensus system is up and running.
All files are also kept on the encrypted persistent data store to
prevent the same bootstrapping problem when the node is already
initialized. The CRL is synchronized using a sync loop on every
node running the consensus service and distributed inside that.

The CA uses Ed25519-based cryptography and identifies the
hosts by their external hostname.

Test Plan:
Initial bootstrapping manually tested on a single node using a
manual gRPC call for Setup() and openssl s_client for connecting
to etcd.

X-Origin-Diff: phab/D233
GitOrigin-RevId: bd67818b5b649b13e0c098e480059ef990826542
diff --git a/core/internal/api/cluster.go b/core/internal/api/cluster.go
index 32a5691..64a757f 100644
--- a/core/internal/api/cluster.go
+++ b/core/internal/api/cluster.go
@@ -60,8 +60,13 @@
 		return nil, ErrAttestationFailed
 	}
 
+	consensusCerts, err := s.consensusService.IssueCertificate(req.Host)
+	if err != nil {
+		return nil, err
+	}
+
 	// Provision cluster info locally
-	memberID, err := s.consensusService.AddMember(ctx, req.Name, fmt.Sprintf("http://%s:%d", req.Host, req.ConsensusPort))
+	memberID, err := s.consensusService.AddMember(ctx, req.Name, fmt.Sprintf("https://%s:%d", req.Host, req.ConsensusPort))
 	if err != nil {
 		return nil, err
 	}
@@ -77,12 +82,17 @@
 		ExternalHost:      req.Host,
 		NodeName:          req.Name,
 		TrustBackend:      req.TrustBackend,
+		Certs:             consensusCerts,
 	})
 	if err != nil {
+		err3 := s.consensusService.RevokeCertificate(req.Host)
+		if err3 != nil {
+			s.Logger.Error("Failed to revoke a certificate after rollback, potential security risk", zap.Error(err3))
+		}
 		// Revert Consensus add member - might fail if consensus cannot be established
 		err2 := s.consensusService.RemoveMember(ctx, memberID)
-		if err2 != nil {
-			return nil, fmt.Errorf("Rollback failed after failed provisioning; err=%v; err_rb=%v", err, err2)
+		if err2 != nil || err3 != nil {
+			return nil, fmt.Errorf("Rollback failed after failed provisioning; err=%v; err_rb=%v; err_revoke=%v", err, err2, err3)
 		}
 		return nil, err
 	}