diff --git a/playbooks/generic/chrony-client.yml b/playbooks/generic/chrony-client.yml index 022f4bb6e..b9c5e384b 100644 --- a/playbooks/generic/chrony-client.yml +++ b/playbooks/generic/chrony-client.yml @@ -5,4 +5,4 @@ tasks: - name: Configure Chrony client include_role: - name: DeepOps.chrony + name: deepops_chrony diff --git a/playbooks/generic/hosts.yml b/playbooks/generic/hosts.yml index d5c394c0e..0b0dfb335 100644 --- a/playbooks/generic/hosts.yml +++ b/playbooks/generic/hosts.yml @@ -9,6 +9,6 @@ - name: set /etc/hosts include_role: - name: DeepOps.hosts + name: deepops_hosts vars: hosts_add_ansible_managed_hosts: true diff --git a/playbooks/generic/users.yml b/playbooks/generic/users.yml index 8c7fce506..a6a3cdd64 100644 --- a/playbooks/generic/users.yml +++ b/playbooks/generic/users.yml @@ -25,4 +25,4 @@ - name: Set user password include_role: - name: DeepOps.users + name: deepops_users diff --git a/playbooks/utilities/user-password.yml b/playbooks/utilities/user-password.yml index c538dc699..d6677f0bb 100644 --- a/playbooks/utilities/user-password.yml +++ b/playbooks/utilities/user-password.yml @@ -27,7 +27,7 @@ ssh_max_auth_retries: 10 - name: Set user password include_role: - name: DeepOps.users + name: deepops_users vars: # User Configuration users: diff --git a/roles/deepops_chrony/.gitignore b/roles/deepops_chrony/.gitignore new file mode 100644 index 000000000..6fca925b7 --- /dev/null +++ b/roles/deepops_chrony/.gitignore @@ -0,0 +1,5 @@ +*.idea +*.DS_Store +*.retry +*/__pycache__ +*.pyc diff --git a/roles/deepops_chrony/.travis.yml b/roles/deepops_chrony/.travis.yml new file mode 100644 index 000000000..acd196317 --- /dev/null +++ b/roles/deepops_chrony/.travis.yml @@ -0,0 +1,26 @@ +--- +language: python +services: docker + +env: + global: + - ROLE_NAME: chrony + +install: + # Install test dependencies. + - pip install molecule docker testinfra + +before_script: + # Use actual Ansible Galaxy role name for the project directory. + - cd ../ + - mv ansible-role-$ROLE_NAME unxnn.$ROLE_NAME + - cd unxnn.$ROLE_NAME + +script: + # Run tests. + - ansible --version + - molecule --version + - molecule test + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/roles/deepops_chrony/LICENSE b/roles/deepops_chrony/LICENSE new file mode 100644 index 000000000..4b6926bb6 --- /dev/null +++ b/roles/deepops_chrony/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) unxnn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE \ No newline at end of file diff --git a/roles/deepops_chrony/README.md b/roles/deepops_chrony/README.md new file mode 100644 index 000000000..7ce00c640 --- /dev/null +++ b/roles/deepops_chrony/README.md @@ -0,0 +1,51 @@ + +# Ansible Role: Chrony + +This role enables users to install and configure chrony on their hosts. + +## Build Status + +[![Build Status](https://travis-ci.org/unxnn/ansible-role-chrony.svg?branch=master)](https://travis-ci.org/unxnn/ansible-role-chrony) + +## Requirements + +None + +## Role Variables + +| Variable | Required | Default | Comments | +|----------|----------|---------|----------| +| `chrony_pkg_state` | No | `present` | Set pkg `enabled`, `disabled`, `latest` | +| `chrony_service_state` | No | `started` | Set service state, started, enabled or disabled | +| `chrony_service_enabled` | No | `yes` | A list of NTP servers to use. | +| `chrony_config_server` | No | `["0.pool.ntp.org","1.pool.ntp.org","2.pool.ntp.org", "3.pool.ntp.org"]` | A list of NTP servers to use. | +| `chrony_config_logdir` | No | `/var/log/chrony` | A list of NTP servers to use. | +| `chrony_timezone` | No | `Etc/UTC` | Set the timezone for your server. | + +## Examples + +1) Install chrony and use the default settings. + +``` + +- hosts: all + roles: + - role: unxnn.chrony +``` + +2) Install chrony and use custom servers. + +``` + +- hosts: all + roles: + - role: unxnn.chrony + vars: + chrony_config_server: + - 0.pool.ntp.org + - 2.pool.ntp.org +``` + +## License + +MIT diff --git a/roles/deepops_chrony/defaults/main.yml b/roles/deepops_chrony/defaults/main.yml new file mode 100644 index 000000000..41776b522 --- /dev/null +++ b/roles/deepops_chrony/defaults/main.yml @@ -0,0 +1,11 @@ +--- +chrony_pkg_state: present +chrony_service_state: started +chrony_service_enabled: yes +chrony_config_server: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 2.pool.ntp.org + - 3.pool.ntp.org +chrony_config_logdir: /var/log/chrony +chrony_timezone: Etc/UTC diff --git a/roles/deepops_chrony/handlers/main.yml b/roles/deepops_chrony/handlers/main.yml new file mode 100644 index 000000000..a917f7809 --- /dev/null +++ b/roles/deepops_chrony/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: restart chrony + service: name={{ chrony_service_name }} state=restarted diff --git a/roles/deepops_chrony/meta/.galaxy_install_info b/roles/deepops_chrony/meta/.galaxy_install_info new file mode 100644 index 000000000..6e3a82dc1 --- /dev/null +++ b/roles/deepops_chrony/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Thu Apr 2 19:22:01 2020', version: c95f84431c8c3ed693e88b35c87edf792fcf152f} diff --git a/roles/deepops_chrony/meta/main.yml b/roles/deepops_chrony/meta/main.yml new file mode 100644 index 000000000..b2c4cbf63 --- /dev/null +++ b/roles/deepops_chrony/meta/main.yml @@ -0,0 +1,29 @@ +--- +galaxy_info: + author: unxnn + role_name: chrony + description: This role enables users to install and configure chrony on their hosts. + license: MIT + min_ansible_version: 2.4 + platforms: + - name: EL + versions: + - 7 + - 8 + - name: Debian + versions: + - jessie + - stretch + - buster + - name: Ubuntu + versions: + - bionic + - cosmic + - disco + - eoan + galaxy_tags: + - system + - chrony + - ntp + - time +dependencies: [] diff --git a/roles/deepops_chrony/tasks/debian.yml b/roles/deepops_chrony/tasks/debian.yml new file mode 100644 index 000000000..3d991c36c --- /dev/null +++ b/roles/deepops_chrony/tasks/debian.yml @@ -0,0 +1,3 @@ +--- +- name: Install the require packages in Debian derivatives + apt: name=chrony state={{ chrony_pkg_state }} diff --git a/roles/deepops_chrony/tasks/main.yml b/roles/deepops_chrony/tasks/main.yml new file mode 100644 index 000000000..f2d805684 --- /dev/null +++ b/roles/deepops_chrony/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- name: Add the OS specific variables + include_vars: "{{ ansible_os_family|lower }}.yml" + +- name: Installation + include_tasks: "{{ ansible_os_family|lower }}.yml" + +- name: Copy the chrony.conf template file + template: + src: chrony.conf.j2 + dest: "{{ chrony_config_location }}" + mode: "0644" + owner: root + group: root + notify: + - restart chrony + +- name: Start and enable chrony service + service: + name: "{{ chrony_service_name }}" + state: "{{ chrony_service_state }}" + enabled: "{{ chrony_service_enabled }}" + +- name: Ensure tzdata package is installed + package: + name: "{{ chrony_tzdata_package }}" + state: present + +- name: Set timezone + timezone: name="{{ chrony_timezone }}" diff --git a/roles/deepops_chrony/tasks/redhat.yml b/roles/deepops_chrony/tasks/redhat.yml new file mode 100644 index 000000000..73aa3d4d3 --- /dev/null +++ b/roles/deepops_chrony/tasks/redhat.yml @@ -0,0 +1,11 @@ +--- +- name: Install the required packages in Redhat derivatives + yum: name=chrony state={{ chrony_pkg_state }} + +- name: Check if ntpd service exists + stat: path="/usr/lib/systemd/system/ntpd.service" + register: ntpd_service_status + +- name: Stop and mask ntpd service + systemd: name=ntpd state=stopped masked=yes + when: ntpd_service_status.stat.exists diff --git a/roles/deepops_chrony/templates/chrony.conf.j2 b/roles/deepops_chrony/templates/chrony.conf.j2 new file mode 100644 index 000000000..072648a57 --- /dev/null +++ b/roles/deepops_chrony/templates/chrony.conf.j2 @@ -0,0 +1,46 @@ +# {{ ansible_managed }} + +# List of NTP servers to use. +{% for server in chrony_config_server %} +server {{ server }} iburst +{% endfor %} + +# Ignore stratum in source selection. +stratumweight 0 + +# Record the rate at which the system clock gains/losses time. +driftfile {{ chrony_config_driftfile }} + +# Enable kernel RTC synchronization. +rtcsync + +# In first three updates step the system clock instead of slew +# if the adjustment is larger than 10 seconds. +makestep 10 3 + +# Allow NTP client access from local network. +#allow 192.168/16 + +# Listen for commands only on localhost. +bindcmdaddress 127.0.0.1 +bindcmdaddress ::1 + +# Serve time even if not synchronized to any NTP server. +#local stratum 10 + +keyfile {{ chrony_config_keyfile }} + +# Specify the key used as password for chronyc. +commandkey 1 + +# Generate command key if missing. +generatecommandkey + +# Disable logging of client accesses. +noclientlog + +# Send a message to syslog if a clock adjustment is larger than 0.5 seconds. +logchange 0.5 + +logdir {{ chrony_config_logdir }} +#log measurements statistics tracking \ No newline at end of file diff --git a/roles/deepops_chrony/vars/debian.yml b/roles/deepops_chrony/vars/debian.yml new file mode 100644 index 000000000..22d2882be --- /dev/null +++ b/roles/deepops_chrony/vars/debian.yml @@ -0,0 +1,6 @@ +--- +chrony_service_name: chrony +chrony_config_location: /etc/chrony/chrony.conf +chrony_config_driftfile: /var/lib/chrony/chrony.drift +chrony_config_keyfile: /etc/chrony/chrony.keys +chrony_tzdata_package: tzdata diff --git a/roles/deepops_chrony/vars/redhat.yml b/roles/deepops_chrony/vars/redhat.yml new file mode 100644 index 000000000..f181975b6 --- /dev/null +++ b/roles/deepops_chrony/vars/redhat.yml @@ -0,0 +1,6 @@ +--- +chrony_service_name: chronyd +chrony_config_location: /etc/chrony.conf +chrony_config_driftfile: /var/lib/chrony/drift +chrony_config_keyfile: /etc/chrony.keys +chrony_tzdata_package: tzdata diff --git a/roles/deepops_hosts/.gitignore b/roles/deepops_hosts/.gitignore new file mode 100644 index 000000000..a4a367f79 --- /dev/null +++ b/roles/deepops_hosts/.gitignore @@ -0,0 +1,16 @@ +# .gitignore + +# Hidden Vagrant-directory +.vagrant + +# Backup files (e.g. Vim, Gedit, etc.) +*~ + +# Vagrant base boxes (you never know when someone puts one in the repository) +*.box + +# Compiled Python +*.pyc + +# Test directory (sits in a separate branch) +tests/ diff --git a/roles/deepops_hosts/LICENSE.md b/roles/deepops_hosts/LICENSE.md new file mode 100644 index 000000000..8411892eb --- /dev/null +++ b/roles/deepops_hosts/LICENSE.md @@ -0,0 +1,13 @@ +# BSD License + +Copyright (c) 2014, Bert Van Vreckem, (bert.vanvreckem@gmail.com) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/roles/deepops_hosts/README.md b/roles/deepops_hosts/README.md new file mode 100644 index 000000000..7ad6afd01 --- /dev/null +++ b/roles/deepops_hosts/README.md @@ -0,0 +1,108 @@ + +# Ansible role `hosts` + +An Ansible role for managing the hosts file (`/etc/hosts`). Specifically, the responsibilities of this role are to: + +- Add the default localhost entry; +- Add entries for basic IPv6 addresses, e.g. ip6-localnet (optional); +- Add entries for Ansible managed hosts (optional); +- Add entries specified in Yaml (optional, see below); +- Add entries specified in text files (optional). + +## Selecting the correct network interface + +When a host has multiple network interfaces, an entry in the hosts file will be generated for each interface with the interface name as suffix. +If `hosts_network_interface` is selected, that IP address will also have a fully qualified domain name included. If `hosts_default_domain` is +set, the FQDN will be generated using it. Otherwise, `ansible_fqdn` will be used. However, the value of `ansible_fqdn` can depend on the +existing /etc/hosts file which makes this depend on /etc/hosts already being correct in order to make a correct /etc/hosts, a catch-22. + +``` +10.10.0.1 node01.example.com node01-eth0 node01 +10.30.0.1 node01-eth1 +192.168.0.50 node01-ib0 +``` + +If `hosts_network_interface` is not set, `ansible_hostname` and `ansible_fqdn` will be named according to the interface providing the default route. +In cases where `hosts_network_interface` is not set and there is no default route, only the interface-based names will be generated. + +`hosts_network_interface` can alternatively be set as an array with unique values per host. + +The network interfaces lo, docker, and nodelocaldns are automatically excluded. The variable `hosts_exclude_interfaces` can +be used to change the excluded interfaces. + +`hosts_interface_domain` allows including a different FQDN for specific interfaces, either cluster wide or per-host + +## Requirements + +No specific requirements + +## Role Variables + +None of the variables below are required. When not set, the default setting is applied. + +| Variable | Default | Comments | +| :--- | :--- | :--- | +| `hosts_file_src` | | When defined, this path will be copied to the hosts file and all other settings are ignored | +| `hosts_add_basic_ipv6` | false | If true, basic IPv6 entries are added (e.g. localhost6, ip6-localnet, etc.) | +| `hosts_add_ansible_managed_hosts` | false | If true, an entry for hosts managed by Ansible is added. (†) | +| `hosts_add_ansible_managed_hosts_groups` | ['all'] | Control which host entries are created when using `hosts_add_ansible_managed_hosts` | +| `hosts_entries` | [] | A list of dicts with custom entries to be added to the hosts file. See below for an example. | +| `hosts_file_snippets` | [] | A list of files containing host file snippets to be added to the hosts file verbatim. | +| `hosts_ip_protocol` | `ipv4` | When adding Ansible managed hosts, this specifies the IP protocol (`ipv4` or `ipv6`) | +| `hosts_network_interface` | '' | When adding Ansible managed hosts, this specifies the network interface for which the IP address is used for the default name | +| `hosts_file_backup` | no | If yes, backup of host file is created with timestamp | +| | | | + +(†) When setting `hosts_add_ansible_managed_hosts`, an entry for the current host will also be added. Consequently, `hosts_add_default_ipv4` doesn't need to be set. + +Individual hosts file entries can be added with `hosts_entries`, a list of dicts with keys `name`, `ip` and (optional) `aliases`. Example: + +```Yaml +hosts_entries: + - name: slashdot + ip: 216.34.181.45 + - name: gns1 + ip: 8.8.8.8 + aliases: + - googledns1 + - googlens1 + - name: gns2 + ip: 8.8.4.4 + aliases: + - googledns2 + - googlens2 +``` + +## Dependencies + +No dependencies. + +## Example Playbook + +See the [test playbook](https://github.com/bertvv/ansible-role-hosts/blob/tests/test.yml) + +Tests for this role are provided in the form of a Vagrant environment that is kept in a separate branch, `tests`. I use [git-worktree(1)](https://git-scm.com/docs/git-worktree) to include the test code into the working directory. Instructions for running the tests: + +1. Fetch the tests branch: `git fetch origin tests` +2. Create a Git worktree for the test code: `git worktree add tests tests` (remark: this requires at least Git v2.5.0). This will create a directory `tests/`. +3. `cd tests/` +4. `vagrant up` will then create a VM and apply a test playbook (`test.yml`). + +You may want to change the base box into one that you like. The current one, [bertvv/centos72](https://atlas.hashicorp.com/bertvv/boxes/centos72) was generated using a Packer template from the [Boxcutter project](https://github.com/boxcutter/centos) with a few modifications. + +## Contributing + +Issues, feature requests, ideas are appreciated and can be posted in the Issues section. Pull requests are also very welcome. Preferably, create a topic branch and when submitting, squash your commits into one (with a descriptive message). + +## License + +BSD, see + +## Contributors + +This role was inspired by the work of [soplakanets](https://github.com/soplakanets/ansible-role-hosts/) (including the [contribution by astrorafael](https://github.com/soplakanets/ansible-role-hosts/pull/1/files)) and [mivok](https://github.com/mivok/ansible-hosts/). + +- [Bert Van Vreckem](https://github.com/bertvv/) (maintainer) +- [Dheeraj Dwivedi](https://github.com/dheerajdwivedi) +- [Ernestas Poskus](https://github.com/ernestas-poskus) +- [Mohammed Naser](https://github.com/mnaser) diff --git a/roles/deepops_hosts/defaults/main.yml b/roles/deepops_hosts/defaults/main.yml new file mode 100644 index 000000000..ae16460f3 --- /dev/null +++ b/roles/deepops_hosts/defaults/main.yml @@ -0,0 +1,51 @@ +# roles/hosts/defaults/main.yml +--- + +hosts_playbook_version: "1.0.1" + +# If set to true, basic IPv6 entries (localhost6, ip6-localnet, etc) are added. +hosts_add_basic_ipv6: false + +# If set to true, an entry for every host managed by Ansible is added. Remark that this makes `hosts_add_default_ipv4` unnecessary, as it will be added as wel by this setting. +hosts_add_ansible_managed_hosts: false + +# Select specific groups of Ansible managed hosts to be added in the hosts file. +hosts_add_ansible_managed_hosts_groups: ['all'] + +# Custom hosts entries to be added +hosts_entries: [] + +# Custom host file snippets to be added +hosts_file_snippets: [] + +# IP protocol to use +hosts_ip_protocol: 'ipv4' + + +# Network interface to use. Can be set either globally or per host +# If empty, the default adapter will be used for all systems. If set per host, hosts not specified will use the default adapter +# NOTE: the global method only works if all nodes in your inventory have an adapter with the same name. +# hosts_network_interface: { head: eth0 } +hosts_network_interface: '' + +# If hosts_default_domain is set, the default interface entry in the hosts file +# will have the default domain used for it. Otherwise, ansible_fqdn will be used +# for the default entry +hosts_default_domain: '' + +# This variable can be set either per interface, or per interface per host +# hosts_interface_domain: { head: { eth1: yourdomain.com } } +# hosts_interface_domain: { eth1: yourdomain.com } +hosts_interface_domain: '' + + +#Which interfaces should be excluded from the hosts file generation +hosts_exclude_interfaces: + - lo + - docker + - nodelocaldns + +# Backup of previous host +host_file_backup: no + +hosts_custom_hosts_marker: "Custom Hosts Managed Block" diff --git a/roles/deepops_hosts/handlers/main.yml b/roles/deepops_hosts/handlers/main.yml new file mode 100644 index 000000000..56efe361f --- /dev/null +++ b/roles/deepops_hosts/handlers/main.yml @@ -0,0 +1,7 @@ +# roles/hosts/handlers/main.yml +--- + +- name: restart hosts + service: + name: hostsd + state: restarted diff --git a/roles/deepops_hosts/meta/main.yml b/roles/deepops_hosts/meta/main.yml new file mode 100644 index 000000000..f09d93c97 --- /dev/null +++ b/roles/deepops_hosts/meta/main.yml @@ -0,0 +1,37 @@ +--- +galaxy_info: + author: Bert Van Vreckem + description: An Ansible role for managing the hosts file (`/etc/hosts`). + license: BSD + min_ansible_version: 1.7 + platforms: + - name: EL + versions: + - all + - name: GenericUNIX + versions: + - all + - any + - name: Fedora + versions: + - all + - name: opensuse + versions: + - all + - name: Ubuntu + versions: + - all + - name: SLES + versions: + - all + - name: GenericLinux + versions: + - all + - any + - name: Debian + versions: + - all + galaxy_tags: + - system + - networking +dependencies: [] diff --git a/roles/deepops_hosts/tasks/main.yml b/roles/deepops_hosts/tasks/main.yml new file mode 100644 index 000000000..995499053 --- /dev/null +++ b/roles/deepops_hosts/tasks/main.yml @@ -0,0 +1,48 @@ +# roles/hosts/tasks/main.yml +--- +- name: Gather facts again + setup: + +- name: register hosts list if ansible group limit is specified + set_fact: + hosts_add_ansible_managed_hosts_groups: + - "{{ ansible_limit }}" + when: ansible_limit is defined + +- name: Install user-defined custom hosts file + copy: + src: "{{ hosts_file_src }}" + dest: "{{ hosts_file }}" + owner: root + group: root + mode: 0644 + backup: "{{ host_file_backup }}" + tags: hosts + when: hosts_file_src is defined + +- name: Install custom hosts file + template: + src: etc_hosts.j2 + dest: "{{ hosts_file }}" + owner: root + group: root + mode: 0644 + backup: "{{ host_file_backup }}" + tags: hosts + when: hosts_file_src is not defined + +- name: render custom hosts template + set_fact: + custom_hosts: "{{ lookup('template', 'custom_hosts.j2') }}" + tags: hosts + when: (hosts_file_src is not defined) and (hosts_entries|length>0) + +- name: Add Managed Block to {{ hosts_file }} + blockinfile: + path: "{{ hosts_file }}" + owner: root + group: root + content: "{{ custom_hosts }}" + marker: "# {{ hosts_custom_hosts_marker }} {mark}" + tags: hosts + when: (hosts_file_src is not defined) and (hosts_entries|length>0) diff --git a/roles/deepops_hosts/templates/custom_hosts.j2 b/roles/deepops_hosts/templates/custom_hosts.j2 new file mode 100644 index 000000000..3ac4e77f8 --- /dev/null +++ b/roles/deepops_hosts/templates/custom_hosts.j2 @@ -0,0 +1,3 @@ +{% for host in hosts_entries %} +{{ host.ip }} {{ host.name.split(".")[0] }} {{ host.name }} +{% endfor %} diff --git a/roles/deepops_hosts/templates/etc_hosts.j2 b/roles/deepops_hosts/templates/etc_hosts.j2 new file mode 100644 index 000000000..7a06d5611 --- /dev/null +++ b/roles/deepops_hosts/templates/etc_hosts.j2 @@ -0,0 +1,84 @@ +# Hosts file -- Don't edit manually! +# +# {{ ansible_managed }} +{% if hosts_network_interface -%} +{% set def_interface = hosts_network_interface -%} +{% else -%} +{% set def_interface = ansible_default_ipv4['interface']|default(None) -%} +{% endif -%} + +# Localhost +{% if def_interface and hosts_add_ansible_managed_hosts -%} +127.0.0.1 localhost.localdomain localhost +{% else -%} +127.0.0.1 localhost.localdomain localhost {{ ansible_fqdn }} {{ansible_hostname }} +{% endif -%} + +{% if hosts_add_basic_ipv6 %} +# Basic IPv6 entries +::1 localhost6.localdomain6 localhost6 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +{% endif %} + +{% if hosts_add_ansible_managed_hosts %} +# +# Hosts managed by Ansible +# +{% for group in hosts_add_ansible_managed_hosts_groups %} +{% for host in groups[group]|sort %} +# {{ host }} +{% set my_interfaces = hostvars[host]['ansible_interfaces']|select('match', '(?!^(' + hosts_exclude_interfaces|join('|') + ')).*')|default(None) -%} + {% if hosts_network_interface[host] is defined -%} + {% set di = hosts_network_interface[host] -%} + {% elif ((hosts_network_interface is not mapping) and hosts_network_interface) -%} + {% set di = hosts_network_interface -%} + {% else -%} + {% set di = hostvars[host]['ansible_default_' + hosts_ip_protocol]['interface']|default(None) -%} + {% endif -%} + {% for interface in my_interfaces|sort -%} + {% set addr = hostvars[host]['ansible_' + interface][hosts_ip_protocol]['address']|default(None) -%} + {% if addr -%} +{% if interface == di -%} +{% if hosts_interface_domain[host][interface] is defined -%} +{{ addr }} {{ host }}.{{ hosts_interface_domain[host][interface] }} {{ host }}-{{ interface }} {{ host }} +{% elif hosts_interface_domain[interface] is defined -%} +{{ addr }} {{ host }}.{{ hosts_interface_domain[interface] }} {{ host }}-{{ interface }} {{ host }} +{% elif hosts_default_domain -%} +{{ addr }} {{ host }}.{{ hosts_default_domain }} {{ host }}-{{ interface }} {{ host }} +{% else -%} +{{ addr }} {{ hostvars[host]['ansible_fqdn'] }} {{ host }}-{{ interface }} {{ host }} +{% endif -%} +{% else -%} +{% if hosts_interface_domain[host][interface] is defined -%} +{{ addr }} {{ host }}.{{ hosts_interface_domain[host][interface] }} {{ host }}-{{ interface }} +{% elif hosts_interface_domain[interface] is defined -%} +{{ addr }} {{ host }}.{{ hosts_interface_domain[interface] }} {{ host }}-{{ interface }} +{% else -%} +{{ addr }} {{ host }}-{{ interface }} +{% endif -%} +{% endif -%} + {% endif -%} + {% endfor -%} +{% endfor -%} +{% endfor -%} +{% endif -%} + +{% if hosts_entries|length != 0 %} +# Custom entries +{% for host in hosts_entries %} +{{ host.ip }} {{ host.name }} {{ host.aliases | default('') | join(' ') }} +{% endfor %} +{% endif %} + +{% if hosts_file_snippets|length != 0 %} +# +# Hosts file snippets +# +{% for snippet in hosts_file_snippets %} +{% include snippet %} + +{% endfor %} +{% endif %} diff --git a/roles/deepops_hosts/vars/main.yml b/roles/deepops_hosts/vars/main.yml new file mode 100644 index 000000000..58343ae29 --- /dev/null +++ b/roles/deepops_hosts/vars/main.yml @@ -0,0 +1,4 @@ +# roles/hosts/vars/main.yml +--- + +hosts_file: /etc/hosts diff --git a/roles/deepops_users/.gitignore b/roles/deepops_users/.gitignore new file mode 100644 index 000000000..5ecf0da54 --- /dev/null +++ b/roles/deepops_users/.gitignore @@ -0,0 +1,2 @@ +*.idea +*.DS_Store \ No newline at end of file diff --git a/roles/deepops_users/.travis.yml b/roles/deepops_users/.travis.yml new file mode 100644 index 000000000..fb6a4e480 --- /dev/null +++ b/roles/deepops_users/.travis.yml @@ -0,0 +1,47 @@ +--- +sudo: required +language: python +python: "2.7" + +install: + - pip install ansible==2.7.8 + + # Add ansible.cfg to pick up roles path. + - "{ echo '[defaults]'; echo 'roles_path = ../'; } >> ansible.cfg" + +script: + # Syntax Check + - ansible-playbook -i localhost, tests/test.yml --syntax-check + + # Run tests.yml + - ansible-playbook -i localhost, --connection=local --sudo tests/test.yml + + # Run the role/playbook again, checking to make sure it's idempotent. + - > + ansible-playbook -i localhost, --connection=local --sudo tests/test.yml + | grep -q 'changed=0.*failed=0' + && (echo 'Idempotence test: pass' && exit 0) + || (echo 'Idempotence test: fail' && exit 1) + + # Lets check on the state of the users. I would invoke severspec myself however + # its a big thing to bring in on a small pull. + - id ansibletestuser | grep --silent "uid=2222(ansibletestuser) gid=2222(ansibletestuser) groups=2222(ansibletestuser),2(bin),100(users)" + - id ansibletestuser2 | grep --silent "uid=2223(ansibletestuser2) gid=2223(ansibletestuser2) groups=2223(ansibletestuser2),2(bin),100(users)" + - id ansibletestuser3 | grep --silent "uid=2224(ansibletestuser3) gid=4001(ansibletestgroup1) groups=4001(ansibletestgroup1),2(bin),100(users)" + - id ansibletestuser4 | grep --silent "uid=2225(ansibletestuser4) gid=100(users) groups=100(users),2(bin)" + - id ansibletestuser5 | grep --silent "uid=2226(ansibletestuser5) gid=4000(ansibletestgroup) groups=4000(ansibletestgroup),2(bin),100(users)" + - grep --silent "^ansibletestgroup:" /etc/group + - grep --silent "^ansibletestgroup1:" /etc/group + - ls -lgd /home/ansibletestuser | awk '{exit $3!="ansibletestuser"}' + - ls -lgd /home/otherdirectory | awk '{exit $3!="ansibletestuser2"}' + - ls -lgd /home/ansibletestuser3 | awk '{exit $3!="ansibletestgroup1"}' + - ls -lgd /home/otherdirectory1 | awk '{exit $3!="users"}' + - ls -lgd /home/ansibletestuser5 | awk '{exit $3!="ansibletestgroup"}' + - ls -lg /home/ansibletestuser/.profile | awk '{exit $3!="ansibletestuser"}' + - ls -lg /home/otherdirectory/.profile | awk '{exit $3!="ansibletestuser2"}' + - ls -lg /home/ansibletestuser3/.profile | awk '{exit $3!="ansibletestgroup1"}' + - ls -lg /home/otherdirectory1/.profile | awk '{exit $3!="users"}' + - ls -lgd /home/ansibletestuser5/.profile | awk '{exit $3!="ansibletestgroup"}' + +notifications: + webhooks: https://galaxy.ansible.com/api/v1/notifications/ diff --git a/roles/deepops_users/LICENSE b/roles/deepops_users/LICENSE new file mode 100644 index 000000000..4b6926bb6 --- /dev/null +++ b/roles/deepops_users/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) unxnn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE \ No newline at end of file diff --git a/roles/deepops_users/README.md b/roles/deepops_users/README.md new file mode 100644 index 000000000..339ac2325 --- /dev/null +++ b/roles/deepops_users/README.md @@ -0,0 +1,109 @@ + +# Ansible Role: users + +[![Build Status](https://travis-ci.org/unxnn/ansible-role-users.svg?branch=master)](https://travis-ci.org/unxnn/ansible-role-users) + +Role to manage users on a system. + +## Role configuration + +* `users_create_per_user_group` (default: true) - when creating users, also + create a group with the same username and make that the user's primary + group. +* `users_group` (default: users) - if users_create_per_user_group is _not_ set, + then this is the primary group for all created users. +* `users_default_shell` (default: /bin/bash) - the default shell if none is + specified for the user. +* `users_create_homedirs` (default: true) - create home directories for new + users. Set this to false if you manage home directories separately. + +## Creating users + +Add a users variable containing the list of users to add. A good place to put +this is in `group_vars/all` or `group_vars/groupname` if you only want the +users to be on certain machines. + +The following attributes are required for each user: + +* `username` - The user's username. +* `name` - The full name of the user (gecos field). +* `home` - The home directory of the user to create (optional, defaults to /home/username). +* `uid` - The numeric user id for the user (optional). This is required for uid consistency + across systems. +* `gid` - The numeric group id for the group (optional). Otherwise, the + `uid` will be used. +* `password` - If a hash is provided then that will be used, but otherwise the + account will be locked. +* `update_password` - This can be either 'always' or 'on_create' + - `'always'` will update passwords if they differ. (default) + - `'on_create'` will only set the password for newly created users. +* `group` - Optional primary group override. +* `groups` - A list of supplementary groups for the user. +* `append` - If yes, will only add groups, not set them to just the list in groups (optional). +* `profile` - A string block for setting custom shell profiles. +* `ssh_key` - This should be a list of SSH keys for the user (optional). Each SSH key + should be included directly and should have no newlines. +* `generate_ssh_key` - Whether to generate a SSH key for the user (optional, defaults to no). + +In addition, the following items are optional for each user: + +* `shell` - The user's shell. This defaults to /bin/bash. The default is + configurable using the users_default_shell variable if you want to give all + users the same shell, but it is different than /bin/bash. + +Example: + + --- + users: + - username: foo + name: Foo Bar + groups: ['admin','systemd-journal'] + uid: 1005 + home: /local/home/foo + profile: | + alias ll='ls -ahl' + ssh_key: + - "ssh-rsa AAAAA.... foo@server" + - "ssh-rsa AAAAB.... foo2@server" + groups_to_create: + - name: developers + gid: 20000 + +Generating a password hash: + + # On Debian/Ubuntu (via the package "whois") + mkpasswd --method=SHA-512 --rounds=4096 + + # OpenSSL (note: this will only make md5crypt. While better than plantext it should not be considered fully secure) + openssl passwd -1 + + # Python (change password and salt values) + python -c "import crypt, getpass, pwd; print crypt.crypt('password', '\$6\$SALT\$')" + + # Perl (change password and salt values) + perl -e 'print crypt("password","\$6\$SALT\$") . "\n"' + +## Deleting users + +The `users_deleted` variable contains a list of users who should no longer be +in the system, and these will be removed on the next ansible run. The format +is the same as for users to add, but the only required field is `username`. +However, it is recommended that you also keep the `uid` field for reference so +that numeric user ids are not accidentally reused. + +You can optionally choose to remove the user's home directory and mail spool with +the `remove` parameter, and force removal of files with the `force` parameter. + + users_deleted: + - username: bar + uid: 1003 + remove: yes + force: yes + +# Dependenices + +None. + +# License + +MIT diff --git a/roles/deepops_users/defaults/main.yml b/roles/deepops_users/defaults/main.yml new file mode 100644 index 000000000..76d00c9a8 --- /dev/null +++ b/roles/deepops_users/defaults/main.yml @@ -0,0 +1,22 @@ +--- +# Create a group for every user and make that their primary group +users_create_per_user_group: true +# If we're not creating a per-user group, then this is the group all users +# belong to +users_group: users +# The default shell for a user if none is specified +users_default_shell: /bin/bash +# Create home dirs for new users? Set this to false if you manage home +# directories in some other way. +users_create_homedirs: true + +# Lists of users to create and delete +users: [] +users_deleted: [] + +# List of groups to create +# Example: +# groups_to_create: +# - name: developers +# gid: 20000 +groups_to_create: [] diff --git a/roles/deepops_users/meta/.galaxy_install_info b/roles/deepops_users/meta/.galaxy_install_info new file mode 100644 index 000000000..94099eede --- /dev/null +++ b/roles/deepops_users/meta/.galaxy_install_info @@ -0,0 +1 @@ +{install_date: 'Thu Apr 2 19:21:46 2020', version: 78fd08ca86678d00da376eaac909d22e1022a020} diff --git a/roles/deepops_users/meta/main.yml b/roles/deepops_users/meta/main.yml new file mode 100644 index 000000000..fe844fceb --- /dev/null +++ b/roles/deepops_users/meta/main.yml @@ -0,0 +1,41 @@ +--- +galaxy_info: + role_name: users + author: unxnn + description: User management role + company: none + license: MIT + min_ansible_version: 1.3 + platforms: + - name: EL + versions: + - all + - name: GenericUNIX + versions: + - all + - any + - name: Fedora + versions: + - all + - name: opensuse + versions: + - all + - name: Ubuntu + versions: + - all + - name: SLES + versions: + - all + - name: GenericLinux + versions: + - all + - any + - name: Debian + versions: + - all + galaxy_tags: + - system + - users + - management + +dependencies: [] diff --git a/roles/deepops_users/tasks/main.yml b/roles/deepops_users/tasks/main.yml new file mode 100644 index 000000000..8c67046c3 --- /dev/null +++ b/roles/deepops_users/tasks/main.yml @@ -0,0 +1,76 @@ +--- +- name: Allow admin group sudo without password + lineinfile: + path: /etc/sudoers + state: present + regexp: '^%admin ALL=' + line: '%admin ALL=(ALL) NOPASSWD: ALL' + validate: '/usr/sbin/visudo -cf %s' + tags: ["users", "groups", "configuration"] + +- name: Creating groups + group: name="{{ item.name }}" gid="{{ item.gid | default(omit) }}" + with_items: "{{ groups_to_create }}" + tags: ["users", "groups", "configuration"] + +- name: Per-user group creation + group: name="{{ item.username }}" + gid="{{ item.gid | default(item.uid) | default(omit) }}" + with_items: "{{ users }}" + when: "'group' not in item and users_create_per_user_group" + tags: ["users", "configuration"] + +- name: User creation + user: + name: "{{ item.username }}" + group: "{{ item.group | default(item.username if users_create_per_user_group else users_group) }}" + # empty string removes user from all secondary groups + groups: "{{ item.groups | join(',') if 'groups' in item else '' }}" + append: "{{ item.append | default(omit) }}" + shell: "{{ item.shell if item.shell is defined else users_default_shell }}" + password: "{{ item.password if item.password is defined else '!' }}" + comment: "{{ item.name if item.name is defined else '' }}" + uid: "{{ item.uid | default(omit) }}" + home: "{{ item.home | default('/home/' + item.username) }}" + createhome: "{{ 'yes' if users_create_homedirs else 'no' }}" + generate_ssh_key: "{{ item.generate_ssh_key | default(omit) }}" + update_password: "{{ item.update_password | default(omit) }}" + with_items: "{{ users }}" + tags: ["users", "configuration"] + +- name: SSH keys + authorized_key: + user: "{{ item.0.username }}" + key: "{{ item.1 }}" + path: "{{ item.0.home | default('/home/' + item.0.username) }}/.ssh/authorized_keys" + with_subelements: + - "{{ users }}" + - ssh_key + - skip_missing: yes + tags: ["users", "configuration"] + +- name: Setup user profiles + blockinfile: + block: "{{ item.profile }}" + dest: "{{ item.home | default('/home/' + item.username) }}/.profile" + owner: "{{ item.username }}" + group: "{{ item.group | default(item.username if users_create_per_user_group else users_group) }}" + mode: 0644 + create: true + when: users_create_homedirs and item.profile is defined + with_items: "{{ users }}" + +- name: Deleted user removal + user: + name: "{{ item.username }}" + state: absent + remove: "{{ item.remove | default(omit) }}" + force: "{{ item.force | default(omit) }}" + with_items: "{{ users_deleted }}" + tags: ["users", "configuration"] + +- name: Deleted per-user group removal + group: name="{{ item.username }}" state=absent + with_items: "{{ users_deleted }}" + when: users_create_per_user_group + tags: ["users", "configuration"] diff --git a/roles/requirements.yml b/roles/requirements.yml index 77f97ef7b..3b14c0005 100644 --- a/roles/requirements.yml +++ b/roles/requirements.yml @@ -15,14 +15,6 @@ collections: roles: -- src: https://github.com/DeepOps/ansible-role-users - name: DeepOps.users - version: 'e67df1db28bf099aa06d0ab250478963532b4d29' - -- src: https://github.com/DeepOps/ansible-role-hosts.git - version: 'c21a676088c07db3db3ac9332cf584eeb068e3fd' - name: DeepOps.hosts - - src: geerlingguy.ntp version: "4.0.0" @@ -60,10 +52,6 @@ roles: name: ansible-maas version: '178a999c9bfc979ef32c42f4f59c034664df10d0' -- src: https://github.com/DeepOps/ansible-role-chrony - name: DeepOps.chrony - version: 'c9022153036dfdde4e2b313aecde4a46cd6f6687' - - src: https://github.com/OSC/ood-ansible.git version: 'v3.0.3'