unicorn-lockdown 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +9 -0
- data/bin/unicorn-lockdown-add +217 -0
- data/bin/unicorn-lockdown-setup +68 -0
- data/lib/unicorn-lockdown.rb +10 -16
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95a450be80782d592a0b960b2855f27332371039ede4ce5459df32a273985240
|
4
|
+
data.tar.gz: 9fb35bc3da1921fb3562bc4f1aa35962e08e628da61c33e14e9beac0b51696e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc4ca6d927ab239a144cc3b3f4f405c03d0280f2e6a1e148f32a8ac416d48eb7ebc898f7b7a7ff09b67863ea0676d0b2b8660e212347fe4466691c000eca591a
|
7
|
+
data.tar.gz: 3240933f222409c814dac82049c055cfd183c275e1a998cf2aa10f539d36a1c9dbec5abef32b35f8c5729dc32d9cc87e506e9d4bf6b2a602e026314dc7e3bd70
|
data/CHANGELOG
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
def sh(*args)
|
7
|
+
puts "Running: #{args.join(' ')}"
|
8
|
+
system(*args) || raise("Error while running: #{args.join(' ')}")
|
9
|
+
end
|
10
|
+
|
11
|
+
unicorn = ''
|
12
|
+
rackup = ''
|
13
|
+
unicorn_file = 'unicorn.conf'
|
14
|
+
dir = nil
|
15
|
+
user = nil
|
16
|
+
new_user_uid = nil
|
17
|
+
owner = nil
|
18
|
+
owner_uid = nil
|
19
|
+
owner_gid = nil
|
20
|
+
|
21
|
+
options = OptionParser.new do |opts|
|
22
|
+
opts.banner = "Usage: unicorn-lockdown-add [options] app_name"
|
23
|
+
opts.separator "Options:"
|
24
|
+
|
25
|
+
opts.on_tail("-h", "-?", "--help", "Show this message") do
|
26
|
+
puts opts
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on("-c rackup_file", "rackup configuration file") do |v|
|
31
|
+
rackup = "rackup_file=#{v}\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-d dir", "application directory name") do |v|
|
35
|
+
dir = v
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-f unicorn_file", "unicorn configuration file relative to application directory") do |v|
|
39
|
+
unicorn_file = v
|
40
|
+
unicorn = "unicorn_conf=#{v}\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-o owner", "operating system application owner") do |v|
|
44
|
+
owner = v
|
45
|
+
ent = Etc.getpwnam(v)
|
46
|
+
owner_uid = ent.uid
|
47
|
+
owner_gid = ent.gid
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on("-u user", "operating system user to run application") do |v|
|
51
|
+
user = v
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on("--uid uid", "user id to use if creating the user when -U is specified") do |v|
|
55
|
+
new_user_uid = Integer(v, 10)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
options.parse!
|
59
|
+
|
60
|
+
app = ARGV.shift
|
61
|
+
dir ||= app
|
62
|
+
base_dir = dir
|
63
|
+
|
64
|
+
root_id = 0
|
65
|
+
bin_id = 7
|
66
|
+
www_id = 67
|
67
|
+
|
68
|
+
www_root = '/var/www'
|
69
|
+
dir = "#{www_root}/#{dir}"
|
70
|
+
etc_dir = "#{dir}/etc"
|
71
|
+
hosts_file = "#{etc_dir}/hosts"
|
72
|
+
resolv_file = "#{etc_dir}/resolv.conf"
|
73
|
+
rc_file = "/etc/rc.d/unicorn_#{app.tr('-', '_')}"
|
74
|
+
nginx_file = "/etc/nginx/#{app}.conf"
|
75
|
+
unicorn_conf_file = "#{dir}/#{unicorn_file}"
|
76
|
+
unicorn_log_file = "/var/log/unicorn/#{app}.log"
|
77
|
+
nginx_access_log_file = "/var/log/nginx/#{app}.access.log"
|
78
|
+
nginx_error_log_file = "/var/log/nginx/#{app}.error.log"
|
79
|
+
|
80
|
+
# Add application user if it doesn't exist
|
81
|
+
if user
|
82
|
+
passwd = begin
|
83
|
+
Etc.getpwnam(user)
|
84
|
+
rescue ArgumentError
|
85
|
+
args = ['/usr/sbin/useradd', '-d', '/var/empty', '-g', '=uid', '-G', '_unicorn', '-L', 'daemon', '-s', '/sbin/nologin']
|
86
|
+
if new_user_uid
|
87
|
+
args << '-u' << new_user_uid.to_s
|
88
|
+
end
|
89
|
+
args << user
|
90
|
+
sh(*args)
|
91
|
+
Etc.getpwnam(user)
|
92
|
+
end
|
93
|
+
app_uid = passwd.uid
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create application directory and chroot directories if they doesn't exist
|
97
|
+
[dir, "#{dir}/var", "#{dir}/var/www", "#{dir}/etc", "#{dir}/public"].each do |d|
|
98
|
+
unless File.directory?(d)
|
99
|
+
puts "Creating #{d}"
|
100
|
+
Dir.mkdir(d)
|
101
|
+
File.chmod(0755, d)
|
102
|
+
File.chown(owner_uid, owner_gid, d) if owner
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# DRY up file ownership code
|
107
|
+
setup_file_owner = lambda do |file|
|
108
|
+
File.chmod(0644, file)
|
109
|
+
File.chown(owner_uid, owner_gid, file) if owner
|
110
|
+
end
|
111
|
+
|
112
|
+
# Setup symlink to root so that the same paths work both when
|
113
|
+
# chrooted and when not chrooted.
|
114
|
+
chroot_link = "#{dir}#{dir}"
|
115
|
+
unless File.symlink?(chroot_link)
|
116
|
+
puts "Creating #{chroot_link}"
|
117
|
+
File.symlink('/', chroot_link)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Add /etc/hosts files to chroot
|
121
|
+
unless File.file?(hosts_file)
|
122
|
+
puts "Creating #{hosts_file}"
|
123
|
+
File.binwrite(hosts_file, <<END)
|
124
|
+
127.0.0.1 localhost
|
125
|
+
END
|
126
|
+
setup_file_owner.call(hosts_file)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add /etc/resolv.conf files to chroot
|
130
|
+
unless File.file?(resolv_file)
|
131
|
+
puts "Creating #{resolv_file}"
|
132
|
+
File.binwrite(resolv_file, <<END)
|
133
|
+
lookup file
|
134
|
+
END
|
135
|
+
setup_file_owner.call(resolv_file)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Setup unicorn configuration file
|
139
|
+
unless File.file?(unicorn_conf_file)
|
140
|
+
puts "Creating #{unicorn_conf_file}"
|
141
|
+
File.binwrite(unicorn_conf_file, <<END)
|
142
|
+
require 'unicorn-lockdown'
|
143
|
+
|
144
|
+
Unicorn.lockdown(self,
|
145
|
+
:app=>#{app.inspect},
|
146
|
+
:user=>#{user.inspect}, # Set application user here
|
147
|
+
:pledge=>'rpath prot_exec inet unix', # More may be needed
|
148
|
+
:email=>'root' # update this with correct email
|
149
|
+
)
|
150
|
+
END
|
151
|
+
setup_file_owner.call(unicorn_conf_file)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Setup /etc/nginx/* file for nginx configuration
|
155
|
+
unless File.file?(nginx_file)
|
156
|
+
puts "Creating #{nginx_file}"
|
157
|
+
File.binwrite(nginx_file, <<END)
|
158
|
+
upstream #{app}_unicorn {
|
159
|
+
server unix:/sockets/#{app}.sock fail_timeout=0;
|
160
|
+
}
|
161
|
+
server {
|
162
|
+
server_name #{app};
|
163
|
+
access_log #{nginx_access_log_file} main;
|
164
|
+
error_log #{nginx_error_log_file} warn;
|
165
|
+
root #{dir}/public;
|
166
|
+
error_page 500 503 /500.html;
|
167
|
+
error_page 502 504 /502.html;
|
168
|
+
proxy_set_header X-Real-IP $remote_addr;
|
169
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
170
|
+
proxy_set_header Host $http_host;
|
171
|
+
proxy_redirect off;
|
172
|
+
add_header X-Content-Type-Options nosniff;
|
173
|
+
add_header X-Frame-Options deny;
|
174
|
+
add_header X-XSS-Protection "1; mode=block";
|
175
|
+
try_files $uri @#{app}_unicorn;
|
176
|
+
location @#{app}_unicorn {
|
177
|
+
proxy_pass http://#{app}_unicorn;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
END
|
181
|
+
|
182
|
+
setup_file_owner.call(nginx_file)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Setup nginx log file
|
186
|
+
[nginx_access_log_file, nginx_error_log_file].each do |f|
|
187
|
+
unless File.file?(f)
|
188
|
+
puts "Creating #{f}"
|
189
|
+
File.binwrite(f, '')
|
190
|
+
File.chmod(0644, f)
|
191
|
+
File.chown(www_id, root_id, f)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Setup unicorn log file
|
196
|
+
unless File.file?(unicorn_log_file)
|
197
|
+
puts "Creating #{unicorn_log_file}"
|
198
|
+
File.binwrite(unicorn_log_file, '')
|
199
|
+
File.chmod(0640, unicorn_log_file)
|
200
|
+
File.chown(app_uid, app_uid, unicorn_log_file) if app_uid
|
201
|
+
end
|
202
|
+
|
203
|
+
# Setup /etc/rc.d/unicorn_* file for daemon management
|
204
|
+
unless File.file?(rc_file)
|
205
|
+
puts "Creating #{rc_file}"
|
206
|
+
File.binwrite(rc_file, <<END)
|
207
|
+
#!/bin/ksh
|
208
|
+
|
209
|
+
unicorn_app=#{app}
|
210
|
+
unicorn_dir=#{dir}
|
211
|
+
#{unicorn}#{rackup}
|
212
|
+
. /etc/rc.d/rc.unicorn
|
213
|
+
END
|
214
|
+
|
215
|
+
File.chmod(0755, rc_file)
|
216
|
+
File.chown(root_id, bin_id, rc_file)
|
217
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
|
5
|
+
def sh(*args)
|
6
|
+
puts "Running: #{args.join(' ')}"
|
7
|
+
system(*args) || raise("Error while running: #{args.join(' ')}")
|
8
|
+
end
|
9
|
+
|
10
|
+
request_dir = '/var/www/requests'
|
11
|
+
socket_dir = '/var/www/sockets'
|
12
|
+
unicorn_log_dir = '/var/log/unicorn'
|
13
|
+
nginx_log_dir = "/var/log/nginx"
|
14
|
+
rc_unicorn_file = '/etc/rc.d/rc.unicorn'
|
15
|
+
|
16
|
+
unicorn_group = '_unicorn'
|
17
|
+
root_id = 0
|
18
|
+
daemon_id = 1
|
19
|
+
www_id = 67
|
20
|
+
|
21
|
+
# Add _unicorn group if it doesn't exist
|
22
|
+
group = begin
|
23
|
+
Etc.getgrnam(unicorn_group)
|
24
|
+
rescue ArgumentError
|
25
|
+
sh('groupadd', unicorn_group)
|
26
|
+
Etc.getgrnam(unicorn_group)
|
27
|
+
end
|
28
|
+
unicorn_group_id = group.gid
|
29
|
+
|
30
|
+
# Setup requests directory to hold per-request information for crash notifications
|
31
|
+
unless File.directory?(request_dir)
|
32
|
+
puts "Creating #{request_dir}"
|
33
|
+
Dir.mkdir(request_dir)
|
34
|
+
File.chmod(0700, request_dir)
|
35
|
+
File.chown(root_id, daemon_id, request_dir)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Setup sockets directory to hold PostgreSQL database connection sockets
|
39
|
+
unless File.directory?(socket_dir)
|
40
|
+
puts "Creating #{socket_dir}"
|
41
|
+
Dir.mkdir(socket_dir)
|
42
|
+
File.chmod(0770, socket_dir)
|
43
|
+
File.chown(www_id, unicorn_group_id, socket_dir)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Setup log directory to hold unicorn application logs
|
47
|
+
unless File.directory?(unicorn_log_dir)
|
48
|
+
puts "Creating #{unicorn_log_dir}"
|
49
|
+
Dir.mkdir(unicorn_log_dir)
|
50
|
+
File.chmod(0755, unicorn_log_dir)
|
51
|
+
File.chown(root_id, daemon_id, unicorn_log_dir)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Setup log directory to hold nginx logs
|
55
|
+
unless File.directory?(nginx_log_dir)
|
56
|
+
puts "Creating #{nginx_log_dir}"
|
57
|
+
Dir.mkdir(nginx_log_dir)
|
58
|
+
File.chmod(0775, nginx_log_dir)
|
59
|
+
File.chown(www_id, root_id, nginx_log_dir)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Setup rc.unicorn file
|
63
|
+
unless File.file?(rc_unicorn_file)
|
64
|
+
puts "Creating #{rc_unicorn_file}"
|
65
|
+
File.binwrite(rc_unicorn_file, File.binread(File.join(File.dirname(__dir__), 'files', 'rc.unicorn')))
|
66
|
+
File.chmod(0644, rc_unicorn_file)
|
67
|
+
File.chown(root_id, root_id, rc_unicorn_file)
|
68
|
+
end
|
data/lib/unicorn-lockdown.rb
CHANGED
@@ -133,12 +133,10 @@ class << Unicorn
|
|
133
133
|
# that are probably needed at runtime. This must be done
|
134
134
|
# before chrooting as attempting to load the constants after
|
135
135
|
# chrooting will break things.
|
136
|
-
|
137
|
-
#
|
138
|
-
# of
|
139
|
-
#
|
140
|
-
# applications using new features at runtime that were
|
141
|
-
# not loaded at load time.
|
136
|
+
|
137
|
+
# Start with rack, which uses autoload for all constants.
|
138
|
+
# Most of rack's constants are not used at runtime, this
|
139
|
+
# lists the ones most commonly needed.
|
142
140
|
Rack::Multipart
|
143
141
|
Rack::Multipart::Parser
|
144
142
|
Rack::Multipart::Generator
|
@@ -150,17 +148,13 @@ class << Unicorn
|
|
150
148
|
Rack::Lint
|
151
149
|
Rack::ShowExceptions
|
152
150
|
end
|
151
|
+
|
152
|
+
# If using the mail library, eagerly autoload all constants.
|
153
|
+
# This costs about 9MB of memory, but the mail gem changes
|
154
|
+
# their autoloaded constants on a regular basis, so it's
|
155
|
+
# better to be safe than sorry.
|
153
156
|
if defined?(Mail)
|
154
|
-
Mail
|
155
|
-
Mail::AddressList
|
156
|
-
Mail::Parsers::AddressListsParser
|
157
|
-
Mail::ContentTransferEncodingElement
|
158
|
-
Mail::ContentDispositionElement
|
159
|
-
Mail::MessageIdsElement
|
160
|
-
Mail::MimeVersionElement
|
161
|
-
Mail::OptionalField
|
162
|
-
Mail::ContentTypeElement
|
163
|
-
Mail::SMTP
|
157
|
+
Mail.eager_autoload!
|
164
158
|
end
|
165
159
|
|
166
160
|
# Strip path prefixes from the reloader. This is only
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn-lockdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pledge
|
@@ -40,12 +40,17 @@ dependencies:
|
|
40
40
|
version: '0'
|
41
41
|
description:
|
42
42
|
email: code@jeremyevans.net
|
43
|
-
executables:
|
43
|
+
executables:
|
44
|
+
- unicorn-lockdown-add
|
45
|
+
- unicorn-lockdown-setup
|
44
46
|
extensions: []
|
45
47
|
extra_rdoc_files: []
|
46
48
|
files:
|
49
|
+
- CHANGELOG
|
47
50
|
- MIT-LICENSE
|
48
51
|
- README.rdoc
|
52
|
+
- bin/unicorn-lockdown-add
|
53
|
+
- bin/unicorn-lockdown-setup
|
49
54
|
- files/rc.unicorn
|
50
55
|
- lib/chrooter.rb
|
51
56
|
- lib/rack/email_exceptions.rb
|