Chef - using a ruby script to edit multiple values programmatically - ruby

I'm attempting to write a ruby script so I can edit the run_lists of a bunch of nodes resulting from a knife search command. I was told to try to use knife exec, but I am yielding the same results as I am just executing the script.
I'm using a OS command dictated by the back ticks. The first command works using the knife search, but when I feed those results into to the following each_line block, it gives me the error in the comments of the script. So, obviously the first part is working, but the second is not even though its setup the exact same way.
#!/usr/bin/env ruby
#
#
#
# %CORPCERT% = C:\Users\myuser\Documents\test\knife.rb
# This contains all the pointers to the client.pem, and other required files.
output = `knife search node "fqdn:node*test*.example.net" -i -c %CORPCERT%`
output.each_line do |result|
#puts result
puts "Adding run_list to #{result}"
`knife node run_list add #{result} "role[role_zabbix_agent_corp_prod]" -c %CORPCERT%`
#puts "#{exitcode}"
end
#C:\U\P028300\Desktop> knife exec apply_run_list.rb -c %CORPCERT%
# => Adding run_list to 8 items found
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeSTtestST0.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestST0.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestST1.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestRT1.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestRT2.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeSTtestRT0.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestRT3.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
# => Adding run_list to nodeGWtestRT0.example.net
# => WARNING: No knife configuration file found
# => ERROR: Your private key could not be loaded from C:\chef\client.pem
I know I'm not doing the knife exec command correctly, and the documentation is confusing to me. I haven't found any examples that are close, or related to what I am trying to do. How should I go about trying to programmatically search for nodes, and then update, or add items to their run_list?

A simple way of doing this:
knife exec -E 'nodes.transform("fqdn:WSO2*") { |n| n.run_list << "role[role_zabbix_agent_corp_prod]" }'
nodes.transform handles the search loop and the save (as long the block doesn't return nil/false) and RunList#<< already checks if it is already in the run list.

A simpler solution is to use ridley, a ruby API library for chef server:
require 'ridley'
Ridley::Logging.logger.level = Logger.const_get 'ERROR'
ridley = Ridley.from_chef_config("C:\Users\myuser\Documents\test\knife.rb", {:ssl => {:verify => false}})
ridley.search(:node, "fqdn:node*test*.example.net").each { |n|
n.merge_data(:run_list => ["role[role_zabbix_agent_corp_prod]"])
n.save
}

This is what I did to accomplish what I wanted to do after talking with some friends and IRC chat rooms.
# similar to knife search node "fqdn:WSO2*"
search("node", "fqdn:WSO2*").each do |search_node|
# This looks at the array of the node information, and if the
# run_list already exists, do nothing and move on.
unless search_node.run_list.include?("role[role_zabbix_agent_corp_prod]")
# This adds the listed role, or recipe to the end of the run_list
search_node.run_list.push "role[role_zabbix_agent_corp_prod]"
# Save the modifications.
search_node.save
end #=> End Unless
end #=> End Search
# Make sure to add this to the end, or it will continue to keep running.
exit 0
Put the above script into a file named script.rb and run it as such:
knife exec script.rb
After doing reading through knife exec and also, chef-shell, I had to play around with the data structures and figure out how they were presented by Chef. The site here: http://www.bonusbits.com/main/Reference:Chef_Node_Data_Structure and http://www.bonusbits.com/main/Reference:Chef_Shell helped a lot as well. I hope this helps someone wanting to understand Chef a bit more.

Related

metasploit couldnt find my new created modules

I created a new folder call ~/.msf6/modules/auxiliary/scanner/mssql first, and then I created a new ruby file call ihaz_sql.rb
and here is the code
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MSSQL
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'I HAZ SQL Utility',
'Version' => '$Revision: 7243 $',
'Description' => 'This just prints some funny stuff.',
'Author' => 'TESTs security',
'License' => MSF_LICENSE
)
deregister_options('RPORT', 'PHOST')
end
def run_host(ip)
begin
puts 'I HAZ SQL!!!!'
end
end
end
and then I open metasploit using msfconsole
and do search ihaz and search ihaz_sql, but all of above results shows
[-] No results from search
here is the tutorial I am following https://www.offensive-security.com/metasploit-unleashed/building-module/
I myself am pretty new to Metasploit, so this answer may or may not help you.
I came across such an issue before, the "fix" for it was just to run the command:
use <full pathname to the ruby script>. For example, after adding a ~/.msf6/modules/exploits/cgi/webapps/example.rb ruby script file, run the updatedb command and directly run the use exploit/cgi/webapps/example.rb command to use the exploit.
Hope this solves your issue.

