scan.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package scan
  2. import (
  3. "encoding/csv"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "os"
  8. "sync"
  9. "github.com/cloudflare/cfssl/cli"
  10. "github.com/cloudflare/cfssl/log"
  11. "github.com/cloudflare/cfssl/scan"
  12. )
  13. var scanUsageText = `cfssl scan -- scan a host for issues
  14. Usage of scan:
  15. cfssl scan [-family regexp] [-scanner regexp] [-timeout duration] [-ip IPAddr] [-num-workers num] [-max-hosts num] [-csv hosts.csv] HOST+
  16. cfssl scan -list
  17. Arguments:
  18. HOST: Host(s) to scan (including port)
  19. Flags:
  20. `
  21. var scanFlags = []string{"list", "family", "scanner", "timeout", "ip", "ca-bundle", "num-workers", "csv", "max-hosts"}
  22. func printJSON(v interface{}) {
  23. b, err := json.MarshalIndent(v, "", " ")
  24. if err != nil {
  25. fmt.Println(err)
  26. }
  27. fmt.Printf("%s\n\n", b)
  28. }
  29. type context struct {
  30. sync.WaitGroup
  31. c cli.Config
  32. hosts chan string
  33. }
  34. func newContext(c cli.Config, numWorkers int) *context {
  35. ctx := &context{
  36. c: c,
  37. hosts: make(chan string, numWorkers),
  38. }
  39. ctx.Add(numWorkers)
  40. for i := 0; i < numWorkers; i++ {
  41. go ctx.runWorker()
  42. }
  43. return ctx
  44. }
  45. func (ctx *context) runWorker() {
  46. for host := range ctx.hosts {
  47. fmt.Printf("Scanning %s...\n", host)
  48. results, err := scan.Default.RunScans(host, ctx.c.IP, ctx.c.Family, ctx.c.Scanner, ctx.c.Timeout)
  49. fmt.Printf("=== %s ===\n", host)
  50. if err != nil {
  51. log.Error(err)
  52. } else {
  53. printJSON(results)
  54. }
  55. }
  56. ctx.Done()
  57. }
  58. func parseCSV(hosts []string, csvFile string, maxHosts int) ([]string, error) {
  59. f, err := os.Open(csvFile)
  60. if err != nil {
  61. return nil, err
  62. }
  63. defer f.Close()
  64. r := csv.NewReader(f)
  65. for err == nil && len(hosts) < maxHosts {
  66. var record []string
  67. record, err = r.Read()
  68. hosts = append(hosts, record[len(record)-1])
  69. }
  70. if err == io.EOF {
  71. err = nil
  72. }
  73. return hosts, err
  74. }
  75. func scanMain(args []string, c cli.Config) (err error) {
  76. if c.List {
  77. printJSON(scan.Default)
  78. } else {
  79. if err = scan.LoadRootCAs(c.CABundleFile); err != nil {
  80. return
  81. }
  82. if len(args) >= c.MaxHosts {
  83. log.Warningf("Only scanning max-hosts=%d out of %d args given", c.MaxHosts, len(args))
  84. args = args[:c.MaxHosts]
  85. } else if c.CSVFile != "" {
  86. args, err = parseCSV(args, c.CSVFile, c.MaxHosts)
  87. if err != nil {
  88. return
  89. }
  90. }
  91. ctx := newContext(c, c.NumWorkers)
  92. // Execute for each HOST argument given
  93. for len(args) > 0 {
  94. var host string
  95. host, args, err = cli.PopFirstArgument(args)
  96. if err != nil {
  97. return
  98. }
  99. ctx.hosts <- host
  100. }
  101. close(ctx.hosts)
  102. ctx.Wait()
  103. }
  104. return
  105. }
  106. // Command assembles the definition of Command 'scan'
  107. var Command = &cli.Command{UsageText: scanUsageText, Flags: scanFlags, Main: scanMain}