uninterruptible 2.0.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f8d400309d6f9bf1354f04b9002aad29d077d2e
4
- data.tar.gz: 3cb256b49089b46fdf7820594a6919c71e9d4d9f
3
+ metadata.gz: 45b1e17d9779c26d177d31c7dcdf49b4128eb0f4
4
+ data.tar.gz: 4a59b5fef72bd683bdfdcfa10d2c27a744271418
5
5
  SHA512:
6
- metadata.gz: 4649cdbf7b3eff9fff3709f343513cf36a21814e03c1092c0d09d4a6ff4905bfdfb32ae61866a41caf513867623689429c44dfe5a8df57e824c2173699c52c63
7
- data.tar.gz: d05d7c375ab9bd546691516abbbc7205a4221cfa60ffa8ddd3ea072fa9028fb12e51dc1951f83053605e7dd37ef9793dbf9c06e29631d7d00425128495ff82bb
6
+ metadata.gz: c3920549cf5cc3b176e385769190561920c9e21abe87ee9ec4deca2b73f99382e68b12e89071282b5258d985c1dbbed1712da90cfe4639fd2df06733167a205d
7
+ data.tar.gz: 615f4a872242d4d7fbade36d0457d7f5fb2433abf22061b8c1917a3a1e36f80288882d93ac6960d9aaab67a524d48fa8e02383a770158e30a2993e8c16cec88b
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ # 2.1.0
4
+ * Add TLS support for TCP connections
5
+
3
6
  # 2.0.0
4
7
  * Use an internal pipe for delivering signals to the main thread.
5
8
  * `accept_connections` retired in favour of a select loop and `accept_client_connection` being called for each waiting connection
data/README.md CHANGED
@@ -64,6 +64,9 @@ echo_server.configure do |config|
64
64
  config.pidfile_path = 'tmp/pids/echoserver.pid' # Location to write a pidfile, falls back to ENV['PID_FILE']
65
65
  config.log_path = 'log/echoserver.log' # Location to write logfile, defaults to STDOUT
66
66
  config.log_level = Logger::INFO # Log writing severity, defaults to Logger::INFO
67
+ config.tls_version = 'TLSv1_2' # TLS version to use, defaults to TLSv1_2, falls back to ENV['TLS_VERSION']
68
+ config.tls_key = nil # Private key to use for TLS, reads file from ENV['TLS_KEY'] if set
69
+ config.tls_certificate = nil # Certificate to use for TLS, reads file from ENV['TLS_CERTIFICATE'] if set
67
70
  end
68
71
  ```
69
72
 
@@ -119,6 +122,10 @@ class EchoServer
119
122
  end
120
123
  ```
121
124
 
125
+ ## TLS Support
126
+
127
+ If you would like to encrypt your TCP socket, Uninterruptible supports TLSv1.1 and TLSv1.2. Simply set `configuration.tls_key` and `configuration.tls_certificate` (see "Configuration" above) and your TCP socket will automatically be wrapped with TLS.
128
+
122
129
  ## Contributing
123
130
 
124
131
  Bug reports and pull requests are welcome on GitHub at https://github.com/darkphnx/uninterruptible.
@@ -1,6 +1,9 @@
1
+ require 'openssl'
1
2
  require "uninterruptible/version"
3
+ require 'uninterruptible/ssl_extensions'
2
4
  require 'uninterruptible/configuration'
3
5
  require 'uninterruptible/binder'
6
+ require 'uninterruptible/tls_server_factory'
4
7
  require 'uninterruptible/server'
5
8
 
6
9
  # All of the interesting stuff is in Uninterruptible::Server
@@ -3,7 +3,10 @@ module Uninterruptible
3
3
  #
4
4
  # See {Server#configure} for usage instructions.
5
5
  class Configuration
6
- attr_writer :bind, :bind_port, :bind_address, :pidfile_path, :start_command, :log_path, :log_level
6
+ AVAILABLE_SSL_VERSIONS = %w[TLSv1_1 TLSv1_2].freeze
7
+
8
+ attr_writer :bind, :bind_port, :bind_address, :pidfile_path, :start_command, :log_path, :log_level, :tls_version,
9
+ :tls_key, :tls_certificate
7
10
 
8
11
  # Available TCP Port for the server to bind to (required). Falls back to environment variable PORT if set.
9
12
  #
@@ -19,7 +22,7 @@ module Uninterruptible
19
22
  @bind_address || "0.0.0.0"
20
23
  end
21
24
 
22
- # URI to bind to, falls back to tcp://bind_address:bind_port if unset
25
+ # URI to bind to, falls back to tcp://bind_address:bind_port if unset. Accepts tcp:// or unix:// schemes.
23
26
  def bind
24
27
  @bind || "tcp://#{bind_address}:#{bind_port}"
25
28
  end
@@ -48,5 +51,35 @@ module Uninterruptible
48
51
  def log_level
49
52
  @log_level || Logger::INFO
50
53
  end
54
+
55
+ # Should the socket server be wrapped with a TLS server (TCP only). Automatically enabled when #tls_key or
56
+ # #tls_certificate is set
57
+ def tls_enabled?
58
+ !tls_key.nil? || !tls_certificate.nil?
59
+ end
60
+
61
+ # TLS version to use for the connection. Must be one of +Uninterruptible::Configuration::AVAILABLE_SSL_VERSIONS+
62
+ # If unset, connection will be unencrypted.
63
+ def tls_version
64
+ version = @tls_version || ENV['TLS_VERSION'] || 'TLSv1_2'
65
+
66
+ unless AVAILABLE_SSL_VERSIONS.include?(version)
67
+ raise ConfigurationError, "Please ensure tls_version is one of #{AVAILABLE_SSL_VERSIONS.join(', ')}"
68
+ end
69
+
70
+ version
71
+ end
72
+
73
+ # Private key used for encrypting TLS connection. If environment variable TLS_KEY is set, attempt to read from a
74
+ # file at that location.
75
+ def tls_key
76
+ @tls_key || (ENV['TLS_KEY'] ? File.read(ENV['TLS_KEY']) : nil)
77
+ end
78
+
79
+ # Certificate used for authenticating TLS connection. If environment variable TLS_CERTIFICATE is set, attempt to
80
+ # read from a file at that location
81
+ def tls_certificate
82
+ @tls_certificate || (ENV['TLS_CERTIFICATE'] ? File.read(ENV['TLS_CERTIFICATE']) : nil)
83
+ end
51
84
  end