Dump :node object in chef

I am a dev tasked with making some changes to the cookbooks owned by the devops team. We don't have access to knife or the chef server.
I tried to add some debugging to an existing chef recipe
members = search(
:node,
search_str,
filter_result: {
'name' => ['name'],
}
#
# print statements NOT working as expected
#
p ":node=" + :node.to_s # <--- prints ":node=node"
pp :node.to_s # <--- prints "node"
pp :node # <--- prints :node
Is it possible to dump the entire json structure of the :node object? Any workarounds. I want to see the structure of the :node object to debug the search() call as the search isn't working as expected.
Thank you.
:node in ruby is a symbol, not a variable. Symbols always start with colon (:). For starters you can think of them as of immutable strings. That is why you get such output. You just print a lot of strings.
What you actually need is just node:
pp "node:", node
But beware, node is a huge object with a lot of attributes.

Could not find class at /tmp/vagrant-puppet

I can't provision, i search in the other threads but no one is useful!
I'm using Vagrant with Puppet both of them at the latest version, my project structure is:
prova
|
|__Vagrantfile
|
|__puppet
|__manifests
| |__ubonda.pp
|
|__modules
|
|__apache
|
|__manifests
|
|__apache.pp
My VagrantFile is:
Vagrant.configure("2") do |config|
config.vm.define "ubonda" do |vm0|
vm0.vm.hostname = "ubonda"
vm0.vm.box = "hashicorp/precise64"
vm0.vm.provision "puppet" do |puppet|
puppet.manifests_path = 'puppet/manifests'
puppet.module_path = 'puppet/modules'
puppet.manifest_file = "ubonda.pp"
end
end
end
My ubonda.pp file is:
# default path
Exec {
path => ["/usr/bin", "/bin", "/usr/sbin", "/sbin", "/usr/local/bin", "/usr/local/sbin"]
}
include apache
My apache.pp file is:
class apache {
# install apache
package { "apache2":
ensure => present,
require => Exec["apt-get update"]
}
# ensures that mode_rewrite is loaded and modifies the default configuration file
file { "/etc/apache2/mods-enabled/rewrite.load":
ensure => link,
target => "/etc/apache2/mods-available/rewrite.load",
require => Package["apache2"]
}
# create directory
file {"/etc/apache2/sites-enabled":
ensure => directory,
recurse => true,
purge => true,
force => true,
before => File["/etc/apache2/sites-enabled/vagrant_webroot"],
require => Package["apache2"],...
}
}
If i launch vagrant provision i obtain :
==> ubonda: Running provisioner: puppet...
==> ubonda: Running Puppet with ubonda.pp...
==> ubonda: warning: Could not retrieve fact fqdn
==> ubonda: Could not find class apache for ubonda at /tmp/vagrant-puppet/manifests-846018e2aa141a5eb79a64b4015fc5f3/ubonda.pp:6 on node ubonda
I search in the tmp/manifests folder of the ubonda vm and inside there is only the ubonda.pp while in the tmp/modules there is the apache but the two are not connected, so I tried to copy inside the manifest but nothing has changed, how can I do?
The solution is very simple but I leave the question for posterity.
The file containing the puppet specifications must be named as init.pp for it to be correctly recognized

Chef template loop: can't convert Chef::Node::immutableMash into String

I've got a Vagrant setup in which I'm trying to use Chef-solo to generate an conf file which loops though defined variables to pass to the application. Everything is working except the loop and I'm not familiar enough with Ruby/Chef to spot the error.
I'm going to lay out the whole chain of events in case there is something along the way that is the problem, but the first portions of this process seem to work fine.
A config file is written in yaml and includes env variable definitions to be passed:
...
variables:
- DEBUG: 2
...
The config file is read in by the Vagrantfile into a ruby hash and used to create the Chef json nodes:
...
settings = YAML::load(File.read("config.yaml"))
# Provision The Virtual Machine Using Chef
config.vm.provision "chef_solo" do |chef|
chef.json = {
"mysql" => {"server_root_password" => "secret"},
"postgresql" => {"password" => {"postgres" => "secret"}},
"nginx" => {"pid" => "/run/nginx.pid"},
"php-fpm" => {"pid" => "/run/php5-fpm.pid"},
"databases" => settings["databases"] || [],
"sites" => settings["sites"] || [],
"variables" => settings["variables"] || []
}
...
A bunch of chef cookbooks are run (apt, php, nginx, mysql etc) and finally my custom cookbook which is whats giving me grief. The portion of the cookbook responsible for creating a the conf file is shown here:
# Configure All Of The Server Environment Variables
template "#{node['php-fpm']['pool_conf_dir']}/vars.conf" do
source "vars.erb"
owner "root"
group "root"
mode 0644
variables(
:vars => node['variables']
)
notifies :restart, "service[php-fpm]"
end
And the vars.erb is just a one-liner
<%= #vars.each {|key, value| puts "env[" + key + " = " + value } %>
So, when I run all this chef spits out an error about not being able to convert a hash to a string.
can't convert Chef::Node::immutableMash into String
So for some reason this is coming across as an immutableMash and the value of key ends up being the hash [{"DEBUG"=>2}] and value ends up a nil object, but I'm not sure why or how to correct it.
The hash is ending up as the value of key in your example because the YAML file declares DEBUG: 2 as a list member of variables. This translates to variables being an array with a single hash member.
Try changing the template code to this:
<%= #vars[0].each {|key, value| puts "env[" + key + " = " + value } %>
Or try changing the YAML to this and not changing the template code:
variables:
DEBUG: 2
Either change will get your template loop iterating over the hash that you are expecting.

privlage issues when attempting to run the passenger-status command from within a rubyscript

I wrote a collectd plugin in ruby that is suppose to check passengers status and report back the various metrics. When I test my script on the all works well, but when I attempt to run my script through collectd it fails with the following message.
"ERROR: You are not authorized to query the status for this Phusion Passenger instance. Please try again with 'sudo'."
I then changed my ruby script to use the sudo command for passenger status which resulted in
"exec plugin: exec_read_one: error = sudo: sorry, you must have a tty to run sudo"
I then tried getting collectd to run the script as root but I got the following
"exec plugin: Cowardly refusing to exec program as root."
I am not sure what else I can try. the command that is failing when used by a user other that root is passenger-status
Here is the script
#!/usr/bin/env ruby
require 'getoptlong'
# The name of the collectd plugin, something like apache, memory, mysql, interface, ...
PLUGIN_NAME = 'passenger-status'
def usage
puts("#{$0} -h [-i ]")
exit
end
# Main
begin
# Sync stdout so that it will flush to collectd properly.
$stdout.sync = true
# Parse command line options
hostname = nil
sampling_interval = 20 # sec, Default value
opts = GetoptLong.new(
[ '--hostid', '-h', GetoptLong::REQUIRED_ARGUMENT ],
[ '--sampling-interval', '-i', GetoptLong::OPTIONAL_ARGUMENT ]
)
opts.each do |opt, arg|
case opt
when '--hostid'
hostname = arg
when '--sampling-interval'
sampling_interval = arg.to_i
end
end
usage if !hostname
# Collection loop
while true do
start_run = Time.now.to_i
next_run = start_run + sampling_interval
# collectd data and print the values
data = `passenger-status`
max = data.match(/max (.*)/).to_s.split.last
count = data.match(/count (.*)/).to_s.split.last
active = data.match(/active (.*)/).to_s.split.last
inactive = data.match(/inactive (.*)/).to_s.split.last
waiting = data.match(/Waiting on global queue: ([\d]+)/).to_s.split.last
puts("PUTVAL #{hostname}/#{PLUGIN_NAME}/gauge-max_allowed_connections #{start_run}:#{max}")
puts("PUTVAL #{hostname}/#{PLUGIN_NAME}/gauge-thread_count #{start_run}:#{count}")
puts("PUTVAL #{hostname}/#{PLUGIN_NAME}/gauge-threads_active #{start_run}:#{active}")
puts("PUTVAL #{hostname}/#{PLUGIN_NAME}/gauge-threads_inactive #{start_run}:#{inactive}")
puts("PUTVAL #{hostname}/#{PLUGIN_NAME}/gauge-waiting_in_queue #{start_run}:#{waiting}")
# sleep to make the interval
while((time_left = (next_run - Time.now.to_i)) > 0) do
sleep(time_left)
end
end
end
Looking into the phusion_passenger/admin_tools/server_instance.rb file I was able to determine what file passenger was using to make the determination weather a user would be able to run the passenger-status command or not.
filename = "#{#generation_path}/passenger-status-password.txt"
password = File.open(filename, "rb") do |f|
f.read
end
rescue Errno::EACCES
raise RoleDeniedError
the file it was trying to read was passenger-status-password.txt this file is located in the /tmp/passenger.1.0.19198/generation-1/directory for Passenger version 3.0.9 on CentOS 5.7. I chmod'd the file to 644 and this fixed the issue. this solution also applies if someone wants to run the passenger-status command without sudo.

Resources