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.

132 lines
3.8 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 = begin
  69. if options[:force]
  70. IpBlock.where('ip >>= ?', address)
  71. else
  72. IpBlock.where('ip <<= ?', address)
  73. end
  74. end
  75. if ip_blocks.empty?
  76. say("#{address} is not yet blocked", :yellow)
  77. skipped += 1
  78. next
  79. end
  80. ip_blocks.in_batches.destroy_all
  81. processed += 1
  82. end
  83. say("Removed #{processed}, skipped #{skipped}", color(processed, 0))
  84. end
  85. option :format, aliases: [:f], enum: %w(plain nginx), desc: 'Format of the output'
  86. desc 'export', 'Export blocked IPs'
  87. long_desc <<-LONG_DESC
  88. Export blocked IPs. Different formats are supported for usage with other
  89. tools. Only blocks with no_access severity are returned.
  90. LONG_DESC
  91. def export
  92. IpBlock.where(severity: :no_access).find_each do |ip_block|
  93. case options[:format]
  94. when 'nginx'
  95. puts "deny #{ip_block.ip}/#{ip_block.ip.prefix};"
  96. else
  97. puts "#{ip_block.ip}/#{ip_block.ip.prefix}"
  98. end
  99. end
  100. end
  101. private
  102. def color(processed, failed)
  103. if !processed.zero? && failed.zero?
  104. :green
  105. elsif failed.zero?
  106. :yellow
  107. else
  108. :red
  109. end
  110. end
  111. end
  112. end