bastion 0.58.0
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
//go:build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An action op on a person's own machine (local, unanchored) reaches the
|
||||
// public internet and nothing around the machine; an anchored fleet keeps its
|
||||
// private destinations, because its ship steps reach private control planes.
|
||||
// The addresses are documentation and private examples, invented here.
|
||||
|
||||
// localOpBox: a local machine, anchored by nobody, with one pairing whose
|
||||
// pairing dir pins a signing key we hold.
|
||||
func localOpBox(t *testing.T) (dcn string, priv ed25519.PrivateKey) {
|
||||
t.Helper()
|
||||
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
|
||||
p, _ := modePath()
|
||||
if err := os.WriteFile(p, []byte(`{"mode":"local"}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writePairing(t, tAnchor)
|
||||
if currentAnchor() != nil {
|
||||
t.Fatal("the local box must not be anchored")
|
||||
}
|
||||
dcn = t.TempDir()
|
||||
pub, priv, _ := ed25519.GenerateKey(nil)
|
||||
if err := dcnSaveFile(dcn, &dcnGrantFile{Stores: map[string]dcnGrantRec{},
|
||||
VerifyPubkey: base64.StdEncoding.EncodeToString(pub)}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return dcn, priv
|
||||
}
|
||||
|
||||
// actionBundle: an unprivileged action op declaring one destination.
|
||||
func actionBundle(dest string) []opFile {
|
||||
man := `name: optest-reach
|
||||
version: 1.0.0
|
||||
summary: An action op that reaches one destination.
|
||||
kind: action
|
||||
target: machine
|
||||
privilege: user
|
||||
interpreter: /bin/sh
|
||||
time_limit: 30
|
||||
destinations: ["` + dest + `"]
|
||||
modes: [dry_run, apply, verify]
|
||||
undo: none
|
||||
`
|
||||
return []opFile{
|
||||
{Path: "op.yaml", Mode: 0o644, B64: base64.StdEncoding.EncodeToString([]byte(man))},
|
||||
{Path: "run", Mode: 0o755, B64: base64.StdEncoding.EncodeToString([]byte("#!/bin/sh\ntrue\n"))},
|
||||
}
|
||||
}
|
||||
|
||||
// syncAction drives op_sync for an action op declaring dest, under op_run
|
||||
// terms signed by priv; it returns the op's output, its error text and code.
|
||||
func syncAction(t *testing.T, dcn string, priv ed25519.PrivateKey, dest string) (string, string, int) {
|
||||
t.Helper()
|
||||
files := actionBundle(dest)
|
||||
sha, err := opDirDigest(files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
terms := opTerm(opKindRun, sha, `{}`, "", 1, time.Hour, "user")
|
||||
terms["op"] = "optest-reach"
|
||||
pair := signTermsPair(t, priv, terms)
|
||||
return driveJob(t, tAnchor, mgmtJob{Op: "op_sync", Args: append(pair, bundleB64(files))}, "", dcn)
|
||||
}
|
||||
|
||||
// RESULT: red before opLocalNetworkDest — a local box held an op declaring
|
||||
// 10.0.0.2:80 (a home network's address), and its apply would have reached it.
|
||||
func TestALocalBoxRefusesAnActionOpThatDeclaresAPrivateNetwork(t *testing.T) {
|
||||
dcn, priv := localOpBox(t)
|
||||
for _, dest := range []string{
|
||||
"10.0.0.2:80", // RFC 1918
|
||||
"192.168.1.2:443", // a home router's range
|
||||
"172.16.0.2:8080", // RFC 1918
|
||||
"100.64.0.2:22", // carrier NAT, and VPN meshes that use it
|
||||
"[fd00::2]:80", // ULA
|
||||
"[64:ff9b::a00:2]:80", // 10.0.0.2 through NAT64
|
||||
"[2002:a00:2::1]:80", // 10.0.0.2 through 6to4
|
||||
"198.18.0.2:80", // benchmarking, never a public host
|
||||
} {
|
||||
out, errOut, code := syncAction(t, dcn, priv, dest)
|
||||
if code == 0 || strings.Contains(out, `"held"`) {
|
||||
t.Errorf("%s: a local box held an op declaring it: %d %s %s", dest, code, out, errOut)
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(errOut, "private network") || !strings.Contains(errOut, "install a bastion on that computer") {
|
||||
t.Errorf("%s: the refusal must say why and what to do instead: %q", dest, errOut)
|
||||
}
|
||||
}
|
||||
// The public internet is still a destination.
|
||||
if out, errOut, code := syncAction(t, dcn, priv, "203.0.113.10:443"); code != 0 || !strings.Contains(out, `"held"`) {
|
||||
t.Fatalf("a public destination is held on a local box: %d %s %s", code, out, errOut)
|
||||
}
|
||||
// A name is judged at apply, by every address it resolves to
|
||||
// (opResolveDests); at sync it is only a name.
|
||||
if out, errOut, code := syncAction(t, dcn, priv, "forge.example.com:443"); code != 0 || !strings.Contains(out, `"held"`) {
|
||||
t.Fatalf("a destination by name is held on a local box: %d %s %s", code, out, errOut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnAnchoredFleetKeepsItsPrivateDestinations(t *testing.T) {
|
||||
dcn, priv := opTestBox(t)
|
||||
enrollFactor(t, dcn)
|
||||
for _, dest := range []string{"10.0.0.2:80", "100.64.0.2:6443", "[fd00::2]:80"} {
|
||||
if out, errOut, code := syncAction(t, dcn, priv, dest); code != 0 || !strings.Contains(out, `"held"`) {
|
||||
t.Errorf("%s: an anchored box holds a ship step to a private control plane: %d %s %s", dest, code, out, errOut)
|
||||
}
|
||||
}
|
||||
// What every box refuses stays refused.
|
||||
if _, errOut, code := syncAction(t, dcn, priv, "127.0.0.1:80"); code == 0 || !strings.Contains(errOut, "loopback") {
|
||||
t.Fatalf("loopback stays refused on an anchored box: %d %q", code, errOut)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user