Skip to content

Commit 3663173

Browse files
yaauierwaweber
andauthored
Fix SSL Subject regression in server mode (#199)
* Fix SSL Subject regression in server mode Alters the TCP#decode_buffer signature to accept the _ssl subject_ instead of expecting a ruby socket, so that it can be interoperable between the ruby-based client mode and the netty-powered server mode. In server mode, the SSL subject is extracted _once_ when initializing the connection IFF SSL is enabled and verification is turned on. Co-authored-by: Will Weber <rwa_weber@comcast.com> Closes: #159 * include docker jdk17 env Co-authored-by: Will Weber <rwa_weber@comcast.com>
1 parent bca64db commit 3663173

File tree

8 files changed

+79
-22
lines changed

8 files changed

+79
-22
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import:
44

55
env:
66
jobs:
7-
- ELASTIC_STACK_VERSION=8.x
8-
- SNAPSHOT=true ELASTIC_STACK_VERSION=8.x
7+
- ELASTIC_STACK_VERSION=8.x DOCKER_ENV=dockerjdk17.env
8+
- SNAPSHOT=true ELASTIC_STACK_VERSION=8.x DOCKER_ENV=dockerjdk17.env

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 6.3.1
2+
- Fixes a regression in which the ssl_subject was missing for SSL-secured connections in server mode [#199](https://github.com/logstash-plugins/logstash-input-tcp/pull/199)
3+
14
## 6.3.0
25
- Feat: ssl_supported_protocols (TLSv1.3) + ssl_cipher_suites [#198](https://github.com/logstash-plugins/logstash-input-tcp/pull/198)
36

lib/logstash/inputs/tcp.rb

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,19 @@ def close
190190
end
191191

192192
def decode_buffer(client_ip_address, client_address, client_port, codec, proxy_address,
193-
proxy_port, tbuf, socket)
193+
proxy_port, tbuf, ssl_subject)
194194
codec.decode(tbuf) do |event|
195195
if @proxy_protocol
196196
event.set(@field_proxy_host, proxy_address) unless event.get(@field_proxy_host)
197197
event.set(@field_proxy_port, proxy_port) unless event.get(@field_proxy_port)
198198
end
199-
enqueue_decorated(event, client_ip_address, client_address, client_port, socket)
199+
enqueue_decorated(event, client_ip_address, client_address, client_port, ssl_subject)
200200
end
201201
end
202202

203-
def flush_codec(codec, client_ip_address, client_address, client_port, socket)
203+
def flush_codec(codec, client_ip_address, client_address, client_port, ssl_subject)
204204
codec.flush do |event|
205-
enqueue_decorated(event, client_ip_address, client_address, client_port, socket)
205+
enqueue_decorated(event, client_ip_address, client_address, client_port, ssl_subject)
206206
end
207207
end
208208

@@ -222,10 +222,14 @@ def run_client()
222222
client_socket.close rescue nil
223223
end
224224

225+
# only called in client mode
225226
def handle_socket(socket)
226227
client_address = socket.peeraddr[3]
227228
client_ip_address = socket.peeraddr[2]
228229
client_port = socket.peeraddr[1]
230+
231+
# Client mode sslsubject extraction, server mode happens in DecoderImpl#decode
232+
ssl_subject = socket.peer_cert.subject.to_s if @ssl_enable && @ssl_verify
229233
peer = "#{client_address}:#{client_port}"
230234
first_read = true
231235
codec = @codec.clone
@@ -249,7 +253,7 @@ def handle_socket(socket)
249253
end
250254
end
251255
decode_buffer(client_ip_address, client_address, client_port, codec, proxy_address,
252-
proxy_port, tbuf, socket)
256+
proxy_port, tbuf, ssl_subject)
253257
end
254258
rescue EOFError
255259
@logger.debug? && @logger.debug("Connection closed", :client => peer)
@@ -263,14 +267,14 @@ def handle_socket(socket)
263267
ensure
264268
# catch all rescue nil on close to discard any close errors or invalid socket
265269
socket.close rescue nil
266-
flush_codec(codec, client_ip_address, client_address, client_port, socket)
270+
flush_codec(codec, client_ip_address, client_address, client_port, ssl_subject)
267271
end
268272

269-
def enqueue_decorated(event, client_ip_address, client_address, client_port, socket)
273+
def enqueue_decorated(event, client_ip_address, client_address, client_port, ssl_subject)
270274
event.set(@field_host, client_address) unless event.get(@field_host)
271275
event.set(@field_host_ip, client_ip_address) unless event.get(@field_host_ip)
272276
event.set(@field_port, client_port) unless event.get(@field_port)
273-
event.set(@field_sslsubject, socket.peer_cert.subject.to_s) if socket && @ssl_enable && @ssl_verify && event.get(@field_sslsubject).nil?
277+
event.set(@field_sslsubject, ssl_subject) unless ssl_subject.nil? || event.get(@field_sslsubject)
274278
decorate(event)
275279
@output_queue << event
276280
end

lib/logstash/inputs/tcp/decoder_impl.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,30 @@ def initialize(codec, tcp)
1111
@first_read = true
1212
end
1313

14-
def decode(channel_addr, data)
14+
def decode(ctx, data)
15+
channel = ctx.channel()
1516
bytes = Java::byte[data.readableBytes].new
1617
data.getBytes(0, bytes)
1718
data.release
1819
tbuf = String.from_java_bytes bytes, "ASCII-8BIT"
1920
if @first_read
20-
tbuf = init_first_read(channel_addr, tbuf)
21+
tbuf = init_first_read(channel, tbuf)
2122
end
2223
@tcp.decode_buffer(@ip_address, @address, @port, @codec,
23-
@proxy_address, @proxy_port, tbuf, nil)
24+
@proxy_address, @proxy_port, tbuf, @sslsubject)
2425
end
2526

