New to puppet and trying to get this module to work, but finding it quite frustrating.
Error: Could not retrieve catalog from remote server: Error 400 on SERVER: can't convert String into Hash at /etc/puppet/modules/collectd/manifests/plugin/network.pp:28
The plugin I am struggling with is this one:
https://forge.puppetlabs.com/pdxcat/collectd#class-collectdpluginnetwork
The value I am trying to set is servers under collectd::plugin::network
I have tried:
('127.0.0.1': port => 25826,) and
('hostname' '127.0.0.1' 'port' 25826) and '127.0.0.1': port => 25826,
and a myriad of other options.
Could somebody please let me know how to write a valid hash?
The manifest:
[root#foreman ~]# cat /etc/puppet/modules/collectd/manifests/plugin/network/server.pp
#
define collectd::plugin::network::server (
$ensure = 'present',
$username = undef,
$password = undef,
$port = undef,
$securitylevel = undef,
$interface = undef,
) {
include collectd::params
include collectd::plugin::network
$conf_dir = $collectd::params::plugin_conf_dir
validate_string($name)
file { "${conf_dir}/network-server-${name}.conf":
ensure => $ensure,
mode => '0640',
owner => 'root',
group => $collectd::params::root_group,
content => template('collectd/plugin/network/server.conf.erb'),
notify => Service['collectd'],
}
}
Could you please attach fragment of manifest that is not working ?
Here you can find the description of puppet types: https://docs.puppetlabs.com/puppet/latest/reference/lang_datatypes.html, also about hashes.
According to documentation:
Hashes are written as key/value pairs surrounded by curly braces; a key is separated from its value by a => (arrow, fat comma, or hash rocket), and adjacent pairs are separated by commas. An optional trailing comma is allowed between the final value and the closing curly brace.
{ key1 => 'val1', key2 => 'val2' }
So definitely you have to change "(" brackets to "{". Also after key should be "=>" not ":"
Something like this should be working:
servers => { '127.0.0.1' =>
{ 'port' => '25826', },
}
When putting hash data into a Foreman smart class parameter (or smart variable), you need to do two things to get it passed to Puppet correctly:
Set the data type on the parameter to Hash, JSON or YAML
Use JSON or YAML to represent the data
The first will ensure that Puppet is given an actual hash of data, instead of a string that looks like a hash (which I think was probably the cause of the error you got), and the second allows Foreman to parse what you enter.
Navigate in Foreman to Configure > Puppet classes > collectd::plugin::network > Smart class parameters > servers, and set Type to JSON (or Hash, or YAML, if you prefer).
Next, change the value of the parameter (either the default or an override further down) to:
{"127.0.0.1":{"port":"25826"}}
This is JSON syntax, which if you compare it to Puppet's DSL, you'll note it uses colons instead of => for key/value separators, and uses double quotes only for strings. Beware that trailing commas aren't valid in JSON.
Pretty much you can copy the example parameter from the documentation or from the other answer, substituting the separators and quotes to convert it to JSON.
Equivalent YAML format would be:
---
127.0.0.1:
port: "25826"
Foreman will accept either when in "Hash" or "Array" mode, and IIRC it will store/retrieve it in YAML format by default.
If Foreman gives an error when saving the parameter, it might be due to the format of the data you're entering. There are numerous validation and linting tools for both JSON and YAML, e.g. jsonlint.com or json_verify (part of the yajl package), so run the data through it first.
There's more information about complex data types in the Foreman manual in section 4.2.6 Smart Matchers.
If you still get an error from Puppet after this, please go to the host page in Foreman, click the YAML button and copy/paste the classes: section of the YAML output (which is what's passed to Puppet), being careful to preserve whitespace.
Related
I have been using nsupdate for a long time in various scripts dealing with dynamic DNS zone updates without any issue. I have always used TSIG to authenticate the requests against the DNS server, where the keys have been generated by ddns-confgen. That means that I didn't use key pairs like those generated by dnssec-keygen; rather, the keys file format is like the following:
key "ddns-key.my.domain.example" {
algorithm hmac-sha512;
secret "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefijklmnopqrstuvwxyzabcdefghij==";
};
The respective zone configuration then contains:
update-policy {
grant ddns-key.my.domain.example name host.my.domain.example ANY;
};
Now I have a more complicated task and try to solve it by a Perl script. I have studied the documentation of Perl::DNS::Update, Perl::DNS and Perl::DNS:RR:TSIG and have studied a lot of examples which should make the usage of those modules clear.
However, each example I saw, when coming to signing a request via TSIG, used key files in the format dnssec-keygen produces, and not key files in the format I have. And indeed, something like
$o_Resolver = new Net::DNS::Resolver(nameservers => ['127.0.0.1']);
$o_Update = new Net::DNS::Update('my.domain.example', 'IN');
$o_Update -> push(update => rr_del('host A'));
$o_Update -> push(update => rr_add('host 1800 IN A 192.0.2.1'));
$o_Update -> sign_tsig('/etc/bind/ddns-key.my.domain.example.key');
$o_Reply = ($o_Resolver -> send($o_Update));
does not work, producing the following message:
TSIG: unable to sign packet at /path/to/script.pl line 240.
unknown type "ddns-key.my.domain.example" at /usr/local/share/perl/5.20.2/Net/DNS/RR.pm line 669.
file /etc/bind/ddns-key.my.domain.example.key line 1
at /usr/local/share/perl/5.20.2/Net/DNS/RR/TSIG.pm line 403.
TSIG: unable to sign packet at /path/to/script.pl line 240.
I suppose I now have two options: Either use keys in the format dnssec-keygen produces, which seem to be directly usable with Net::DNS and its friends, or construct the TSIG key manually as shown in the docs:
my $key_name = 'tsig-key';
my $key = 'awwLOtRfpGE+rRKF2+DEiw==';
my $tsig = new Net::DNS::RR("$key_name TSIG $key");
$tsig->fudge(60);
my $update = new Net::DNS::Update('example.com');
$update->push( update => rr_add('foo.example.com A 10.1.2.3') );
$update->push( additional => $tsig );
[Of course, I wouldn't hard-code the key in my Perl script, but read it from the key file instead.]
Switching to another key file format would mean changing the DNS server configuration, which is not an elegant solution. "Manually" reading the key files and then "manually" constructing the keys is not very satisfying either, hence the question:
Did I understand correctly that it is not possible to use key files in the ddns-confgen format directly with Net::DNS and its sub-modules to TSIG-sign DNS update requests?
In one of my scripts I use the Getopt::Long library. At the beginning of the program I make a call:
&GetOptions ('help', 'debug', 'user=s' => \$GetUser);
The first two arguments are simple: I discover their existance by checking $opt_help and $opt_debug respectively. However the third argument is tricky, because I need to distinguish between no option at all ($GetUser is undefined, which is ok for me), using "--user" alone ($GetUser is also undefined, but this time I want to display an error message) and "--user FooBar" (where the $GetUser receives 'FooBar', which I can use in further processing).
How can I distinguish between using no "--user" option and using it alone, without a username?
You are looking for : instead of =, so 'user:s' => \$GetUser. From Options with values
Using a colon : instead of the equals sign indicates that the option value is optional. In this case, if no suitable value is supplied, string valued options get an empty string '' assigned, while numeric options are set to 0
This allows you to legitimately call the program with --user and no value (with = it's an error). Then you only declare my $GetUser; and after the options are processed you can tell what happened. If it is undef it wasn't mentioned, if it is '' (empty string) it was invoked without a value and you can emit your message. This assumes that it being '' isn't of any other use in your program.
Otherwise, when you use 'user=s' and no value is given, the GetOptions reports an error by returning false and emits a descriptive message to STDERR. So you may well leave it and do
GetOptions( 'user=s' => ...) or die "Option error\n";
and rely on the module to catch and report wrong use. Our own message above isn't really needed as module's messages clearly describe the problem.
One other way of doing this would go along the lines of
usage(), exit if not GetOptions('user=s' => \$GetUser, ...);
sub usage {
# Your usage message, briefly listing options etc.
}
I'd like to add – you don't need & in front of a function call. It makes the caller's #_ visible, ignores function prototype, and does a few other similarly involved things. One common use is to get a coderef, $rc = \&fun, where it is needed. See for example this post
I'm new to puppet. The following is tutorial code:
user { '<your username>':
ensure => 'present',
groups => ['sudo'],
home => '/home/<username>',
managehome => true,
password => '$6$lY2Gp3Cr$zNrUB7T3yibUF/gWn5cTQ0fNv7MUmx/DZuw3E7I..Vh9tITG28BtgvXJPU4Gm4Z/9oNvlbX24KzQ9Ib1QH1B9.',
shell => '/bin/bash',
}
the password field is a hash of the string "test". What's the simplest way to derive sha1 hashes from strings? I know puppet has a specific "sha1" function, but I don't know how to implement it.
First off, that is not a sha1 hash - the $6$ indicates sha512.
Beyond that, you should use tools such as mkpasswd to create hashes for you, then enter them into Puppet. The ability to include plain text passwords into your manifests or Hiera data is not especially useful.
I want to validate some hidden input fields (to make sure they arent changed on submission) with the help of a sha-encoded string of the key value pairs of these hidden fields. I saw examples of this online but I didnt understand how to encode and
decode the values with a dynamic secret value. Can someone help me understand how to do this in perl?
Also which signature type (MD5, SHA1, SHA256, etc), has a good balance of performance and security?
update
So, how do you decode the string once you get it encoded?
What you really need is not a plain hash function, but a message authentication code such as HMAC. Since you say you'd like to use SHA-256, you might like HMAC_SHA256, which is available in Perl via the Digest::SHA module:
use Digest::SHA qw(hmac_sha256_base64);
my $mac = hmac_sha256_base64( $string, $key );
Here, $key is an arbitrary key, which you should keep secret, and $string contains the data you want to sign. To apply this to a more complex data structure (such as a hash of key–value pairs), you first need to convert it to a string. There are several ways to do that; for example, you could use Storable:
use Storable qw(freeze);
sub pairs_to_string {
local $Storable::canonical = 1;
my %hash = #_;
return freeze( \%hash );
}
You could also URL-encoding, as suggested by David Schwartz. The important thing is that, whatever method you use, it should always return the exact same string when given the same hash as input.
Then, before sending the data to the user, you calculate a MAC for them and include it as an extra field in the data. When you receive the data back, you remove the MAC field (and save its value), recalculate the MAC for the remaining fields and compare it to the value you received. If they don't match, someone (or something) has tampered with the data. Like this:
my $key = "secret";
sub mac { hmac_sha256_base64( pairs_to_string(#_), $key ) }
# before sending data to client:
my %data = (foo => "something", bar => "whatever");
$data{mac} = mac( %data );
# after receiving %data back from client:
my $mac = delete $data{mac};
die "MAC mismatch" if $mac ne mac( %data );
Note that there are some potential tricks this technique doesn't automatically prevent, such as replay attacks: once you send the data and MAC to the user, they'll learn the MAC corresponding to the particular set of data, and could potentially replace the fields in a later form with values saved from an earlier form. To protect yourself against such attacks, you should include enough identifying information in the data protected by the MAC to ensure that you can detect any potentially harmful replays. Ideally, you'd want to include a unique ID in every form and check that no ID is ever submitted twice, but that may not always be practical. Failing that, it may be a good idea to include a user ID (so that a malicious user can't trick someone else into submitting their data) and a form ID (so that a user can't copy data from one form to another) and perhaps a timestamp and/or a session ID (so that you can reject old data) in the form (and in the MAC calculation).
I don't know what you mean by "unpack", but you can't get original string from the hash.
Let's understand the problem: you render some hidden fields and you want to make sure that they're submitted unchanged, right? Here's how you can ensure that.
Let's suppose you have two variables:
first: foo
second: bar
You can hash them together with a secret key:
secret_key = "ysEJbKTuJU6u"
source_string = secret_key + "first" + "foo" + "second" + "bar"
hash = MD5(source_string)
# => "1adfda97d28af6535ef7e8fcb921d3f0"
Now you can render your markup:
<input type="hidden" name="first" value="foo" />
<input type="hidden" name="second" value="bar" />
<input type="hidden" name="hash" value="1adfda97d28af6535ef7e8fcb921d3f0">
Upon form submission, you get values of first and second fields, concat them to your secret key in a similar manner and hash again.
If hashes are equal, your values haven't been changed.
Note: never render secret key to the client. And sort key/value pairs before hashing (to eliminate dependency on order).
( disclaimer: I am not a crypto person, so you may just stop reading now)
As for performance/security, even though MD5 was found to have a weakness, it's still pretty usable, IMHO. SHA1 has a theoretical weakness, although no successful attack has been made yet. There are no known weaknesses in SHA-256.
For this application, any of the encryption algorithms is fine. You can pack the values any way you want, so long as it's repeatable. One common method is to pack the fields into a string the same way you would encode them into a URL for a GET request (name=value).
To compute the hash, create a text secret that can be whatever you want. It should be at least 12 bytes long though. Compute the hash of the secret concatenated with the packed fields and append that onto the end.
So, say you picked MD5, a secret of JS90320ERHe2 and you have these fields:
first_name = Jack
last_name = Smith
other_field = 7=2
First, URL encode it:
first_name=Jack&last_name=Smith&other_field=7%3d=2
Then compute the MD5 hash of
JS90320ERHe2first_name=Jack&last_name=Smith&other_field=7%3d=2
Which is 6d0fa69703935efaa183be57f81d38ea. The final encoded field is:
first_name=Jack&last_name=Smith&other_field=7%3d=2&hash=6d0fa69703935efaa183be57f81d38ea
So that's what you pass to the user. To validate it, remove the hash from the end, compute the MD5 hash by concatenating what's left with the secret, and if the hashes match, the field hasn't been tampered with.
Nobody can compute their own valid MD5 because they don't know to prefix the string with.
Note that an adversary can re-use any old valid value set. They just can't create their own value set from scratch or modify an existing one and have it test valid. So make sure you include something in the information so you can verify that it is suitable for the purpose it has been used.
In order to store arbitrary data in jsTree nodes, I'm passing a custom attribute (say node-data) to the create_node method. But I need to store more than one piece of data in the single attribute. (For example name,age etc.) What format would be best suited for the value of the node-data attribute? What I'm looking for is a format that will allow easy retrieval of data.
For example we can have a format like "name:John,age:26". But I will have to split the string by using the comma as the delimiter and split at the colon to separate the name and value. Is there a better way to do this?
Any reason you're not using data- attributes? Supported by jquery and every current browser (HTML5). So instead of obj.attr("myattrib", true); You can assign arbitrary data to any object. You could easily do things like:
node.data("name", "John");
node.data("age", 26);
and later you retrieve them when needed:
var name = node.data("name");
on the server end, presuming you're sending your data back as Json data, you simply define (in the simplest form, you can get fancier) a Dictionary metadata in the json object in the server, then you retrieve "name" from the dictionary, cast the object to the appropriate type and away you go!
You can pass html attributes to node, using li_attr and a_attr in json data array.
$list[] = array(
'id' => "year_" . $year->Year,
'text' => $year->Year,
'children' => TRUE,
'folder' => 'folder',
'li_attr' => array('year' => $year->Year),
'a_attr' => array('month' => $year->month)
);