Configure Rails for ElasticSearch on an ubuntu remote server over https - ruby-on-rails

My prod app (on an Ubuntu server) can't access my ElasticSearch server (on another Ubuntu server) over HTTPS.
My ES server is based on a proxy pass with Nginx with https and a basic auth. I can access it via https://elastic.myapp.com/, set my basic auth username/pass and get the ES response:
{
"name": "Cutthroat",
"cluster_name": "elasticsearch",
"version": {
"number": "2.0.0",
"build_hash": "ab54438d6af8f9340d50c5c786151783ce7d6be5",
"build_timestamp": "2015-10-22T08:09:48Z",
"build_snapshot": false,
"lucene_version": "5.2.1"
},
"tagline": "You Know, for Search"
}
My 4.2 rails app works well in local with ES on localhost:9200.
I used these gems:
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
My 4.2 rails elasticsearch.yml config in production looks like:
host1:
scheme: https
host: elastic.myapp.com
port: 443
username: username
password: password
My ruby connection file is:
Elasticsearch::Model.client = Elasticsearch::Client.new hosts: hosts, transport_options: { ssl: { ca_file: '/etc/ssl/certs/cacert.pem' } }
The error is:
(SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed)
I don't know why cos' I have installed /etc/ssl/certs/cacert.pem on my Ubuntu 14.04 (installed on the server where is the Rails app, not the https ES server, is that right ?)
Note that if I change the connection to:
Elasticsearch::Model.client = Elasticsearch::Client.new hosts: hosts, transport_options: { ssl: { verify: false } }
it works... bu the SSL is not verified in production, too bad...
What did I miss on my Ubuntu servers ?
Answer: In fact, I need top copy the SSL cert from the Elastic server to the App server, indicate the location to Elastic config and now it works.

Related

Rails CQL cannot connect to AWS Keyspaces (AWS Cassandra)

I am trying to connect from a Ruby on Rails application to AWS Keyspaces (AWS Cassandra), but I cannot manage to do it. I use the cequel gem and generated the config/cequel.yml which contains a similar thing to the following:
development:
host: "CONTACT_POINT"
username: "USER"
password: "PASS"
port: 9142
keyspace: key_development
max_retries: 3
retry_delay: 0.5
newrelic: true
ssl: true
server_cert: 'config/certs/AmazonRootCA1.pem'
replication:
class: NetworkTopologyStrategy
datacenter1: 3
datacenter2: 2
durable_writes: false
(Credentials where used in another app and they work which is working as expected.)
when I try to run:
rake cequel:keyspace:create
I get the following errors:
Cassandra::Errors::NoHostsAvailable: All attempted hosts failed: x.xxx.xxx.xxx (Cassandra::Errors::ServerError: Internal Server Error)
Set the dc to us-east-1 . drop the replication definition.

Why is elasticserach-rails suddenly raising Faraday::ConnectionFailed (execution expired)?

I'm using Elasticsearch in a Rails app via the elasticsearch-model and elasticsearch-rails gems.
Everything was previously working fine, but after some updates I am now getting a Connection Failed error whenever I attempt to interact with the remote cluster (AWS Elasticsearch).
> MyModel.__elasticsearch__.create_index! force: true
=> Faraday::ConnectionFailed (execution expired)
I'm struggling to work out what is causing this connection error. After searching for similar issues, I've adjusted timeouts and tried various combinations of http, https and naked urls, but no success.
What is a sensible way to debug this connection error?
My Elasticsearch is initialized like this.
#initializers/elasticsearch.rb
require 'faraday_middleware'
require 'faraday_middleware/aws_sigv4'
credentials = Aws::Credentials.new(
ENV.fetch('AWS_ACCESS_KEY_ID'),
ENV.fetch('AWS_SECRET_ACCESS_KEY')
)
config = {
url: ENV.fetch('AWS_ELASTICSEARCH_URL'),
retry_on_failure: true,
transport_options: {
request: { timeout: 10 }
}
}
client = Elasticsearch::Client.new( config ) do |f|
f.request :aws_sigv4, credentials: credentials, service: 'es', region: ENV.fetch('AWS_ELASTICSEARCH_REGION')
end
Elasticsearch::Model.client = client
It turns out that there were two parts to this issue.
First, the Elasticsearch::Client, as configured above, was using the default ES port 9200. My ES is hosted on AWS, which appears to not expose this port.
After fixing this, I ran into the second issue (which I suspect is more specific to this app). I started getting a Faraday::ConnectionFailed (end of file) error. I don't know what caused this, but configuring the client with host and scheme fixed it.
My final config is as follows:
#initializers/elasticsearch.rb
# ...
config = {
host: ENV.fetch('AWS_ELASTICSEARCH_URL'),
port: 443,
scheme: "https",
retry_on_failure: true,
transport_options: {
request: { timeout: 10 }
}
}
client = Elasticsearch::Client.new( config ) do |f|
# ...
N.B. AWS_ELASTICSEARCH_URL must return a URL without protocol.
This is because of version issue.
Use this gem 'elasticsearch-model', '~> 5'

AWS elasticsearch service throws Faraday::ConnectionFailed: Failed to open TCP connection to https:80 (getaddrinfo: Name or service not known)

I am currently trying to configure AWS’ elasticsearch service in my Rails v 5.1.4 application. I am using elasticsearch-rails 6.0.0. The issue I am currently getting I believe is with how my elasticsearch client is being set up in my initializer. One restriction I have is I can’t use the faraday_middleware-aws-signers-v4 gem to help communication between my AWS elastisearch instance and my app. I am attempting to do this with just aws-sdk-rails 1.0.1. Since this server is in the same security group as the elasticsearch instance I am assuming I don't need to pass in credentials.
Here is the my error:
Faraday::ConnectionFailed: Failed to open TCP connection to https:80 (getaddrinfo: Name or service not known)
from /usr/local/lib/ruby/2.4.0/net/http.rb:906:in `rescue in block in connect'`
Here is my initializers/elasticsearch.rb:
config = {
hosts: {host: 'https://search-epl-elasticsearch-xxxxxxxxxxxxxxxxxx.us-east-2.es.amazonaws.com', port: '80'},
transport_options: {
request: { timeout: 5 }
}
}
Elasticsearch::Model.client = Elasticsearch::Client.new(config)
I realise it's months later, but it appears you're configuring elasticsearch with an https url and telling it to use port 80 instead of 443. Try this instead:
config = {
url: 'https://search-epl-elasticsearch-xxxxxxxxxxxxxxxxxx.us-east-2.es.amazonaws.com',
transport_options: {
request: { timeout: 5 }
}
}
Elasticsearch::Model.client = Elasticsearch::Client.new(config)

ActionMailer SMTP "certificate verify failed"

