shared-infrastructure 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54a7741f3afa9a30ed79a1b175e2b311ac336094
4
+ data.tar.gz: fe517bf7ea380c63501d80e2c600eda30a225561
5
+ SHA512:
6
+ metadata.gz: 24eb0cb374861a315bfe55d73aed35d2bbf9780208eb0a917ad6ca41531804884434ea909280b676f2cac8fdb832293603a2f3331f88b2b5f5d48a3b3c06ff95
7
+ data.tar.gz: a22e5af7d7dd39576f97e80d458d299d8c61a1cb7429d0b974514bf1b5cdbd26d328834a3be155a67afe2613d4450a34ee67094a05348353899ebc7143c4c75d
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
5
+ require "shared_infrastructure"
6
+
7
+ Runner::Rails.new.main.save
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
5
+ require "shared_infrastructure"
6
+
7
+ Runner::ReverseProxy.new.main.save
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
5
+ require "shared_infrastructure"
6
+
7
+ Runner::StaticSite.new.main.save
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shared_infrastructure/nginx/nginx.rb"
4
+ require "shared_infrastructure/nginx/server_block.rb"
5
+ require "shared_infrastructure/nginx/server.rb"
6
+ require "shared_infrastructure/nginx/lines.rb"
7
+ require "shared_infrastructure/nginx/listen.rb"
8
+ require "shared_infrastructure/nginx/location.rb"
9
+ require "shared_infrastructure/nginx/upstream.rb"
10
+ require "shared_infrastructure/nginx/site.rb"
11
+ require "shared_infrastructure/nginx/builder.rb"
12
+ require "shared_infrastructure/runner/base.rb"
13
+ require "shared_infrastructure/runner/reverse_proxy.rb"
14
+ require "shared_infrastructure/runner/static_site.rb"
15
+ require "shared_infrastructure/systemd/systemd.rb"
16
+ require "shared_infrastructure/systemd/rails.rb"
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nginx
4
+ ##
5
+ # Builders.
6
+ # Builders build different files.
7
+ module Builder
8
+ module Https
9
+ def save
10
+ `openssl dhparam #{Nginx.dhparam} -out #{Nginx.certificate_directory(certificate_domain)}/dhparam.pem`
11
+ super
12
+ end
13
+ end
14
+
15
+ class Base
16
+ def https_reminder_message
17
+ puts %(You have to obtain a certificate and enable TLS for the site.
18
+ To do so, reload the Nginx configuration:
19
+
20
+ sudo nginx -s reload
21
+
22
+ Then run the following command:
23
+
24
+ sudo certbot certonly --webroot -w #{Nginx.root_directory(domain_name)} #{Nginx.certbot_domain_names(domain_name)}
25
+
26
+ You can test renewal with:
27
+
28
+ sudo certbot renew --dry-run
29
+
30
+ Finally, re-run this script to configure nginx for TLS.
31
+ )
32
+ end
33
+
34
+ def initialize(domain_name, *server_blocks)
35
+ # puts "Base#initialize domain_name: #{domain_name}"
36
+ # puts "Base#initialize server_blocks.inspect: #{server_blocks.inspect}"
37
+ @server_blocks = server_blocks
38
+ @domain_name = domain_name
39
+ end
40
+
41
+ def save
42
+ File.open(Nginx.server_block_location(domain_name), "w") do |f|
43
+ f << to_s
44
+ end
45
+ `ln -fs ../sites-available/#{domain_name} #{Nginx.enabled_server_block_location(domain_name)}`
46
+ end
47
+
48
+ def to_s
49
+ server_blocks.map(&:to_s).join("\n")
50
+ end
51
+
52
+ attr_reader :domain_name, :server_blocks
53
+ end
54
+
55
+ class ReverseProxyHttp < Base
56
+ def initialize(domain_name, proxy_url, _certificate_domain = nil)
57
+ super(domain_name,
58
+ Nginx::ServerBlock.new(
59
+ server: Nginx::Server.new(domain_name),
60
+ listen: Nginx::ListenHttp.new,
61
+ location: Nginx::ReverseProxyLocation.new(proxy_url)
62
+ )
63
+ )
64
+ end
65
+
66
+ def save
67
+ result = super
68
+ https_reminder_message
69
+ result
70
+ end
71
+ end
72
+
73
+ class ReverseProxyHttps < Base
74
+ include Https
75
+
76
+ def initialize(domain_name, proxy_url, certificate_domain = nil)
77
+ @certificate_domain = certificate_domain || domain_name
78
+
79
+ super(domain_name,
80
+ Nginx::ServerBlock.new(
81
+ server: Nginx::Server.new(domain_name),
82
+ listen: Nginx::ListenHttps.new(domain_name, certificate_domain),
83
+ location: Nginx::ReverseProxyLocation.new(proxy_url)
84
+ ),
85
+ Nginx::TlsRedirectServerBlock.new(domain_name)
86
+ )
87
+ end
88
+
89
+ attr_reader :certificate_domain
90
+ end
91
+
92
+ class Site < Base
93
+ def initialize(domain_name, user, *server_blocks)
94
+ super(domain_name, *server_blocks)
95
+ @user = user
96
+ end
97
+
98
+ def save
99
+ FileUtils.mkdir_p(Nginx.root_directory(domain_name))
100
+ if Process.uid.zero?
101
+ FileUtils.chown(user,
102
+ "www-data",
103
+ Nginx.root_directory(domain_name))
104
+ end
105
+ super
106
+ end
107
+
108
+ attr_reader :user
109
+ end
110
+
111
+ class SiteHttp < Site
112
+ def initialize(domain_name, user, _certificate_domain = nil)
113
+ super(domain_name,
114
+ user,
115
+ Nginx::StaticServerBlock.new(
116
+ server: Nginx::Site.new(domain_name, user),
117
+ listen: Nginx::ListenHttp.new,
118
+ location: Nginx::Location.new
119
+ )
120
+ )
121
+ end
122
+
123
+ def save
124
+ result = super
125
+ https_reminder_message
126
+ result
127
+ end
128
+ end
129
+
130
+ class SiteHttps < Site
131
+ include Https
132
+
133
+ def initialize(domain_name, user, certificate_domain = nil)
134
+ @certificate_domain = certificate_domain || domain_name
135
+
136
+ super(domain_name,
137
+ user,
138
+ Nginx::StaticServerBlock.new(
139
+ server: Nginx::Site.new(domain_name, user),
140
+ listen: Nginx::ListenHttps.new(domain_name, certificate_domain),
141
+ location: Nginx::Location.new
142
+ ),
143
+ Nginx::TlsRedirectServerBlock.new(domain_name)
144
+ )
145
+ end
146
+
147
+ attr_reader :certificate_domain
148
+ end
149
+
150
+ class RailsHttp < Site
151
+ def initialize(domain_name, user, _certificate_domain = nil)
152
+ super(domain_name,
153
+ user,
154
+ Nginx::RailsServerBlock.new(
155
+ upstream: Nginx::Upstream.new(domain_name),
156
+ server: Nginx::RailsServer.new(domain_name),
157
+ listen: Nginx::ListenHttp.new,
158
+ location: [
159
+ Nginx::RailsLocation.new(domain_name),
160
+ Nginx::ActionCableLocation.new(domain_name)
161
+ ]
162
+ )
163
+ )
164
+ end
165
+
166
+ def save
167
+ Systemd::Rails.write_unit_file(domain_name) && super
168
+ end
169
+ end
170
+
171
+ class RailsHttps < Site
172
+ include Https
173
+
174
+ def initialize(domain_name, user, _certificate_domain = nil)
175
+ @certificate_domain = certificate_domain || domain_name
176
+ super(domain_name,
177
+ user,
178
+ Nginx::RailsServerBlock.new(
179
+ upstream: Nginx::Upstream.new(domain_name),
180
+ server: Nginx::RailsServer.new(domain_name),
181
+ listen: Nginx::ListenHttps.new(domain_name, certificate_domain),
182
+ location: [
183
+ Nginx::RailsLocation.new(domain_name),
184
+ Nginx::ActionCableLocation.new(domain_name)
185
+ ]
186
+ ),
187
+ Nginx::TlsRedirectServerBlock.new(domain_name)
188
+ )
189
+ end
190
+
191
+ # FIXME: DRY this up with the HTTP class.
192
+ def save
193
+ Systemd::Rails.write_unit_file(domain_name) && super
194
+ end
195
+
196
+ attr_reader :certificate_domain
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,19 @@
1
+ module Nginx
2
+ ##
3
+ # A class to format lines nicely in a file.
4
+ class Lines < Array
5
+ def initialize(*lines)
6
+ @lines = Array(lines)
7
+ end
8
+
9
+ def format(level = 0)
10
+ @lines.map { |x| Lines.indent(x, level) }.join("\n")
11
+ end
12
+
13
+ class << self
14
+ def indent(s, level = 0)
15
+ s.empty? ? s : (" " * level * 2) + s
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nginx
4
+ class Listen
5
+ def initialize(port)
6
+ @port = port
7
+ end
8
+
9
+ def to_s(level = 0)
10
+ Lines.new("listen #{port};", "listen [::]:#{port};").format(level)
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :port
16
+ end
17
+
18
+ class ListenHttp < Listen
19
+ def initialize
20
+ super 80
21
+ end
22
+ end
23
+
24
+ class ListenHttps < Listen
25
+ def initialize(domain_name, certificate_domain = nil)
26
+ @domain_name = domain_name
27
+ @certificate_domain = certificate_domain || domain_name
28
+ super 443
29
+ end
30
+
31
+ def to_s(level = 0)
32
+ Lines.new(
33
+ "# TLS config from: http://nginx.org/en/docs/http/configuring_https_servers.html",
34
+ "# HTTP2 doesn't require encryption, but at last reading, no browsers support",
35
+ "# HTTP2 without TLS, so only do http2 when we have TLS.",
36
+ "listen #{port} ssl http2;",
37
+ "listen [::]:#{port} ssl http2;",
38
+ "# Let's Encrypt file names and locations from: https://certbot.eff.org/docs/using.html#where-are-my-certificates",
39
+ "ssl_certificate_key #{Nginx.certificate_directory(certificate_domain)}/privkey.pem;",
40
+ "ssl_certificate #{Nginx.certificate_directory(certificate_domain)}/fullchain.pem;",
41
+ "",
42
+ "# Test the site using: https://www.ssllabs.com/ssltest/index.html",
43
+ "# Optimize TLS, from: https://www.bjornjohansen.no/optimizing-https-nginx, steps 1-3",
44
+ "ssl_session_cache shared:SSL:1m; # Enough for 4,000 sessions.",
45
+ "ssl_session_timeout 180m;",
46
+ "ssl_protocols TLSv1 TLSv1.1 TLSv1.2;",
47
+ "ssl_prefer_server_ciphers on;",
48
+ "ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;",
49
+ "# Step 4",
50
+ "ssl_dhparam #{Nginx.certificate_directory(certificate_domain)}/dhparam.pem;",
51
+ "# Step 5",
52
+ "ssl_stapling on;",
53
+ "ssl_stapling_verify on;",
54
+ "ssl_trusted_certificate #{Nginx.certificate_directory(certificate_domain)}/chain.pem;",
55
+ "resolver 8.8.8.8 8.8.4.4;",
56
+ "# Step 6 pin for a fortnight",
57
+ "add_header Strict-Transport-Security \"max-age=1209600\" always;",
58
+ "# Other steps TBD"
59
+ ).format(level)
60
+ end
61
+
62
+ private
63
+
64
+ attr_reader :certificate_domain, :domain_name
65
+ end
66
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nginx
4
+ class Location
5
+ def initialize(location = "/")
6
+ @location = location
7
+ end
8
+
9
+ def to_s(level = 0)
10
+ Lines.new("location #{location} {",
11
+ " try_files $uri $uri/ =404;",
12
+ "}").format(level)
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :location
18
+ end
19
+
20
+ class ActionCableLocation < Location
21
+ def initialize(domain_name, location = "/cable")
22
+ super(location)
23
+ @domain_name = domain_name
24
+ end
25
+
26
+ def to_s(level = 0)
27
+ Lines.new("location #{location} {",
28
+ " proxy_pass http://#{domain_name};",
29
+ " proxy_http_version 1.1;",
30
+ " proxy_set_header Upgrade $http_upgrade;",
31
+ " proxy_set_header Connection \"upgrade\";",
32
+ "}").format(level)
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :domain_name
38
+ end
39
+
40
+ class RailsLocation
41
+ def initialize(domain_name)
42
+ @domain_name = domain_name
43
+ end
44
+
45
+ def to_s(level = 0)
46
+ Lines.new("location @#{domain_name} {",
47
+ " # A Rails app should force \"SSL\" so that it generates redirects to HTTPS,",
48
+ " # among other things.",
49
+ " # However, you want Nginx to handle the workload of TLS.",
50
+ " # The trick to proxying to a Rails app, therefore, is to proxy pass to HTTP,",
51
+ " # but set the header to HTTPS",
52
+ " # Next two lines.",
53
+ " proxy_pass http://#{domain_name};",
54
+ " proxy_set_header X-Forwarded-Proto $scheme; # $scheme says http or https",
55
+ " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;",
56
+ " proxy_set_header Host $http_host;",
57
+ " proxy_redirect off;",
58
+ "}").format(level)
59
+ end
60
+
61
+ private
62
+
63
+ attr_reader :domain_name
64
+ end
65
+
66
+ class ReverseProxyLocation < Location
67
+ def initialize(proxy_url, location = "/")
68
+ super location
69
+ @proxy_url = proxy_url
70
+ end
71
+
72
+ def to_s(level = 0)
73
+ Lines.new("location #{location} {",
74
+ " proxy_pass #{proxy_url};",
75
+ " proxy_set_header Host $http_host;",
76
+ " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;",
77
+ " proxy_set_header X-Forwarded-Proto $scheme;",
78
+ " proxy_set_header X-Real-IP $remote_addr;",
79
+ " proxy_redirect off;",
80
+ "}").format(level)
81
+ end
82
+
83
+ private
84
+
85
+ attr_reader :proxy_url
86
+ end
87
+
88
+ class RedirectLocation < Location
89
+ def initialize
90
+ super
91
+ @location = nil
92
+ end
93
+
94
+ def to_s(level = 0)
95
+ Lines.new("return 301 https://$server_name/$request_uri;").format(level)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Nginx
6
+ class Configuration
7
+ def certbot_domain_names(domain_name)
8
+ "#{domain_name} www.#{domain_name}"
9
+ end
10
+
11
+ def certificate_directory(domain_name)
12
+ "#{root}/etc/letsencrypt/live/#{domain_name}"
13
+ end
14
+
15
+ def initialize(root = nil)
16
+ @dhparam = 2048
17
+ @root = root
18
+ end
19
+
20
+ def root?
21
+ !(root.nil? || root.empty?)
22
+ end
23
+
24
+ def root_directory(domain_name)
25
+ "#{root}/var/www/#{domain_name}/html"
26
+ end
27
+
28
+ def server_block_location(domain_name)
29
+ "#{root}/etc/nginx/sites-available/#{domain_name}"
30
+ end
31
+
32
+ def enabled_server_block_location(domain_name)
33
+ "#{root}/etc/nginx/sites-enabled/#{domain_name}"
34
+ end
35
+
36
+ attr_accessor :dhparam, :root
37
+ end
38
+
39
+ class << self
40
+ ##
41
+ # Change root. If block is given, change the root only for the duration
42
+ # of the block. If no block is given, is the same as configure.
43
+ def chroot(root = nil)
44
+ if block_given?
45
+ begin
46
+ save_root = configuration.root
47
+ chroot(root)
48
+ result = yield
49
+ ensure
50
+ chroot(save_root)
51
+ result
52
+ end
53
+ else
54
+ configuration.root = root
55
+ end
56
+ end
57
+
58
+ def configure
59
+ yield configuration
60
+ end
61
+
62
+ def configuration
63
+ @configuration ||= Configuration.new
64
+ end
65
+
66
+ def dhparam
67
+ configuration.dhparam
68
+ end
69
+
70
+ def dhparam=(key_length)
71
+ configuration.dhparam = key_length
72
+ end
73
+
74
+ def prepare_fake_files(domain_name, certificate_domain = nil)
75
+ ::FileUtils.mkdir_p(File.dirname(server_block_location(domain_name)))
76
+ ::FileUtils.mkdir_p(File.dirname(enabled_server_block_location(domain_name)))
77
+ ::FileUtils.mkdir_p(certificate_directory(certificate_domain || domain_name))
78
+ end
79
+
80
+ def root
81
+ configuration.root
82
+ end
83
+
84
+ def root?
85
+ configuration.root?
86
+ end
87
+
88
+ %i[
89
+ certbot_domain_names
90
+ certificate_directory
91
+ enabled_server_block_location
92
+ root_directory
93
+ server_block_location
94
+ ].each do |method|
95
+ define_method method do |domain_name|
96
+ configuration.send(method, domain_name)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nginx
4
+ ##
5
+ # The server_name line of a server block.
6
+ class Server
7
+ attr_reader :domain_name
8
+
9
+ def initialize(domain_name)
10
+ @domain_name = domain_name
11
+ end
12
+
13
+ def to_s(level = 0)
14
+ Lines.new("server_name #{Nginx.certbot_domain_names(domain_name)};").format(level)
15
+ end
16
+ end
17
+
18
+ ##
19
+ # Server name and site location for a static site.
20
+ # TODO: I don't like the way this gets twisted when subclassing.
21
+ class StaticServer < Server
22
+ def to_s(level = 0)
23
+ [
24
+ super(level),
25
+ Lines.new(
26
+ "root #{root_directory};",
27
+ "index index.html index.htm;"
28
+ ).format(level)
29
+ ].join("\n\n")
30
+ end
31
+ end
32
+
33
+ class RailsServer < Server
34
+ def root_directory
35
+ File.join(Nginx.root_directory(domain_name), "public")
36
+ end
37
+
38
+ def to_s(level = 0)
39
+ [
40
+ super(level),
41
+ Lines.new(
42
+ "# http://stackoverflow.com/a/11313241/3109926 said the following",
43
+ "# is what serves from public directly without hitting Puma",
44
+ "root #{root_directory};",
45
+ "try_files $uri/index.html $uri @example.com;",
46
+ "error_page 500 502 503 504 /500.html;",
47
+ "client_max_body_size 4G;",
48
+ "keepalive_timeout 10;"
49
+ ).format(level)
50
+ ].join("\n\n")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Write nginx configuration files.
5
+ module Nginx
6
+ class ServerBlock
7
+ def initialize(upstream: nil, server: nil, listen: nil, location: nil)
8
+ @listen = listen
9
+ @location = Array(location)
10
+ @server = server
11
+ @upstream = upstream
12
+ end
13
+
14
+ def save
15
+ File.open(Nginx.server_block_location(server.domain_name), "w") do |f|
16
+ f << to_s
17
+ end
18
+ `ln -fs ../sites-available/#{server.domain_name} #{Nginx.enabled_server_block_location(server.domain_name)}`
19
+ end
20
+
21
+ def to_s
22
+ [
23
+ upstream_string,
24
+ server_block_string
25
+ ].compact.join("\n\n")
26
+ end
27
+
28
+ private
29
+
30
+ def server_block_string
31
+ <<~SERVER_BLOCK
32
+ server {
33
+ #{[
34
+ @server&.to_s(1),
35
+ @listen&.to_s(1),
36
+ @location&.map { |l| l.to_s(1) }
37
+ ].compact.join("\n\n")}
38
+ }
39
+ SERVER_BLOCK
40
+ end
41
+
42
+ def upstream_string
43
+ upstream&.to_s
44
+ end
45
+
46
+ attr_reader :listen, :location, :server, :upstream
47
+ end
48
+
49
+ class SiteServerBlock < ServerBlock
50
+ def make_root_directory(root_directory)
51
+ FileUtils.mkdir_p(server.root_directory)
52
+ if Process.uid.zero?
53
+ FileUtils.chown(server.user,
54
+ "www-data",
55
+ server.root_directory)
56
+ end
57
+ end
58
+
59
+ def save
60
+ make_root_directory(root_directory)
61
+ super
62
+ end
63
+ end
64
+
65
+ class RailsServerBlock < SiteServerBlock
66
+ def root_directory
67
+ File.join(server.root_directory, "/public")
68
+ end
69
+ end
70
+
71
+ class StaticServerBlock < SiteServerBlock
72
+ end
73
+
74
+ class TlsRedirectServerBlock < ServerBlock
75
+ def initialize(domain_name)
76
+ super(
77
+ server: Server.new(domain_name),
78
+ listen: ListenHttp.new,
79
+ location: RedirectLocation.new
80
+ )
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nginx
4
+ ##
5
+ # Server name and site location for a static site.
6
+ # TODO: I don't like the way this gets twisted when subclassing.
7
+ class Site < Server
8
+ attr_reader :user
9
+
10
+ def initialize(domain_name, user = "ubuntu")
11
+ super domain_name
12
+ @user = user
13
+ end
14
+
15
+ def root_directory
16
+ Nginx.root_directory(domain_name)
17
+ end
18
+
19
+ def to_s(level = 0)
20
+ [
21
+ super(level),
22
+ Lines.new(
23
+ "root #{Nginx.root_directory(domain_name)};",
24
+ "index index.html index.htm;"
25
+ ).format(level)
26
+ ].join("\n\n")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Runner
6
+ ##
7
+ # Basic runner for nginx config file generation.
8
+ class Base
9
+ def main
10
+ options = process_options
11
+ options.merge!(process_args)
12
+
13
+ puts "options: #{options.inspect}" if options[:debug]
14
+
15
+ Nginx.prepare_fake_files(options[:domain_name], options[:certificate_domain]) if Nginx.root?
16
+
17
+ @builder_class = protocol_factory(options)
18
+ puts "builder_class: #{builder_class.inspect}" if options[:debug]
19
+ builder_class
20
+ end
21
+
22
+ def options_for_config(options)
23
+ options.select { |k, _v| k == :user }
24
+ end
25
+
26
+ def process_args
27
+ $stderr.puts "domain required" unless ARGV.size == 1
28
+ { domain_name: ARGV[0] }
29
+ end
30
+
31
+ def process_options(http_builder_class = Nginx::Builder::SiteHttp,
32
+ https_builder_class = Nginx::Builder::SiteHttps)
33
+ options = {}
34
+ OptionParser.new do |opts|
35
+ opts.banner = "Usage: [options]"
36
+
37
+ opts.on("-c DOMAIN",
38
+ "--certificate-domain DOMAIN",
39
+ "Use the certificate for DOMAIN.") do |certificate_domain|
40
+ options[:certificate_domain] = certificate_domain
41
+ end
42
+
43
+ opts.on("-h", "--help", "Prints this help") do
44
+ puts opts
45
+ exit
46
+ end
47
+
48
+ opts.on("-d", "--debug", "Print debugging information.") do
49
+ options[:debug] = true
50
+ end
51
+
52
+ opts.on("-p PROTOCOL",
53
+ "--protocol PROTOCOL",
54
+ "HTTP|HTTPS. Default: HTTPS if key files exist, else HTTP.") do |protocol|
55
+ options[:protocol] = case protocol
56
+ when "HTTP"
57
+ http_builder_class
58
+ when "HTTPS"
59
+ https_builder_class
60
+ else
61
+ puts opts
62
+ exit
63
+ end
64
+ end
65
+
66
+ opts.on("-r DIRECTORY",
67
+ "--root DIRECTORY",
68
+ "DIRECTORY. Set a root for files. This options is for debugging.") do |directory|
69
+ Nginx.chroot(directory)
70
+ end
71
+
72
+ opts.on("-u USER",
73
+ "--user USER",
74
+ "User to be the owner of certain files. Default: ubuntu.") do |user|
75
+ options[:user] = user
76
+ end
77
+
78
+ opts.on("--dhparam KEYSIZE",
79
+ "KEYSIZE. Default: 2048 should be used. This option is for testing.") do |keysize|
80
+ Nginx.dhparam = keysize
81
+ end
82
+
83
+ yield opts if block_given?
84
+ end.parse!
85
+ options
86
+ end
87
+
88
+ attr_reader :builder_class
89
+
90
+ def protocol_factory(options,
91
+ http_builder_class = Nginx::Builder::SiteHttp,
92
+ https_builder_class = Nginx::Builder::SiteHttps)
93
+ if options[:protocol]
94
+ options[:protocol]
95
+ else
96
+ certificate_directory = Nginx.certificate_directory(
97
+ options[:certificate_domain] || options[:domain_name]
98
+ )
99
+ if File.exist?(File.join(certificate_directory, "privkey.pem")) &&
100
+ File.exist?(File.join(certificate_directory, "fullchain.pem")) &&
101
+ File.exist?(File.join(certificate_directory, "chain.pem")) &&
102
+ File.exist?(File.join(certificate_directory, "cert.pem"))
103
+ https_builder_class
104
+ else
105
+ http_builder_class
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runner
4
+ ##
5
+ # Generate reverse proxy config files for Nginx.
6
+ class ReverseProxy < Base
7
+ def options_for_config(options)
8
+ super(options).merge(proxy_url: ARGV[1])
9
+ end
10
+
11
+ def process_args
12
+ $stderr.puts "domain and target url required" unless ARGV.size == 2
13
+ {
14
+ domain_name: ARGV[0],
15
+ proxy_url: ARGV[1]
16
+ }
17
+ end
18
+
19
+ def process_options
20
+ super(Nginx::Builder::ReverseProxyHttp, Nginx::Builder::ReverseProxyHttps)
21
+ end
22
+
23
+ def protocol_factory(options)
24
+ protocol_class = super(
25
+ options,
26
+ Nginx::Builder::ReverseProxyHttp,
27
+ Nginx::Builder::ReverseProxyHttps
28
+ )
29
+
30
+ domain_name = options.delete(:domain_name)
31
+ proxy_url = options.delete(:proxy_url)
32
+ certificate_domain = options.delete(:certificate_domain)
33
+ protocol_class.new(domain_name, proxy_url, certificate_domain)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Runner
4
+ ##
5
+ # Generate static site config files for Nginx.
6
+ class StaticSite < Base
7
+ def protocol_factory(options)
8
+ protocol_class = super(
9
+ options,
10
+ Nginx::Builder::SiteHttp,
11
+ Nginx::Builder::SiteHttps
12
+ )
13
+
14
+ domain_name = options.delete(:domain_name)
15
+ user = options.delete(:user) || "ubuntu"
16
+ certificate_domain = options.delete(:certificate_domain)
17
+ protocol_class.new(domain_name, user, certificate_domain)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Systemd
4
+ module Rails
5
+ class << self
6
+ def puma_uri(domain_name)
7
+ "unix:///tmp/#{domain_name}.sock"
8
+ end
9
+
10
+ def redis_location(domain_name)
11
+ "redis." + domain_name
12
+ end
13
+
14
+ def write_unit_file(domain_name)
15
+ if ENV["SECRET_KEY_BASE"].nil? ||
16
+ ENV["DATABASE_USERNAME"].nil? ||
17
+ ENV["DATABASE_PASSWORD"].nil? ||
18
+ ENV["EMAIL_PASSWORD"].nil?
19
+ raise "Missing environment variable"
20
+ end
21
+
22
+ result = File.open(Systemd.unit_file(domain_name), "w") do |f|
23
+ f << <<~UNIT_FILE
24
+ [Unit]
25
+ Description=Puma HTTP Server for #{domain_name}
26
+ After=network.target
27
+
28
+ # Uncomment for socket activation (see below)
29
+ # Requires=#{domain_name}.socket
30
+
31
+ [Service]
32
+ # Foreground process (do not use --daemon in ExecStart or config.rb)
33
+ Type=simple
34
+
35
+ User=nobody
36
+ Group=www-data
37
+
38
+ # Specify the path to the Rails application root
39
+ WorkingDirectory=#{Nginx.root_directory(domain_name)}
40
+
41
+ # Helpful for debugging socket activation, etc.
42
+ # Environment=PUMA_DEBUG=1
43
+ Environment=RACK_ENV=production
44
+ Environment=RAILS_ENV=production
45
+ Environment=SECRET_KEY_BASE=#{ENV['SECRET_KEY_BASE']}
46
+ Environment=DATABASE_USERNAME=#{ENV['DATABASE_USERNAME']}
47
+ Environment=DATABASE_PASSWORD=#{ENV['DATABASE_PASSWORD']}
48
+ Environment=EMAIL_PASSWORD=#{ENV['EMAIL_PASSWORD']}
49
+ Environment=REDIS_URL=unix:///tmp/#{redis_location(domain_name)}.sock
50
+
51
+ # The command to start Puma
52
+ # NOTE: TLS would be handled by Nginx
53
+ ExecStart=#{Nginx.root_directory(domain_name)}/bin/puma -b #{puma_uri(domain_name)} \
54
+ --redirect-stdout=#{Nginx.root_directory(domain_name)}/log/puma-production.stdout.log \
55
+ --redirect-stderr=#{Nginx.root_directory(domain_name)}/log/puma-production.stderr.log
56
+ # ExecStart=/usr/local/bin/puma -b tcp://#{puma_uri(domain_name)}
57
+
58
+ Restart=always
59
+
60
+ [Install]
61
+ WantedBy=multi-user.target
62
+ UNIT_FILE
63
+ end
64
+
65
+ FileUtils.chmod(0o600, Systemd.unit_file(domain_name))
66
+ `systemctl enable $domain_name.service` if Process.uid.zero?
67
+
68
+ result
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ module Systemd
2
+ class Configuration
3
+ def initialize
4
+ @unit_file_path = File.join(Nginx.root, "/lib/systemd/system")
5
+ end
6
+
7
+ def unit_file(domain_name)
8
+ File.join(unit_file_path, domain_name + ".service")
9
+ end
10
+
11
+ attr_accessor :unit_file_path
12
+ end
13
+
14
+ class << self
15
+ def configure
16
+ yield configuration
17
+ end
18
+
19
+ def configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ %i[
24
+ unit_file
25
+ ].each do |method|
26
+ define_method method do |domain_name|
27
+ configuration.send(method, domain_name)
28
+ end
29
+ end
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shared-infrastructure
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Larry Reid
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 'For static sites, Rails apps, and reverse proxies.
14
+
15
+ '
16
+ email: lcreid@jadesystems.ca
17
+ executables:
18
+ - create-server-block
19
+ - create-rails-app
20
+ - create-reverse-proxy
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - bin/create-rails-app
25
+ - bin/create-reverse-proxy
26
+ - bin/create-server-block
27
+ - lib/shared_infrastructure.rb
28
+ - lib/shared_infrastructure/nginx/builder.rb
29
+ - lib/shared_infrastructure/nginx/lines.rb
30
+ - lib/shared_infrastructure/nginx/listen.rb
31
+ - lib/shared_infrastructure/nginx/location.rb
32
+ - lib/shared_infrastructure/nginx/nginx.rb
33
+ - lib/shared_infrastructure/nginx/server.rb
34
+ - lib/shared_infrastructure/nginx/server_block.rb
35
+ - lib/shared_infrastructure/nginx/site.rb
36
+ - lib/shared_infrastructure/runner/base.rb
37
+ - lib/shared_infrastructure/runner/reverse_proxy.rb
38
+ - lib/shared_infrastructure/runner/static_site.rb
39
+ - lib/shared_infrastructure/systemd/rails.rb
40
+ - lib/shared_infrastructure/systemd/systemd.rb
41
+ homepage: https://github.com/weenhanceit/infrastructure
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.5.1
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Configure nginx, systemd, and/or Puma
65
+ test_files: []