139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// L3 s11 (design 47): the bus's certificate command, moved from mesh-controller with the same output, made once, and
|
|
// its check false without a usable pair. Its cases, as the controller's tests had them, are rows of one test.
|
|
func TestTheBusCertificate(t *testing.T) {
|
|
for _, row := range []struct {
|
|
name string
|
|
run func(*testing.T)
|
|
}{
|
|
{"it loads as a server loads it", loadsAsAServersWould},
|
|
{"it is made once", isMadeOnce},
|
|
{"the check is false until there is a pair", checkIsFalseUntilAPair},
|
|
{"a certificate without its key is not usable", withoutItsKeyIsNotUsable},
|
|
{"where to write is required", whereToWriteIsRequired},
|
|
} {
|
|
t.Run(row.name, row.run)
|
|
}
|
|
}
|
|
|
|
// novox/hq 04-ISSUES/146. The bootstrap could not make the bus a certificate: it asked an image for
|
|
// `openssl` and the image it asks has none. What replaces it is this command, so what is checked is
|
|
// what the bootstrap needs from it — a pair a TLS server can actually load, made once and only once.
|
|
|
|
func loadsAsAServersWould(t *testing.T) {
|
|
into := t.TempDir()
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatalf("the bus could not be given a certificate: %v", err)
|
|
}
|
|
|
|
// The check a cheaper test would not make. The key was present and valid and the server could
|
|
// not start, once, because nothing loaded the pair the way a server loads it
|
|
// (novox/hq 04-ISSUES/014).
|
|
pair, err := tls.LoadX509KeyPair(filepath.Join(into, "tls.crt"), filepath.Join(into, "tls.key"))
|
|
if err != nil {
|
|
t.Fatalf("a TLS server cannot load what was written: %v", err)
|
|
}
|
|
leaf := pair.Leaf
|
|
if leaf == nil {
|
|
if leaf, err = x509.ParseCertificate(pair.Certificate[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := leaf.VerifyHostname("mesh-broker"); err != nil {
|
|
t.Errorf("the certificate is not for the name the bus is reached by: %v", err)
|
|
}
|
|
if len(leaf.IPAddresses) == 0 || leaf.IPAddresses[0].String() != "127.0.0.1" {
|
|
t.Errorf("the certificate does not cover the loopback address the foundation dials: %v", leaf.IPAddresses)
|
|
}
|
|
|
|
// The key is not readable by anything else on the machine; the certificate is public and is.
|
|
key, err := os.Stat(filepath.Join(into, "tls.key"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if key.Mode().Perm() != 0o600 {
|
|
t.Errorf("the key is %v", key.Mode().Perm())
|
|
}
|
|
crt, err := os.Stat(filepath.Join(into, "tls.crt"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if crt.Mode().Perm() != 0o644 {
|
|
t.Errorf("the certificate is %v, which the server runs as another user cannot read", crt.Mode().Perm())
|
|
}
|
|
}
|
|
|
|
// **Made once.** The step is applied again on every reconcile, and a second certificate is one the
|
|
// hosts that pinned the first no longer believe (novox/hq ADR 0004).
|
|
func isMadeOnce(t *testing.T) {
|
|
into := t.TempDir()
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first, err := os.ReadFile(filepath.Join(into, "tls.crt"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
again, err := os.ReadFile(filepath.Join(into, "tls.crt"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(first) != string(again) {
|
|
t.Fatal("running it twice replaced the certificate every host had pinned")
|
|
}
|
|
}
|
|
|
|
// The verify half: false before, true after, which is what makes the bootstrap run the step at all.
|
|
func checkIsFalseUntilAPair(t *testing.T) {
|
|
into := t.TempDir()
|
|
if err := busCertificate([]string{"--check", "--into", into}); err == nil {
|
|
t.Fatal("an empty directory reported a usable certificate")
|
|
}
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := busCertificate([]string{"--check", "--into", into}); err != nil {
|
|
t.Fatalf("the certificate it just made does not satisfy its own check: %v", err)
|
|
}
|
|
}
|
|
|
|
// A half-written pair is not a pair. An interrupted bootstrap leaves exactly this, and a step that
|
|
// called it done would hand the server a certificate with no key and report success.
|
|
func withoutItsKeyIsNotUsable(t *testing.T) {
|
|
into := t.TempDir()
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Remove(filepath.Join(into, "tls.key")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := busCertificate([]string{"--check", "--into", into}); err == nil {
|
|
t.Fatal("a certificate with no key passed the check")
|
|
}
|
|
if err := busCertificate([]string{"--into", into}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := tls.LoadX509KeyPair(filepath.Join(into, "tls.crt"), filepath.Join(into, "tls.key")); err != nil {
|
|
t.Fatalf("it did not replace the unusable pair: %v", err)
|
|
}
|
|
}
|
|
|
|
func whereToWriteIsRequired(t *testing.T) {
|
|
if err := busCertificate(nil); err == nil || !strings.Contains(err.Error(), "--into") {
|
|
t.Fatalf("it did not ask where to write: %v", err)
|
|
}
|
|
}
|