Merge branch 'master' of github.com:pcdummy/saltstack-users-formula

Signed-off-by: René Jochum <rene@jochums.at>

Conflicts:
	pillar.example
	users/init.sls
This commit is contained in:
René Jochum 2015-07-13 13:27:27 +02:00
commit 2c4ed3edc9
10 changed files with 427 additions and 47 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2014 Salt Stack Formulas
Copyright (c) 2014-2015 Salt Stack Formulas
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -11,4 +11,3 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -27,3 +27,17 @@ and associated keys. Also configures sudo access, and absent users.
Ensures the sudo group exists, the sudo package is installed and the sudo file
is configured.
``users.bashrc``
----------------
Ensures the bashrc file exists in the users home directory. Set manage_bashrc:
True in pillar per user. Defaults to False
``users.vimrc``
---------------
Ensures the vimrc file exists in the users home directory. Set manage_vimrc:
True in pillar per user. Defaults to False
This depends on the vim-formula to be installed

View File

@ -9,11 +9,22 @@ users:
password: $6$w.............
home: /custom/buser
createhome: True
manage_vimrc: False
manage_bashrc: False
expire: 16426
sudouser: True
# sudo_rules doesn't need the username as a prefix for the rule
# this is added automatically by the formula.
# ----------------------------------------------------------------------
# In case your sudo_rules have a colon please have in mind to not leave
# spaces around it. For example:
# ALL=(ALL) NOPASSWD: ALL <--- THIS WILL NOT WORK (Besides syntax is ok)
# ALL=(ALL) NOPASSWD:ALL <--- THIS WILL WORK
sudo_rules:
- 'ALL=(root) /usr/bin/find'
- 'ALL=(otheruser) /usr/bin/script.sh'
- ALL=(root) /usr/bin/find
- ALL=(otheruser) /usr/bin/script.sh
sudo_defaults:
- '!requiretty'
shell: /bin/bash
prime_group:
name: primarygroup
@ -21,13 +32,40 @@ users:
groups:
- users
ssh_key_type: rsa
# You can inline the private keys ...
ssh_keys:
privkey: PRIVATEKEY
pubkey: PUBLICKEY
# ... or you can pull them from a different pillar,
# for example one called "ssh_keys":
ssh_keys_pillar:
id_rsa: "ssh_keys"
another_key_pair: "ssh_keys"
ssh_auth:
- PUBLICKEY
ssh_auth.absent:
- PUBLICKEY_TO_BE_REMOVED
# Generates an authorized_keys file for the user
# with the given keys
ssh_auth_file:
- PUBLICKEY
# If you prefer to keep public keys as files rather
# than inline in pillar, this works.
ssh_auth_sources:
- salt://keys/buser.id_rsa.pub
# Manage the ~/.ssh/config file
ssh_config:
all:
hostname: "*"
options:
- "StrictHostKeyChecking no"
- "UserKnownHostsFile=/dev/null"
importanthost:
hostname: "needcheck.example.com"
options:
- "StrictHostKeyChecking yes"
google_2fa: True
google_auth:
ssh: |
SOMEGAUTHHASHVAL

27
users/bashrc.sls Normal file
View File

@ -0,0 +1,27 @@
{% from "users/map.jinja" import users with context %}
include:
- users
{% for name, user in pillar.get('users', {}).items() if user.absent is not defined or not user.absent %}
{%- if user == None -%}
{%- set user = {} -%}
{%- endif -%}
{%- set home = user.get('home', "/home/%s" % name) -%}
{%- set manage = user.get('manage_bashrc', False) -%}
{%- if 'prime_group' in user and 'name' in user['prime_group'] %}
{%- set user_group = user.prime_group.name -%}
{%- else -%}
{%- set user_group = name -%}
{%- endif %}
{%- if manage -%}
users_{{ name }}_user_bashrc:
file.managed:
- name: {{ home }}/.bashrc
- user: {{ name }}
- group: {{ user_group }}
- mode: 644
- source:
- salt://users/files/bashrc/{{ name }}/bashrc
- salt://users/files/bashrc/bashrc
{% endif %}
{% endfor %}

View File

@ -0,0 +1,9 @@
#
# ~/.bashrc
#
#
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
alias ls='ls --color=auto'
PS1='[\u@\h \W]\$ '

160
users/files/vimrc/vimrc Normal file
View File

