potluck-nginx 0.0.3 → 0.0.4
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/lib/potluck/nginx/ssl.rb +25 -3
- data/lib/potluck/nginx/util.rb +28 -0
- data/lib/potluck/nginx.rb +143 -11
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f489f7ab1e64d5447a26b87a96bfb1ceb50174cce9a25d9131e64ee1a06a7f66
|
4
|
+
data.tar.gz: 7f7518e1835ef8d454b2925bded018cc189fde565dedae2f8b8274f105531040
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8cd92c4fdbd976b2330a433ff6cacac50ee143c2b90dd91776af91350b4540b304fc89f3ad0456acb108ad4f29affe37e58699c8ba122a3652eca86306f495c
|
7
|
+
data.tar.gz: 0f366d7c77baef42dae840b4e410c596623297bede06a7daa2c15c52d079b789118e89c8f7319d91012a74766e9baa033b416b8f56603a61ddd7200ff4de6d8e
|
data/lib/potluck/nginx/ssl.rb
CHANGED
@@ -3,7 +3,11 @@
|
|
3
3
|
require('time')
|
4
4
|
|
5
5
|
module Potluck
|
6
|
-
class Nginx <
|
6
|
+
class Nginx < Service
|
7
|
+
##
|
8
|
+
# SSL-specific configuration for Nginx. Provides self-signed certificate generation for use in
|
9
|
+
# developemnt.
|
10
|
+
#
|
7
11
|
class SSL
|
8
12
|
# Reference: https://ssl-config.mozilla.org/#server=nginx&config=intermediate&guideline=5.6
|
9
13
|
DEFAULT_CONFIG = {
|
@@ -26,8 +30,18 @@ module Potluck
|
|
26
30
|
|
27
31
|
attr_reader(:csr_file, :key_file, :crt_file, :dhparam_file, :config)
|
28
32
|
|
29
|
-
|
30
|
-
|
33
|
+
##
|
34
|
+
# Creates a new instance. Providing no SSL files will cue generation of a self-signed certificate.
|
35
|
+
#
|
36
|
+
# * +nginx+ - Nginx instance.
|
37
|
+
# * +dir+ - Directory where SSL files are located or should be written to.
|
38
|
+
# * +host+ - Name of the host for determining file names and generating a self-signed certificate.
|
39
|
+
# * +crt_file+ - Path to the CRT file (optional).
|
40
|
+
# * +key_file+ - Path to the KEY file (optional).
|
41
|
+
# * +dhparam_file+ - Path to the DH parameters file (optional).
|
42
|
+
# * +config+ - Nginx configuration hash (optional).
|
43
|
+
#
|
44
|
+
def initialize(nginx, dir, host, crt_file: nil, key_file: nil, dhparam_file: nil, config: {})
|
31
45
|
@nginx = nginx
|
32
46
|
@dir = dir
|
33
47
|
@host = host
|
@@ -52,6 +66,11 @@ module Potluck
|
|
52
66
|
}.merge!(DEFAULT_CONFIG).merge!(config)
|
53
67
|
end
|
54
68
|
|
69
|
+
##
|
70
|
+
# If SSL files were passed to SSL.new, does nothing. Otherwise checks if auto-generated SSL files
|
71
|
+
# exist and generates them if not. If they do exist, the expiration for the certificate is checked and
|
72
|
+
# the certificate regenerated if the expiration date is soon or in the past.
|
73
|
+
#
|
55
74
|
def ensure_files
|
56
75
|
return if !@auto_generated || (
|
57
76
|
File.exists?(@csr_file) &&
|
@@ -88,6 +107,9 @@ module Potluck
|
|
88
107
|
|
89
108
|
private
|
90
109
|
|
110
|
+
##
|
111
|
+
# OpenSSL configuration content used when auto-generating an SSL certificate.
|
112
|
+
#
|
91
113
|
def openssl_config
|
92
114
|
<<~EOS
|
93
115
|
[ req ]
|
data/lib/potluck/nginx/util.rb
CHANGED
@@ -2,7 +2,35 @@
|
|
2
2
|
|
3
3
|
module Potluck
|
4
4
|
class Nginx
|
5
|
+
##
|
6
|
+
# Utility methods for Nginx class.
|
7
|
+
#
|
5
8
|
class Util
|
9
|
+
##
|
10
|
+
# Merges one or more other hashes into a hash by merging nested hashes rather than overwriting them as
|
11
|
+
# is the case with <tt>Hash#merge!</tt>.
|
12
|
+
#
|
13
|
+
# * +hashes+ - Hashes to deep merge. The first one will be modified with the result of the merge.
|
14
|
+
# * +arrays+ - True if arrays should be merged rather than overwritten (optional, default: false).
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# h1 = {hello: {item1: 'world'}}
|
19
|
+
# h2 = {hello: {item2: 'friend'}}
|
20
|
+
#
|
21
|
+
# Util.deep_merge!(h1, h2)
|
22
|
+
# # => {hello: {item1: 'world', item2: 'friend'}}
|
23
|
+
#
|
24
|
+
# By default, only hashes are merged and arrays are still overwritten as they are with
|
25
|
+
# <tt>Hash#merge!</tt>. But passing <tt>arrays: true</tt> will result in arrays being merged similarly
|
26
|
+
# to hashes. Example:
|
27
|
+
#
|
28
|
+
# h1 = {hello: {item1: ['world']}}
|
29
|
+
# h2 = {hello: {item1: ['friend']}}
|
30
|
+
#
|
31
|
+
# Util.deep_merge!(h1, h2, arrays: true)
|
32
|
+
# # => {hello: {item1: ['world', 'friend']}}
|
33
|
+
#
|
6
34
|
def self.deep_merge!(*hashes, arrays: false)
|
7
35
|
hash = hashes[0]
|
8
36
|
|
data/lib/potluck/nginx.rb
CHANGED
@@ -6,7 +6,14 @@ require_relative('nginx/ssl')
|
|
6
6
|
require_relative('nginx/util')
|
7
7
|
|
8
8
|
module Potluck
|
9
|
-
|
9
|
+
##
|
10
|
+
# A Ruby interface for configuring and controlling Nginx. Each instance of this class manages a separate
|
11
|
+
# Nginx configuration file, which is loaded and unloaded from the base Nginx configuration when #start and
|
12
|
+
# #stop are called, respectively. Any number of Ruby processes can thus each manage their own Nginx
|
13
|
+
# configuration and control whether or not it is active without interfering with any other instances or
|
14
|
+
# non-Ruby processes leveraging Nginx.
|
15
|
+
#
|
16
|
+
class Nginx < Service
|
10
17
|
CONFIG_NAME_ACTIVE = 'nginx.conf'
|
11
18
|
CONFIG_NAME_INACTIVE = 'nginx-stopped.conf'
|
12
19
|
ACTIVE_CONFIG_PATTERN = File.join(DIR, '*', CONFIG_NAME_ACTIVE).freeze
|
@@ -20,6 +27,32 @@ module Potluck
|
|
20
27
|
stop: 'nginx -s stop',
|
21
28
|
}.freeze
|
22
29
|
|
30
|
+
##
|
31
|
+
# Creates a new instance.
|
32
|
+
#
|
33
|
+
# * +hosts+ - One or more hosts.
|
34
|
+
# * +port+ - Port that the upstream (Ruby web server) is running on.
|
35
|
+
# * +subdomains+ - One or more subdomains (optional).
|
36
|
+
# * +ssl+ - SSL configuration arguments to pass to SSL.new (optional).
|
37
|
+
# * +one_host+ - True if URLs should be normalized to the first host in +hosts+ (optional, default:
|
38
|
+
# false).
|
39
|
+
# * +www+ - +true+ if URLs should be normalized to include 'www.' prefix, +false+ to exclude 'www.', and
|
40
|
+
# +nil+ if either is acceptable (optional, default: +nil+).
|
41
|
+
# * +multiple_slashes+ - +false+ if any occurrence of multiple slashes in the path portion of the URL
|
42
|
+
# should be normalized to a single slash (optional, default: +nil+).
|
43
|
+
# * +multiple_question_marks+ - +false+ if multiple question marks in the URL signifying the start of
|
44
|
+
# the query string should be normalized to a single question mark (optional, default: +nil+).
|
45
|
+
# * +trailing_slash+ - +true+ if URLs should be normalized to include a trailing slash at the end of the
|
46
|
+
# path portion, +false+ to strip any trailing slash, and +nil+ if either is acceptable (optional,
|
47
|
+
# default: +nil+).
|
48
|
+
# * +trailing_question_mark+ - +true+ if URLs should be normalized to include a trailing question mark
|
49
|
+
# when the query string is empty, +false+ to strip any trailing question mark, and +nil+ if either is
|
50
|
+
# acceptable (optional, default: +nil+).
|
51
|
+
# * +config+ - Nginx configuration hash; see #config (optional).
|
52
|
+
# * +ensure_host_entries+ - True if +hosts+ should be added to system /etc/hosts file as mappings to
|
53
|
+
# localhost (optional, default: false).
|
54
|
+
# * +args+ - Arguments to pass to Potluck::Service.new (optional).
|
55
|
+
#
|
23
56
|
def initialize(hosts, port, subdomains: nil, ssl: nil, one_host: false, www: nil, multiple_slashes: nil,
|
24
57
|
multiple_question_marks: nil, trailing_slash: nil, trailing_question_mark: nil, config: {},
|
25
58
|
ensure_host_entries: false, **args)
|
@@ -49,13 +82,16 @@ module Potluck
|
|
49
82
|
@trailing_question_mark = trailing_question_mark
|
50
83
|
@additional_config = config
|
51
84
|
|
52
|
-
FileUtils.mkdir_p(DIR)
|
53
85
|
FileUtils.mkdir_p(@dir)
|
54
86
|
|
55
87
|
@config_file_active = File.join(@dir, CONFIG_NAME_ACTIVE).freeze
|
56
88
|
@config_file_inactive = File.join(@dir, CONFIG_NAME_INACTIVE).freeze
|
57
89
|
end
|
58
90
|
|
91
|
+
##
|
92
|
+
# Ensures this instance's configuration file is active and starts Nginx if it's managed. If Nginx is
|
93
|
+
# already running, a reload signal is sent to the process after activating the configuration file.
|
94
|
+
#
|
59
95
|
def start
|
60
96
|
return unless manage?
|
61
97
|
|
@@ -71,6 +107,13 @@ module Potluck
|
|
71
107
|
status == :active ? reload : super
|
72
108
|
end
|
73
109
|
|
110
|
+
##
|
111
|
+
# Ensures this instance's configuration file is inactive and optionally stops the Nginx process if it's
|
112
|
+
# managed.
|
113
|
+
#
|
114
|
+
# * +hard+ - True if the Nginx process should be stopped, false to just inactivate this instance's
|
115
|
+
# configuration file and leave Nginx running (optional, default: false).
|
116
|
+
#
|
74
117
|
def stop(hard = false)
|
75
118
|
return unless manage?
|
76
119
|
|
@@ -79,14 +122,29 @@ module Potluck
|
|
79
122
|
hard || status != :active ? super() : reload
|
80
123
|
end
|
81
124
|
|
125
|
+
##
|
126
|
+
# Reloads Nginx if it's managed.
|
127
|
+
#
|
82
128
|
def reload
|
83
129
|
return unless manage?
|
84
130
|
|
85
131
|
run('nginx -s reload')
|
86
132
|
end
|
87
133
|
|
134
|
+
##
|
135
|
+
# Returns the content for the Nginx configuration file as a string.
|
136
|
+
#
|
137
|
+
def config_file_content
|
138
|
+
self.class.to_nginx_config(config)
|
139
|
+
end
|
140
|
+
|
88
141
|
private
|
89
142
|
|
143
|
+
##
|
144
|
+
# Returns a hash representation of the Nginx configuration file content. Any configuration passed to
|
145
|
+
# Nginx.new is deep-merged into a base configuration hash, meaning nested hashes are merged rather than
|
146
|
+
# overwritten (see Util.deep_merge!).
|
147
|
+
#
|
90
148
|
def config
|
91
149
|
host_subdomains_regex = ([@host] + @subdomains).join('|')
|
92
150
|
hosts_subdomains_regex = (@hosts + @subdomains).join('|')
|
@@ -111,14 +169,15 @@ module Potluck
|
|
111
169
|
'server_name' => (@hosts + @subdomains).join(' '),
|
112
170
|
|
113
171
|
'gzip' => 'on',
|
114
|
-
'gzip_types' => 'application/javascript application/json text/css
|
172
|
+
'gzip_types' => 'application/javascript application/json application/xml text/css '\
|
173
|
+
'text/javascript text/plain',
|
115
174
|
|
116
175
|
'add_header' => {
|
117
176
|
repeat: true,
|
118
|
-
'Referrer-Policy' => 'same-origin',
|
119
|
-
'X-Frame-Options' => 'DENY',
|
120
|
-
'X-XSS-Protection' => '\'1; mode=block\'',
|
121
|
-
'X-Content-Type-Options' => 'nosniff',
|
177
|
+
'Referrer-Policy' => '\'same-origin\' always',
|
178
|
+
'X-Frame-Options' => '\'DENY\' always',
|
179
|
+
'X-XSS-Protection' => '\'1; mode=block\' always',
|
180
|
+
'X-Content-Type-Options' => '\'nosniff\' always',
|
122
181
|
},
|
123
182
|
}, @ssl ? @ssl.config : {}).merge!(
|
124
183
|
'location /' => {
|
@@ -128,6 +187,7 @@ module Potluck
|
|
128
187
|
set $r 0;
|
129
188
|
set $s $scheme;
|
130
189
|
set $h $host;
|
190
|
+
set $port #{@ssl ? '443' : '80'};
|
131
191
|
set $p '';
|
132
192
|
set $u '';
|
133
193
|
set $q '';
|
@@ -147,7 +207,7 @@ module Potluck
|
|
147
207
|
end}
|
148
208
|
|
149
209
|
if ($scheme = #{@other_scheme}) { set $s #{@scheme}; set $r 1; }
|
150
|
-
if ($http_host ~ :[0-9]
|
210
|
+
if ($http_host ~ :([0-9]+)$) { set $p :$1; set $port $1; }
|
151
211
|
if ($request_uri ~ ^([^\\?]+)(\\?+.*)?$) { set $u $1; set $q $2; }
|
152
212
|
|
153
213
|
#{'if ($u ~ //) { set $u $uri; set $r 1; }' if @multiple_slashes == false}
|
@@ -174,11 +234,11 @@ module Potluck
|
|
174
234
|
'proxy_redirect' => 'off',
|
175
235
|
'proxy_set_header' => {
|
176
236
|
repeat: true,
|
177
|
-
'Host' =>
|
237
|
+
'Host' => '$http_host',
|
178
238
|
'X-Real-IP' => '$remote_addr',
|
179
239
|
'X-Forwarded-For' => '$proxy_add_x_forwarded_for',
|
180
240
|
'X-Forwarded-Proto' => @ssl ? 'https' : 'http',
|
181
|
-
'X-Forwarded-Port' =>
|
241
|
+
'X-Forwarded-Port' => '$port',
|
182
242
|
},
|
183
243
|
},
|
184
244
|
),
|
@@ -189,20 +249,33 @@ module Potluck
|
|
189
249
|
config
|
190
250
|
end
|
191
251
|
|
252
|
+
##
|
253
|
+
# Writes the Nginx configuration to the (inactive) configuration file.
|
254
|
+
#
|
192
255
|
def write_config
|
193
256
|
File.open(@config_file_inactive, 'w') do |file|
|
194
|
-
file.write(
|
257
|
+
file.write(config_file_content)
|
195
258
|
end
|
196
259
|
end
|
197
260
|
|
261
|
+
##
|
262
|
+
# Renames the inactive Nginx configuration file to its active name.
|
263
|
+
#
|
198
264
|
def activate_config
|
199
265
|
FileUtils.mv(@config_file_inactive, @config_file_active)
|
200
266
|
end
|
201
267
|
|
268
|
+
##
|
269
|
+
# Renames the active Nginx configuration file to its inactive name.
|
270
|
+
#
|
202
271
|
def deactivate_config
|
203
272
|
FileUtils.mv(@config_file_active, @config_file_inactive) if File.exists?(@config_file_active)
|
204
273
|
end
|
205
274
|
|
275
|
+
##
|
276
|
+
# Ensures hosts are mapped to localhost in the system /etc/hosts file. Useful in development. Uses sudo
|
277
|
+
# to perform the write, which will prompt for the system user's password.
|
278
|
+
#
|
206
279
|
def ensure_host_entries
|
207
280
|
content = File.read('/etc/hosts')
|
208
281
|
missing_entries = (@hosts + @subdomains).each_with_object([]) do |h, a|
|
@@ -222,6 +295,11 @@ module Potluck
|
|
222
295
|
)
|
223
296
|
end
|
224
297
|
|
298
|
+
##
|
299
|
+
# Ensures Nginx's base configuration file contains an include statement for Potluck's Nginx
|
300
|
+
# configuration files. Sudo is not used, so Nginx's base configuration file must be writable by the
|
301
|
+
# system user running this Ruby process.
|
302
|
+
#
|
225
303
|
def ensure_include
|
226
304
|
config_file = `nginx -t 2>&1`[TEST_CONFIG_REGEX, :config]
|
227
305
|
config_content = File.read(config_file)
|
@@ -232,6 +310,57 @@ module Potluck
|
|
232
310
|
end
|
233
311
|
end
|
234
312
|
|
313
|
+
##
|
314
|
+
# Converts a hash to an Nginx configuration file content string. Keys should be strings and values
|
315
|
+
# either strings or hashes. A +nil+ value in a hash will result in that key-value pair being omitted.
|
316
|
+
#
|
317
|
+
# * +hash+ - Hash to convert to the string content of an Nginx configuration file.
|
318
|
+
# * +indent+ - Number of spaces to indent; used when the method is called recursively and should not be
|
319
|
+
# set explicitly (optional, default: 0).
|
320
|
+
# * +repeat+ - Value to prepend to each entry of the hash; used when the method is called recursively
|
321
|
+
# and should not be set explicitly (optional).
|
322
|
+
#
|
323
|
+
# Symbol keys in hashes are used as special directives. Including <tt>repeat: true</tt> will cause the
|
324
|
+
# parent hash's key for the child hash to be prefixed to each line of the output. Example:
|
325
|
+
#
|
326
|
+
# {
|
327
|
+
# # ...
|
328
|
+
#
|
329
|
+
# 'add_header' => {
|
330
|
+
# repeat: true,
|
331
|
+
# 'X-Frame-Options' => 'DENY',
|
332
|
+
# 'X-Content-Type-Options' => 'nosniff',
|
333
|
+
# }
|
334
|
+
# }
|
335
|
+
#
|
336
|
+
# Result:
|
337
|
+
#
|
338
|
+
# # ...
|
339
|
+
#
|
340
|
+
# add_header X-Frame-Options DENY;
|
341
|
+
# add_header X-Content-Type-Options nosniff;
|
342
|
+
#
|
343
|
+
# A hash containing <tt>raw: '...'</tt> can be used to include a raw chunk of text rather than key-value
|
344
|
+
# pairs. Example:
|
345
|
+
#
|
346
|
+
# {
|
347
|
+
# # ...
|
348
|
+
#
|
349
|
+
# 'location /' => {
|
350
|
+
# raw: """
|
351
|
+
# if ($scheme = https) { ... }
|
352
|
+
# if ($host ~ ^www.) { ... }
|
353
|
+
# """,
|
354
|
+
# }
|
355
|
+
# }
|
356
|
+
#
|
357
|
+
# Result:
|
358
|
+
#
|
359
|
+
# location / {
|
360
|
+
# if ($scheme = https) { ... }
|
361
|
+
# if ($host ~ ^www.) { ... }
|
362
|
+
# }
|
363
|
+
#
|
235
364
|
def self.to_nginx_config(hash, indent: 0, repeat: nil)
|
236
365
|
hash.each_with_object(+'') do |(k, v), config|
|
237
366
|
next if v.nil?
|
@@ -253,6 +382,9 @@ module Potluck
|
|
253
382
|
end
|
254
383
|
end
|
255
384
|
|
385
|
+
##
|
386
|
+
# Content of the launchctl plist file.
|
387
|
+
#
|
256
388
|
def self.plist
|
257
389
|
super(
|
258
390
|
<<~EOS
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: potluck-nginx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Pickens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: potluck
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.
|
19
|
+
version: 0.0.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.0.
|
26
|
+
version: 0.0.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
94
|
requirements: []
|
95
|
-
rubygems_version: 3.2.
|
95
|
+
rubygems_version: 3.2.32
|
96
96
|
signing_key:
|
97
97
|
specification_version: 4
|
98
98
|
summary: A Ruby manager for Nginx.
|