diff --git a/add-users-groups-authorized_keys-dot-files/README.md b/add-users-groups-authorized_keys-dot-files/README.md new file mode 100644 index 0000000..a791366 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/README.md @@ -0,0 +1,9 @@ +# Users and ssh-configAnsible roles +This repo contains 2 roles: + +- **users**: Add users and configure `.bashrc` and `authorized_keys` +- **ssh-config**: Configures a user's `~/.ssh/config` + +Both roles make use of the same _users_ variable and are created to give users the freedom to add their own configuration outside of Ansible. + +Detailed configuration can be found in the README files inside the role's folders. diff --git a/add-users-groups-authorized_keys-dot-files/ansible.cfg b/add-users-groups-authorized_keys-dot-files/ansible.cfg new file mode 100644 index 0000000..6efa305 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/ansible.cfg @@ -0,0 +1,17 @@ +[ssh_connection] + +[defaults] +retry_files_enabled = False +retry_files_save_path = /tmp/ +inventory=./hosts +host_key_checking=False +gathering = smart +stdout_callback=unixy +#stdout_callback=debug + +[privilege_escalation] +become=True +become_method=sudo +become_user=root +#become_ask_pass=False + diff --git a/add-users-groups-authorized_keys-dot-files/group_vars/all b/add-users-groups-authorized_keys-dot-files/group_vars/all new file mode 100644 index 0000000..c8e37a5 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/group_vars/all @@ -0,0 +1,46 @@ +--- +user_groups: + - name: mygroup + gid: 700 + - name: mysecondgroup + gid: 702 + state: absent + - name: admin + gid: 703 + state: present + + +users: + - name: remember + state: present + password: "blabla" + groups: + - mygroup + - admin + uid: 1100 + enable_sudo: false + keys: + - file: key1 + state: present + bash_lines: + - line: "#testline" + state: present + - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" + state: present + - line: "alias ls='ls lah'" + state: present + bash_blocks: + - content: | + #testing + #multiline + state: absent + ssh_config: + - ServerAliveInterval: 10 + - name: test + state: present + keys: + - file: key2 + state: absent + csh_lines: + - line: "alias ls ls -lah" + state: present diff --git a/add-users-groups-authorized_keys-dot-files/hosts b/add-users-groups-authorized_keys-dot-files/hosts new file mode 100644 index 0000000..17d4bc8 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/hosts @@ -0,0 +1,3 @@ +10.106.116.157 ssh_short_name=host1 ansible_user=root +10.106.116.139 ssh_short_name=host2 ansible_user=root +34.242.108.38 ssh_short_name=freebsd1 ansible_user=ec2-user ansible_python_interpreter=/usr/local/bin/python2.7 diff --git a/add-users-groups-authorized_keys-dot-files/roles/ssh-config/README.md b/add-users-groups-authorized_keys-dot-files/roles/ssh-config/README.md new file mode 100644 index 0000000..d071612 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/ssh-config/README.md @@ -0,0 +1,89 @@ +# ssh-config +Ansible role to configure a user's `~/.ssh/config` file. This will add a +configuration in the ssh config file for each host in the inventory. + +**NOTE: this role works in conjunction with the _users_ variable** + +## Variables + +| _variable name_ | Description | +| ---: |--- | +| ssh_short_name | host identifier name in the ssh config.
This should be added to the _host variables_ | +| ssh_config | name of the key in the *users* variable. Contains a list of +key/value items| + +## Example: + +**Host inventory** +``` +10.106.116.157 ssh_short_name=host1 +10.106.116.139 ssh_short_name=host2 +``` + +**Variables** +populate the *ssh_config* key. +``` +users: + - name: remember + state: present + password: "blabla" + groups: + - mygroup + uid: 1100 + keys: + - file: key1 + state: present + shell_lines: + - line: "testline" + state: present + - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" + state: present + - line: "alias ls='ls lah'" + state: present + ssh_config: + - ServerAliveInterval: 10 +``` + +**Result:** +``` +# BEGIN ANSIBLE MANAGED BLOCK +Host host1 + Hostname 10.106.116.157 + RemoteForward /home/remember/.gnupg/S.gpg-agent $HOME/.gnupg/S.gpg-agent + RemoteForward /home/remember/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh + ServerAliveInterval 10 +Host host2 + Hostname 10.106.116.139 + RemoteForward /home/remember/.gnupg/S.gpg-agent $HOME/.gnupg/S.gpg-agent + RemoteForward /home/remember/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh + ServerAliveInterval 10 +# END ANSIBLE MANAGED BLOCK + +``` + +**Break down** + +The host identifier is populated with the `ssh_short_name` host variable. +``` +Host host1 +``` + +The `Hostname` is populated with the `inventory_hostname` variable +``` +Hostname 10.106.116.139 +``` + +These lines are added by default: +``` +RemoteForward /home/remember/.gnupg/S.gpg-agent $HOME/.gnupg/S.gpg-agent +RemoteForward /home/remember/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh +``` + +Everything below this is populated with the key/values defined in the +`ssh_config` list of the `users` variable + +``` +ServerAliveInterval 10 +``` + + diff --git a/add-users-groups-authorized_keys-dot-files/roles/ssh-config/defaults/main.yml b/add-users-groups-authorized_keys-dot-files/roles/ssh-config/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/add-users-groups-authorized_keys-dot-files/roles/ssh-config/tasks/main.yml b/add-users-groups-authorized_keys-dot-files/roles/ssh-config/tasks/main.yml new file mode 100644 index 0000000..ef8a5bf --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/ssh-config/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: Check if user has ~/.ssh/config + stat: + path: "/home/{{ item.name }}/.ssh/config" + with_items: "{{ users }}" + register: sshconfig + + +- name: Create ~/.ssh/config when absent + file: + path: "/home/{{ item.item.name }}/.ssh/config" + owner: "{{ item.item.name }}" + group: "{{ item.item.name }}" + mode: 0600 + state: touch + when: item.stat.exists == False and item.item.state == "present" + with_items: + - "{{ sshconfig.results }}" + no_log: True + + +- name: Configure ~/.ssh/config + blockinfile: + path: "/home/{{ item.0.name }}/.ssh/config" + owner: "{{ item.0.name }}" + group: "{{ item.0.name }}" + mode: 0600 + marker: "# {mark} ANSIBLE MANAGED BLOCK" + content: | + {% for host in groups['all'] -%} + Host {{ hostvars[host]['ssh_short_name'] }} + Hostname {{ hostvars[host]['inventory_hostname'] }} + RemoteForward /home/{{ item.0.name }}/.gnupg/S.gpg-agent $HOME/.gnupg/S.gpg-agent + RemoteForward /home/{{ item.0.name }}/.gnupg/S.gpg-agent.ssh $HOME/.gnupg/S.gpg-agent.ssh + {% for k,v in item.1.items() %} + {% if k|lower != "host" and k|lower != "hostname" %} + {{k}} {{v}} + {% endif %} + {% endfor %} + {% endfor %} + with_subelements: + - "{{ users }}" + - ssh_config + - skip_missing: true + when: item.0.state == "present" diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/README.md b/add-users-groups-authorized_keys-dot-files/roles/users/README.md new file mode 100644 index 0000000..bf5f62f --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/README.md @@ -0,0 +1,135 @@ +# Users +Ansible roles to create/configure users on Linux/FreeBSD. + +## Variables +| user_groups | | | +| ---: |--- |--- | +| name | name of the group | Data type | +| gid | Optionally set the group ID | int | +| state | whether the group shoud be created or removed | present/absent | + + +| users | | | +| ---: |---| ---| +| _variable name_ | Description | Data type | +| name | username | string | +| state | whether the user should be created or removed | present/absent | +| password | string of an encrypted value(1) | string | +| groups | additional groups the user should belong to | list | +| uid | optionally specify a user id | int | +| enable_sudo | Enable passwordless sudo for the given user | bool | +| keys | list of dictionaries | list | +| bash_lines | configure lines in .bashrc | list | +| bash_blocks | configure lines in .bashrc | list | +| csh_lines | configure lines in .cshrc | list | +| csh__blocks | configure lines in .cshrc | list | + +(1) https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#how-do-i-generate-crypted-passwords-for-the-user-module + +## Default variables +The default shells depending on the OS are: + +- Linux: `/bin/bash` +- FreeBSD: `/bin/cshrc` + +This is defined in the `defaults` section of the **users** role + + +## Example inventory +``` +user_groups: + - name: mygroup + gid: 700 + + +users: + - name: remember + state: present + password: "blabla" + groups: + - mygroup + uid: 1100 + enable_sudo: true + keys: + - file: key1 + state: present + bash_lines: + - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" + state: present + - line: "alias ls='ls lah'" + state: present + bash_blocks: + - content: | + #testing + #multiline + state: present + - name: test + enable_sudo: false + keys: + - file: key2 + state: absent + csh_lines: + - line: "ls ls -lah" + state: absent +``` +## Using the Role +### Example Playbook +``` +--- +- name: Manage user configuration + hosts: all + remote_user: root + roles: + - users +``` +### Configure a user's ssh keys +For every user a directory matching the username should be created under the _keys_ folder in the role's _files_ folder. In this folder the user's ssh keys can be stored. + +``` +├── files +│   └── keys +│   ├── remember +│   │   └── key1.pub +│   └── test +│   └── key2.pub +``` +The name of the file holding the key should match the name in the _users_ variable + +``` + keys: + - file: key1 + state: present +``` + +### Configure a user's shell +This role allows you to add or remove lines to a user's `.bashrc` or `cshrc` file. Since this is not based on a template that overwrites the complete file, users can still add their own configuration too. + +Add items to the **shell_lines** key in the **users** variable. Each item exists of a _line_ and _state_ key. + +**lines** + +Use _lines_ if you want to make sure a single line is present or not. +Example: +``` +shell_lines: + - line: "testline" + state: absent + - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" + state: present + - line: "alias ls='ls lah'" + state: present +``` + +**blocks** + +use blocks if you want to make sure a number of lines that belong together are +present or not. + +Example: +``` +bash_blocks: + - content: | + if [ condition ]; then + do something + state: present +``` diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/defaults/main.yml b/add-users-groups-authorized_keys-dot-files/roles/users/defaults/main.yml new file mode 100644 index 0000000..6577b10 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/defaults/main.yml @@ -0,0 +1,40 @@ +default_freebsd_shell: "/bin/csh" +default_linux_shell: "/bin/bash" + + +# Example variables +#--- +#user_groups: +# - name: mygroup +# gid: 700 +# - name: mysecondgroup +# gid: 702 +# state: absent +# +# +#users: +# - name: remember +# state: present +# password: "blabla" +# groups: +# - mygroup +# uid: 1100 +# keys: +# - file: key1 +# state: present +# bash_lines: +# - line: "testline" +# state: present +# - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" +# state: present +# - line: "alias ls='ls lah'" +# state: present +# ssh_config: +# - ServerAliveInterval: 10 +# - name: test +# keys: +# - file: key2 +# state: absent +# csh_lines: +# - line: "alias ls ls -lah" +# state: present diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/remember/key1.pub b/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/remember/key1.pub new file mode 100644 index 0000000..3013f1a --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/remember/key1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMfztaQoo3Alf4Ie4ZrSEkhojOcKl8VRdoRiYb/7FL3IS/5IcSKcan/MGJlRht3ibwJBx9/CY8wZivHgNKCqtbZWGepfOtgWOqI4ROo4sELmRgV8PZUACjCSfaOkOdvCJEjhw3n+aI5jmK9IUA+mwdXkZj/NckNDZAQ+FRqwR6sX7svM4TF/zEI70JvO3xnDgCuC2PgiztVFfMqbWl33NgkG3kWkJ+JarF2pNsxO/+82s/hoC4P+dpZD1PHhJC7OxUiAHe5nwF7heQh9DUBQxJBhitn7C3XqlxEf7Kx3/kO9CUJVDaxS84UUnfUPc0u1iYpE+5ypqkDSyj3yQNpwXf diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/test/key2.pub b/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/test/key2.pub new file mode 100644 index 0000000..88ca4dd --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/files/keys/test/key2.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMfztaQoo3Alf4Ie4ZrSEkhojOcKl8VRdoRiYb/7FL3IS/5IcSKcan/MGJlRht3ibwJBx9/CY8wZivHgNKCqtbZWGepfOtgWOqI4ROo4sELmRgV8PZUACjCSfaOkOdvCJEjhw3n+aI5jmK9IUA+mwdXkZj/NckNDZAQ+FRqwR6sX7svM4TF/zEI70JvO3xnDgCuC2PgiztVFfMqbWl33NgkG3kWkJ+JarF2pNsxO/+82s/hoC4P+dpZD1PHhJC7OxUiAHe5nwF7heQh9DUBQxJBhitn7C3XqlxEf7Kx3/kO9CUJVDaxS84UUnfUPc0u1iYpE+5ypqkDSyj3yQNpwXd diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/tasks/main.yml b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/main.yml new file mode 100644 index 0000000..b3a54e4 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Check for required variables + fail: + msg: "Variable: 'users.name' or 'users.state' NOT defined!" + with_items: "{{ users }}" + when: item.state is not defined or item.name is not defined + + +- include_tasks: set_facts.yml +- include_tasks: users.yml +- include_tasks: ssh_config.yml diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/tasks/set_facts.yml b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/set_facts.yml new file mode 100644 index 0000000..9bc332f --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/set_facts.yml @@ -0,0 +1,26 @@ +# Set default shell +- set_fact: + default_shell: "{{ default_freebsd_shell }}" + when: ansible_os_family == 'FreeBSD' + +- set_fact: + default_shell: "{{ default_linux_shell }}" + when: ansible_os_family == 'Debian' + +# Set sudoers path +- set_fact: + sudoers_path: /usr/local/etc/sudoers.d + when: ansible_os_family == 'FreeBSD' + +- set_fact: + sudoers_path: /etc/sudoers.d + when: ansible_os_family == 'Debian' + +# Set sudo config path +- set_fact: + sudo_config_path: /usr/local/etc/sudoers + when: ansible_os_family == 'FreeBSD' + +- set_fact: + sudo_config_path: /etc/sudoers + when: ansible_os_family == 'Debian' diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/tasks/ssh_config.yml b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/ssh_config.yml new file mode 100644 index 0000000..52b1c81 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/ssh_config.yml @@ -0,0 +1,23 @@ + +- name: Ensure .ssh folder is created + file: + path: "/home/{{item.name}}/.ssh" + state: directory + mode: 0700 + owner: "{{ item.name }}" + group: "{{ item.name }}" + with_items: + - "{{ users }}" + when: item.state == "present" + + +- name: Configure authorized_keys + authorized_key: + user: "{{ item.0.name }}" + key: "{{ lookup('file', 'keys/' + item.0.name + '/' + item.1.file + '.pub') }}" + state: "{{ item.1.state | default('present') }}" + with_subelements: + - "{{ users }}" + - keys + when: item.0.state is defined and item.0.state == "present" + diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/tasks/users.yml b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/users.yml new file mode 100644 index 0000000..2df15e0 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/tasks/users.yml @@ -0,0 +1,113 @@ +--- +- name: Add/Remove group + group: + name: "{{ item.name }}" + gid: "{{ item.gid | default(omit) }}" + state: "{{ item.state | default('present') }}" + with_items: "{{ user_groups }}" + + +- name: Add/Remove user + user: + name: "{{ item.name }}" + state: "{{ item.state | default('present') }}" + password: "{{ item.password | default(omit) }}" + groups: "{{ item.groups | default(omit) }}" + uid: "{{ item.uid | default(omit) }}" + shell: "{{ item.shell | default(default_shell) }}" + remove: yes + no_log: False + with_items: "{{ users }}" + + +- name: Configure bashrc lines + lineinfile: + path: "/home/{{ item.0.name }}/.bashrc" + line: "{{ item.1.line }}" + state: "{{ item.1.state | default('present') }}" + backup: yes + with_subelements: + - "{{ users }}" + - bash_lines + - skip_missing: true + when: ansible_os_family == 'Debian' and item.0.state == "present" + +- name: Configure bashrc blocks + blockinfile: + path: "/home/{{ item.0.name }}/.bashrc" + content: "{{ item.1.content }}" + marker: "# {mark} ANSIBLE managed content. Block item #{{ listitem }}" + state: "{{ item.1.state | default('present') }}" + backup: yes + with_subelements: + - "{{ users }}" + - bash_blocks + - skip_missing: true + when: ansible_os_family == 'Debian' and item.0.state == "present" + loop_control: + index_var: listitem + +- name: Configure cshrc lines + lineinfile: + path: "/home/{{ item.0.name }}/.cshrc" + line: "{{ item.1.line }}" + state: "{{ item.1.state | default('present')}}" + backup: yes + with_subelements: + - "{{ users }}" + - csh_lines + - skip_missing: true + when: ansible_os_family == 'FreeBSD' and item.0.state == "present" + +- name: Configure cshrc blocks + blockinfile: + path: "/home/{{ item.0.name }}/.cshrc" + content: "{{ item.1.conent }}" + marker: "# {mark} ANSIBLE managed content. Block item #{{ listitem }}" + state: "{{ item.1.state | default('present')}}" + backup: yes + with_subelements: + - "{{ users }}" + - csh_blocks + - skip_missing: true + when: ansible_os_family == 'FreeBSD' and item.0.state == "present" + loop_control: + index_var: listitem + +- name: Ensure sudo is installed (Debian) + apt: + name: sudo + update_cache: yes + cache_valid_time: "{{ apt_cache_valid | default('86400') }}" + when: ansible_os_family == "Debian" + +- name: Ensure sudo is installed (FreeBSD) + portinstall: + name: sudo + state: present + when: ansible_os_family == "FreeBSD" + +- name: Enable sudo for user + lineinfile: + path: "{{ sudoers_path }}/{{ item.name }}" + line: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL" + state: present + create: true + when: item.enable_sudo is defined and item.enable_sudo == true + with_items: "{{ users }}" + +- name: Disable sudo for user + file: + path: "{{ sudoers_path }}/{{ item.name }}" + state: absent + when: item.enable_sudo is defined and item.enable_sudo == false + with_items: "{{ users }}" + +- name: Include sudoers.d + lineinfile: + dest: "{{ sudo_config_path }}" + state: present + regexp: '^\#includedir {{ sudoers_path }}' + line: '#includedir {{ sudoers_path }}' + validate: 'visudo -cf %s' + diff --git a/add-users-groups-authorized_keys-dot-files/roles/users/vars/main.yml b/add-users-groups-authorized_keys-dot-files/roles/users/vars/main.yml new file mode 100644 index 0000000..ce6a2f4 --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/roles/users/vars/main.yml @@ -0,0 +1,33 @@ +#--- +#user_groups: +# - name: mygroup +# gid: 700 +# - name: mysecondgroup +# gid: 702 +# state: absent +# +# +#users: +# - name: remember +# state: present +# password: "blabla" +# groups: +# - mygroup +# uid: 1100 +# keys: +# - file: key1 +# state: present +# shell_lines: +# - line: "testline" +# state: present +# - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" +# state: present +# - line: "alias ls='ls lah'" +# state: present +# - name: test +# keys: +# - file: key2 +# state: absent +# shell_lines: +# - line: "export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh" +# state: present diff --git a/add-users-groups-authorized_keys-dot-files/site.yaml b/add-users-groups-authorized_keys-dot-files/site.yaml new file mode 100644 index 0000000..9fe41ea --- /dev/null +++ b/add-users-groups-authorized_keys-dot-files/site.yaml @@ -0,0 +1,8 @@ +--- +- name: Manage user configuration + hosts: all + # remote_user: root + roles: + - users + - ssh-config +