@ -0,0 +1,160 @@
" URL: http://vim.wikia.com/wiki/Example_vimrc
" Authors: http://vim.wikia.com/wiki/Vim_on_Freenode
" Description: A minimal, but feature rich, example .vimrc. If you are a
" newbie, basing your first .vimrc on this file is a good choice.
" If you're a more advanced user, building your own .vimrc based
" on this file is still a good idea.
"------------------------------------------------------------
" Features {{{1
"
" These options and commands enable some very useful features in Vim, that
" no user should have to live without.
" Set 'nocompatible' to ward off unexpected things that your distro might
" have made, as well as sanely reset options when re-sourcing .vimrc
set nocompatible
" Attempt to determine the type of a file based on its name and possibly its
" contents. Use this to allow intelligent auto-indenting for each filetype,
" and for plugins that are filetype specific.
filetype indent plugin on
" Enable syntax highlighting
syntax on
"------------------------------------------------------------
" Must have options {{{1
"
" These are highly recommended options.
" Vim with default settings does not allow easy switching between multiple files
" in the same editor window. Users can use multiple split windows or multiple
" tab pages to edit multiple files, but it is still best to enable an option to
" allow easier switching between files.
"
" One such option is the 'hidden' option, which allows you to re-use the same
" window and switch from an unsaved buffer without saving it first. Also allows
" you to keep an undo history for multiple files when re-using the same window
" in this way. Note that using persistent undo also lets you undo in multiple
" files even in the same window, but is less efficient and is actually designed
" for keeping undo history after closing Vim entirely. Vim will complain if you
" try to quit without saving, and swap files will keep you safe if your computer
" crashes.
set hidden
" Note that not everyone likes working this way (with the hidden option).
" Alternatives include using tabs or split windows instead of re-using the same
" window as mentioned above, and/or either of the following options:
" set confirm
" set autowriteall
" Better command-line completion
set wildmenu
" Show partial commands in the last line of the screen
set showcmd
" Highlight searches (use <C-L> to temporarily turn off highlighting; see the
" mapping of <C-L> below)
set hlsearch
" Modelines have historically been a source of security vulnerabilities. As
" such, it may be a good idea to disable them and use the securemodelines
" script, <http://www.vim.org/scripts/script.php?script_id=1876>.
" set nomodeline
"------------------------------------------------------------
" Usability options {{{1
"
" These are options that users frequently set in their .vimrc. Some of them
" change Vim's behaviour in ways which deviate from the true Vi way, but
" which are considered to add usability. Which, if any, of these options to
" use is very much a personal preference, but they are harmless.
" Use case insensitive search, except when using capital letters
set ignorecase
set smartcase
" Allow backspacing over autoindent, line breaks and start of insert action
set backspace=indent,eol,start
" When opening a new line and no filetype-specific indenting is enabled, keep
" the same indent as the line you're currently on. Useful for READMEs, etc.
set autoindent
" Stop certain movements from always going to the first character of a line.
" While this behaviour deviates from that of Vi, it does what most users
" coming from other editors would expect.
set nostartofline
" Display the cursor position on the last line of the screen or in the status
" line of a window
set ruler
" Always display the status line, even if only one window is displayed
set laststatus=2
" Instead of failing a command because of unsaved changes, instead raise a
" dialogue asking if you wish to save changed files.
set confirm
" Use visual bell instead of beeping when doing something wrong
set visualbell
" And reset the terminal code for the visual bell. If visualbell is set, and
" this line is also included, vim will neither flash nor beep. If visualbell
" is unset, this does nothing.
set t_vb=
" Enable use of the mouse for all modes
set mouse=a
" Set the command window height to 2 lines, to avoid many cases of having to
" "press <Enter> to continue"
set cmdheight=2
" Display line numbers on the left
set number
" Quickly time out on keycodes, but never time out on mappings
set notimeout ttimeout ttimeoutlen=200
" Use <F11> to toggle between 'paste' and 'nopaste'
set pastetoggle=<F11>
"------------------------------------------------------------
" Indentation options {{{1
"
" Indentation settings according to personal preference.
" Indentation settings for using 4 spaces instead of tabs.
" Do not change 'tabstop' from its default value of 8 with this setup.
set shiftwidth=4
set softtabstop=4
set expandtab
" Indentation settings for using hard tabs for indent. Display tabs as
" four characters wide.
"set shiftwidth=4
"set tabstop=4
"------------------------------------------------------------
" Mappings {{{1
"
" Useful mappings
" Map Y to act like D and C, i.e. to yank until EOL, rather than act as yy,
" which is the default
map Y y$
" Map <C-L> (redraw screen) to also turn off search highlighting until the
" next search
nnoremap <C-L> :nohl<CR><C-L>
"------------------------------------------------------------

