command_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package cli
  2. import (
  3. "path/filepath"
  4. "reflect"
  5. "strings"
  6. "testing"
  7. "debtik/internal/aerotun"
  8. "debtik/internal/auth"
  9. "debtik/internal/vpn"
  10. )
  11. func TestQuotedCommandArguments(t *testing.T) {
  12. got, err := splitCommand(`/interface aerotun set test comment="office uplink" psk='literal $\\key' name=""`)
  13. want := []string{"/interface", "aerotun", "set", "test", "comment=office uplink", `psk=literal $\\key`, "name="}
  14. if err != nil || !reflect.DeepEqual(got, want) {
  15. t.Fatalf("%q %v", got, err)
  16. }
  17. if _, err := splitCommand(`comment="unfinished`); err == nil {
  18. t.Fatal("accepted unterminated quote")
  19. }
  20. }
  21. func TestBareLogAndHandlerRecovery(t *testing.T) {
  22. e := &Executor{}
  23. user := &auth.PublicUser{Role: auth.RoleFull}
  24. output, _ := e.Execute("/log", user)
  25. if commandExitStatus(output) != 0 {
  26. t.Fatal(output)
  27. }
  28. output, _ = e.Execute("/interface print", user) // absent manager simulates a handler failure
  29. if commandExitStatus(output) == 0 || !strings.Contains(output, "still running") {
  30. t.Fatal(output)
  31. }
  32. output, _ = e.Execute("nonsense", user)
  33. if commandExitStatus(output) == 0 {
  34. t.Fatal("unknown command returned success")
  35. }
  36. output, _ = e.Execute("/help", user)
  37. if commandExitStatus(output) != 0 {
  38. t.Fatal("help returned failure")
  39. }
  40. }
  41. func TestAeroTUNCLIConfigurationAndPermissions(t *testing.T) {
  42. m := aerotun.NewManagerWithPath(filepath.Join(t.TempDir(), "aerotun.json"))
  43. defer m.StopAll()
  44. e := &Executor{vpnMgr: &vpn.Manager{AeroTUN: m}}
  45. admin := &auth.PublicUser{Role: auth.RoleFull}
  46. psk := strings.Repeat("12", 32)
  47. command := `/interface aerotun add name=atest mode=server local-address=10.90.0.1/30 peer-address=10.90.0.2/32 allowed-ips=10.90.0.2/32 psk=` + psk + ` comment="office uplink"`
  48. output, _ := e.Execute(command, admin)
  49. if commandExitStatus(output) != 0 {
  50. t.Fatal(output)
  51. }
  52. inst := m.List()[0]
  53. if !inst.Disabled || inst.PublicKey == "" || inst.Comment != "office uplink" {
  54. t.Fatal("draft configuration not retained")
  55. }
  56. for _, role := range []auth.Role{auth.RoleRead, auth.RoleWrite} {
  57. output, _ = e.Execute("/interface aerotun remove atest", &auth.PublicUser{Role: role})
  58. if commandExitStatus(output) == 0 {
  59. t.Fatal("non-admin removed tunnel")
  60. }
  61. }
  62. output, _ = e.Execute("/interface aerotun print detail=yes", admin)
  63. if strings.Contains(output, psk) || strings.Contains(output, inst.PrivateKey) || !strings.Contains(output, inst.PublicKey) {
  64. t.Fatal("secret disclosure or missing public identity")
  65. }
  66. output, _ = e.Execute(`/interface aerotun set atest comment="updated uplink"`, admin)
  67. if commandExitStatus(output) != 0 {
  68. t.Fatal(output)
  69. }
  70. updated, _ := m.Get(inst.ID)
  71. if updated.PSK != psk || updated.PublicKey != inst.PublicKey {
  72. t.Fatal("edit changed identity")
  73. }
  74. output, _ = e.Execute("/interface aerotun remove atest", admin)
  75. if commandExitStatus(output) != 0 || len(m.List()) != 0 {
  76. t.Fatal(output)
  77. }
  78. }
  79. func TestVXLANCLIConfigurationAndPermissions(t *testing.T) {
  80. vxlanMgr := vpn.NewVXLANManager(filepath.Join(t.TempDir(), "vxlan.json"))
  81. e := &Executor{vpnMgr: &vpn.Manager{VXLAN: vxlanMgr}}
  82. admin := &auth.PublicUser{Role: auth.RoleFull}
  83. viewer := &auth.PublicUser{Role: auth.RoleRead}
  84. // 1. Add VXLAN
  85. output, _ := e.Execute(`/interface vxlan add name=vxlan99 vni=99 port=4789 mtu=1500 comment="test overlay"`, admin)
  86. if strings.Contains(output, "Error") {
  87. t.Fatalf("unexpected error adding vxlan: %s", output)
  88. }
  89. // 2. Print VXLAN
  90. output, _ = e.Execute("/interface vxlan print", viewer)
  91. if !strings.Contains(output, "vxlan99") || !strings.Contains(output, "99") {
  92. t.Fatalf("vxlan99 not found in print output: %s", output)
  93. }
  94. // 3. Add VTEP
  95. output, _ = e.Execute("/interface vxlan vteps add interface=vxlan99 remote-ip=192.168.1.50 comment=peera", admin)
  96. if strings.Contains(output, "Error") {
  97. t.Fatalf("unexpected error adding vtep: %s", output)
  98. }
  99. // 4. Print VTEP
  100. output, _ = e.Execute("/interface vxlan vteps print", viewer)
  101. if !strings.Contains(output, "192.168.1.50") {
  102. t.Fatalf("vtep not found in print output: %s", output)
  103. }
  104. // 5. Viewer cannot modify
  105. output, _ = e.Execute("/interface vxlan remove vxlan99", viewer)
  106. if !strings.Contains(output, "Permission denied") {
  107. t.Fatalf("expected permission denied for viewer, got: %s", output)
  108. }
  109. // 6. Set comment
  110. output, _ = e.Execute(`/interface vxlan set vxlan99 comment="updated vxlan"`, admin)
  111. if strings.Contains(output, "Error") {
  112. t.Fatalf("unexpected error setting vxlan: %s", output)
  113. }
  114. // 7. Remove VTEP
  115. output, _ = e.Execute("/interface vxlan vteps remove interface=vxlan99 remote-ip=192.168.1.50", admin)
  116. if strings.Contains(output, "Error") {
  117. t.Fatalf("unexpected error removing vtep: %s", output)
  118. }
  119. // 8. Remove VXLAN
  120. output, _ = e.Execute("/interface vxlan remove vxlan99", admin)
  121. if strings.Contains(output, "Error") {
  122. t.Fatalf("unexpected error removing vxlan: %s", output)
  123. }
  124. }