2627
def copy
2728
self.class.new(@codec.clone, @tcp)
2829
end
2930

3031
def flush
31-
@tcp.flush_codec(@codec, @ip_address, @address, @port, nil)
32+
@tcp.flush_codec(@codec, @ip_address, @address, @port, @sslsubject)
3233
end
3334

3435
private
35-
def init_first_read(channel_addr, received)
36+
def init_first_read(channel, received)
37+
channel_addr = channel.remoteAddress()
3638
if @tcp.proxy_protocol
3739
pp_hdr, filtered = received.split("\r\n", 2)
3840
pp_info = pp_hdr.split(/\s/)
@@ -53,10 +55,20 @@ def init_first_read(channel_addr, received)
5355
@address = extract_host_name(channel_addr) # name _or_ address of sender
5456
@port = channel_addr.get_port # outgoing port of sender (probably random)
5557
end
58+
@sslsubject = extract_sslsubject(channel)
5659
@first_read = false
5760
filtered
5861
end
5962

63+
private
64+
def extract_sslsubject(channel)
65+
return nil unless @tcp.ssl_enable && @tcp.ssl_verify
66+
67+
channel.pipeline().get("ssl-handler").engine().getSession().getPeerPrincipal().getName()
68+
rescue Exception => e
69+
nil
70+
end
71+
6072
private
6173
def extract_host_name(channel_addr)
6274
channel_addr = java.net.InetSocketAddress.new(channel_addr, 0) if channel_addr.kind_of?(String)

spec/inputs/tcp_spec.rb

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,15 +541,18 @@ def get_port
541541
end
542542
end
543543

544-
describe "#receive" do
544+
describe "#receive", :ecs_compatibility_support do
545545
shared_examples "receiving events" do
546546
# TODO(sissel): Implement normal event-receipt tests as as a shared example
547547
end
548548

549549
context "when ssl_enable is true" do
550550
let(:input) { subject }
551551
let(:queue) { Queue.new }
552-
before(:each) { subject.register }
552+
before(:each) do
553+
allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility) if defined?(ecs_compatibility)
554+
subject.register
555+
end
553556

