-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP server helper supports https #2787
Changes from 7 commits
d9ef2b6
b24bbd0
e22cca6
3df24ff
3ab898f
0f51575
38f80c5
6d53664
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
|
||
require 'fluent/plugin_helper/http_server/methods' | ||
require 'fluent/plugin_helper/http_server/compat/webrick_handler' | ||
require 'fluent/plugin_helper/http_server/compat/ssl_context_extractor' | ||
|
||
module Fluent | ||
module PluginHelper | ||
|
@@ -24,16 +25,26 @@ module Compat | |
class Server | ||
# @param logger [Logger] | ||
# @param default_app [Object] ignored option. only for compat | ||
def initialize(addr:, port:, logger:, default_app: nil) | ||
# @param tls_context [OpenSSL::SSL::SSLContext] | ||
def initialize(addr:, port:, logger:, default_app: nil, tls_context: nil) | ||
@addr = addr | ||
@port = port | ||
@logger = logger | ||
@server = WEBrick::HTTPServer.new( | ||
|
||
config = { | ||
BindAddress: @addr, | ||
Port: @port, | ||
Logger: WEBrick::Log.new(STDERR, WEBrick::Log::FATAL), | ||
AccessLog: [], | ||
) | ||
} | ||
if tls_context | ||
require 'webrick/https' | ||
@logger.warn('Webrick ignores given TLS version') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this bug of webrick or spec? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spec. webrick does not provide an interface of setting the TLS version. |
||
tls_opt = Fluent::PluginHelper::HttpServer::Compat::SSLContextExtractor.extract(tls_context) | ||
config = tls_opt.merge(**config) | ||
end | ||
|
||
@server = WEBrick::HTTPServer.new(config) | ||
|
||
# @example ["/example.json", :get, handler object] | ||
@methods = [] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# | ||
# Fluentd | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
module Fluent | ||
module PluginHelper | ||
module HttpServer | ||
module Compat | ||
# This class converts OpenSSL::SSL::SSLContext to Webrick SSL Config because webrick does not have interface to pass OpenSSL::SSL::SSLContext directory | ||
# https://github.com/ruby/webrick/blob/v1.6.0/lib/webrick/ssl.rb#L67-L88 | ||
class SSLContextExtractor | ||
|
||
# | ||
# memo: https://github.com/ruby/webrick/blob/v1.6.0/lib/webrick/ssl.rb#L180-L205 | ||
# @param ctx [OpenSSL::SSL::SSLContext] | ||
def self.extract(ctx) | ||
{ | ||
SSLEnable: true, | ||
SSLPrivateKey: ctx.key, | ||
SSLCertificate: ctx.cert, | ||
SSLClientCA: ctx.client_ca, | ||
SSLExtraChainCert: ctx.extra_chain_cert, | ||
SSLCACertificateFile: ctx.ca_file, | ||
SSLCACertificatePath: ctx.ca_path, | ||
SSLCertificateStore: ctx.cert_store, | ||
SSLTmpDhCallback: ctx.tmp_dh_callback, | ||
SSLVerifyClient: ctx.verify_mode, | ||
SSLVerifyDepth: ctx.verify_depth, | ||
SSLVerifyCallback: ctx.verify_callback, | ||
SSLServerNameCallback: ctx.servername_cb, | ||
SSLTimeout: ctx.timeout, | ||
SSLOptions: ctx.options, | ||
SSLCiphers: ctx.ciphers, | ||
} | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# | ||
# Fluentd | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
require 'fluent/plugin_helper/cert_option' | ||
|
||
module Fluent | ||
module PluginHelper | ||
module HttpServer | ||
# In order not to expose CertOption's methods unnecessary | ||
class SSLContextBuilder | ||
include Fluent::PluginHelper::CertOption | ||
|
||
def initialize(log) | ||
@log = log | ||
end | ||
|
||
# @param config [Fluent::Config::Section] @transport_config | ||
def build(config) | ||
cert_option_create_context(config.version, config.insecure, config.ciphers, config) | ||
end | ||
|
||
private | ||
|
||
attr_reader :log | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
require 'fluent/plugin_helper/cert_option' | ||
require 'fileutils' | ||
|
||
module CertUtil | ||
extend Fluent::PluginHelper::CertOption | ||
end | ||
|
||
WITHOUT_CA_DIR = './without_ca'.freeze | ||
WITH_CA_DIR = './with_ca'.freeze | ||
|
||
CA_OPTION = { | ||
private_key_length: 2048, | ||
country: 'US', | ||
state: 'CA', | ||
locality: 'Mountain View', | ||
common_name: 'ca.testing.fluentd.org', | ||
expiration: 30 * 86400, | ||
digest: :sha256, | ||
} | ||
|
||
SERVER_OPTION = { | ||
private_key_length: 2048, | ||
country: 'US', | ||
state: 'CA', | ||
locality: 'Mountain View', | ||
common_name: 'server.testing.fluentd.org', | ||
expiration: 30 * 86400, | ||
digest: :sha256, | ||
} | ||
|
||
def write_cert_and_key(cert_path, cert, key_path, key, passphrase) | ||
File.open(cert_path, 'w') { |f| f.write(cert.to_pem) } | ||
|
||
# Write the secret key (raw or encrypted by AES256) in PEM format | ||
key_str = passphrase ? key.export(OpenSSL::Cipher.new('AES-256-CBC'), passphrase) : key.export | ||
File.open(key_path, 'w') { |f| f.write(key_str) } | ||
File.chmod(0o600, cert_path, key_path) | ||
end | ||
|
||
def create_server_pair_signed_by_self(cert_path, private_key_path, passphrase) | ||
cert, key, _ = CertUtil.cert_option_generate_server_pair_self_signed(SERVER_OPTION) | ||
write_cert_and_key(cert_path, cert, private_key_path, key, passphrase) | ||
cert | ||
end | ||
|
||
def create_ca_pair_signed_by_self(cert_path, private_key_path, passphrase) | ||
cert, key, _ = CertUtil.cert_option_generate_ca_pair_self_signed(CA_OPTION) | ||
write_cert_and_key(cert_path, cert, private_key_path, key, passphrase) | ||
cert | ||
end | ||
|
||
def create_server_pair_signed_by_ca(ca_cert_path, ca_key_path, ca_key_passphrase, cert_path, private_key_path, passphrase) | ||
cert, key, _ = CertUtil.cert_option_generate_server_pair_by_ca(ca_cert_path, ca_key_path, ca_key_passphrase, SERVER_OPTION) | ||
write_cert_and_key(cert_path, cert, private_key_path, key, passphrase) | ||
cert | ||
end | ||
|
||
def create_without_ca | ||
FileUtils.mkdir_p(WITHOUT_CA_DIR) | ||
cert_path = File.join(WITHOUT_CA_DIR, 'cert.pem') | ||
cert_key_path = File.join(WITHOUT_CA_DIR, 'cert-key.pem') | ||
cert_pass_path = File.join(WITHOUT_CA_DIR, 'cert-pass.pem') | ||
cert_key_pass_path = File.join(WITHOUT_CA_DIR, 'cert-key-pass.pem') | ||
|
||
create_server_pair_signed_by_self(cert_path, cert_key_path, nil) | ||
create_server_pair_signed_by_self(cert_pass_path, cert_key_pass_path, 'apple') # with passphrase | ||
end | ||
|
||
def create_with_ca | ||
FileUtils.mkdir_p(WITH_CA_DIR) | ||
cert_path = File.join(WITH_CA_DIR, 'cert.pem') | ||
cert_key_path = File.join(WITH_CA_DIR, 'cert-key.pem') | ||
ca_cert_path = File.join(WITH_CA_DIR, 'ca-cert.pem') | ||
ca_key_path = File.join(WITH_CA_DIR, 'ca-cert-key.pem') | ||
create_ca_pair_signed_by_self(ca_cert_path, ca_key_path, nil) | ||
create_server_pair_signed_by_ca(ca_cert_path, ca_key_path, nil, cert_path, cert_key_path, nil) | ||
|
||
cert_pass_path = File.join(WITH_CA_DIR, 'cert-pass.pem') | ||
cert_key_pass_path = File.join(WITH_CA_DIR, 'cert-key-pass.pem') | ||
ca_cert_pass_path = File.join(WITH_CA_DIR, 'ca-cert-pass.pem') | ||
ca_key_pass_path = File.join(WITH_CA_DIR, 'ca-cert-key-pass.pem') | ||
create_ca_pair_signed_by_self(ca_cert_pass_path, ca_key_pass_path, 'orange') # with passphrase | ||
create_server_pair_signed_by_ca(ca_cert_pass_path, ca_key_pass_path, 'orange', cert_pass_path, cert_key_pass_path, 'apple') | ||
end | ||
|
||
create_without_ca | ||
create_with_ca |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for short-cut or forget to call
.dup
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot to call dup..
abb50a8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@repeatedly https://github.com/fluent/fluentd/pull/298/files#diff-d34ab903425415e82b8cd4d891adadddR41
I've found all tests failed in travis because they expected Section class not to have #dup.
Do you know why it does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I don't remember. If all tests are succeded after removed it, we can remove it.