8d1cdceb5c
Sub-wave 1: internal/emitter/nft.go (nftables emitter, DNAT :443→127.0.0.1:8443, rate-limit, SYN-flood filter); Traefik static config 127.0.0.1:8443 binding (D-220); orca doctor nft; orca nft CLI (show/diff/doctor/country-block/rate-limit). Sub-wave 2: docs/threat-model.md (R-017 trust boundary, R-020 deadlock, D-234 secret exclusion, orca user blast radius, step-ca SPOF); orca doctor mTLS (chain verification + live handshake probe, C5). ---ci--- project: orca phase: 15.5 milestone: v0.11 status: execute ---/ci---
253 lines
6.6 KiB
Go
253 lines
6.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/emitter"
|
|
)
|
|
|
|
func TestNftCmd_Registered(t *testing.T) {
|
|
found := false
|
|
for _, c := range rootCmd.Commands() {
|
|
if c.Name() == "nft" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("nft command not registered on root")
|
|
}
|
|
}
|
|
|
|
func TestNftShowCmd(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
want := "table inet orca-ingress { dnat }"
|
|
nftTransportOverride = &mockNftTransport{
|
|
execOut: map[string][]byte{
|
|
"nft list table inet orca-ingress": []byte(want),
|
|
},
|
|
}
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "show"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft show: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), want) {
|
|
t.Errorf("nft show output missing %q:\n%s", want, buf.String())
|
|
}
|
|
}
|
|
|
|
func TestNftShowCmd_PeerFlag(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
defer func() { nftTransportOverride = origTransport }()
|
|
mt := &mockNftTransport{
|
|
execOut: map[string][]byte{"nft list table inet orca-ingress": []byte("ok")},
|
|
}
|
|
nftTransportOverride = mt
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "show", "--peer", "node-2"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft show: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNftDiffCmd_NoDrift(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
// Use the full default nft render so the diff detects no drift
|
|
rendered, rerr := emitter.NftEmitter{}.RenderNftConfig(emitter.NftClusterConfig{})
|
|
if rerr != nil {
|
|
t.Fatalf("render: %v", rerr)
|
|
}
|
|
live := string(rendered[0].Content)
|
|
nftTransportOverride = &mockNftTransport{
|
|
execOut: map[string][]byte{
|
|
"nft list table inet orca-ingress": []byte(live),
|
|
},
|
|
}
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "diff", "--against", "txn-123"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft diff: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "no drift") {
|
|
t.Errorf("expected no drift, got:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestNftDiffCmd_RequiresAgainst(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "diff"})
|
|
if err := rootCmd.Execute(); err == nil {
|
|
t.Fatal("expected error for missing --against, got nil")
|
|
}
|
|
}
|
|
|
|
func TestNftRateLimitSetCmd(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
mt := &mockNftTransport{
|
|
execOut: map[string][]byte{},
|
|
}
|
|
nftTransportOverride = mt
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "rate", "limit", "set", "--rate", "250"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft rate limit set: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "rate-limit set to 250/s burst 500") {
|
|
t.Errorf("unexpected output:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestNftRateLimitSetCmd_RequiresRate(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "rate", "limit", "set"})
|
|
if err := rootCmd.Execute(); err == nil {
|
|
t.Fatal("expected error for missing --rate, got nil")
|
|
}
|
|
}
|
|
|
|
func TestNftCountryBlockAddCmd(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
mt := &mockNftTransport{execOut: map[string][]byte{}}
|
|
nftTransportOverride = mt
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "country", "block", "add", "RU,CN"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft country block add: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "added 2 country code(s)") {
|
|
t.Errorf("unexpected output:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestNftCountryBlockAddCmd_BadCode(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "country", "block", "add", "USA"})
|
|
if err := rootCmd.Execute(); err == nil {
|
|
t.Fatal("expected error for bad country code, got nil")
|
|
}
|
|
}
|
|
|
|
func TestNftDoctorAliasCmd(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
nftTransportOverride = &mockNftTransport{
|
|
execOut: map[string][]byte{
|
|
"nft list table inet orca-ingress": []byte("dnat to 127.0.0.1:8443 dnat to 127.0.0.1:8080 ora_rl"),
|
|
},
|
|
}
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "doctor"})
|
|
if err := rootCmd.Execute(); err != nil {
|
|
t.Fatalf("nft doctor: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "nft:table") {
|
|
t.Errorf("nft doctor alias did not run doctor nft checks:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestNftShowCmd_ExecError(t *testing.T) {
|
|
_, cleanup := initTestEnv(t)
|
|
defer cleanup()
|
|
origTransport := nftTransportOverride
|
|
origPeer := nftLeadPeerOverride
|
|
defer func() {
|
|
nftTransportOverride = origTransport
|
|
nftLeadPeerOverride = origPeer
|
|
}()
|
|
nftLeadPeerOverride = "lead"
|
|
nftTransportOverride = &mockNftTransport{
|
|
execErr: map[string]error{
|
|
"nft list table inet orca-ingress": errors.New("connection refused"),
|
|
},
|
|
}
|
|
resetRootFlags(t)
|
|
var buf bytes.Buffer
|
|
rootCmd.SetOut(&buf)
|
|
rootCmd.SetErr(&buf)
|
|
rootCmd.SetArgs([]string{"nft", "show"})
|
|
if err := rootCmd.Execute(); err == nil {
|
|
t.Fatal("expected error from exec failure, got nil")
|
|
}
|
|
}
|
|
|
|
var _ context.Context = context.Background()
|