The original Opscode recipe dated 2009 is half broken due to syntax change and might put your Chef server at risk of being overloaded with search queries (unless you use Opscode platform of course which I don't), also chef client keeps full search output in memory making chef-client to be a little memory hungry.... So I wrote a different Chef recipe which generates ssh_known_hosts only on one server and distributes it using web server since I was unable to figure out how to store dynamic files in chef not using databags/search. Probably this will get obsolete when search gets more robust. But right now this one works like a charm. First you designate a server which runs Apache and generates known_hosts file then assign this recipe to that server (the recipe is tweaked to remove extra stuff I use here, so there can be some typos): # # Cookbook Name: gen_known_hosts # Recipe:: default # nodes = [] # We do it this way until CHEF-1205 is fixed search(:node, "*:*", "X_CHEF_id_CHEF_X asc",0,10000) { |n| if n['keys']['ssh'].has_key?('host_rsa_public') && n['keys']['ssh']['host_rsa_public'].length > 0 t=Hash.new t['keys'] = Hash.new t['keys']['ssh'] = Hash.new t['keys']['ssh']['host_rsa_public']=n['keys']['ssh']['host_rsa_public'] t['fqdn']=n['fqdn'] t['ipaddress']=n['ipaddress'] t['hostname']=n['hostname'] nodes << t end n=nil } # Need to make own file write script because chef file operations are not atomic yet s = bash "gensshknownhosts" do user "root" cwd "/tmp" action :nothing case node[:datacenter] code <<-EOH #!/bin/sh chefsshfile=/etc/ssh/ssh_known_hosts.chef outputfile=/var/www/ssh/ssh_known_hosts toutputfile=/var/www/ssh/.ssh_known_hosts rm -rf "${ toutputfile }" cp /etc/ssh/ssh_known_hosts.chef "${ toutputfile }" if [ $? -ne 0 ] ; then echo "Cp error $?" >&2 exit 1 fi if [ ! -e "${ toutputfile }" ] ; then echo "Cp didnt transfer file ${ toutputfile }" >&2 exit 1 fi # Sanity check - file needs to have at least some hosts if [ 0`ls -s "${ toutputfile }" | cut -f1 -d' '` -le 4 ] ; then echo "The ${ toutputfile } file is too small" >&2 fi # See if file changed if [ 0`cksum "${toutputfile}" | cut -f1 -d' '` \ -ne 0`cksum "${ outputfile }" | cut -f1 -d' '` ] ; then mv -f "${toutputfile}" "${outputfile}" fi #EOF EOH end end template "/etc/ssh/ssh_known_hosts.chef" do source "known_hosts.erb" mode 0444 owner "root" group "root" backup false variables( :nodes => nodes ) notifies :run, resources(:bash => "gensshknownhosts") end s.run_action(:run) nodes=nil GC.start The template known_hosts.erb looks like this: #THIS FILE IS MAINTAINED BY CHEF, DO NOT MODIFY AS IT WILL BE OVERWRITTEN <% @nodes.each do |n| -%> <% if n['keys']['ssh'].has_key?('host_rsa_public') && n['keys']['ssh']['host_rsa_public'].length > 0 -%> <%= n['hostname'] %>,<%= n['fqdn'] %>,<%= n['ipaddress'] %> ssh-rsa <%= n['keys']['ssh']['host_rsa_public'] %> <% end -%> <% end -%> Now the client Chef recipe looks very lightweight and does not use much of resources: # remote_file "/etc/ssh/ssh_known_hosts" do mode "644" owner "root" group "root" action :create backup false source "http://#{node[:utilityweb]}/ssh/ssh_known_hosts" end All you need is to define "node[:utilityweb]" attribute (where your Web server is which contains generated known_hosts file) via role or as default attribute to the recipe. |
Articles > Opscode Chef >