View File

@ -1,15 +1,31 @@
# vim: sts=2 ts=2 sw=2 et ai
{% from "users/map.jinja" import users with context %}
googleauth-package:
users_googleauth-package:
pkg.installed:
- name: {{ users.googleauth_package }}
- require:
- file: {{ users.googleauth_dir }}
{{ users.googleauth_dir }}:
file:
- directory
users_{{ users.googleauth_dir }}:
file.directory:
- name: {{ users.googleauth_dir }}
- user: root
- group: {{ users.root_group }}
- mode: 600
{% for name, user in pillar.get('users', {}).items() if user.absent is not defined or not user.absent %}
{%- if 'google_auth' in user %}
{%- for svc in user['google_auth'] %}
{%- if user.get('google_2fa', True) %}
users_googleauth-pam-{{ svc }}-{{ name }}:
file.replace:
- name: /etc/pam.d/{{ svc }}
- pattern: "^@include common-auth"
- repl: "auth [success=done new_authtok_reqd=done default=die] pam_google_authenticator.so user=root secret={{ users.googleauth_dir }}/${USER}_{{ svc }} echo_verification_code\n@include common-auth"
- unless: grep pam_google_authenticator.so /etc/pam.d/{{ svc }}
- backup: .bak
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endfor %}

View File

