Puppet Function: localuser
- Defined in:
- lib/puppet/parser/functions/localuser.rb
- Function type:
- Ruby 3.x API
Overview
Pull a pre-set password from a password list and return an
array
of user details associated with the passed hostname.
If the password starts with the string $1$
and the length is
34
characters, then it will be assumed to be an
MD5
hash to be directly applied to the system.
If the password is in plain text form, then it will be hashed and stored back into the source file for future use. The plain text version will be commented out in the file.
Lines beginning with the #
symbol are ignored and commas
,
are not allowed in usernames or hostnames though any
characters are allowed in passwords.
homedir
is the home directory of the user and is optional. By
default, the system will choose the home directory.
The function will return a String
with the following contents:
[attr]<username>,MD5-based password hash with random
salt
Hostname Ruby regular expressions are fully supported. The following formats are allowed:
-
/regex/opts,<username>
-
/regex/,<username>
-
regex,<username>
-
*.<domain>,<username>
-
fqdn,<username>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/puppet/parser/functions/localuser.rb', line 2 newfunction(:localuser, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args| filename = args[0] Pull a pre-set password from a password list and return an `array` of user details associated with the passed hostname. If the password starts with the string `$1$` and the length is `34` characters, then it will be assumed to be an `MD5` hash to be directly applied to the system. If the password is in plain text form, then it will be hashed and stored back into the source file for future use. The plain text version will be commented out in the file. @example Password Line syntax [+-!]<fqdn-regex>,<username>,<uid>,<gid>,[<homedir>],<password> Lines beginning with the `#` symbol are ignored and commas `,` are not allowed in usernames or hostnames though any characters are allowed in passwords. `homedir` is the home directory of the user and is optional. By default, the system will choose the home directory. The function will return a `String` with the following contents: `[attr]<username>,MD5-based password hash with random salt` Hostname Ruby regular expressions are fully supported. The following formats are allowed: * /regex/opts,<username> * /regex/,<username> * regex,<username> * *.<domain>,<username> * fqdn,<username> @param filename [Stdlib::Absolutepath] path to the file containing the local users @param hostname host that you are trying to match against @return [String] ENDHEREDOC hostname = args[1] retval = [] if not FileTest.exists?("#{filename}") then return ["### You must have a file on the server at #{filename} from which to read usernames and hashes ###\n### These lines will be ignored ###\n"] end File.open(filename, 'r+') do |file| oldfile = file.readlines.map(&:chomp) oldfile.each_with_index do |line,index| # If it isn't a comment, do stuff. if ( line !~ /^#/ ) then host = hostname # Chunk the line, the first field is the regex. vals = line.split(',') orighost = vals.shift if ( orighost =~ /^([+-\\!]?)(.*)/ ) then extattr = $1 orighost = $2 end # Copy this to a variable for mangling orighost_tmp = orighost # This covers the legacy format, which could start with a '*' if ( orighost_tmp =~ /^\*/ ) then orighost_tmp = ".#{orighost_tmp}" end # If this is a formatted regex, treat it as such. if ( orighost_tmp =~ /^\// ) then orighost_tmp = orighost_tmp.split(/\//) hostregex = Regexp.new(orighost_tmp[1],orighost_tmp[2]) else hostregex = Regexp.new("^#{orighost_tmp}$") end # Match against the passed hostname. if hostregex.match(host) then username = vals.shift uid = vals.shift gid = vals.shift homedir = nil if ( vals.length == 2 ) then homedir = vals.shift end pass = "#{vals.shift.chomp}" vals.each {|x| pass = pass + "," + x.chomp} # See if we already have a hashed pass. if ( pass =~ /\$[156]\$/ ) hash = pass # If not, then create one. else chars = ("a".."z").to_a + ("0".."9").to_a + %w{. /} salt = "$6$rounds=10000$" + Array.new(8, '').collect{chars[rand(chars.size)]}.join hash = pass.crypt(salt) # Check to be sure that we got a hashed password. # We really should never get here on a modern system. if not hash.include?(salt) then # Fall back to MD5 salt = "$1$" + Array.new(8, '').collect{chars[rand(chars.size)]}.join hash = pass.crypt(salt) end oldfile[index] = "# " + oldfile[index] if ( homedir ) then oldfile.insert(index+1, "#{extattr}#{orighost},#{username},#{uid},#{gid},#{homedir},#{hash}\n") else oldfile.insert(index+1, "#{extattr}#{orighost},#{username},#{uid},#{gid},#{hash}\n") end end if ( homedir ) then retval << "#{extattr}#{username},#{uid},#{gid},#{homedir},#{hash}" else retval << "#{extattr}#{username},#{uid},#{gid},#{hash}" end end end end file.pos = 0 file.print oldfile.join("\n") file.truncate(file.pos) file.close end retval end |