Add warning for object storage misconfiguration (#24137)
parent
75e5a6e437
commit
8fdf49b11d
@ -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
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::SystemCheck::Message
|
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
|
@key = key
|
||||||
@value = value
|
@value = value
|
||||||
@action = action
|
@action = action
|
||||||
|
@critical = critical
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in new issue