You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
3.9 KiB

  1. # frozen_string_literal: true
  2. require 'rubygems/package'
  3. require_relative '../../config/boot'
  4. require_relative '../../config/environment'
  5. require_relative 'cli_helper'
  6. module Mastodon
  7. class IpBlocksCLI < Thor
  8. def self.exit_on_failure?
  9. true
  10. end
  11. option :severity, required: true, enum: %w(no_access sign_up_requires_approval), desc: 'Severity of the block'
  12. option :comment, aliases: [:c], desc: 'Optional comment'
  13. option :duration, aliases: [:d], type: :numeric, desc: 'Duration of the block in seconds'
  14. option :force, type: :boolean, aliases: [:f], desc: 'Overwrite existing blocks'
  15. desc 'add IP...', 'Add one or more IP blocks'
  16. long_desc <<-LONG_DESC
  17. Add one or more IP blocks. You can use CIDR syntax to
  18. block IP ranges. You must specify --severity of the block. All
  19. options will be copied for each IP block you create in one command.
  20. You can add a --comment. If an IP block already exists for one of
  21. the provided IPs, it will be skipped unless you use the --force
  22. option to overwrite it.
  23. LONG_DESC
  24. def add(*addresses)
  25. if addresses.empty?
  26. say('No IP(s) given', :red)
  27. exit(1)
  28. end
  29. skipped = 0
  30. processed = 0
  31. failed = 0
  32. addresses.each do |address|
  33. ip_block = IpBlock.find_by(ip: address)
  34. if ip_block.present? && !options[:force]
  35. say("#{address} is already blocked", :yellow)
  36. skipped += 1
  37. next
  38. end
  39. ip_block ||= IpBlock.new(ip: address)
  40. ip_block.severity = options[:severity]
  41. ip_block.comment = options[:comment] if options[:comment].present?
  42. ip_block.expires_in = options[:duration]
  43. if ip_block.save
  44. processed += 1
  45. else
  46. say("#{address} could not be saved", :red)
  47. failed += 1
  48. end
  49. end
  50. say("Added #{processed}, skipped #{skipped}, failed #{failed}", color(processed, failed))
  51. end
  52. option :force, type: :boolean, aliases: [:f], desc: 'Remove blocks for ranges that cover given IP(s)'
  53. desc 'remove IP...', 'Remove one or more IP blocks'
  54. long_desc <<-LONG_DESC
  55. Remove one or more IP blocks. Normally, only exact matches are removed. If
  56. you want to ensure that all of the given IP addresses are unblocked, you
  57. can use --force which will also remove any blocks for IP ranges that would
  58. cover the given IP(s).
  59. LONG_DESC
  60. def remove(*addresses)
  61. if addresses.empty?
  62. say('No IP(s) given', :red)
  63. exit(1)
  64. end
  65. processed = 0
  66. skipped = 0
  67. addresses.each do |address|
  68. ip_blocks = if options[:force]
  69. IpBlock.where('ip >>= ?', address)
  70. else
  71. IpBlock.where('ip <<= ?', address)
  72. end
  73. if ip_blocks.empty?
  74. say("#{address} is not yet blocked", :yellow)
  75. skipped += 1
  76. next
  77. end
  78. ip_blocks.in_batches.destroy_all
  79. processed += 1
  80. end
  81. say("Removed #{processed}, skipped #{skipped}", color(processed, 0))
  82. end
  83. option :format, aliases: [:f], enum: %w(plain nginx), desc: 'Format of the output'
  84. desc 'export', 'Export blocked IPs'
  85. long_desc <<-LONG_DESC
  86. Export blocked IPs. Different formats are supported for usage with other
  87. tools. Only blocks with no_access severity are returned.
  88. LONG_DESC
  89. def export
  90. IpBlock.where(severity: :no_access).find_each do |ip_block|
  91. case options[:format]
  92. when 'nginx'
  93. puts "deny #{ip_block.ip}/#{ip_block.ip.prefix};"
  94. else
  95. puts "#{ip_block.ip}/#{ip_block.ip.prefix}"
  96. end
  97. end
  98. end
  99. private
  100. def color(processed, failed)
  101. if !processed.zero? && failed.zero?
  102. :green
  103. elsif failed.zero?
  104. :yellow
  105. else
  106. :red
  107. end
  108. end
  109. end
  110. end