namespace :deploy do
desc 'Secure app with file permissions' task :secure_permissions do on roles(:app) do |server| web_user = fetch(:web_user) app_user = fetch(:app_user) deploy_user = server.user execute :setfacl, '-m', "u:#{web_user}:x", release_path # This is before symlinking, so we can do this recursively. execute :setfacl, '-R', '-m', "u:#{web_user}:rx,d:u:#{web_user}:rx", release_path.join('public') execute :setfacl, '-R', '-m', "u:#{app_user}:rx,d:u:#{app_user}:rx", release_path end end before 'deploy:symlink:shared', 'deploy:secure_permissions'
end
namespace :secure_permissions do
task :validate do on roles(:app) do if fetch(:app_user).nil? || fetch(:app_user).empty? error "secure_permissions: app_user is not set" exit 1 end end end desc 'Sets permissions on the shared folders, only needs to be done once, not on every deploy. And there might be a lot of files, so it might take a while.' task :setup do on roles(:app) do |server| web_user = fetch(:web_user) app_user = fetch(:app_user) deploy_user = server.user # Public is writable by app_user by default, so exclude that one. # To avoid going through the files twice. writable_dirs = fetch(:writable_dirs, fetch(:linked_dirs)). reject { |dir| dir.start_with?('public/') } # All of shared readable by app_user. # Try to subtract the writable_dirs, to avoid going through them twice. # But won't subtract public/system for instance... :( readable_dirs = within(shared_path) { capture(:ls, '-A').lines.map(&:chomp) } - writable_dirs execute :setfacl, '-m', "u:#{web_user}:x,d:u:#{web_user}:x,u:#{app_user}:rx,d:u:#{app_user}:rx", shared_path within shared_path do execute :setfacl, '-R', '-m', "u:#{app_user}:rx,d:u:#{app_user}:rx", *readable_dirs # Set permissions for files in public, readable by web_user and writable by app_user. # Also make sure that deploy_user retains access, to the files that app_user creates. execute :setfacl, '-R', '-m', "u:#{web_user}:rx,u:#{app_user}:rwx,u:#{deploy_user}:rwx,d:u:#{deploy_user}:rwx,d:u:#{web_user}:rx,d:u:#{app_user}:rwx", 'public' # Allow app_user access to writable_dirs in shared # Also make sure that deploy_user retains access, to the files that app_user creates. execute :setfacl, '-R', '-m', "u:#{app_user}:rwx,d:u:#{app_user}:rwx", *writable_dirs end end end
end
Capistrano::DSL.stages.each do |stage|
after stage, 'secure_permissions:validate'
end
namespace :load do
task :defaults do set :web_user, 'www-data' end
end