bouncers.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package main
  2. import (
  3. "encoding/csv"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "strings"
  8. "time"
  9. "github.com/fatih/color"
  10. log "github.com/sirupsen/logrus"
  11. "github.com/spf13/cobra"
  12. "golang.org/x/exp/slices"
  13. middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1"
  14. "github.com/crowdsecurity/crowdsec/pkg/database"
  15. "github.com/crowdsecurity/crowdsec/pkg/types"
  16. )
  17. func getBouncers(out io.Writer, dbClient *database.Client) error {
  18. bouncers, err := dbClient.ListBouncers()
  19. if err != nil {
  20. return fmt.Errorf("unable to list bouncers: %s", err)
  21. }
  22. if csConfig.Cscli.Output == "human" {
  23. getBouncersTable(out, bouncers)
  24. } else if csConfig.Cscli.Output == "json" {
  25. enc := json.NewEncoder(out)
  26. enc.SetIndent("", " ")
  27. if err := enc.Encode(bouncers); err != nil {
  28. return fmt.Errorf("failed to unmarshal: %w", err)
  29. }
  30. return nil
  31. } else if csConfig.Cscli.Output == "raw" {
  32. csvwriter := csv.NewWriter(out)
  33. err := csvwriter.Write([]string{"name", "ip", "revoked", "last_pull", "type", "version", "auth_type"})
  34. if err != nil {
  35. return fmt.Errorf("failed to write raw header: %w", err)
  36. }
  37. for _, b := range bouncers {
  38. var revoked string
  39. if !b.Revoked {
  40. revoked = "validated"
  41. } else {
  42. revoked = "pending"
  43. }
  44. err := csvwriter.Write([]string{b.Name, b.IPAddress, revoked, b.LastPull.Format(time.RFC3339), b.Type, b.Version, b.AuthType})
  45. if err != nil {
  46. return fmt.Errorf("failed to write raw: %w", err)
  47. }
  48. }
  49. csvwriter.Flush()
  50. }
  51. return nil
  52. }
  53. func NewBouncersListCmd() *cobra.Command {
  54. cmdBouncersList := &cobra.Command{
  55. Use: "list",
  56. Short: "List bouncers",
  57. Long: `List bouncers`,
  58. Example: `cscli bouncers list`,
  59. Args: cobra.ExactArgs(0),
  60. DisableAutoGenTag: true,
  61. RunE: func(cmd *cobra.Command, arg []string) error {
  62. err := getBouncers(color.Output, dbClient)
  63. if err != nil {
  64. return fmt.Errorf("unable to list bouncers: %s", err)
  65. }
  66. return nil
  67. },
  68. }
  69. return cmdBouncersList
  70. }
  71. func runBouncersAdd(cmd *cobra.Command, args []string) error {
  72. flags := cmd.Flags()
  73. keyLength, err := flags.GetInt("length")
  74. if err != nil {
  75. return err
  76. }
  77. key, err := flags.GetString("key")
  78. if err != nil {
  79. return err
  80. }
  81. keyName := args[0]
  82. var apiKey string
  83. if keyName == "" {
  84. return fmt.Errorf("please provide a name for the api key")
  85. }
  86. apiKey = key
  87. if key == "" {
  88. apiKey, err = middlewares.GenerateAPIKey(keyLength)
  89. }
  90. if err != nil {
  91. return fmt.Errorf("unable to generate api key: %s", err)
  92. }
  93. _, err = dbClient.CreateBouncer(keyName, "", middlewares.HashSHA512(apiKey), types.ApiKeyAuthType)
  94. if err != nil {
  95. return fmt.Errorf("unable to create bouncer: %s", err)
  96. }
  97. if csConfig.Cscli.Output == "human" {
  98. fmt.Printf("API key for '%s':\n\n", keyName)
  99. fmt.Printf(" %s\n\n", apiKey)
  100. fmt.Print("Please keep this key since you will not be able to retrieve it!\n")
  101. } else if csConfig.Cscli.Output == "raw" {
  102. fmt.Printf("%s", apiKey)
  103. } else if csConfig.Cscli.Output == "json" {
  104. j, err := json.Marshal(apiKey)
  105. if err != nil {
  106. return fmt.Errorf("unable to marshal api key")
  107. }
  108. fmt.Printf("%s", string(j))
  109. }
  110. return nil
  111. }
  112. func NewBouncersAddCmd() *cobra.Command {
  113. cmdBouncersAdd := &cobra.Command{
  114. Use: "add MyBouncerName [--length 16]",
  115. Short: "add bouncer",
  116. Long: `add bouncer`,
  117. Example: `cscli bouncers add MyBouncerName
  118. cscli bouncers add MyBouncerName -l 24
  119. cscli bouncers add MyBouncerName -k <random-key>`,
  120. Args: cobra.ExactArgs(1),
  121. DisableAutoGenTag: true,
  122. RunE: runBouncersAdd,
  123. }
  124. flags := cmdBouncersAdd.Flags()
  125. flags.IntP("length", "l", 16, "length of the api key")
  126. flags.StringP("key", "k", "", "api key for the bouncer")
  127. return cmdBouncersAdd
  128. }
  129. func runBouncersDelete(cmd *cobra.Command, args []string) error {
  130. for _, bouncerID := range args {
  131. err := dbClient.DeleteBouncer(bouncerID)
  132. if err != nil {
  133. return fmt.Errorf("unable to delete bouncer '%s': %s", bouncerID, err)
  134. }
  135. log.Infof("bouncer '%s' deleted successfully", bouncerID)
  136. }
  137. return nil
  138. }
  139. func NewBouncersDeleteCmd() *cobra.Command {
  140. cmdBouncersDelete := &cobra.Command{
  141. Use: "delete MyBouncerName",
  142. Short: "delete bouncer",
  143. Args: cobra.MinimumNArgs(1),
  144. Aliases: []string{"remove"},
  145. DisableAutoGenTag: true,
  146. ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
  147. var err error
  148. dbClient, err = getDBClient()
  149. if err != nil {
  150. cobra.CompError("unable to create new database client: " + err.Error())
  151. return nil, cobra.ShellCompDirectiveNoFileComp
  152. }
  153. bouncers, err := dbClient.ListBouncers()
  154. if err != nil {
  155. cobra.CompError("unable to list bouncers " + err.Error())
  156. }
  157. ret := make([]string, 0)
  158. for _, bouncer := range bouncers {
  159. if strings.Contains(bouncer.Name, toComplete) && !slices.Contains(args, bouncer.Name) {
  160. ret = append(ret, bouncer.Name)
  161. }
  162. }
  163. return ret, cobra.ShellCompDirectiveNoFileComp
  164. },
  165. RunE: runBouncersDelete,
  166. }
  167. return cmdBouncersDelete
  168. }
  169. func NewBouncersCmd() *cobra.Command {
  170. var cmdBouncers = &cobra.Command{
  171. Use: "bouncers [action]",
  172. Short: "Manage bouncers [requires local API]",
  173. Long: `To list/add/delete bouncers.
  174. Note: This command requires database direct access, so is intended to be run on Local API/master.
  175. `,
  176. Args: cobra.MinimumNArgs(1),
  177. Aliases: []string{"bouncer"},
  178. DisableAutoGenTag: true,
  179. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  180. var err error
  181. if err := csConfig.LoadAPIServer(); err != nil || csConfig.DisableAPI {
  182. return fmt.Errorf("local API is disabled, please run this command on the local API machine")
  183. }
  184. dbClient, err = database.NewClient(csConfig.DbConfig)
  185. if err != nil {
  186. return fmt.Errorf("unable to create new database client: %s", err)
  187. }
  188. return nil
  189. },
  190. }
  191. cmdBouncers.AddCommand(NewBouncersListCmd())
  192. cmdBouncers.AddCommand(NewBouncersAddCmd())
  193. cmdBouncers.AddCommand(NewBouncersDeleteCmd())
  194. return cmdBouncers
  195. }