I want to copy ssh keys for users automatically, some users do not have keys.
What I have now is:
ssh_auth:
- present
- user: {{ usr }}
- source: salt://users/keys/{{usr}}.id_rsa.pub
When a key for a user does not exist on salt:// fileserver, I get an error. Is there some function to check for existence of a file in salt:// fileserver?
If you feel you MUST learn how to do this with just states, you can use the fallback mechanism by specifying a list of sources:
From the docs:
ssh_auth:
- present
- user:{{usr}}
- source:
- salt://users/keys/{{usr}}.id_rsa.pub
- salt://users/keys/null.id_rsa.pub
Where cat /dev/null > /srv/salt/users/keys/null.id_dsa.pub
Professionally, user keys should be stored in pillars. This presents the additional functionality that pillars are stored and retrieved from the master at execution time - which means you can test for the existence of the file per your original request. I do something just like that for openvpn certificates:
http://garthwaite.org/virtually-secure-with-openvpn-pillars-and-salt.html
I don't know of a jinja or salt function which can check the master's file server for a specific file. I would recommend you put those keys as a key in the pillar's file which contains your user's and use jinja to detect the existence of that key and create the key when necessary. For example:
The pillars file:
# Name of file : user_pillar.sls
users:
root:
ssh_key: some_key_value
home : /root
createhome: True
The state file:
# Name of file : users_salt_state_file.sls
{% for user,args in salt['pillar.get']('users',{}).iteritems() %}
# Ensure user is present
{{ user }}_user:
user.present:
- name: {{ user }}
# Home Creation
{% if args and 'home' in args %}
- home: {{ args['home'] }}
{% endif %}
{% if args and 'createhome' in args %}
- createhome: {{ args['createhome'] }}
{% endif %}
# SSH_auth
{% if args and 'ssh_key' in args %}
{{ args['ssh_key'] }}
ssh_auth:
- present
- user: {{ user }}
{% endfor %}
Related
I would like to check if a file exist in salt file system (salt://) and add an instruction depending on that.
I precise that I use gitfs as a mount point in the salt file system and don't use /srv directory.
So more concretely I want to do something like that :
{% if salt['file.directory_exists']('salt://a_directory') %}
file.recurse:
- name: dest
- source: salt://a_directory
- template: jinja
{% endif %}
but it seem not to work.
I wanted to load yaml-files with package-lists for pkg.installed, but only if the yaml is on the master (file names are constructed by variables).
I'm using salt.modules.cp.list_master:
# Search path on the master:
{% set found = salt.cp.list_master(prefix='my/path/example.yaml') | count %}
{% if found %}
{# Do something… #}
{% endif %}
If you want to check a Directory, you can use this:
{% set found = salt.cp.list_master_dirs(prefix='my/path') | count %}
after seeing Mayr's answer, I did this:
{%- if not salt.cp.list_master(saltenv=saltenv, prefix='some/path/' ~ some_version ~ '/server.conf') | count %}
config_not_found_failure:
test.fail_without_changes:
- name: "some/path/{{some_version}}/server.conf not found in salt"
- failhard: True
{%- endif %}
I've just installed a vanilla SaltStack master and minions. I'm trying to follow the instructions on https://docs.saltstack.com/en/latest/topics/development/conventions/formulas.html to create a very basic PHP state.
/srv/salt/php.sls:
php_ini:
file.managed:
- name: /etc/php.ini
- source: salt://php.ini.tmpl
- template: jinja
- context:
php_ini_settings: {{ salt.pillar.get('php_ini', {}) | json() }}
/srv/pillar/php.sls:
php_ini:
PHP:
engine: 'On'
short_open_tag: 'Off'
error_reporting: 'E_ALL & ~E_DEPRECATED & ~E_STRICT'
/srv/salt/php.ini.tmpl:
{% macro php_ini_serializer(data) %}
{% for section_name, name_val_pairs in data.items() %}
[{{ section_name }}]
{% for name, val in name_val_pairs.items() -%}
{{ name }} = "{{ val }}"
{% endfor %}
{% endfor %}
{% endmacro %}
; File managed by Salt at <{{ source }}>.
; Your changes will be overwritten.
{{ php_ini_serializer(php_ini_settings) }}
The output file looks like this:
VVV File starts on the next line
; File managed by Salt at <salt://php.ini.tmpl>.
; Your changes will be overwritten.
^^^ File ends on the previous line
I've added extra lines so that Stack Overflow will display the blank lines in the output file correctly.
I would expect it to look something link this:
VVV File starts on the next line
; File managed by Salt at <salt://php.ini.tmpl>.
; Your changes will be overwritten.
[PHP]
engine = "On"
short_open_tag = "Off"
error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT"
^^^ File ends on the previous line
It appears that Salt isn't reading the pillar file at all. What have I done wrong?
It looks like php_ini_settings: {{ salt.pillar.get('php_ini', {}) | json() }} does not add any data to the context of your jinja template salt://php.ini.tmpl.
To debug check if the pillar data is available use the pillar module
salt 'minionid' pilar.ls # to check the existence of keys
salt 'minionid' pilar.items # to explore whole the pillar data of your minion
Pillar data needs to be applied to minions like states using a top.sls file. My guess is that you did not apply your pillar data to your minion. Does /srv/pillar/top.sls includes somthing like that?
base:
'minionid':
- php
I want to add a mine function that gets the hostname of a minion.
pillar/custom.sls
mine_functions:
custom:
- mine_function: grains.get
- nodename
I manually refresh the pillar data by running a
salt '*' saltutil.refresh_pillar
and when running salt '*' mine.get '*' custom the output is as expected, showing a list of minions all with the nodename data underneath.
The issue is when I try to do thew following in a template file:
{%- set custom_nodes = [] %}
bootstrap.servers={% for host, custom in salt['mine.get']('role:foo', 'custom', expr_form='grain').items() %}
{% do hosts.append(custom + ':2181') %}
{% endfor %}{{ custom_nodes|join(',') }}
I just get an empty space where my list of server nodenames should be.
I was hoping someone might be able to point out where I'm going wrong with this?
It looks like you are appending the list to hosts but then using custom_nodes with the join?
Was this on purpose?
I think what you actually want is
{%- set custom_nodes = [] %}
bootstrap.servers={% for host, custom in salt['mine.get']('role:foo', 'custom', expr_form='grain').items() %}
{% do custom_nodes.append(custom + ':2181') %}
{% endfor %}{{ custom_nodes|join(',') }}
This works fine for me:
pillar/custom.sls
mine_functions:
id_list:
mine_function: grains.get
key : nodename
templete.sls
{% for server in salt['mine.get']('*', 'id_list') | dictsort() %}
server {{ server }} {{ addrs[0] }}:80 check
{% endfor %}
Actually the answer was quite simple. I was unaware one needed to restart existing minions before they could access the mine data.
I'm trying to learn SaltStack and now I'm facing a problem.
I have a property file (propertyfile.properties) with values being populated by salt pillar. Now this property file exists in multiple directories. The issue that I have is that I want the values of the property file be populated/rendered by salt pillar.get function base on what directory currently it is into. To give you more idea, let's consider this example:
propertyfile.property (in directory 1)
name={{ salt['pillar.get']['dir1.name'] }}
propertyfile.property (in directory 2)
name={{ salt['pillar.get']['dir2.name'] }}
#pillar
dir1.name=dir1
dir2.name=dir2
note that the property file is only one and is generated by salt to multiple directories via loop like this:
{% for dir in 'dir1', 'dir2' %}
propertyfile_properties_{{ dir }}:
file.managed:
- name: /home/devuser/{{ dir }}/propertyfile.properties
- source: {{ propertyfile_source }}
- source_hash: {{ propertyfile_source }}.MD5
- template: jinja
- show_diff: True
- makedirs: True
{% endfor %}
Any ideas? Your help is very much appreciated. Thanks
You can do this by passing each directory to the template as context, as it's rendered:
# In .sls
{% for dir in salt['pillar.get']("directories") %}
propertyfile_properties_{{ dir }}:
file.managed:
- name: /home/devuser/{{ dir }}/propertyfile.properties
- source: salt://path/to/template.jinja
- template: jinja
- context:
dir: {{ dir }}
{% endfor %}
# In template:
dirname={{ dir }}
fullpath=/home/devuser/{{ dir }}/propertyfile.properties
# In pillar:
directories:
- dir1
- dir2
- ...and so on
Note the extra indentation of the contents of the context dictionary. There's an explanation of why that's sometimes necessary here.
I am trying to automate the creation of my users with Saltstack.
I created a pillar conf:
users:
homer:
fullname: Homer Simpson
uid: 1007
gid: 1007
groups:
- sudo
- adm
crypt: $6H7kNJefhBeY
pub_ssh_keys:
- ssh-rsa ...
And in my state I use the following:
{% for username, details in pillar.get('users', {}).items() %}
{{ username }}:
group:
- present
- name: {{ username }}
- gid: {{ details.get('gid', '') }}
user:
- present
- fullname: {{ details.get('fullname','') }}
- name: {{ username }}
- shell: /bin/bash
- home: /home/{{ username }}
- uid: {{ details.get('uid', '') }}
- gid: {{ details.get('gid', '') }}
- password: {{ details.get('crypt','') }}
{% if 'groups' in details %}
- groups:
{% for group in details.get('groups', []) %}
- {{ group }}
{% endfor %}
{% endif %}
{% if 'pub_ssh_keys' in details %}
ssh_auth:
- present
- user: {{ username }}
- names:
{% for pub_ssh_key in details.get('pub_ssh_keys', []) %}
- {{ pub_ssh_key }}
{% endfor %}
- require:
- user: {{ username }}
{% endif %}
{% endfor %}
The user creation is okay, ssh-rsa keys are added properly but my main isssue is with the password: I tried the following:
crypt: password
crypt: some-hash
But when I connect to my server, I have a wrong password issue for this user.
Can you tell me how can I generate a good password compliant with the format salt is expecting? Is there a special command to use to generate it ?
Thank you.
To create hashed user passwords in Debian/Ubuntu, usable in salt, I do the following:
apt-get install makepasswd
echo '<password>' | makepasswd --clearfrom=- --crypt-md5 | awk '{ print $2 }'
This gives e.g.: $id$salt$encrypted
The id in "$id$salt$encrypted" should be 1, meaning it's an md5 hash.
Copy/paste this hash into your pillar.
Hope this works for you as well.
I wouldn't use md5, which is denoted by $1
If you look in your /etc/shadow file and see other passwords are $6 then it is using sha-512.
Don't use makepassword, use "mkpasswd"
mkpasswd -m sha-512
Password: [enter password]
$6$fYewyeO5lMP/$CLbYqRdUootlGA3hJzXye84k0Of9VX4z39TOnsDxfIaFcL4uGznfJsGEJMiEaHKHZDSIUK7o4r22krvhezpZq1
Thanks die the makepasswd example. It whould be great, if it works. But in my case it doesn't.
The hashed password seems not to be correct. Maybe another encryption should be used?
I have used the standard user formula from the salt stack GitHub repositories,