554557
context "when using a certificate chain" do
555558
chain_of_certificates = TcpHelpers.new.chain_of_certificates
@@ -651,6 +654,38 @@ def get_port
651654
end
652655
end
653656

657+
context "with a regular TLS setup" do
658+
let(:config) do
659+
{
660+
"host" => "127.0.0.1",
661+
"port" => port,
662+
"ssl_enable" => true,
663+
"ssl_cert" => chain_of_certificates[:b_cert].path,
664+
"ssl_key" => chain_of_certificates[:b_key].path,
665+
"ssl_extra_chain_certs" => [ chain_of_certificates[:a_cert].path ],
666+
"ssl_certificate_authorities" => [ chain_of_certificates[:root_ca].path ],
667+
"ssl_verify" => true
668+
}
669+
end
670+
671+
ecs_compatibility_matrix(:disabled,:v1, :v8 => :v1) do |ecs_select|
672+
it "extracts the TLS subject from connections" do
673+
result = TcpHelpers.pipelineless_input(subject, 1) do
674+
sslsocket.connect
675+
sslsocket.write("#{message}\n")
676+
tcp.flush
677+
sslsocket.close
678+
tcp.close
679+
end
680+
expect(result.size).to eq(1)
681+
event = result.first
682+
683+
ssl_subject_field = ecs_select[disabled: 'sslsubject', v1:'[@metadata][input][tcp][tls][client][subject]']
684+
expect(event.get(ssl_subject_field)).to eq("CN=RubyAA_Cert,DC=ruby-lang,DC=org")
685+
end
686+
end
687+
end
688+
654689
context "with enforced protocol version" do
655690
let(:config) do
656691
base_config.merge 'ssl_supported_protocols' => [ tls_version ]

src/main/java/org/logstash/tcp/Decoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.logstash.tcp;
22

33
import io.netty.buffer.ByteBuf;
4-
import java.net.SocketAddress;
4+
import io.netty.channel.ChannelHandlerContext;
55

66
/**
77
* Decoder bridge to implement in JRuby.
@@ -13,7 +13,7 @@ public interface Decoder {
1313
* @param key {@link SocketAddress}
1414
* @param message Data {@link ByteBuf} for this address
1515
*/
16-
void decode(SocketAddress key, ByteBuf message);
16+
void decode(ChannelHandlerContext context, ByteBuf message);
1717

1818
/**
1919
* Creates a copy of this decoder, that has all internal meta data cleared.

src/main/java/org/logstash/tcp/InputLoop.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public void close() {
107107
* {@link Decoder}.
108108
*/
109109
private static final class InputHandler extends ChannelInitializer<SocketChannel> {
110+
private final String SSL_HANDLER = "ssl-handler";
110111

111112
/**
112113
* {@link Decoder} supplied by JRuby.
@@ -133,7 +134,7 @@ protected void initChannel(final SocketChannel channel) throws Exception {
133134

134135
// if SSL is enabled, the SSL handler must be added to the pipeline first
135136
if (sslContext != null) {
136-
channel.pipeline().addLast(sslContext.newHandler(channel.alloc()));
137+
channel.pipeline().addLast(SSL_HANDLER, sslContext.newHandler(channel.alloc()));
137138
}
138139

139140
channel.pipeline().addLast(new DecoderAdapter(localCopy, logger));
@@ -196,9 +197,11 @@ private static final class DecoderAdapter extends ChannelInboundHandlerAdapter {
196197
this.decoder = decoder;
197198
}
198199

200+
// 6.07 updated to pass in the full netty ChannelHandlerContext instead of the remoteaddress field
201+
// corresponding interface updated
199202
@Override
200203
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
201-
decoder.decode(ctx.channel().remoteAddress(), (ByteBuf) msg);
204+
decoder.decode(ctx, (ByteBuf) msg);
202205
}
203206

204207
@Override

version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6.3.0
1+
6.3.1

0 commit comments

Comments
 (0)