52
85
  end
@@ -108,7 +108,7 @@ module Uninterruptible
108
108
  end
109
109
  end
110
110
 
111
- # Listen (or reconnect) to the bind address and port specified in the config. If socket_server_FD is set in the env,
111
+ # Listen (or reconnect) to the bind address and port specified in the config. If SERVER_FD_VAR is set in the env,
112
112
  # reconnect to that file descriptor. Once @socket_server is set, write the file descriptor ID to the env.
113
113
  def establish_socket_server
114
114
  @socket_server = Uninterruptible::Binder.new(server_configuration.bind).bind_to_socket
@@ -118,6 +118,10 @@ module Uninterruptible
118
118
  @socket_server.autoclose = false
119
119
  @socket_server.close_on_exec = false
120
120
 
121
+ if server_configuration.tls_enabled?
122
+ @socket_server = Uninterruptible::TLSServerFactory.new(server_configuration).wrap_with_tls(@socket_server)
123
+ end
124
+
121
125
  ENV[SERVER_FD_VAR] = @socket_server.to_i.to_s
122
126
  end
123
127
 
@@ -0,0 +1,30 @@
1
+ # Augment some parts of OpenSSL::SSL::SSLServer from stdlib for extra functionality
2
+ module OpenSSL
3
+ module SSL
4
+ # Extend this module from stdlib to delegate additional methods to the underlying TCP transport when wrapping
5
+ # with an OpenSSL::SSL::SSLServer
6
+ module SocketForwarder
7
+ # Fetch the file descriptor ID from the underlying transport
8
+ def to_i
9
+ to_io.to_i
10
+ end
11
+ end
12
+
13
+ # Extend OpenSSL::SSL::SSLServer to implement accept_nonblock (only #accept is implemented by stdlib)
14
+ class SSLServer
15
+ def accept_nonblock
16
+ sock = @svr.accept_nonblock
17
+
18
+ begin
19
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
20
+ ssl.sync_close = true
21
+ ssl.accept if @start_immediately
22
+ ssl
23
+ rescue SSLError => ex
24
+ sock.close
25
+ raise ex
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ module Uninterruptible
2
+ # Wraps a bound TCP server with an OpenSSL::SSL::SSLServer according to the Uninterruptible::Configuration for
3
+ # this server.
4
+ class TLSServerFactory
5
+ attr_reader :configuration
6
+
7
+ # @param [Uninterruptible::Configuration] configuration Object with valid TLS configuration options
8
+ #
9
+ # @raise [Uninterruptible::ConfigurationError] Correct options are not set for TLS
10
+ def initialize(configuration)
11
+ @configuration = configuration
12
+ check_configuration!
13
+ end
14
+
15
+ # Accepts a TCP server, gives it a nice friendly SSLServer wrapper and returns the SSLServer
16
+ #
17
+ # @param [TCPServer] tcp_server Server to be wrapped
18
+ #
19
+ # @return [OpenSSL::SSL::SSLServer] tcp_server with a TLS layer
20
+ def wrap_with_tls(tcp_server)
21
+ server = OpenSSL::SSL::SSLServer.new(tcp_server, ssl_context)
22
+ server.start_immediately = true
23
+ server
24
+ end
25
+
26
+ private
27
+
28
+ # Build an OpenSSL::SSL::SSLContext object from the configuration passed to the initializer
29
+ #
30
+ # @return [OpenSSL::SSL::SSLContext] SSL context for the server config
31
+ def ssl_context
32
+ context = OpenSSL::SSL::SSLContext.new
33
+ context.cert = OpenSSL::X509::Certificate.new(configuration.tls_certificate)
34
+ context.key = OpenSSL::PKey::RSA.new(configuration.tls_key)
35
+ context.ssl_version = configuration.tls_version.to_sym
36
+ context
37
+ end
38
+
39
+ # Check the configuration parameters for TLS are correct
40
+ #
41
+ # @raise [Uninterruptible::ConfigurationError] Correct options are not set for TLS
42
+ def check_configuration!
43
+ raise ConfigurationError, "TLS can only be used on TCP servers" unless configuration.bind.start_with?('tcp://')
44
+
45
+ empty = %i[tls_certificate tls_key].any? { |config_param| configuration.send(config_param).nil? }
46
+ raise ConfigurationError, "tls_certificate and tls_key must be set to use TLS" if empty
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Uninterruptible
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uninterruptible
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Wentworth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-21 00:00:00.000000000 Z
11
+ date: 2017-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -76,6 +76,8 @@ files:
76
76
  - lib/uninterruptible/binder.rb
77
77
  - lib/uninterruptible/configuration.rb
78
78
  - lib/uninterruptible/server.rb
79
+ - lib/uninterruptible/ssl_extensions.rb
80
+ - lib/uninterruptible/tls_server_factory.rb
79
81
  - lib/uninterruptible/version.rb
80
82
  - uninterruptible.gemspec
81
83
  homepage: https://github.com/darkphnx/uninterruptible