module Proxy
Provides an environment for hosting a TLS-capable reverse proxy using SNI.
Definitions
def url
The host that this proxy will receive connections for.
Implementation
def url
"https://[::]:443"
end
def ssl_session_id
The default SSL session identifier.
Implementation
def ssl_session_id
"falcon"
end
def environments
The services we will proxy to.
Signature
-
returns
Array(Async::Service::Environment)
Implementation
def environments
[]
end
def hosts
The hosts we will proxy to. This is a hash of SNI authority -> evaluator.
Signature
-
returns
Hash(String, Async::Service::Environment::Evaluator)
Implementation
def hosts
hosts = {}
environments.each do |environment|
evaluator = environment.evaluator
# next unless environment.implements?(Falcon::Environment::Application)
if evaluator.key?(:authority) and evaluator.key?(:ssl_context) and evaluator.key?(:endpoint)
Console.info(self) {"Proxying #{self.url} to #{evaluator.authority} using #{evaluator.endpoint}"}
hosts[evaluator.authority] = evaluator
if RUBY_VERSION < "3.1"
# Ensure the SSL context is set up before forking - it's buggy on Ruby < 3.1:
evaluator.ssl_context
end
end
end
return hosts
end
def host_context(socket, hostname)
Look up the host context for the given hostname, and update the socket hostname if necessary.
Signature
-
parameter
socket
OpenSSL::SSL::SSLSocket
The incoming connection.
-
parameter
hostname
String
The negotiated hostname.
Implementation
def host_context(socket, hostname)
hosts = self.hosts
if host = hosts[hostname]
Console.logger.debug(self) {"Resolving #{hostname} -> #{host}"}
socket.hostname = hostname
return host.ssl_context
else
Console.logger.warn(self, hosts: hosts.keys) {"Unable to resolve #{hostname}!"}
return nil
end
end
def ssl_context
Generate an SSL context which delegates to Falcon::Environment::Proxy#host_context
to multiplex based on hostname.
Implementation
def ssl_context
@server_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
context.servername_cb = Proc.new do |socket, hostname|
self.host_context(socket, hostname)
end
context.session_id_context = self.ssl_session_id
context.set_params(
ciphers: ::Falcon::TLS::SERVER_CIPHERS,
verify_mode: ::OpenSSL::SSL::VERIFY_NONE,
)
context.setup
end
end
def endpoint
The endpoint the server will bind to.
Implementation
def endpoint
super.with(
ssl_context: self.ssl_context,
)
end