Skip to content

Instantly share code, notes, and snippets.

@dobryakov
Last active September 30, 2016 12:08
Show Gist options
  • Save dobryakov/b5147dcaa2e26fd92e782d928b98980a to your computer and use it in GitHub Desktop.
Save dobryakov/b5147dcaa2e26fd92e782d928b98980a to your computer and use it in GitHub Desktop.
Hide AWS S3 endpoint of your static content (Rails+Nginx example)
server {
valid_referers none blocked frontend.yourdomain.com *.frontend.yourdomain.com;
if ($invalid_referer) {
return 403;
}
map $http_x_project_private $prevent_hack {
default "1";
'SecretHeader' "0";
}
location ~* secret_store {
if ($prevent_hack) {
# prevent direct requesting for secret_store location
# (the requester should pass 'X-Project-Private' header with secret volume)
return 405;
}
proxy_set_header User-Agent S3_SECRET_UA;
rewrite ^(.*)$ $arg_path;
# make sure to use correct S3 host!
proxy_pass https://s3-eu-west-1.amazonaws.com;
break;
}
location / {
# usual request processing for Rails
}
}
before_action :set_model, only: [:show, :edit, :update, :destroy]
# assumed @model has_attached_file :source
def show
if @model && ( params[:original_filename] == @model.source.original_filename ) # some simple checking
if @type.to_s.empty? || @type.to_s == 'source'
s = @model.source.path
else
s = @model.source.path(@type)
end
# DO NOT TRY TO SET AWS HOST HERE! IT IS UNSAFE! (The host is hardcoded at nginx location, and it is right)
p = "/secret_store/?path=" + URI::escape("/#{ENV['S3_BUCKET']}#{s}")
response.headers['X-Accel-Redirect'] = p
head :ok
else
render :nothing => true, status: 404
end
end
private
def set_model
@model = YourModel.find(params[:id])
@type = params[:type]
end
server {
location ~* ^/p/ {
proxy_set_header 'X-Project-Private' 'SecretHeader';
proxy_pass http://backend.yourdomain.com;
break;
}
}
has_attached_file :source,
# some options for paperclip and aws-sdk
def p(type = 'source')
if ENV['S3_HIDE_URLS'].to_s == '1'
"/p/#{type.to_s}/#{self.id}/#{self.source.original_filename}"
elsif type.to_s == 'source'
self.source.url
else
self.source.url(type)
end
end
get 'p/:type/:id/:original_filename', to: 'controller#show', constraints: { original_filename: /.+/ }
{
"Id": "Policy...",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt...",
"Action": [
"s3:GetObject"
],
"Effect": "Deny",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*",
"Condition": {
"StringNotLike": {
"aws:UserAgent": "S3_SECRET_UA"
}
},
"Principal": "*"
}
]
}
  1. Setup your policy in S3 bucket properties (use policy generator!).
  2. Upload a sample image "cat.jpg" to your system.
  3. GET something like "/p/thumb/123/cat.jpg" from your browser.
  4. Use @model.p(:thumb) helper in your views or API templates.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment