Ruby script Net::SSH::HostKeyMismatch but ssh works - ruby

I can ssh into a remote host on my aws network but using net/ssh fails in a ruby script. my gem is net-ssh(4.2.0) on Ubuntu 16.04. It doesn't prompt for a passphrase even with non_interactive => false.
error:
Authentication failed for user Net::SSH::AuthenticationFailed
Why does this code fail?
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
HOST = 'myhost'
Net::SSH.start(HOST,
:auth_methods => ['publickey'],
:passphrase => 'mypassphrase',
:non_interactive => true,
:host_key => "ssh-rsa",
:keys => [ '/home/markhorrocks/.ssh/id_rsa' ]
) do |session|
output = session.exec!('ls')
puts output
end
After editing my code to this I get error
(Net::SSH::HostKeyMismatch)
HOST = 'myhost'
USER = 'markhorrocks'
Net::SSH.start(HOST, USER,
:auth_methods => ['publickey'],
:passphrase => 'mypassphrase',
:non_interactive => true,
:host_key => "ssh-rsa",
:encryption => "blowfish-cbc",
:keys => [ '/home/markhorrocks/.ssh/id_rsa' ],
:port => '1234',
:forward_agent => true,
:compression => "zlib#openssh.com"
) do |session|
output = session.exec!('ls')
puts output
end

The keys array needs to point at your private key(s). authorized_keys is the public fingerprints for keys allowed to log in to the current host. Also you seem to have put a private key type in for host_key.

Here is my solution:
#!/usr/bin/env ruby
require 'net/ssh'
HOST = 'myhost'
USER = 'markhorrocks'
Net::SSH.start(HOST, USER,
:auth_methods => ['publickey'],
:passphrase => 'mypassphrase',
:non_interactive => true,
:host_key => "ssh-rsa",
:encryption => "blowfish-cbc",
:keys => [ '/home/markhorrocks/.ssh/id_rsa' ],
:port => '1234',
:forward_agent => true,
:verify_host_key => false,
:compression => "zlib#openssh.com"
) do |session|
begin
rescue Net::SSH::HostKeyMismatch => e
puts "remembering new key: #{e.fingerprint}"
e.remember_host!
retry
end
output = session.exec!('ls')
puts output
end

Related

how to create a filter of net-ldap for Ruby

I have a working ldap search query
ldapsearch -H ldaps://ldap.google.com -b dc=pangeare,dc=com '(memberOf=cn=pcore_readonly,ou=Groups,dc=pangeare,dc=com)'
I wonder how should I create the corresponding filter in ruby net-ldap:
treebase="dc=pangeare,dc=com"
filter = Net::LDAP::Filter.XXXXXXXXXX
ldap.search(:base => treebase, :filter=> filter, :return_result => true) do |entry|
entry.each do |attr,values|
puts "DN: #{entry.dn}"
Net::LDAP::Filter#eq should be what your looking for
require 'net/ldap'
host = 'ldap.google.com'
port = 636
auth = {
:method => :simple, # or :simple_tls ? (I don't know Google's LDAP)
:username => 'uid=username,dc=xxx,dc=xxx',
:password => 'password'
}
ldap = Net::LDAP.new( :host => host, :port => port, :auth => auth)
base = 'dc=pangeare,dc=com'
filter = Net::LDAP::Filter.eq('memberOf','cn=pcore_readonly,ou=Groups,dc=pangeare,dc=com')
ldap.search(:base => base, :filter => filter) do |entry|
p entry
end
Or you can just use Net::LDAP::Filter#construct:
filter = Net::LDAP::Filter.construct('(memberOf=cn=pcore_readonly,ou=Groups,dc=pangeare,dc=com)')

Ruby Curl::Multi.http 302 redirect problems

I seem to be having a problem with 302 redirects using curb (Ruby's curl programs)
Here's the code snippet that is ** NOT working** (it's NOT doing the 302 redirect)
easy_options = {:follow_location => true, :enable_cookies => true, :useragent => 'curb', :cookiefile => 'cookie.txt'}
multi_options = {:pipeline => true}
url_fields = [
{
:url => 'https://x.y.z/webapps/login/',
:method => :post,
:follow_location => true,
:enable_cookies => true,
:useragent => 'curb',
:post_fields => {
'user_id' => 'xxxx',
'password' => 'xxxx',
'action' => 'login',
'encoded_pw' => Base64.strict_encode64('xxxx')},
}
]
Curl::Multi.http(url_fields,{:pipeline => true}) do |easy, code, method|
puts easy.header_str
end
Here's the code snippet that appears to be working (it's doing the 302 redirect)
easy_options = {:follow_location => true, :enable_cookies => true, :useragent => 'curb', :cookiefile => 'cookie.txt'}
multi_options = {:pipeline => true}
url_fields = [
{ :url => 'https://x.y.z/webapps/login/',
:post_fields => {
'user_id' => 'xxxx',
'password' => 'yyyy',
'action' => 'login',
'encoded_pw' => Base64.strict_encode64('yyyy')}}
]
Curl::Multi.post(url_fields, easy_options, multi_options) do|easy|
# do something interesting with the easy response
puts easy.last_effective_url
end
Question: Why is the first block not doing the 302 redirect as expected? :follow_location is set to true?
Thanks in advance!
Let me know if you need more information

Ruby: Resque queues instantly

I have the following code. Its job to is to send an email based on data given through the browser (using Sinatra). It sends an email to the address given after 20 seconds. When I run the program, It instantly sends the email, without waiting for the time. Can anyone help me out with this issue.
require 'rubygems'
require 'sinatra'
require 'pony'
require 'resque'
require 'resque_scheduler'
require 'active_support/time'
Resque.redis = 'localhost:6379'
Resque::Scheduler.dynamic = true
def sendMail
Pony.mail({
:to => 'eldurotaduro#gmail.com',
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'EMAIL',
:password => 'PASSWD',
:authentication => :plain, # :plain, :login, :cram_md5, no auth by default
:domain => "localhost.localdomain" # the HELO domain provided by the client to the server
},
:body => 'roar'
})
end
class Roar
def self.queue; :app; end
end
class ChildJob
#message
#email
def setMess(mes)
#message = mes
end
def setMail(mail)
#email = mail
end
def self.queue; :app; sendMail; end
def self.perform
Pony.mail({
:to => 'eldurotaduro#gmail.com',
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'EMAILHERE#gmail.com',
:password => 'PASSWD',
:authentication => :plain, # :plain, :login, :cram_md5, no auth by default
:domain => "localhost.localdomain" # the HELO domain provided by the client to the server
},
:body => 'HAHAH'
})
end
end
get '/:email/:message/:time' do
email = params[:email]
message = params[:message]
time = params[:time]
time = time.to_i
Resque.enqueue_in(20.seconds, ChildJob)
end
Keep the :app symbol in self.queue as that's what sets the default queue (see this StackOverflow answer). Put the sendMail into a self.perform method, as that's the thing you want done when the schedule is met. e.g.
def self.queue
:app
end
def self.perform
sendMail
end

2 forms on same page, Sending using Pony in Sinatra, same email address

i am using Pony.mail to send mail within Sinatra, what i have now is two forms, one that only sends an email address for subscription to newsletter and the second form is a contact form, both are going through the same action.
What I am trying to achieve is if the subscription field is completed then only send those params or if the contact form is completed and sent then send those params
Heres what i come up with so far, but getting undefined method nil
post '/' do
require 'pony'
Pony.mail(
:from => params[:name] || params[:subscribe],
:to => 'myemailaddress',
:subject => params[:name] + " has contacted you via the Website" || params[:subscribe] + " has subscribed to the newsletter",
:body => params[:email] + params[:comment],
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'myemailaddress',
:password => 'mypassword',
:authentication => :plain,
:domain => "localhost.localdomain"
})
redirect '/success'
end
is this even possible or would each form have to be dealt with individually?
Thanks
There are several stages I'd go through to refactor this code.
1. Extract the things that are changing (and make them more Rubyish)
post '/' do
require 'pony'
from = params[:name] || params[:subscribe]
subject = "#{params[:name]} has contacted you via the Website" ||
"#{params[:subscribe]} has subscribed to the newsletter"
body = "#{params[:email]}#{params[:comment]}"
Pony.mail(
:from => from,
:to => 'myemailaddress',
:subject => subject,
:body => body,
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'myemailaddress',
:password => 'mypassword',
:authentication => :plain,
:domain => "localhost.localdomain"
})
redirect '/success'
end
2. Make clear your intentions
in this case, that there are two branches through the code.
post '/' do
require 'pony'
if params[:name] # contact form
from = params[:name]
subject = "#{params[:name]} has contacted you via the Website"
else # subscription form
from = params[:subscribe]
subject = "#{params[:subscribe]} has subscribed to the newsletter"
end
body = "#{params[:email]}#{params[:comment]}"
Pony.mail(
:from => from,
:to => 'myemailaddress',
:subject => subject,
:body => body,
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'myemailaddress',
:password => 'mypassword',
:authentication => :plain,
:domain => "localhost.localdomain"
})
redirect '/success'
end
(I'm not a big fan of setting local vars within conditional branches, but we'll ignore that for clarity. I'd probably create a hash before the conditional with the keys already done, and then populate it in the branches but YMMV.)
3. Extract what doesn't change from what does.
Sinatra has a configure block just for this kind of thing.
require 'pony'
configure :development do
set :email_options, {
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'myemailaddress',
:password => 'mypassword',
:authentication => :plain,
:domain => "localhost.localdomain"
}
end
Pony.options = settings.email_options
Notice I've added :development as you may want to set it up differently for production.
Now your route is a lot cleaner and easier to debug:
post '/' do
if params[:name] # contact form
from = params[:name]
subject = "#{params[:name]} has contacted you via the Website"
else # subscription form
from = params[:subscribe]
subject = "#{params[:subscribe]} has subscribed to the newsletter"
end
body = "#{params[:email]}#{params[:comment]}"
Pony.mail
:from => from,
:to => 'myemailaddress',
:subject => subject,
:body => body,
redirect '/success'
end
My last tip, would be to put as many of those Pony options into ENV vars, which will not only keep things like passwords out of source control but also allow you to change the settings a lot easier. Perhaps put them in a Rakefile and load different environments for different contexts etc.
To use environment variables, I do the following:
# Rakefile
# in this method set up some env vars
def basic_environment
# I load them in from a YAML file that is *not* in source control
# but you could just specify them here
# e.g. ENV["EMAIL_A"] = "me#example.com"
end
namespace :app do
desc "Set up the environment locally"
task :environment do
warn "Entering :app:environment"
basic_environment()
end
desc "Run the app locally"
task :run_local => "app:environment" do
exec "bin/rackup config.ru -p 4630"
end
end
# from the command line, I'd run
`bin/rake app:run_local`
# in the Sinatra app file
configure :production do
# these are actual settings I use for a Heroku app using Sendgrid
set "email_options", {
:from => ENV["EMAIL_FROM"],
:via => :smtp,
:via_options => {
:address => 'smtp.sendgrid.net',
:port => '587',
:domain => 'heroku.com',
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:authentication => :plain,
:enable_starttls_auto => true
},
}
end
# then a block with slightly different settings for development
configure :development do
# local settingsā€¦
set "email_options", {
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => ENV["EMAIL_A"],
:password => ENV["EMAIL_P"],
:authentication => :plain,
:domain => "localhost.localdomain"
}
}
end
I usually keep most of these setting in a YAML file locally for development, but add these to the production server directly. There are lots of ways to handle this, YMMV.