@ -38,13 +38,13 @@ include:
{%- endif %}
{% for group in user.get('groups', []) %}
{{ name }}_{{ group }}_group:
users_{{ name }}_{{ group }}_group:
group:
- name: {{ group }}
- present
{% endfor %}
{{ name }}_user:
users_{{ name }}_user:
{% if user.get('createhome', True) %}
file.directory:
- name: {{ home }}
@ -101,6 +101,7 @@ include:
- group: {{ group }}
{% endfor %}
{% if 'ssh_keys' in user or 'ssh_auth' in user or 'ssh_auth.absent' in user %}
user_keydir_{{ name }}:
file.directory:
@ -119,7 +120,7 @@ user_keydir_{{ name }}:
{% if 'ssh_keys' in user %}
{% set key_type = 'id_' + user.get('ssh_key_type', 'rsa') %}
user_{{ name }}_private_key:
users_user_{{ name }}_private_key:
file.managed:
- name: {{ user.get('home', '/home/{0}'.format(name)) }}/.ssh/{{ key_type }}
- user: {{ name }}
@ -128,11 +129,11 @@ user_{{ name }}_private_key:
- show_diff: False
- contents_pillar: users:{{ name }}:ssh_keys:privkey
- require:
- user: {{ name }}_user
- user: users_{{ name }}_user
{% for group in user.get('groups', []) %}
- group: {{ name }}_{{ group }}_group
- group: users_{{ name }}_{{ group }}_group
{% endfor %}
user_{{ name }}_public_key:
users_user_{{ name }}_public_key:
file.managed:
- name: {{ user.get('home', '/home/{0}'.format(name)) }}/.ssh/{{ key_type }}.pub
- user: {{ name }}
@ -141,45 +142,106 @@ user_{{ name }}_public_key:
- show_diff: False
- contents_pillar: users:{{ name }}:ssh_keys:pubkey
- require:
- user: {{ name }}_user
- user: users_{{ name }}_user
{% for group in user.get('groups', []) %}
- group: {{ name }}_{{ group }}_group
- group: users_{{ name }}_{{ group }}_group
{% endfor %}
{% endif %}
{% if 'ssh_auth_file' in user %}
users_authorized_keys_{{ name }}:
file.managed:
- name: {{ home }}/.ssh/authorized_keys
- user: {{ name }}
- group: {{ name }}
- mode: 600
- contents: |
{% for auth in user.ssh_auth_file -%}
{{ auth }}
{% endfor -%}
{% endif %}
{% if 'ssh_auth' in user %}
{% for auth in user['ssh_auth'] %}
ssh_auth_{{ name }}_{{ loop.index0 }}:
users_ssh_auth_{{ name }}_{{ loop.index0 }}:
ssh_auth.present:
- user: {{ name }}
- name: {{ auth }}
- require:
- file: {{ name }}_user
- user: {{ name }}_user
- file: users_{{ name }}_user
- user: users_{{ name }}_user
{% endfor %}
{% endif %}
{% if 'ssh_keys_pillar' in user %}
{% for key_name, pillar_name in user['ssh_keys_pillar'].iteritems() %}
users_ssh_keys_files_{{ name }}_{{ key_name }}_pub:
file.managed:
- name: {{ user.get('home', '/home/{0}'.format(name)) }}/.ssh/{{ key_name
}}.pub
- contents: |
{{ pillar[pillar_name][key_name]['pubkey'] }}
users_ssh_keys_files_{{ name }}_{{ key_name }}_priv:
file.managed:
- name: {{ user.get('home', '/home/{0}'.format(name)) }}/.ssh/{{ key_name
}}
- contents: |
{{ pillar[pillar_name][key_name]['privkey'] | indent(8) }}
{% endfor %}
{% endif %}
{% if 'ssh_auth_sources' in user %}
{% for pubkey_file in user['ssh_auth_sources'] %}
users_ssh_auth_source_{{ name }}_{{ loop.index0 }}:
ssh_auth.present:
- user: {{ name }}
- source: {{ pubkey_file }}
- require:
- file: users_{{ name }}_user
- user: users_{{ name }}_user
{% endfor %}
{% endif %}
{% if 'ssh_auth.absent' in user %}
{% for auth in user['ssh_auth.absent'] %}
ssh_auth_delete_{{ name }}_{{ loop.index0 }}:
users_ssh_auth_delete_{{ name }}_{{ loop.index0 }}:
ssh_auth.absent:
- user: {{ name }}
- name: {{ auth }}
- require:
- file: {{ name }}_user
- user: {{ name }}_user
- file: users_{{ name }}_user
- user: users_{{ name }}_user
{% endfor %}
{% endif %}
{% if 'ssh_config' in user %}
users_ssh_config_{{ name }}:
file.managed:
- name: {{ home }}/.ssh/config
- user: {{ name }}
- group: {{ user_group }}
- mode: 640
- contents: |
# Managed by Saltstack
# Do Not Edit
{% for label, setting in user.ssh_config.items() %}
# {{ label }}
Host {{ setting.get('hostname') }}
{%- for opts in setting.get('options') %}
{{ opts }}
{%- endfor %}
{% endfor -%}
{% endif %}
{% if 'sudouser' in user and user['sudouser'] %}
sudoer-{{ name }}:
users_sudoer-{{ name }}:
file.managed:
- name: {{ users.sudoers_dir }}/{{ name }}
- user: root
- group: {{ users.root_group }}
- mode: '0440'
{% if 'sudo_rules' in user or 'sudo_defaults' in user %}
{% if 'sudo_rules' in user %}
{% for rule in user['sudo_rules'] %}
"validate {{ name }} sudo rule {{ loop.index0 }} {{ name }} {{ rule }}":
@ -191,46 +253,71 @@ sudoer-{{ name }}:
# Specify the rule via an env var to avoid shell quoting issues.
- rule: "{{ name }} {{ rule }}"
- require_in:
- file: {{ users.sudoers_dir }}/{{ name }}
- file: users_{{ users.sudoers_dir }}/{{ name }}
{% endfor %}
{% endif %}
{% if 'sudo_defaults' in user %}
{% for entry in user['sudo_defaults'] %}
"validate {{ name }} sudo Defaults {{ loop.index0 }} {{ name }} {{ entry }}":
cmd.run:
- name: 'visudo -cf - <<<"$rule" | { read output; if [[ $output != "stdin: parsed OK" ]] ; then echo $output ; fi }'
- stateful: True
- shell: {{ users.visudo_shell }}
- env:
# Specify the rule via an env var to avoid shell quoting issues.
- rule: "Defaults:{{ name }} {{ entry }}"
- require_in:
- file: users_{{ users.sudoers_dir }}/{{ name }}
{% endfor %}
{% endif %}
{{ users.sudoers_dir }}/{{ name }}:
users_{{ users.sudoers_dir }}/{{ name }}:
file.managed:
- name: {{ users.sudoers_dir }}/{{ name }}
- contents: |
{%- if 'sudo_defaults' in user %}
{%- for entry in user['sudo_defaults'] %}
Defaults:{{ name }} {{ entry }}
{%- endfor %}
{%- endif %}
{%- if 'sudo_rules' in user %}
{%- for rule in user['sudo_rules'] %}
{{ name }} {{ rule }}
{%- endfor %}
{%- endif %}
- require:
- file: sudoer-defaults
- file: sudoer-{{ name }}
- file: users_sudoer-defaults
- file: users_sudoer-{{ name }}
{% endif %}
{% else %}
{{ users.sudoers_dir }}/{{ name }}:
users_{{ users.sudoers_dir }}/{{ name }}:
file.absent:
- name: {{ users.sudoers_dir }}/{{ name }}
{% endif %}
{%- if 'google_auth' in user %}
{%- for svc in user['google_auth'] %}
googleauth-{{ svc }}-{{ name }}:
users_googleauth-{{ svc }}-{{ name }}:
file.managed:
- replace: false
- name: {{ users.googleauth_dir }}/{{ name }}_{{ svc }}
- contents_pillar: 'users:{{ name }}:google_auth:{{ svc }}'
- user: root
- group: {{ users.root_group }}
- mode: 600
- mode: 400
- require:
- pkg: googleauth-package
- pkg: users_googleauth-package
{%- endfor %}
{%- endif %}
{% endfor %}
{% for name, user in pillar.get('users', {}).iteritems() if user.absent is defined and user.absent %}
{{ name }}:
users_absent_user_{{ name }}:
{% if 'purge' in user or 'force' in user %}
user.absent:
- name: {{ name }}
{% if 'purge' in user %}
- purge: {{ user['purge'] }}
{% endif %}
@ -238,22 +325,24 @@ googleauth-{{ svc }}-{{ name }}:
- force: {{ user['force'] }}
{% endif %}
{% else %}
user.absent
user.absent:
- name: {{ name }}
{% endif -%}
{{ users.sudoers_dir }}/{{ name }}:
users_{{ users.sudoers_dir }}/{{ name }}:
file.absent:
- name: {{ users.sudoers_dir }}/{{ name }}
{% endfor %}
{% for user in pillar.get('absent_users', []) %}
{{ user }}:
users_absent_user_2_{{ user }}:
user.absent
{{ users.sudoers_dir }}/{{ user }}:
users_2_{{ users.sudoers_dir }}/{{ user }}:
file.absent:
- name: {{ users.sudoers_dir }}/{{ user }}
{% endfor %}
{% for group in pillar.get('absent_groups', []) %}
{{ group }}:
group.absent
users_absent_group_{{ group }}:
group.absent:
- name: {{ group }}
{% endfor %}

