horv77
horv77

Reputation: 43

Ruby TCP server with OpenSSL and Letsencrypt certificate

I'd like to setup a minimal Ruby TCP server with SSL layer using certificate generated from Letsencrypt.

I install packages (system is Ubuntu 18, 64 bit):

    sudo apt install ruby certbot

I generate the cert like this:

    certonly -d example.com

Then run the following code:

    require "socket"
    require "openssl"
    require "base64"

    port = 443
    cert_path = "/etc/letsencrypt/live/example.com/fullchain.pem"
    key_path = "/etc/letsencrypt/live/example.com/privkey.pem"
    chain_path = "/etc/letsencrypt/live/example.com/chain.pem"

    context = OpenSSL::SSL::SSLContext.new
    context.cert = OpenSSL::X509::Certificate.new( File.read( cert_path ))
    context.key = OpenSSL::PKey::RSA.new( File.read( key_path ))
    context.extra_chain_cert = OpenSSL::X509::Certificate.new( File.read( chain_path ))
    tcp_server = TCPServer.new( port )
    ssl_server = OpenSSL::SSL::SSLServer.new( tcp_server, context )

    loop {

        client = ssl_server.accept

        data = client.gets
        response = "I got #{data.dump}"

        puts response
        client.puts response
        client.close

    }

Then send a POST request using curl like this:

    curl --data "param1=value1&param2=value2" https://example.com

Then I get the following error message:

Traceback (most recent call last):
5: from api.rb:35:in `<main>'
4: from api.rb:35:in `loop'
3: from api.rb:37:in `block in <main>'
2: from /var/lib/gems/2.5.0/gems/openssl-2.1.2/lib/openssl/ssl.rb:483:in `accept'
1: from /var/lib/gems/2.5.0/gems/openssl-2.1.2/lib/openssl/ssl.rb:483:in `new'
/var/lib/gems/2.5.0/gems/openssl-2.1.2/lib/openssl/ssl.rb:483:in `initialize': undefined method `each' for #<OpenSSL::X509::Certificate:0x0000556df03c22c0> (NoMethodError)

Could you guys kindly help me track down the problem? I've been struggling with this for some time now. The official example does not work either which is here:

https://docs.ruby-lang.org/en/2.1.0/OpenSSL.html#module-OpenSSL-label-SSL+and+TLS+Connections

I've read in a SO thread that for Letsencrpyt certs I should include the .extra_chain_cert property. Still the issue is the same.

Thanks.

Upvotes: 2

Views: 433

Answers (1)

David Grayson
David Grayson

Reputation: 87486

The extra_chain_cert property is supposed to be an array according to its documentation, but you are just setting it to be a single certificate. The openssl gem is crashing because it expected that property to be an array and called the each method on it to iterate over the elements of the array.

You can try putting your certificate into a one-element array by enclosing it in square brackets ([ and ]).

context.extra_chain_cert = [OpenSSL::X509::Certificate.new(File.read(chain_path))]

I just tested it here using my own Lets Encrypt certificate and it worked for me. I used port 444, and when I pointed a browser to https://example.com:444 the Ruby program said:

I got "GET / HTTP/1.1\r\n"

Upvotes: 2

Related Questions