Add warning for object storage misconfiguration (#24137)
This commit is contained in:
		
							parent
							
								
									6db76875fd
								
							
						
					
					
						commit
						6a7b91a038
					
				@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
class Admin::SystemCheck
 | 
			
		||||
  ACTIVE_CHECKS = [
 | 
			
		||||
    Admin::SystemCheck::MediaPrivacyCheck,
 | 
			
		||||
    Admin::SystemCheck::DatabaseSchemaCheck,
 | 
			
		||||
    Admin::SystemCheck::SidekiqProcessCheck,
 | 
			
		||||
    Admin::SystemCheck::RulesCheck,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										105
									
								
								app/lib/admin/system_check/media_privacy_check.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								app/lib/admin/system_check/media_privacy_check.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Admin::SystemCheck::MediaPrivacyCheck < Admin::SystemCheck::BaseCheck
 | 
			
		||||
  include RoutingHelper
 | 
			
		||||
 | 
			
		||||
  def skip?
 | 
			
		||||
    !current_user.can?(:view_devops)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pass?
 | 
			
		||||
    check_media_uploads!
 | 
			
		||||
    @failure_message.nil?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def message
 | 
			
		||||
    Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, true)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def check_media_uploads!
 | 
			
		||||
    if Rails.configuration.x.use_s3
 | 
			
		||||
      check_media_listing_inaccessible_s3!
 | 
			
		||||
    else
 | 
			
		||||
      check_media_listing_inaccessible!
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_media_listing_inaccessible!
 | 
			
		||||
    full_url = full_asset_url(media_attachment.file.url(:original, false))
 | 
			
		||||
 | 
			
		||||
    # Check if we can list the uploaded file. If true, that's an error
 | 
			
		||||
    directory_url = Addressable::URI.parse(full_url)
 | 
			
		||||
    directory_url.query = nil
 | 
			
		||||
    filename = directory_url.path.gsub(%r{.*/}, '')
 | 
			
		||||
    directory_url.path = directory_url.path.gsub(%r{/[^/]+\Z}, '/')
 | 
			
		||||
    Request.new(:get, directory_url, allow_local: true).perform do |res|
 | 
			
		||||
      if res.truncated_body&.include?(filename)
 | 
			
		||||
        @failure_message = use_storage? ? :upload_check_privacy_error_object_storage : :upload_check_privacy_error
 | 
			
		||||
        @failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#FS'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  rescue
 | 
			
		||||
    nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_media_listing_inaccessible_s3!
 | 
			
		||||
    urls_to_check = []
 | 
			
		||||
    paperclip_options = Paperclip::Attachment.default_options
 | 
			
		||||
    s3_protocol = paperclip_options[:s3_protocol]
 | 
			
		||||
    s3_host_alias = paperclip_options[:s3_host_alias]
 | 
			
		||||
    s3_host_name  = paperclip_options[:s3_host_name]
 | 
			
		||||
    bucket_name = paperclip_options.dig(:s3_credentials, :bucket)
 | 
			
		||||
 | 
			
		||||
    urls_to_check << "#{s3_protocol}://#{s3_host_alias}/" if s3_host_alias.present?
 | 
			
		||||
    urls_to_check << "#{s3_protocol}://#{s3_host_name}/#{bucket_name}/"
 | 
			
		||||
    urls_to_check.uniq.each do |full_url|
 | 
			
		||||
      check_s3_listing!(full_url)
 | 
			
		||||
      break if @failure_message.present?
 | 
			
		||||
    end
 | 
			
		||||
  rescue
 | 
			
		||||
    nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_s3_listing!(full_url)
 | 
			
		||||
    bucket_url = Addressable::URI.parse(full_url)
 | 
			
		||||
    bucket_url.path = bucket_url.path.delete_suffix(media_attachment.file.path(:original))
 | 
			
		||||
    bucket_url.query = "max-keys=1&x-random=#{SecureRandom.hex(10)}"
 | 
			
		||||
    Request.new(:get, bucket_url, allow_local: true).perform do |res|
 | 
			
		||||
      if res.truncated_body&.include?('ListBucketResult')
 | 
			
		||||
        @failure_message = :upload_check_privacy_error_object_storage
 | 
			
		||||
        @failure_action  = 'https://docs.joinmastodon.org/admin/optional/object-storage/#S3'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def media_attachment
 | 
			
		||||
    @media_attachment ||= begin
 | 
			
		||||
      attachment = Account.representative.media_attachments.first
 | 
			
		||||
      if attachment.present?
 | 
			
		||||
        attachment.touch # rubocop:disable Rails/SkipsModelValidations
 | 
			
		||||
        attachment
 | 
			
		||||
      else
 | 
			
		||||
        create_test_attachment!
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create_test_attachment!
 | 
			
		||||
    Tempfile.create(%w(test-upload .jpg), binmode: true) do |tmp_file|
 | 
			
		||||
      tmp_file.write(
 | 
			
		||||
        Base64.decode64(
 | 
			
		||||
          '/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' \
 | 
			
		||||
          'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' \
 | 
			
		||||
          'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' \
 | 
			
		||||
          'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' \
 | 
			
		||||
          'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' \
 | 
			
		||||
          'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q=='
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
      tmp_file.flush
 | 
			
		||||
      Account.representative.media_attachments.create!(file: tmp_file)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Admin::SystemCheck::Message
 | 
			
		||||
  attr_reader :key, :value, :action
 | 
			
		||||
  attr_reader :key, :value, :action, :critical
 | 
			
		||||
 | 
			
		||||
  def initialize(key, value = nil, action = nil)
 | 
			
		||||
  def initialize(key, value = nil, action = nil, critical = false)
 | 
			
		||||
    @key      = key
 | 
			
		||||
    @value    = value
 | 
			
		||||
    @action   = action
 | 
			
		||||
    @critical = critical
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
- unless @system_checks.empty?
 | 
			
		||||
  .flash-message-stack
 | 
			
		||||
    - @system_checks.each do |message|
 | 
			
		||||
      .flash-message.warning
 | 
			
		||||
      .flash-message{ class: message.critical ? 'alert' : 'warning' }
 | 
			
		||||
        = t("admin.system_checks.#{message.key}.message_html", value: message.value ? content_tag(:strong, message.value) : nil)
 | 
			
		||||
        - if message.action
 | 
			
		||||
          = link_to t("admin.system_checks.#{message.key}.action"), message.action
 | 
			
		||||
 | 
			
		||||
@ -805,6 +805,12 @@ en:
 | 
			
		||||
        message_html: You haven't defined any server rules.
 | 
			
		||||
      sidekiq_process_check:
 | 
			
		||||
        message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration
 | 
			
		||||
      upload_check_privacy_error:
 | 
			
		||||
        action: Check here for more information
 | 
			
		||||
        message_html: "<strong>Your web server is misconfigured. The privacy of your users is at risk.</strong>"
 | 
			
		||||
      upload_check_privacy_error_object_storage:
 | 
			
		||||
        action: Check here for more information
 | 
			
		||||
        message_html: "<strong>Your object storage is misconfigured. The privacy of your users is at risk.</strong>"
 | 
			
		||||
    tags:
 | 
			
		||||
      review: Review status
 | 
			
		||||
      updated_msg: Hashtag settings updated successfully
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user