potluck-nginx 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +16 -0
- data/README.md +31 -0
- data/lib/potluck/nginx.rb +255 -0
- data/lib/potluck/nginx/ssl.rb +110 -0
- data/lib/potluck/nginx/util.rb +27 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 599537fcab6df50b0126d73460295be4b366101667ad4c2317199b361153a5c1
|
4
|
+
data.tar.gz: 6a38b16b915efa971b1b93361dd1b995937b67a61031376879cf0aae857438be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f007969613094559c4b68707b8bf87f570def7da9b5122673de84ca2a4c45409450ba348f7d8ff522191188afacdf1cf1a94c15f1a0b4cc36419e446ca921644
|
7
|
+
data.tar.gz: 6c3672941908c91d9151e7863ed0cb93c7e7ebf4463bcc2b7c8d997ac3baf529ef124bad4a1733c33ee3221a12cfad0cd41c60f72f85f9f2c1921f4d2646a436
|
data/LICENSE
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Copyright 2021 Nate Pickens
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
4
|
+
documentation files (the "Software"), to deal in the Software without restriction, including without
|
5
|
+
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
6
|
+
the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
|
7
|
+
conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
10
|
+
portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
13
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
14
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
15
|
+
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
16
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Potluck - Nginx
|
2
|
+
|
3
|
+
An extension to the Potluck gem that provides control over the Nginx process and its configuration files
|
4
|
+
from Ruby.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem('potluck-nginx')
|
12
|
+
```
|
13
|
+
|
14
|
+
Or install manually on the command line:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
gem install potluck-nginx
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
[Coming soon.]
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/npickens/potluck.
|
27
|
+
|
28
|
+
## License
|
29
|
+
|
30
|
+
The gem is available as open source under the terms of the
|
31
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('fileutils')
|
4
|
+
require('potluck')
|
5
|
+
require_relative('nginx/ssl')
|
6
|
+
require_relative('nginx/util')
|
7
|
+
|
8
|
+
module Potluck
|
9
|
+
class Nginx < Dish
|
10
|
+
CONFIG_NAME_ACTIVE = 'nginx.conf'
|
11
|
+
CONFIG_NAME_INACTIVE = 'nginx-stopped.conf'
|
12
|
+
ACTIVE_CONFIG_PATTERN = File.join(DIR, '*', CONFIG_NAME_ACTIVE).freeze
|
13
|
+
|
14
|
+
TEST_CONFIG_REGEX = /nginx: configuration file (?<config>.+) test (failed|is successful)/.freeze
|
15
|
+
INCLUDE_REGEX = /^ *include +#{Regexp.escape(ACTIVE_CONFIG_PATTERN)} *;/.freeze
|
16
|
+
|
17
|
+
def initialize(hosts, port, subdomains: nil, ssl: nil, one_host: false, www: nil, multiple_slashes: nil,
|
18
|
+
multiple_question_marks: nil, trailing_slash: nil, trailing_question_mark: nil, config: {}, **args)
|
19
|
+
super(**args)
|
20
|
+
|
21
|
+
@hosts = Array(hosts).map { |h| h.sub(/^www\./, '') }.uniq
|
22
|
+
@hosts += @hosts.map { |h| "www.#{h}" }
|
23
|
+
@host = @hosts.first
|
24
|
+
@port = port
|
25
|
+
|
26
|
+
@dir = File.join(DIR, @host)
|
27
|
+
@ssl = SSL.new(self, @dir, @host, **ssl) if ssl
|
28
|
+
|
29
|
+
@scheme = @ssl ? 'https' : 'http'
|
30
|
+
@other_scheme = @ssl ? 'http' : 'https'
|
31
|
+
@one_host = !!one_host
|
32
|
+
@subdomains = Array(subdomains)
|
33
|
+
@www = www
|
34
|
+
@multiple_slashes = multiple_slashes
|
35
|
+
@multiple_question_marks = multiple_question_marks
|
36
|
+
@trailing_slash = trailing_slash
|
37
|
+
@trailing_question_mark = trailing_question_mark
|
38
|
+
@additional_config = config
|
39
|
+
|
40
|
+
FileUtils.mkdir_p(DIR)
|
41
|
+
FileUtils.mkdir_p(@dir)
|
42
|
+
|
43
|
+
@config_file_active = File.join(@dir, CONFIG_NAME_ACTIVE).freeze
|
44
|
+
@config_file_inactive = File.join(@dir, CONFIG_NAME_INACTIVE).freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
def start
|
48
|
+
@ssl&.ensure_files
|
49
|
+
ensure_host_entries
|
50
|
+
ensure_include
|
51
|
+
|
52
|
+
write_config
|
53
|
+
activate_config
|
54
|
+
|
55
|
+
run('nginx -t')
|
56
|
+
|
57
|
+
status == :active ? reload : super
|
58
|
+
end
|
59
|
+
|
60
|
+
def stop(hard = false)
|
61
|
+
deactivate_config
|
62
|
+
|
63
|
+
hard || status != :active ? super() : reload
|
64
|
+
end
|
65
|
+
|
66
|
+
def reload
|
67
|
+
run('nginx -s reload')
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def config
|
73
|
+
host_subdomains_regex = ([@host] + @subdomains).join('|')
|
74
|
+
hosts_subdomains_regex = (@hosts + @subdomains).join('|')
|
75
|
+
|
76
|
+
config = {
|
77
|
+
"upstream #{@host}" => {
|
78
|
+
'server' => "127.0.0.1:#{@port}",
|
79
|
+
},
|
80
|
+
|
81
|
+
'server' => Util.deep_merge!({
|
82
|
+
'charset' => 'UTF-8',
|
83
|
+
'access_log' => File.join(@dir, 'nginx-access.log'),
|
84
|
+
'error_log' => File.join(@dir, 'nginx-error.log'),
|
85
|
+
|
86
|
+
'listen' => {
|
87
|
+
repeat: true,
|
88
|
+
'8080' => true,
|
89
|
+
'[::]:8080' => true,
|
90
|
+
'4433 ssl http2' => @ssl ? true : nil,
|
91
|
+
'[::]:4433 ssl http2' => @ssl ? true : nil,
|
92
|
+
},
|
93
|
+
'server_name' => (@hosts + @subdomains).join(' '),
|
94
|
+
|
95
|
+
'gzip' => 'on',
|
96
|
+
'gzip_types' => 'application/javascript application/json text/css text/plain',
|
97
|
+
|
98
|
+
'add_header' => {
|
99
|
+
repeat: true,
|
100
|
+
'Referrer-Policy' => 'same-origin',
|
101
|
+
'X-Frame-Options' => 'DENY',
|
102
|
+
'X-XSS-Protection' => '\'1; mode=block\'',
|
103
|
+
'X-Content-Type-Options' => 'nosniff',
|
104
|
+
},
|
105
|
+
}, @ssl ? @ssl.config : {}).merge!(
|
106
|
+
'location /' => {
|
107
|
+
raw: """
|
108
|
+
if ($host !~ ^#{hosts_subdomains_regex}$) { return 404; }
|
109
|
+
|
110
|
+
set $r 0;
|
111
|
+
set $s $scheme;
|
112
|
+
set $h $host;
|
113
|
+
set $p '';
|
114
|
+
set $u '';
|
115
|
+
set $q '';
|
116
|
+
|
117
|
+
#{if @www.nil? && @one_host == false
|
118
|
+
nil
|
119
|
+
elsif @www.nil? && @one_host == true
|
120
|
+
"if ($host !~ ^(www.)?#{host_subdomains_regex}$) { set $h $1#{@host}; set $r 1; }"
|
121
|
+
elsif @www == false && @one_host == false
|
122
|
+
"if ($host ~ ^www.(.+)$) { set $h $1; set $r 1; }"
|
123
|
+
elsif @www == false && @one_host == true
|
124
|
+
"if ($host !~ ^#{host_subdomains_regex}$) { set $h #{@host}; set $r 1; }"
|
125
|
+
elsif @www == true && @one_host == false
|
126
|
+
"if ($host !~ ^www.(.+)$) { set $h $1; set $r 1; }"
|
127
|
+
elsif @www == true && @one_host == true
|
128
|
+
"if ($host !~ ^www.#{host_subdomains_regex}$) { set $h www.#{@host}; set $r 1; }"
|
129
|
+
end}
|
130
|
+
|
131
|
+
if ($scheme = #{@other_scheme}) { set $s #{@scheme}; set $r 1; }
|
132
|
+
if ($http_host ~ :[0-9]+$) { set $p :#{@ssl ? '4433' : '8080'}; }
|
133
|
+
if ($request_uri ~ ^([^\\?]+)(\\?+.*)?$) { set $u $1; set $q $2; }
|
134
|
+
|
135
|
+
#{'if ($u ~ //) { set $u $uri; set $r 1; }' if @multiple_slashes == false}
|
136
|
+
#{'if ($q ~ ^\?\?+(.*)$) { set $q ?$1; set $r 1; }' if @multiple_question_marks == false}
|
137
|
+
|
138
|
+
#{if @trailing_question_mark == false
|
139
|
+
'if ($q ~ \?+$) { set $q \'\'; set $r 1; }'
|
140
|
+
elsif @trailing_question_mark == true
|
141
|
+
'if ($q !~ .) { set $q ?; set $r 1; }'
|
142
|
+
end}
|
143
|
+
#{if @trailing_slash == false
|
144
|
+
'if ($u ~ (.+?)/+$) { set $u $1; set $r 1; }'
|
145
|
+
elsif @trailing_slash == true
|
146
|
+
'if ($u ~ [^/]$) { set $u $u/; set $r 1; }'
|
147
|
+
end}
|
148
|
+
|
149
|
+
set $mr $request_method$r;
|
150
|
+
|
151
|
+
if ($mr ~ ^(GET|HEAD)1$) { return 301 $s://$h$p$u$q; }
|
152
|
+
if ($mr ~ 1$) { return 308 $s://$h$p$u$q; }
|
153
|
+
""".strip.gsub(/^ +/, '').gsub(/\n{3,}/, "\n\n"),
|
154
|
+
|
155
|
+
'proxy_pass' => "http://#{@host}",
|
156
|
+
'proxy_redirect' => 'off',
|
157
|
+
'proxy_set_header' => {
|
158
|
+
repeat: true,
|
159
|
+
'Host' => @host,
|
160
|
+
'X-Real-IP' => '$remote_addr',
|
161
|
+
'X-Forwarded-For' => '$proxy_add_x_forwarded_for',
|
162
|
+
'X-Forwarded-Proto' => @ssl ? 'https' : 'http',
|
163
|
+
'X-Forwarded-Port' => @ssl ? '443' : '80',
|
164
|
+
},
|
165
|
+
},
|
166
|
+
),
|
167
|
+
}
|
168
|
+
|
169
|
+
Util.deep_merge!(config['server'], @additional_config)
|
170
|
+
|
171
|
+
config
|
172
|
+
end
|
173
|
+
|
174
|
+
def write_config
|
175
|
+
File.open(@config_file_inactive, 'w') do |file|
|
176
|
+
file.write(self.class.to_nginx_config(config))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def activate_config
|
181
|
+
FileUtils.mv(@config_file_inactive, @config_file_active)
|
182
|
+
end
|
183
|
+
|
184
|
+
def deactivate_config
|
185
|
+
FileUtils.mv(@config_file_active, @config_file_inactive) if File.exists?(@config_file_active)
|
186
|
+
end
|
187
|
+
|
188
|
+
def ensure_host_entries
|
189
|
+
content = File.read('/etc/hosts')
|
190
|
+
missing_entries = (@hosts + @subdomains).each_with_object([]) do |h, a|
|
191
|
+
a << h unless content.include?(" #{h}\n")
|
192
|
+
end
|
193
|
+
|
194
|
+
return if missing_entries.empty?
|
195
|
+
|
196
|
+
log('Writing host entries to /etc/hosts...')
|
197
|
+
|
198
|
+
run(
|
199
|
+
<<~CMD
|
200
|
+
sudo sh -c 'printf "
|
201
|
+
#{missing_entries.map { |h| "127.0.0.1 #{h}\n::1 #{h}"}.join("\n")}
|
202
|
+
" >> /etc/hosts'
|
203
|
+
CMD
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def ensure_include
|
208
|
+
config_file = `nginx -t 2>&1`[TEST_CONFIG_REGEX, :config]
|
209
|
+
config_content = File.read(config_file)
|
210
|
+
|
211
|
+
if config_content !~ INCLUDE_REGEX
|
212
|
+
File.write(config_file, config_content.sub(/^( *http *{)( *\n?)( *)/,
|
213
|
+
"\\1\\2\\3include #{ACTIVE_CONFIG_PATTERN};\n\n\\3"))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.to_nginx_config(hash, indent: 0, repeat: nil)
|
218
|
+
hash.each_with_object(+'') do |(k, v), config|
|
219
|
+
next if v.nil?
|
220
|
+
next if k == :repeat
|
221
|
+
|
222
|
+
config << (
|
223
|
+
if v.kind_of?(Hash)
|
224
|
+
if v[:repeat]
|
225
|
+
to_nginx_config(v, indent: indent, repeat: k)
|
226
|
+
else
|
227
|
+
"#{' ' * indent}#{k} {\n#{to_nginx_config(v, indent: indent + 2)}#{' ' * indent}}\n"
|
228
|
+
end
|
229
|
+
elsif k == :raw
|
230
|
+
"#{v.gsub(/^(?=.)/, ' ' * indent)}\n\n"
|
231
|
+
else
|
232
|
+
"#{' ' * indent}#{"#{repeat} " if repeat}#{k}#{" #{v}" unless v == true};\n"
|
233
|
+
end
|
234
|
+
)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.plist
|
239
|
+
super(
|
240
|
+
<<~EOS
|
241
|
+
<key>ProgramArguments</key>
|
242
|
+
<array>
|
243
|
+
<string>/usr/local/opt/nginx/bin/nginx</string>
|
244
|
+
<string>-g</string>
|
245
|
+
<string>daemon off;</string>
|
246
|
+
</array>
|
247
|
+
<key>StandardOutPath</key>
|
248
|
+
<string>/usr/local/var/log/nginx/access.log</string>
|
249
|
+
<key>StandardErrorPath</key>
|
250
|
+
<string>/usr/local/var/log/nginx/error.log</string>
|
251
|
+
EOS
|
252
|
+
)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('time')
|
4
|
+
|
5
|
+
module Potluck
|
6
|
+
class Nginx < Dish
|
7
|
+
class SSL
|
8
|
+
# Based on https://hackernoon.com/how-properly-configure-nginx-server-for-tls-sg1d3udt
|
9
|
+
DEFAULT_CONFIG = {
|
10
|
+
'ssl_ciphers' => 'ECDH+AESGCM:ECDH+AES256-CBC:ECDH+AES128-CBC:DH+3DES:!ADH:!AECDH:!MD5',
|
11
|
+
'ssl_prefer_server_ciphers' => 'on',
|
12
|
+
'ssl_protocols' => 'TLSv1.2 TLSv1.3',
|
13
|
+
'ssl_session_cache' => 'shared:SSL:40m',
|
14
|
+
'ssl_session_tickets' => 'on',
|
15
|
+
'ssl_session_timeout' => '4h',
|
16
|
+
'add_header' => {
|
17
|
+
repeat: true,
|
18
|
+
'Strict-Transport-Security' => '\'max-age=31536000; includeSubDomains\' always',
|
19
|
+
}.freeze,
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
CERT_DAYS = 365
|
23
|
+
CERT_RENEW_DAYS = 14
|
24
|
+
|
25
|
+
attr_reader(:csr_file, :key_file, :crt_file, :dhparam_file, :config)
|
26
|
+
|
27
|
+
def initialize(nginx, dir, host, crt_file: nil, key_file: nil, dhparam_file: nil,
|
28
|
+
config: {})
|
29
|
+
@nginx = nginx
|
30
|
+
@dir = dir
|
31
|
+
@host = host
|
32
|
+
|
33
|
+
@auto_generated = !crt_file && !key_file && !dhparam_file
|
34
|
+
|
35
|
+
if !@auto_generated && (!crt_file || !key_file || !dhparam_file)
|
36
|
+
raise('Must supply values for all three or none: crt_file, key_file, dhparam_file')
|
37
|
+
end
|
38
|
+
|
39
|
+
@csr_file = File.join(@dir, "#{@host}.csr").freeze
|
40
|
+
@crt_file = crt_file || File.join(@dir, "#{@host}.crt").freeze
|
41
|
+
@key_file = key_file || File.join(@dir, "#{@host}.key").freeze
|
42
|
+
@dhparam_file = dhparam_file || File.join(@dir, 'dhparam.pem').freeze
|
43
|
+
|
44
|
+
@config = {
|
45
|
+
'ssl_certificate' => @crt_file,
|
46
|
+
'ssl_certificate_key' => @key_file,
|
47
|
+
'ssl_dhparam' => @dhparam_file,
|
48
|
+
'ssl_stapling' => ('on' unless @auto_generated),
|
49
|
+
'ssl_stapling_verify' => ('on' unless @auto_generated),
|
50
|
+
}.merge!(DEFAULT_CONFIG).merge!(config)
|
51
|
+
end
|
52
|
+
|
53
|
+
def ensure_files
|
54
|
+
return if !@auto_generated || (
|
55
|
+
File.exists?(@csr_file) &&
|
56
|
+
File.exists?(@key_file) &&
|
57
|
+
File.exists?(@crt_file) &&
|
58
|
+
File.exists?(@dhparam_file) &&
|
59
|
+
(Time.parse(
|
60
|
+
@nginx.run("openssl x509 -enddate -noout -in #{@crt_file}").sub('notAfter=', '')
|
61
|
+
) - Time.now) >= CERT_RENEW_DAYS * 24 * 60 * 60
|
62
|
+
)
|
63
|
+
|
64
|
+
@nginx.log('Generating SSL files...')
|
65
|
+
|
66
|
+
@nginx.run("openssl genrsa -out #{@key_file} 4096", redirect_stderr: false)
|
67
|
+
@nginx.run("openssl req -out #{@csr_file} -key #{@key_file} -new -sha256 -config /dev/stdin <<< "\
|
68
|
+
"'#{openssl_config}'", redirect_stderr: false)
|
69
|
+
@nginx.run("openssl x509 -in #{@csr_file} -out #{@crt_file} -signkey #{@key_file} -days "\
|
70
|
+
"#{CERT_DAYS} -req -sha256 -extensions req_ext -extfile /dev/stdin <<< '#{openssl_config}'",
|
71
|
+
redirect_stderr: false)
|
72
|
+
@nginx.run("openssl dhparam -out #{@dhparam_file} 2048", redirect_stderr: false)
|
73
|
+
|
74
|
+
if IS_MACOS
|
75
|
+
@nginx.log('Adding cert to keychain...')
|
76
|
+
|
77
|
+
@nginx.run(
|
78
|
+
"sudo security delete-certificate -t -c #{@host} 2>&1 || "\
|
79
|
+
"sudo security delete-certificate -c #{@host} 2>&1 || :"
|
80
|
+
)
|
81
|
+
|
82
|
+
@nginx.run("sudo security add-trusted-cert -d -r trustRoot -k "\
|
83
|
+
"/Library/Keychains/System.keychain #{@crt_file}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def openssl_config
|
90
|
+
<<~EOS
|
91
|
+
[ req ]
|
92
|
+
prompt = no
|
93
|
+
default_bits = 4096
|
94
|
+
distinguished_name = req_distinguished_name
|
95
|
+
req_extensions = req_ext
|
96
|
+
|
97
|
+
[ req_distinguished_name ]
|
98
|
+
commonName = #{@host}
|
99
|
+
|
100
|
+
[ req_ext ]
|
101
|
+
subjectAltName = @alt_names
|
102
|
+
|
103
|
+
[alt_names]
|
104
|
+
DNS.1 = #{@host}
|
105
|
+
DNS.2 = *.#{@host}
|
106
|
+
EOS
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Potluck
|
4
|
+
class Nginx
|
5
|
+
class Util
|
6
|
+
def self.deep_merge!(*hashes, arrays: false)
|
7
|
+
hash = hashes[0]
|
8
|
+
|
9
|
+
hashes[1..-1].each do |other_hash|
|
10
|
+
other_hash.each do |key, other_value|
|
11
|
+
this_value = hash[key]
|
12
|
+
|
13
|
+
if this_value.kind_of?(Hash) && other_value.kind_of?(Hash)
|
14
|
+
deep_merge!(this_value, other_value, arrays: arrays)
|
15
|
+
elsif arrays && this_value.kind_of?(Array)
|
16
|
+
hash[key] |= Array(other_value)
|
17
|
+
else
|
18
|
+
hash[key] = other_value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
hash
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: potluck-nginx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nate Pickens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: potluck
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 5.11.2
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 6.0.0
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 5.11.2
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 6.0.0
|
61
|
+
description: An extension to the Potluck gem that provides control over the Nginx
|
62
|
+
process and its configuration files from Ruby.
|
63
|
+
email:
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- LICENSE
|
69
|
+
- README.md
|
70
|
+
- lib/potluck/nginx.rb
|
71
|
+
- lib/potluck/nginx/ssl.rb
|
72
|
+
- lib/potluck/nginx/util.rb
|
73
|
+
homepage: https://github.com/npickens/potluck/tree/master/potluck-nginx
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
allowed_push_host: https://rubygems.org
|
78
|
+
homepage_uri: https://github.com/npickens/potluck/tree/master/potluck-nginx
|
79
|
+
source_code_uri: https://github.com/npickens/potluck/tree/master/potluck-nginx
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.5.8
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.2.3
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: A Ruby manager for Nginx.
|
99
|
+
test_files: []
|