1
0

ssh.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. package nebula
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "flag"
  7. "fmt"
  8. "log/slog"
  9. "maps"
  10. "net"
  11. "net/netip"
  12. "os"
  13. "slices"
  14. "sort"
  15. "strings"
  16. "github.com/slackhq/nebula/config"
  17. "github.com/slackhq/nebula/header"
  18. "github.com/slackhq/nebula/logging"
  19. "github.com/slackhq/nebula/sshd"
  20. )
  21. type sshListHostMapFlags struct {
  22. Json bool
  23. Pretty bool
  24. ByIndex bool
  25. }
  26. type sshPrintCertFlags struct {
  27. Json bool
  28. Pretty bool
  29. Raw bool
  30. }
  31. type sshPrintTunnelFlags struct {
  32. Pretty bool
  33. }
  34. type sshChangeRemoteFlags struct {
  35. Address string
  36. }
  37. type sshCloseTunnelFlags struct {
  38. LocalOnly bool
  39. }
  40. type sshCreateTunnelFlags struct {
  41. Address string
  42. }
  43. type sshDeviceInfoFlags struct {
  44. Json bool
  45. Pretty bool
  46. }
  47. func wireSSHReload(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) {
  48. c.RegisterReloadCallback(func(c *config.C, r *config.Reload) {
  49. if c.GetBool("sshd.enabled", false) {
  50. // Restarting the server drops every open session, the one that asked for this reload included. Only restart
  51. // when it isn't running or its config changed. The host key file can change under the same config, new
  52. // handshakes use the new key without a restart
  53. if ssh.Running() && !slices.ContainsFunc(sshdServerKeys, c.HasChanged) {
  54. key, err := loadSSHHostKey(c)
  55. if err == nil {
  56. err = ssh.SetHostKey(key)
  57. }
  58. if err != nil {
  59. l.Error("Failed to reconfigure the sshd", "error", err)
  60. ssh.Stop()
  61. r.Failed()
  62. }
  63. return
  64. }
  65. sshRun, err := configSSH(l, ssh, c, r)
  66. if err != nil {
  67. l.Error("Failed to reconfigure the sshd", "error", err)
  68. ssh.Stop()
  69. r.Failed()
  70. }
  71. if sshRun != nil {
  72. go sshRun()
  73. }
  74. } else {
  75. ssh.Stop()
  76. }
  77. })
  78. }
  79. // sshdServerKeys are what configSSH reads
  80. var sshdServerKeys = []string{"sshd.enabled", "sshd.listen", "sshd.host_key", "sshd.trusted_cas", "sshd.authorized_users"}
  81. // loadSSHHostKey returns sshd.host_key, read from its file when it names one.
  82. func loadSSHHostKey(c *config.C) ([]byte, error) {
  83. hostKeyPathOrKey := c.GetString("sshd.host_key", "")
  84. if hostKeyPathOrKey == "" {
  85. return nil, fmt.Errorf("sshd.host_key must be provided")
  86. }
  87. if strings.Contains(hostKeyPathOrKey, "-----BEGIN") {
  88. return []byte(hostKeyPathOrKey), nil
  89. }
  90. b, err := os.ReadFile(hostKeyPathOrKey)
  91. if err != nil {
  92. return nil, fmt.Errorf("error while loading sshd.host_key file: %s", err)
  93. }
  94. return b, nil
  95. }
  96. // configSSH reads the ssh info out of the passed-in Config and
  97. // updates the passed-in SSHServer. On success, it returns a function
  98. // that callers may invoke to run the configured ssh server. On
  99. // failure, it returns nil, error.
  100. // r is nil at startup, entries that are skipped with a warning fail it on a reload
  101. func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C, r *config.Reload) (func(), error) {
  102. listen := c.GetString("sshd.listen", "")
  103. if listen == "" {
  104. return nil, fmt.Errorf("sshd.listen must be provided")
  105. }
  106. _, port, err := net.SplitHostPort(listen)
  107. if err != nil {
  108. return nil, fmt.Errorf("invalid sshd.listen address: %s", err)
  109. }
  110. if port == "22" {
  111. return nil, fmt.Errorf("sshd.listen can not use port 22")
  112. }
  113. hostKeyBytes, err := loadSSHHostKey(c)
  114. if err != nil {
  115. return nil, err
  116. }
  117. err = ssh.SetHostKey(hostKeyBytes)
  118. if err != nil {
  119. return nil, fmt.Errorf("error while adding sshd.host_key: %s", err)
  120. }
  121. // Clear existing trusted CAs and authorized keys
  122. ssh.ClearTrustedCAs()
  123. ssh.ClearAuthorizedKeys()
  124. rawCAs := c.GetStringSlice("sshd.trusted_cas", []string{})
  125. for _, caAuthorizedKey := range rawCAs {
  126. err := ssh.AddTrustedCA(caAuthorizedKey)
  127. if err != nil {
  128. l.Warn("SSH CA had an error, ignoring", "error", err, "sshCA", caAuthorizedKey)
  129. r.Failed()
  130. continue
  131. }
  132. }
  133. rawKeys := c.Get("sshd.authorized_users")
  134. keys, ok := rawKeys.([]any)
  135. if ok {
  136. for _, rk := range keys {
  137. kDef, ok := rk.(map[string]any)
  138. if !ok {
  139. l.Warn("Authorized user had an error, ignoring", "sshKeyConfig", rk)
  140. r.Failed()
  141. continue
  142. }
  143. user, ok := kDef["user"].(string)
  144. if !ok {
  145. l.Warn("Authorized user is missing the user field", "sshKeyConfig", rk)
  146. r.Failed()
  147. continue
  148. }
  149. k := kDef["keys"]
  150. switch v := k.(type) {
  151. case string:
  152. err := ssh.AddAuthorizedKey(user, v)
  153. if err != nil {
  154. l.Warn("Failed to authorize key",
  155. "error", err,
  156. "sshKeyConfig", rk,
  157. "sshKey", v,
  158. )
  159. r.Failed()
  160. continue
  161. }
  162. case []any:
  163. for _, subK := range v {
  164. sk, ok := subK.(string)
  165. if !ok {
  166. l.Warn("Did not understand ssh key",
  167. "sshKeyConfig", rk,
  168. "sshKey", subK,
  169. )
  170. r.Failed()
  171. continue
  172. }
  173. err := ssh.AddAuthorizedKey(user, sk)
  174. if err != nil {
  175. l.Warn("Failed to authorize key",
  176. "error", err,
  177. "sshKeyConfig", sk,
  178. )
  179. r.Failed()
  180. continue
  181. }
  182. }
  183. default:
  184. l.Warn("Authorized user is missing the keys field or was not understood", "sshKeyConfig", rk)
  185. r.Failed()
  186. }
  187. }
  188. } else {
  189. l.Info("no ssh users to authorize")
  190. }
  191. var runner func()
  192. if c.GetBool("sshd.enabled", false) {
  193. ssh.Stop()
  194. runner = func() {
  195. if err := ssh.Run(listen); err != nil {
  196. l.Warn("Failed to run the SSH server", "error", err)
  197. }
  198. }
  199. } else {
  200. ssh.Stop()
  201. }
  202. return runner, nil
  203. }
  204. func attachCommands(l *slog.Logger, c *config.C, ssh *sshd.SSHServer, f *Interface) {
  205. ssh.RegisterCommand(&sshd.Command{
  206. Name: "list-hostmap",
  207. ShortDescription: "List all known previously connected hosts",
  208. Flags: func() (*flag.FlagSet, any) {
  209. fl := flag.NewFlagSet("", flag.ContinueOnError)
  210. s := sshListHostMapFlags{}
  211. fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
  212. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
  213. fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
  214. return fl, &s
  215. },
  216. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  217. return sshListHostMap(f.hostMap, fs, w)
  218. },
  219. })
  220. ssh.RegisterCommand(&sshd.Command{
  221. Name: "list-pending-hostmap",
  222. ShortDescription: "List all handshaking hosts",
  223. Flags: func() (*flag.FlagSet, any) {
  224. fl := flag.NewFlagSet("", flag.ContinueOnError)
  225. s := sshListHostMapFlags{}
  226. fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
  227. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
  228. fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
  229. return fl, &s
  230. },
  231. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  232. return sshListHostMap(f.handshakeManager, fs, w)
  233. },
  234. })
  235. ssh.RegisterCommand(&sshd.Command{
  236. Name: "list-lighthouse-addrmap",
  237. ShortDescription: "List all lighthouse map entries",
  238. Flags: func() (*flag.FlagSet, any) {
  239. fl := flag.NewFlagSet("", flag.ContinueOnError)
  240. s := sshListHostMapFlags{}
  241. fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
  242. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
  243. return fl, &s
  244. },
  245. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  246. return sshListLighthouseMap(f.lightHouse, fs, w)
  247. },
  248. })
  249. ssh.RegisterCommand(&sshd.Command{
  250. Name: "reload",
  251. ShortDescription: "Reloads configuration from disk, same as sending HUP to the process",
  252. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  253. return sshReload(l, c, w)
  254. },
  255. })
  256. ssh.RegisterCommand(&sshd.Command{
  257. Name: "log-level",
  258. ShortDescription: "Gets or sets the current log level",
  259. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  260. return sshLogLevel(l, fs, a, w)
  261. },
  262. })
  263. ssh.RegisterCommand(&sshd.Command{
  264. Name: "log-format",
  265. ShortDescription: "Gets or sets the current log format",
  266. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  267. return sshLogFormat(l, fs, a, w)
  268. },
  269. })
  270. ssh.RegisterCommand(&sshd.Command{
  271. Name: "version",
  272. ShortDescription: "Prints the currently running version of nebula",
  273. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  274. return sshVersion(f, fs, a, w)
  275. },
  276. })
  277. ssh.RegisterCommand(&sshd.Command{
  278. Name: "device-info",
  279. ShortDescription: "Prints information about the network device.",
  280. Flags: func() (*flag.FlagSet, any) {
  281. fl := flag.NewFlagSet("", flag.ContinueOnError)
  282. s := sshDeviceInfoFlags{}
  283. fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
  284. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
  285. return fl, &s
  286. },
  287. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  288. return sshDeviceInfo(f, fs, w)
  289. },
  290. })
  291. ssh.RegisterCommand(&sshd.Command{
  292. Name: "print-cert",
  293. ShortDescription: "Prints the current certificate being used or the certificate for the provided vpn addr",
  294. Flags: func() (*flag.FlagSet, any) {
  295. fl := flag.NewFlagSet("", flag.ContinueOnError)
  296. s := sshPrintCertFlags{}
  297. fl.BoolVar(&s.Json, "json", false, "outputs as json")
  298. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
  299. fl.BoolVar(&s.Raw, "raw", false, "raw prints the PEM encoded certificate, not compatible with -json or -pretty")
  300. return fl, &s
  301. },
  302. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  303. return sshPrintCert(f, fs, a, w)
  304. },
  305. })
  306. ssh.RegisterCommand(&sshd.Command{
  307. Name: "print-tunnel",
  308. ShortDescription: "Prints json details about a tunnel for the provided vpn addr",
  309. Flags: func() (*flag.FlagSet, any) {
  310. fl := flag.NewFlagSet("", flag.ContinueOnError)
  311. s := sshPrintTunnelFlags{}
  312. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
  313. return fl, &s
  314. },
  315. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  316. return sshPrintTunnel(f, fs, a, w)
  317. },
  318. })
  319. ssh.RegisterCommand(&sshd.Command{
  320. Name: "print-relays",
  321. ShortDescription: "Prints json details about all relay info",
  322. Flags: func() (*flag.FlagSet, any) {
  323. fl := flag.NewFlagSet("", flag.ContinueOnError)
  324. s := sshPrintTunnelFlags{}
  325. fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
  326. return fl, &s
  327. },
  328. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  329. return sshPrintRelays(f, fs, a, w)
  330. },
  331. })
  332. ssh.RegisterCommand(&sshd.Command{
  333. Name: "change-remote",
  334. ShortDescription: "Changes the remote address used in the tunnel for the provided vpn addr",
  335. Flags: func() (*flag.FlagSet, any) {
  336. fl := flag.NewFlagSet("", flag.ContinueOnError)
  337. s := sshChangeRemoteFlags{}
  338. fl.StringVar(&s.Address, "address", "", "The new remote address, ip:port")
  339. return fl, &s
  340. },
  341. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  342. return sshChangeRemote(f, fs, a, w)
  343. },
  344. })
  345. ssh.RegisterCommand(&sshd.Command{
  346. Name: "close-tunnel",
  347. ShortDescription: "Closes a tunnel for the provided vpn addr",
  348. Flags: func() (*flag.FlagSet, any) {
  349. fl := flag.NewFlagSet("", flag.ContinueOnError)
  350. s := sshCloseTunnelFlags{}
  351. fl.BoolVar(&s.LocalOnly, "local-only", false, "Disables notifying the remote that the tunnel is shutting down")
  352. return fl, &s
  353. },
  354. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  355. return sshCloseTunnel(f, fs, a, w)
  356. },
  357. })
  358. ssh.RegisterCommand(&sshd.Command{
  359. Name: "create-tunnel",
  360. ShortDescription: "Creates a tunnel for the provided vpn address",
  361. Help: "The lighthouses will be queried for real addresses but you can provide one as well.",
  362. Flags: func() (*flag.FlagSet, any) {
  363. fl := flag.NewFlagSet("", flag.ContinueOnError)
  364. s := sshCreateTunnelFlags{}
  365. fl.StringVar(&s.Address, "address", "", "Optionally provide a real remote address, ip:port ")
  366. return fl, &s
  367. },
  368. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  369. return sshCreateTunnel(f, fs, a, w)
  370. },
  371. })
  372. ssh.RegisterCommand(&sshd.Command{
  373. Name: "query-lighthouse",
  374. ShortDescription: "Query the lighthouses for the provided vpn address",
  375. Help: "This command is asynchronous. Only currently known udp addresses will be printed.",
  376. Callback: func(fs any, a []string, w sshd.StringWriter) error {
  377. return sshQueryLighthouse(f, fs, a, w)
  378. },
  379. })
  380. }
  381. func sshListHostMap(hl controlHostLister, a any, w sshd.StringWriter) error {
  382. fs, ok := a.(*sshListHostMapFlags)
  383. if !ok {
  384. return nil
  385. }
  386. var hm []ControlHostInfo
  387. if fs.ByIndex {
  388. hm = listHostMapIndexes(hl)
  389. } else {
  390. hm = listHostMapHosts(hl)
  391. }
  392. sort.Slice(hm, func(i, j int) bool {
  393. return hm[i].VpnAddrs[0].Compare(hm[j].VpnAddrs[0]) < 0
  394. })
  395. if fs.Json || fs.Pretty {
  396. js := json.NewEncoder(w.GetWriter())
  397. if fs.Pretty {
  398. js.SetIndent("", " ")
  399. }
  400. err := js.Encode(hm)
  401. if err != nil {
  402. return nil
  403. }
  404. } else {
  405. for _, v := range hm {
  406. err := w.WriteLine(fmt.Sprintf("%s: %s", v.VpnAddrs, v.RemoteAddrs))
  407. if err != nil {
  408. return err
  409. }
  410. }
  411. }
  412. return nil
  413. }
  414. func sshListLighthouseMap(lightHouse *LightHouse, a any, w sshd.StringWriter) error {
  415. fs, ok := a.(*sshListHostMapFlags)
  416. if !ok {
  417. return nil
  418. }
  419. type lighthouseInfo struct {
  420. VpnAddr string `json:"vpnAddr"`
  421. Addrs *CacheMap `json:"addrs"`
  422. }
  423. lightHouse.RLock()
  424. addrMap := make([]lighthouseInfo, len(lightHouse.addrMap))
  425. x := 0
  426. for k, v := range lightHouse.addrMap {
  427. addrMap[x] = lighthouseInfo{
  428. VpnAddr: k.String(),
  429. Addrs: v.CopyCache(),
  430. }
  431. x++
  432. }
  433. lightHouse.RUnlock()
  434. sort.Slice(addrMap, func(i, j int) bool {
  435. return strings.Compare(addrMap[i].VpnAddr, addrMap[j].VpnAddr) < 0
  436. })
  437. if fs.Json || fs.Pretty {
  438. js := json.NewEncoder(w.GetWriter())
  439. if fs.Pretty {
  440. js.SetIndent("", " ")
  441. }
  442. err := js.Encode(addrMap)
  443. if err != nil {
  444. return nil
  445. }
  446. } else {
  447. for _, v := range addrMap {
  448. b, err := json.Marshal(v.Addrs)
  449. if err != nil {
  450. return err
  451. }
  452. err = w.WriteLine(fmt.Sprintf("%s: %s", v.VpnAddr, string(b)))
  453. if err != nil {
  454. return err
  455. }
  456. }
  457. }
  458. return nil
  459. }
  460. func sshVersion(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  461. return w.WriteLine(fmt.Sprintf("%s", ifce.version))
  462. }
  463. func sshQueryLighthouse(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  464. if len(a) == 0 {
  465. return w.WriteLine("No vpn address was provided")
  466. }
  467. vpnAddr, err := netip.ParseAddr(a[0])
  468. if err != nil {
  469. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  470. }
  471. if !vpnAddr.IsValid() {
  472. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  473. }
  474. var cm *CacheMap
  475. rl := ifce.lightHouse.Query(vpnAddr)
  476. if rl != nil {
  477. cm = rl.CopyCache()
  478. }
  479. return json.NewEncoder(w.GetWriter()).Encode(cm)
  480. }
  481. func sshCloseTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  482. flags, ok := fs.(*sshCloseTunnelFlags)
  483. if !ok {
  484. return nil
  485. }
  486. if len(a) == 0 {
  487. return w.WriteLine("No vpn address was provided")
  488. }
  489. vpnAddr, err := netip.ParseAddr(a[0])
  490. if err != nil {
  491. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  492. }
  493. if !vpnAddr.IsValid() {
  494. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  495. }
  496. hostInfo := ifce.hostMap.QueryVpnAddr(vpnAddr)
  497. if hostInfo == nil {
  498. return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn address: %v", a[0]))
  499. }
  500. if !flags.LocalOnly {
  501. ifce.send(
  502. header.CloseTunnel,
  503. 0,
  504. hostInfo.ConnectionState,
  505. hostInfo,
  506. []byte{},
  507. make([]byte, 12, 12),
  508. make([]byte, mtu),
  509. )
  510. }
  511. ifce.closeTunnel(hostInfo)
  512. return w.WriteLine("Closed")
  513. }
  514. func sshCreateTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  515. flags, ok := fs.(*sshCreateTunnelFlags)
  516. if !ok {
  517. return nil
  518. }
  519. if len(a) == 0 {
  520. return w.WriteLine("No vpn address was provided")
  521. }
  522. vpnAddr, err := netip.ParseAddr(a[0])
  523. if err != nil {
  524. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  525. }
  526. if !vpnAddr.IsValid() {
  527. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  528. }
  529. hostInfo := ifce.hostMap.QueryVpnAddr(vpnAddr)
  530. if hostInfo != nil {
  531. return w.WriteLine(fmt.Sprintf("Tunnel already exists"))
  532. }
  533. hostInfo = ifce.handshakeManager.QueryVpnAddr(vpnAddr)
  534. if hostInfo != nil {
  535. return w.WriteLine(fmt.Sprintf("Tunnel already handshaking"))
  536. }
  537. var addr netip.AddrPort
  538. if flags.Address != "" {
  539. addr, err = netip.ParseAddrPort(flags.Address)
  540. if err != nil {
  541. return w.WriteLine("Address could not be parsed")
  542. }
  543. }
  544. // Set the remote from the callback, which StartHandshake runs under the handshake manager lock
  545. // before the hostinfo reaches the handshake loop or the static-host trigger, so the address is
  546. // in the RemoteList before the first handshake is sent.
  547. ifce.handshakeManager.StartHandshake(vpnAddr, func(hh *HandshakeHostInfo) {
  548. if addr.IsValid() {
  549. hh.hostinfo.SetRemote(addr)
  550. }
  551. })
  552. return w.WriteLine("Created")
  553. }
  554. func sshChangeRemote(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  555. flags, ok := fs.(*sshChangeRemoteFlags)
  556. if !ok {
  557. return nil
  558. }
  559. if len(a) == 0 {
  560. return w.WriteLine("No vpn address was provided")
  561. }
  562. if flags.Address == "" {
  563. return w.WriteLine("No address was provided")
  564. }
  565. addr, err := netip.ParseAddrPort(flags.Address)
  566. if err != nil {
  567. return w.WriteLine("Address could not be parsed")
  568. }
  569. vpnAddr, err := netip.ParseAddr(a[0])
  570. if err != nil {
  571. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  572. }
  573. if !vpnAddr.IsValid() {
  574. return w.WriteLine(fmt.Sprintf("The provided vpn address could not be parsed: %s", a[0]))
  575. }
  576. hostInfo := ifce.hostMap.QueryVpnAddr(vpnAddr)
  577. if hostInfo == nil {
  578. return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn address: %v", a[0]))
  579. }
  580. hostInfo.SetRemote(addr)
  581. return w.WriteLine("Changed")
  582. }
  583. func sshLogLevel(l *slog.Logger, fs any, a []string, w sshd.StringWriter) error {
  584. ctrl, ok := l.Handler().(interface {
  585. GetLevel() slog.Level
  586. SetLevel(slog.Level)
  587. })
  588. if !ok {
  589. return w.WriteLine("Log level is not reconfigurable on this logger")
  590. }
  591. if len(a) == 0 {
  592. return w.WriteLine(fmt.Sprintf("Log level is: %s", logging.LevelName(ctrl.GetLevel())))
  593. }
  594. level, err := logging.ParseLevel(strings.ToLower(a[0]))
  595. if err != nil {
  596. return w.WriteLine(fmt.Sprintf("Unknown log level %s. Possible log levels: trace, debug, info, warn, error", a))
  597. }
  598. ctrl.SetLevel(level)
  599. return w.WriteLine(fmt.Sprintf("Log level is: %s", logging.LevelName(ctrl.GetLevel())))
  600. }
  601. func sshLogFormat(l *slog.Logger, fs any, a []string, w sshd.StringWriter) error {
  602. ctrl, ok := l.Handler().(interface {
  603. GetFormat() string
  604. SetFormat(string) error
  605. })
  606. if !ok {
  607. return w.WriteLine("Log format is not reconfigurable on this logger")
  608. }
  609. if len(a) == 0 {
  610. return w.WriteLine(fmt.Sprintf("Log format is: %s", ctrl.GetFormat()))
  611. }
  612. if err := ctrl.SetFormat(strings.ToLower(a[0])); err != nil {
  613. return err
  614. }
  615. return w.WriteLine(fmt.Sprintf("Log format is: %s", ctrl.GetFormat()))
  616. }
  617. func sshPrintCert(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  618. args, ok := fs.(*sshPrintCertFlags)
  619. if !ok {
  620. return nil
  621. }
  622. cert := ifce.pki.getCertState().GetDefaultCertificate()
  623. if len(a) > 0 {
  624. vpnAddr, err := netip.ParseAddr(a[0])
  625. if err != nil {
  626. return w.WriteLine(fmt.Sprintf("The provided vpn addr could not be parsed: %s", a[0]))
  627. }
  628. if !vpnAddr.IsValid() {
  629. return w.WriteLine(fmt.Sprintf("The provided vpn addr could not be parsed: %s", a[0]))
  630. }
  631. hostInfo := ifce.hostMap.QueryVpnAddr(vpnAddr)
  632. if hostInfo == nil {
  633. return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn addr: %v", a[0]))
  634. }
  635. cert = hostInfo.GetCert().Certificate
  636. }
  637. if args.Json || args.Pretty {
  638. b, err := cert.MarshalJSON()
  639. if err != nil {
  640. return nil
  641. }
  642. if args.Pretty {
  643. buf := new(bytes.Buffer)
  644. err := json.Indent(buf, b, "", " ")
  645. b = buf.Bytes()
  646. if err != nil {
  647. return nil
  648. }
  649. }
  650. return w.WriteBytes(b)
  651. }
  652. if args.Raw {
  653. b, err := cert.MarshalPEM()
  654. if err != nil {
  655. return nil
  656. }
  657. return w.WriteBytes(b)
  658. }
  659. return w.WriteLine(cert.String())
  660. }
  661. func sshPrintRelays(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  662. args, ok := fs.(*sshPrintTunnelFlags)
  663. if !ok {
  664. w.WriteLine(fmt.Sprintf("sshPrintRelays failed to convert args type"))
  665. return nil
  666. }
  667. relays := map[uint32]*HostInfo{}
  668. ifce.hostMap.Lock()
  669. maps.Copy(relays, ifce.hostMap.Relays)
  670. ifce.hostMap.Unlock()
  671. type RelayFor struct {
  672. Error error
  673. Type string
  674. State string
  675. PeerAddr netip.Addr
  676. LocalIndex uint32
  677. RemoteIndex uint32
  678. RelayedThrough []netip.Addr
  679. }
  680. type RelayOutput struct {
  681. NebulaAddr netip.Addr
  682. RelayForAddrs []RelayFor
  683. }
  684. type CmdOutput struct {
  685. Relays []*RelayOutput
  686. }
  687. co := CmdOutput{}
  688. enc := json.NewEncoder(w.GetWriter())
  689. if args.Pretty {
  690. enc.SetIndent("", " ")
  691. }
  692. for k, v := range relays {
  693. ro := RelayOutput{NebulaAddr: v.vpnAddrs[0]}
  694. co.Relays = append(co.Relays, &ro)
  695. relayHI := ifce.hostMap.QueryVpnAddr(v.vpnAddrs[0])
  696. if relayHI == nil {
  697. ro.RelayForAddrs = append(ro.RelayForAddrs, RelayFor{Error: errors.New("could not find hostinfo")})
  698. continue
  699. }
  700. for _, vpnAddr := range relayHI.relayState.CopyRelayForIps() {
  701. rf := RelayFor{Error: nil}
  702. r, ok := relayHI.relayState.GetRelayForByAddr(vpnAddr)
  703. if ok {
  704. t := ""
  705. switch r.Type {
  706. case ForwardingType:
  707. t = "forwarding"
  708. case TerminalType:
  709. t = "terminal"
  710. default:
  711. t = "unknown"
  712. }
  713. s := ""
  714. switch r.State {
  715. case Requested:
  716. s = "requested"
  717. case Established:
  718. s = "established"
  719. default:
  720. s = "unknown"
  721. }
  722. rf.LocalIndex = r.LocalIndex
  723. rf.RemoteIndex = r.RemoteIndex
  724. rf.PeerAddr = r.PeerAddr
  725. rf.Type = t
  726. rf.State = s
  727. if rf.LocalIndex != k {
  728. rf.Error = fmt.Errorf("hostmap LocalIndex '%v' does not match RelayState LocalIndex", k)
  729. }
  730. }
  731. relayedHI := ifce.hostMap.QueryVpnAddr(vpnAddr)
  732. if relayedHI != nil {
  733. rf.RelayedThrough = append(rf.RelayedThrough, relayedHI.relayState.CopyRelayIps()...)
  734. }
  735. ro.RelayForAddrs = append(ro.RelayForAddrs, rf)
  736. }
  737. }
  738. err := enc.Encode(co)
  739. if err != nil {
  740. return err
  741. }
  742. return nil
  743. }
  744. func sshPrintTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
  745. args, ok := fs.(*sshPrintTunnelFlags)
  746. if !ok {
  747. return nil
  748. }
  749. if len(a) == 0 {
  750. return w.WriteLine("No vpn address was provided")
  751. }
  752. vpnAddr, err := netip.ParseAddr(a[0])
  753. if err != nil {
  754. return w.WriteLine(fmt.Sprintf("The provided vpn addr could not be parsed: %s", a[0]))
  755. }
  756. if !vpnAddr.IsValid() {
  757. return w.WriteLine(fmt.Sprintf("The provided vpn addr could not be parsed: %s", a[0]))
  758. }
  759. hostInfo := ifce.hostMap.QueryVpnAddr(vpnAddr)
  760. if hostInfo == nil {
  761. return w.WriteLine(fmt.Sprintf("Could not find tunnel for vpn addr: %v", a[0]))
  762. }
  763. enc := json.NewEncoder(w.GetWriter())
  764. if args.Pretty {
  765. enc.SetIndent("", " ")
  766. }
  767. return enc.Encode(copyHostInfo(hostInfo, ifce.hostMap.GetPreferredRanges()))
  768. }
  769. func sshDeviceInfo(ifce *Interface, fs any, w sshd.StringWriter) error {
  770. data := struct {
  771. Name string `json:"name"`
  772. Cidr []netip.Prefix `json:"cidr"`
  773. }{
  774. Name: ifce.inside.Name(),
  775. Cidr: make([]netip.Prefix, len(ifce.inside.Networks())),
  776. }
  777. copy(data.Cidr, ifce.inside.Networks())
  778. flags, ok := fs.(*sshDeviceInfoFlags)
  779. if !ok {
  780. return fmt.Errorf("internal error: expected flags to be sshDeviceInfoFlags but was %+v", fs)
  781. }
  782. if flags.Json || flags.Pretty {
  783. js := json.NewEncoder(w.GetWriter())
  784. if flags.Pretty {
  785. js.SetIndent("", " ")
  786. }
  787. return js.Encode(data)
  788. } else {
  789. return w.WriteLine(fmt.Sprintf("name=%v cidr=%v", data.Name, data.Cidr))
  790. }
  791. }
  792. // sshReload reloads the config and writes the result to the client. It reloads even if the client has gone away
  793. func sshReload(l *slog.Logger, c *config.C, w sshd.StringWriter) error {
  794. writeErr := w.WriteLine("Reloading config")
  795. res := c.ReloadConfig()
  796. res.Log(l)
  797. if err := w.WriteLine(res.String()); writeErr == nil {
  798. writeErr = err
  799. }
  800. return writeErr
  801. }