Contents
The Plan
This guide builds on the minimum version of a the playbook for Ansible to create user accounts and setup ssh keys outlined in the first part of this tutorial
Now we are going to look at improving that example to a more Viable version with the following steps:
- defining expanding the vars to have multiple properties per item to add groups per user.
- using user state for a method to disable users accounts.
- Update the servers SSH config, and add a notify rule so sshd is restarted if required.
Adding multiple properties to an Ansible var
Our user var in the first example we used was just a simple list of usernames, but we probably want a bit more control of how each user is setup and so, we can expand on this.
Instead of just adding a simple string, we add each additional parameter at the same indentation and then when looped over using with_items
the properties become available such as "{{ item.username }}"
vars with complex data
4 5 6 7 8 9 10 11 | vars: users: - username: "paul" groups: "admin,www-data" - username: "tanya" groups: "www-data" - username: "ruby" groups: "www-data" |
13 14 15 16 17 | - name: "Create user accounts" user: name: "{{ item.username }}" groups: "{{ item.groups }}" with_items: "{{ users }}" |
18 19 20 21 22 | - name: "Add authorized keys" authorized_key: user: "{{ item.username }}" key: "{{ lookup('file', 'files/'+ item.username + '.key.pub') }}" with_items: "{{ users }}" |
users.yml
1 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 | --- - hosts: "localhost" connection: "local" vars: users: - username: "paul" groups: "admin,www-data" - username: "tanya" groups: "www-data" - username: "ruby" groups: "www-data" tasks: - name: "Create user accounts" user: name: "{{ item.username }}" groups: "{{ item.groups }}" with_items: "{{ users }}" - name: "Add authorized keys" authorized_key: user: "{{ item.username }}" key: "{{ lookup('file', 'files/'+ item.username + '.key.pub') }}" with_items: "{{ users }}" - name: "Allow admin users to sudo without a password" lineinfile: dest: "/etc/sudoers" # path: in version 2.3 state: "present" regexp: "^%admin" line: "%admin ALL=(ALL) NOPASSWD: ALL" ... |
Using user state to remove user
Now we are adding users to the server using an automated script, we will at some point need a method to remove them.
When we’ve used user
so far, we’ve relied on the default state:
"present"
. You could add an other property to the current users list, but I think to make it clear at a glance which are active users and which are disabled users, we going to create a new var as a simple list of old users.
We will also update the existing user task to explicitly define state as present. Even though it’s the default, when it’s next to a similar task it helps make it really clear what happens in each task.
Remove users with user state
12 13 14 | vars: remove_users: - ruby |
15 16 17 18 19 20 21 22 23 24 25 | - name: "Create user accounts" user: name: "{{ item.username }}" groups: "{{ item.groups }}" state: "present" with_items: "{{ users }}" - name: "Remove old user accounts" user: name: "{{ item }}" state: "absent" with_items: "{{ remove_users }}" |
users.yml
1 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 | --- - hosts: "localhost" connection: "local" vars: users: - username: "paul" groups: "admin,www-data" - username: "tanya" groups: "www-data" remove_users: - "ruby" tasks: - name: "Create user accounts" user: name: "{{ item.username }}" groups: "{{ item.groups }}" state: "present" with_items: "{{ users }}" - name: "Remove old user accounts in remove_users" user: name: "{{ item }}" state: "absent" with_items: "{{ remove_users }}" - name: "Add authorized keys" authorized_key: user: "{{ item.username }}" key: "{{ lookup('file', 'files/'+ item.username + '.key.pub') }}" with_items: "{{ users }}" - name: "Allow admin users to sudo without a password" lineinfile: dest: "/etc/sudoers" # path: in version 2.3 state: "present" regexp: "^%admin" line: "%admin ALL=(ALL) NOPASSWD: ALL" ... |
Update sshd config to disable root login and restart ssh service
Now we are managing adding and removing users to the server we can improve security by disabling root logins via ssh. Using lineinfile
we will update /etc/ssh/config
Handlers have two important traits: They at the end of the playbook only run if notified, and they run only once no matter how many times they are notified.
handlers and notify
notify
matches name
12 13 14 15 16 | handlers: - name: "Restart sshd" service: name: "sshd" state: "restarted" |
20 21 22 23 24 25 | - name: "Disable root login via SSH" lineinfile: dest: "/etc/ssh/sshd_config" regexp: "^PermitRootLogin" line: "PermitRootLogin no" notify: "Restart sshd" |
users.yml
1 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 | --- - hosts: "localhost" connection: "local" vars: users: - username: "paul" groups: "admin,www-data" - username: "tanya" groups: "www-data" remove_users: - "ruby" handlers: - name: "Restart sshd" service: name: "sshd" state: "restarted" tasks: - name: "Create user accounts" user: name: "{{ item.username }}" groups: "{{ item.groups }}" state: "present" with_items: "{{ users }}" - name: "Remove old user accounts in remove_users" user: name: "{{ item }}" state: "absent" with_items: "{{ remove_users }}" - name: "Add authorized keys" authorized_key: user: "{{ item.username }}" key: "{{ lookup('file', 'files/'+ item.username + '.key.pub') }}" with_items: "{{ users }}" - name: "Allow admin users to sudo without a password" lineinfile: dest: "/etc/sudoers" # path: in version 2.3 state: "present" regexp: "^%admin" line: "%admin ALL=(ALL) NOPASSWD: ALL" - name: "Disable root login via SSH" lineinfile: dest: "/etc/ssh/sshd_config" regexp: "^PermitRootLogin" line: "PermitRootLogin no" notify: "Restart sshd" ... |