I want to send emails from my Rails web application, and I do not want to disable TLS certificate verification. However for some reason, it always fails with "SSLv3 read server certificate B: certificate verify failed", even though the server certificate is valid.
I doubled checked with openssl s_client (using /etc/ssl/certs/ca-certificates.crt), and running the following in the rails console also works, delivering successfully.
smtp = Net::SMTP.new(host, port)
smtp.enable_tls
smtp.start("localhost", username, password, :login) do |smtp|
smtp.send_message msgstr, from, to
end
The server has Rails 4.2.6 and Ruby 2.3.0
config.action_mailer.smtp_setting = {
address:
port: 465,
user_name:
password:
authentication: :login,
openssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
enable_starttls_auto: false,
ssl: true
}
From the described behavior I am quite sure that peer verification has not been done in the console and that you need to explicitly set the certificate store for verifying peer certificates in your Rails configuration.
Why it "works" in the console and how to actually verify peers there:
The observation that it works from the console but does not from Rails code is caused by the fact that smtp.enable_tls in your console code does not force peer verification whereas your Rails configuration apparently does. Indeed, when you write the command to the console, you get the SSLContext printed out:
smtp.enable_tls
# => #<OpenSSL::SSL::SSLContext:0x000000064043d0 #cert=nil, #key=nil,
#client_ca=nil, #ca_file=nil, #ca_path=nil, #timeout=nil,
#verify_mode=nil, #verify_depth=nil, #renegotiation_cb=nil,
#verify_callback=nil, #cert_store=nil, #extra_chain_cert=nil,
#client_cert_cb=nil, #session_id_context=nil, #tmp_dh_callback=nil,
#session_get_cb=nil, #session_new_cb=nil, #session_remove_cb=nil,
#tmp_ecdh_callback=nil, #servername_cb=nil, #npn_protocols=nil,
#alpn_protocols=nil, #alpn_select_cb=nil, #npn_select_cb=nil>
Note that #verify_mode is nil so there is no peer verification enabled by default on the SSLContext.
To force peer verification in console, so that you can play with the SSL settings manually, you need to use a custom SSLContext and pass it to enable_tls:
ssl_context = Net::SMTP.default_ssl_context
ssl_context.set_params
smtp.enable_tls(ssl_context)
# => #<OpenSSL::SSL::SSLContext:0x000000063c27c8 #cert=nil, #key=nil,
#client_ca=nil, #ca_file=nil, #ca_path=nil, #timeout=nil,
#verify_mode=1, #verify_depth=nil, #renegotiation_cb=nil,
#verify_callback=nil, #cert_store=#<OpenSSL::X509::Store:0x00000002894408 #verify_callback=nil, #error=nil, #error_string=nil, #chain=nil, #time=nil>, #extra_chain_cert=nil,
#client_cert_cb=nil, #session_id_context=nil, #tmp_dh_callback=nil,
#session_get_cb=nil, #session_new_cb=nil, #session_remove_cb=nil,
#tmp_ecdh_callback=nil, #servername_cb=nil, #npn_protocols=nil,
#alpn_protocols=nil, #alpn_select_cb=nil, #npn_select_cb=nil>
Watch closely the differences: the SSLContext now has verify_mode set to 1 and has a certificate store for the verifications defined. This is (among other things) what the set_params method in SSLContext does.
How to configure the certificate store in Rails
Now, Rails does not call the set_params methods when constructing the SSLContext for SMTP connection. Instead, it sets the individual attributes on it according to the options (see here and here in the source code). You have properly configured Rails that you want to verify peer certificates but you have not configured a certificate store to verify peers against.
This can be done using the ca_file or ca_path options, so the following Rails configuration should work for you:
config.action_mailer.smtp_setting = {
...
ssl: true
enable_starttls_auto: false,
openssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
ca_file: "/etc/ssl/certs/ca-certificates.crt",
...
}
I have no idea why this is not properly documented in the Rails Guides...
This Rails configuration works for me (using Ruby 2.2.2 and Rails 5):
ActionMailer::Base.smtp_setting = {
...
enable_starttls_auto: true,
openssl_verify_mode: OpenSSL::SSL::VERIFY_PEER,
openssl_verify_depth: 3, # if your CA is a sub signer
ca_file: "/etc/ssl/certs/ca-certificates.crt",
...
}

Server not available on localhost with response code 403 & RuntimeError in Ruby on Rails

After changing proxy settings in open_uri.rb and server_manage.rb I finally managed to install neo4j behind a proxy server. The neo4j server is running on port 7000 ( It opens in the browser) but when i enter :
$rails generate scaffold post title body
Error:
/.rvm/gems/ruby-2.2.3/gems/neo4j-core-5.1.6/lib/neo4j-server/cypher_session.rb:51:in `open': Server not available on http://localhost:7000 (response code 403) (RuntimeError)
What should I do ?
Any help is appreciated!!
$ ruby --version
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
$ rails --version
Rails 4.2.2
My guess - proxy issues. Things may behave differently in your browser and code (because those are 2 different environment).
To check what exactly is going on with your database, you should try to make request to Neo4j manually, from command line.
Example with using curl:
# if auth enabled
curl -i --user username:password http://localhost:7000/db/data/
# if auth disabled
curl -i http://localhost:7000/db/data/
This will give you more details on what exactly is not working.
Also you can assemble basic ruby script that will make HTTP request, to check what you receive in response in this case.
A 403 might mean that your Neo4j authentication credentials are wrong. See http://neo4jrb.readthedocs.org/en/5.1.x/Setup.html#rails-configuration for details but basically, adding something like this to application.rb might do the trick:
config.neo4j.session_options = { basic_auth: { username: 'foo', password: 'bar'} }
Also, since you mentioned needing help with the proxy, you can add an initialize key to set that.
init = { proxy: { uri: 'http://myproxy', user: 'username', password: 'password' }}
auth = { username: 'neo4j', password: 'pwhere'}
config.neo4j.session_options = { basic_auth: auth, initialize: init }

Resources