Ruby - multidimensional hash

How do I make this multidimensional? Each search result entry has multiple attributes :attributes => ['sAMAccountName','givenName','SN','mail']. and there can be many entries in the result. This code is good for creating only one entry with multiple attributes.
def self.Find(attribute, loginID)
conn = Net::LDAP.new :host => SERVER,
:port => PORT,
:base => BASE,
:auth => {
:username => 'admin',
:password => 'admin',
:method => :simple
}
if conn.bind
result = HashWithIndifferentAccess.new
conn.search( :base => LDAPBASE,
:filter => Net::LDAP::Filter.eq( attribute, loginID+"*" ),
:attributes => ['sAMAccountName','givenName','SN','mail'],
:return_result => true
).each do |entries|
entries.each do |attribute, values|
values.each do |value|
result[attribute] = value
end
end
end
result
end
end
The actual response I get from ldap is like this -
puts result.to_s
{
"dn"=>"CN=somename\\, somefirstname,OU=Users,DC=site,DC=com",
"sn"=>"somename",
"givenname"=>"somefirstname",
"samaccountname"=>"someuserid",
"mail"=>"someone#somthing.com"
}
Since the search is has * wildcard to find all matching entries. Ldap will return multiple entries in the format above. similar to this -
{
"dn"=>"CN=somename\\, somefirstname1,OU=Users,DC=site,DC=com",
"sn"=>"somename1",
"givenname"=>"somefirstname1",
"samaccountname"=>"someuserid1",
"mail"=>"someone1#somthing.com"
},
{
"dn"=>"CN=somename\\, somefirstname2,OU=Users,DC=site,DC=com",
"sn"=>"somename2",
"givenname"=>"somefirstname2",
"samaccountname"=>"someuserid2",
"mail"=>"someone2#somthing.com"
},
{
"dn"=>"CN=somename\\, somefirstname3,OU=Users,DC=site,DC=com",
"sn"=>"somename3",
"givenname"=>"somefirstname3",
"samaccountname"=>"someuserid3",
"mail"=>"someone3#somthing.com"
},
A[a=>1,b=>11,c=>111]
B[a=>2,b=>22,c=>222]
C[a=>3,b=>33,c=>333]
D[a=>4,b=>44,c=>444]
I'm not sure that I understood the question.
def self.Find(attribute, loginID)
conn = Net::LDAP.new :host => SERVER,
:port => PORT,
:base => BASE,
:auth => {
:username => 'admin',
:password => 'admin',
:method => :simple
}
if conn.bind
conn.search( :base => LDAPBASE,
:filter => Net::LDAP::Filter.eq( attribute, loginID+"*" ),
:attributes => ['sAMAccountName','givenName','SN','mail'],
:return_result => true
).reduce(Array.new) do |acc, el|
#
# Any modification of the entry must be here
#
acc + [el]
end
end
end
end
I think Array.map is good choice too
My assumption:
conn.search return Array of Hash
the form of result you want:
[
{"uid":"123","displayName":"User 123","mail":"123#example.com"},
{"uid":"456","displayName":"User 456","mail":"456#example.com"},
{"uid":"789","displayName":"User 789","mail":"789#example.com"}
]

Resources