View File

@ -2,31 +2,31 @@
{% from "users/map.jinja" import users with context %}
# Ensure availability of bash
bash-package:
users_bash-package:
pkg.installed:
- name: {{ users.bash_package }}
sudo-group:
users_sudo-group:
group.present:
- name: sudo
- system: True
sudo-package:
users_sudo-package:
pkg.installed:
- name: {{ users.sudo_package }}
- require:
- group: sudo-group
- group: users_sudo-group
- file: {{ users.sudoers_dir }}
{{ users.sudoers_dir }}:
file:
- directory
users_{{ users.sudoers_dir }}:
file.directory:
- name: {{ users.sudoers_dir }}
sudoer-defaults:
users_sudoer-defaults:
file.append:
- name: {{ users.sudoers_file }}
- require:
- pkg: sudo-package
- pkg: users_sudo-package
- text:
- Defaults env_reset
- Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

28
users/vimrc.sls Normal file
View File

@ -0,0 +1,28 @@
{% from "users/map.jinja" import users with context %}
include:
- users
- vim
{% for name, user in pillar.get('users', {}).items() if user.absent is not defined or not user.absent %}
{%- if user == None -%}
{%- set user = {} -%}
{%- endif -%}
{%- set home = user.get('home', "/home/%s" % name) -%}
{%- set manage = user.get('manage_vimrc', False) -%}
{%- if 'prime_group' in user and 'name' in user['prime_group'] %}
{%- set user_group = user.prime_group.name -%}
{%- else -%}
{%- set user_group = name -%}
{%- endif %}
{%- if manage -%}
users_{{ name }}_user_vimrc:
file.managed:
- name: {{ home }}/.vimrc
- user: {{ name }}
- group: {{ user_group }}
- mode: 644
- source:
- salt://users/files/vimrc/{{ name }}/vimrc
- salt://users/files/vimrc/vimrc
{% endif %}
{% endfor %}