From 8c3257ecdf4ad09085948797bb8c2d8ea4bafd6f Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 15 Oct 2025 11:47:34 -0500 Subject: [PATCH] Refine bootstrap and consolidate base role management --- ansible/bootstrap.yml | 63 +++++++------ ansible/group_vars/all/all.yml | 61 ++++++------ ansible/roles/almalinux/tasks/main.yml | 31 ------ ansible/roles/alpine/tasks/main.yml | 22 +---- ansible/roles/base/defaults/main.yml | 106 +++++++++++++++++++++ ansible/roles/base/tasks/main.yml | 8 +- ansible/roles/base/tasks/packages.yml | 19 ++++ ansible/roles/base/tasks/users.yml | 125 ++++++++++++++++--------- ansible/roles/ubuntu/tasks/main.yml | 28 ------ 9 files changed, 279 insertions(+), 184 deletions(-) create mode 100644 ansible/roles/base/defaults/main.yml create mode 100644 ansible/roles/base/tasks/packages.yml diff --git a/ansible/bootstrap.yml b/ansible/bootstrap.yml index 97f3a5d..4346645 100644 --- a/ansible/bootstrap.yml +++ b/ansible/bootstrap.yml @@ -7,39 +7,48 @@ vars: ansible_user: graham tasks: - - name: Install sudo package first (if needed) - raw: "which sudo || (apt-get update && apt-get install -y sudo) || (yum install -y sudo)" + - name: Ensure sudo is present + raw: | + if command -v sudo >/dev/null 2>&1; then + exit 0 + fi + if command -v apt-get >/dev/null 2>&1; then + apt-get update && apt-get install -y sudo + elif command -v dnf >/dev/null 2>&1; then + dnf install -y sudo + elif command -v yum >/dev/null 2>&1; then + yum install -y sudo + elif command -v apk >/dev/null 2>&1; then + apk add --no-cache sudo + else + echo "Unsupported package manager for sudo installation" >&2 + exit 1 + fi become: yes become_method: su become_user: root - - - name: Configure sudo for graham user - raw: "echo 'graham ALL=NOPASSWD: ALL' | tee /etc/sudoers.d/graham && chmod 0440 /etc/sudoers.d/graham" + + - name: Ensure Python 3 is present + raw: | + if command -v python3 >/dev/null 2>&1; then + exit 0 + fi + if command -v apt-get >/dev/null 2>&1; then + apt-get update && apt-get install -y python3 python3-apt + elif command -v dnf >/dev/null 2>&1; then + dnf install -y python3 + elif command -v yum >/dev/null 2>&1; then + yum install -y python3 + elif command -v apk >/dev/null 2>&1; then + apk add --no-cache python3 py3-pip + else + echo "Unsupported package manager for python3 installation" >&2 + exit 1 + fi + changed_when: false become: yes become_method: su become_user: root - - - name: Test sudo access - raw: "sudo whoami" - register: sudo_test - changed_when: false - - - name: Show sudo test result - debug: - msg: "Sudo test result: {{ sudo_test.stdout }}" - - - name: Install Python if needed - raw: "which python3 || sudo apt-get update && sudo apt-get install -y python3 python3-apt" - changed_when: false - - - name: Find Python interpreter - raw: "which python3 || which python" - register: python_path - changed_when: false - - - name: Show Python path - debug: - msg: "Python path: {{ python_path.stdout }}" - name: Bootstrap new host - Base configuration hosts: all diff --git a/ansible/group_vars/all/all.yml b/ansible/group_vars/all/all.yml index 3edd2e3..d762a59 100644 --- a/ansible/group_vars/all/all.yml +++ b/ansible/group_vars/all/all.yml @@ -4,37 +4,38 @@ # Timezone configuration timezone: America/Chicago -# GitHub usernames for SSH key retrieval -github_users: - - username: graham - github: gmcintire - - username: andy - github: nsnw +# Managed groups and users +managed_groups: + - name: ansible + gid: 10001 -# Base packages to install on all systems +managed_users: + - name: ansible + uid: 10001 + shell: /bin/bash + home: /home/ansible + primary_group: ansible + authorized_keys: + - type: file + value: ansible.pub + exclusive: true + - name: graham + shell: /bin/bash + home: /home/graham + admin: true + authorized_keys: + - type: github + value: gmcintire + - name: andy + shell: /bin/bash + home: /home/andy + admin: true + authorized_keys: + - type: github + value: nsnw + +# Base settings base: - packages: - - git - - fzf - - zsh - - jq - - sysstat - - smartmontools - - lm-sensors - - debconf-utils - - ntp - - ntpdate - - samba - - samba-common - - sssd - - krb5-user - - zsh - - wget - - vim - - htop - - mosh - - tmux - ntp: server: 0.us.pool.ntp.org @@ -50,4 +51,4 @@ network: nameservers: - 9.9.9.9 - 149.112.112.112 - domain: local \ No newline at end of file + domain: local diff --git a/ansible/roles/almalinux/tasks/main.yml b/ansible/roles/almalinux/tasks/main.yml index 73ca1b5..1d39aab 100755 --- a/ansible/roles/almalinux/tasks/main.yml +++ b/ansible/roles/almalinux/tasks/main.yml @@ -21,37 +21,6 @@ name: "*" state: latest -- name: Install required software - ansible.builtin.dnf: - state: present - name: - - bind-utils - - lsof - - htop - - btop - - mosh - - rsync - - tmux - - nano - - the-silver-searcher - - net-tools - - plocate - - fail2ban - - munin-node - # - nethogs # Not available in AlmaLinux EPEL - - pv - - selinux-policy - - tar - - unzip - - qemu-guest-agent - - python3-libselinux - -- name: Create a user for andys - ansible.builtin.user: - name: andy - groups: - - wheel - state: present # - name: Put SELinux in permissive mode, logging actions that would be blocked. # ansible.posix.selinux: # policy: targeted diff --git a/ansible/roles/alpine/tasks/main.yml b/ansible/roles/alpine/tasks/main.yml index 98d9908..9f32d49 100644 --- a/ansible/roles/alpine/tasks/main.yml +++ b/ansible/roles/alpine/tasks/main.yml @@ -18,26 +18,6 @@ name: sudo state: present -- name: Install base packages - apk: - name: - - bash - - curl - - git - - htop - - iotop - - nano - - net-tools - - openssh-server - - python3 - - py3-pip - - rsync - - shadow # For useradd/usermod commands - - tmux - - vim - - wget - state: present - - name: Ensure sshd is running and enabled service: name: sshd @@ -62,4 +42,4 @@ name: ansible groups: sudo append: yes - when: ansible_user == "ansible" \ No newline at end of file + when: ansible_user == "ansible" diff --git a/ansible/roles/base/defaults/main.yml b/ansible/roles/base/defaults/main.yml new file mode 100644 index 0000000..d0071b8 --- /dev/null +++ b/ansible/roles/base/defaults/main.yml @@ -0,0 +1,106 @@ +--- +managed_groups: [] +managed_users: [] + +admin_group_map: + Debian: sudo + RedHat: wheel + Alpine: wheel + +admin_group: "{{ admin_group_map.get(ansible_os_family, 'sudo') }}" + +base_common_packages: + - git + - vim + - wget + - htop + - tmux + - rsync + - zsh + +base_packages_by_os_family: + Debian: + - sudo + - jq + - sysstat + - smartmontools + - lm-sensors + - debconf-utils + - ntp + - ntpdate + - samba + - samba-common + - sssd + - krb5-user + - python3 + - python3-apt + RedHat: + - sudo + - jq + - sysstat + - smartmontools + - lm_sensors + - python3 + Alpine: + - sudo + - bash + - curl + - python3 + - py3-pip + - shadow + +base_packages_by_distribution: + Ubuntu: + - lsof + - mosh + - unattended-upgrades + - silversearcher-ag + - net-tools + - locate + - fail2ban + - munin-node + - nethogs + - pv + - fzf + - qemu-guest-agent + - python3-apt + Debian: + - lsof + - mosh + - unattended-upgrades + - silversearcher-ag + - net-tools + - locate + - fail2ban + - munin-node + - nethogs + - pv + - fzf + - qemu-guest-agent + AlmaLinux: + - bind-utils + - lsof + - btop + - mosh + - nano + - net-tools + - mlocate + - fail2ban + - munin-node + - pv + - selinux-policy + - tar + - unzip + - qemu-guest-agent + - python3-libselinux + Alpine: + - git + - htop + - iotop + - nano + - net-tools + - openssh-server + - rsync + - tmux + - vim + - wget diff --git a/ansible/roles/base/tasks/main.yml b/ansible/roles/base/tasks/main.yml index f46aefc..14289e3 100644 --- a/ansible/roles/base/tasks/main.yml +++ b/ansible/roles/base/tasks/main.yml @@ -16,6 +16,12 @@ msg: "{{ ansible_default_ipv6.address }}" when: ansible_default_ipv6.address is defined +# Base package installation +- import_tasks: packages.yml + tags: + - packages + - common + # User and group setup - import_tasks: users.yml tags: @@ -39,4 +45,4 @@ - include_role: name: alpine - when: ansible_os_family == "Alpine" \ No newline at end of file + when: ansible_os_family == "Alpine" diff --git a/ansible/roles/base/tasks/packages.yml b/ansible/roles/base/tasks/packages.yml new file mode 100644 index 0000000..ffbbebb --- /dev/null +++ b/ansible/roles/base/tasks/packages.yml @@ -0,0 +1,19 @@ +--- +- name: Determine base package set + ansible.builtin.set_fact: + base_packages_combined: >- + {{ (base_common_packages + + base_packages_by_os_family.get(ansible_os_family, []) + + base_packages_by_distribution.get(ansible_distribution, [])) | unique }} + tags: + - packages + - common + +- name: Install base packages + ansible.builtin.package: + name: "{{ base_packages_combined }}" + state: present + when: base_packages_combined | length > 0 + tags: + - packages + - common diff --git a/ansible/roles/base/tasks/users.yml b/ansible/roles/base/tasks/users.yml index 9085c4c..0be5091 100644 --- a/ansible/roles/base/tasks/users.yml +++ b/ansible/roles/base/tasks/users.yml @@ -1,73 +1,106 @@ ---- # User and group management tasks -- name: Install sudo package (Debian) - apt: - name: sudo - state: present - when: ansible_os_family == "Debian" +- name: Ensure admin group exists when defined + ansible.builtin.group: + name: "{{ admin_group }}" + when: admin_group | default('') | length > 0 -- name: Create ansible group - group: - name: ansible - gid: 10001 - state: present +- name: Ensure managed groups exist + ansible.builtin.group: + name: "{{ item.name }}" + gid: "{{ item.gid | default(omit) }}" + state: "{{ item.state | default('present') }}" + loop: "{{ managed_groups | default([]) }}" + loop_control: + label: "{{ item.name }}" -- name: Create ansible user - user: - name: ansible - group: ansible - uid: 10001 - shell: /bin/bash - home: /home/ansible +- name: Ensure primary groups for managed users exist + ansible.builtin.group: + name: "{{ item.primary_group }}" + gid: "{{ item.primary_gid | default(omit) }}" state: present + loop: "{{ managed_users | default([]) }}" + loop_control: + label: "{{ item.name }}" + when: item.primary_group is defined -- name: Create graham user - user: - name: graham - shell: /bin/bash - home: /home/graham - state: present +- name: Ensure managed users are present + ansible.builtin.user: + name: "{{ item.name }}" + uid: "{{ item.uid | default(omit) }}" + shell: "{{ item.shell | default('/bin/bash') }}" + home: "{{ item.home | default(omit) }}" + group: "{{ item.primary_group | default(omit) }}" + groups: "{{ (user_groups | join(',')) if user_groups else omit }}" + append: true + create_home: "{{ item.create_home | default(true) }}" + state: "{{ item.state | default('present') }}" + vars: + user_groups: >- + {{ ((item.groups | default([])) + + ([admin_group] if item.admin | default(false) and admin_group | default('') | length > 0 else [])) + | unique }} + loop: "{{ managed_users | default([]) }}" + loop_control: + label: "{{ item.name }}" + when: item.state | default('present') != 'absent' -- name: Create andy user - user: - name: andy - shell: /bin/bash - home: /home/andy - state: present +- name: Remove managed users marked absent + ansible.builtin.user: + name: "{{ item.name }}" + state: absent + remove: "{{ item.remove | default(false) }}" + loop: "{{ managed_users | default([]) }}" + loop_control: + label: "{{ item.name }}" + when: item.state | default('present') == 'absent' -- name: Set up SSH key for ansible user - authorized_key: - user: ansible - key: "{{ lookup('file', 'ansible.pub') }}" - exclusive: true - state: present +- name: Install authorized keys from GitHub + ansible.posix.authorized_key: + user: "{{ item.0.name }}" + state: "{{ item.1.state | default('present') }}" + key: "https://github.com/{{ item.1.value }}.keys" + manage_dir: "{{ item.1.manage_dir | default(true) }}" + exclusive: "{{ item.1.exclusive | default(false) }}" + loop: "{{ managed_users | default([]) | subelements('authorized_keys', skip_missing=True) }}" + loop_control: + label: "{{ item.0.name }} (github: {{ item.1.value }})" + when: + - item.1.type | default('github') == 'github' + - item.0.state | default('present') != 'absent' + +- name: Install authorized keys from files + ansible.posix.authorized_key: + user: "{{ item.0.name }}" + state: "{{ item.1.state | default('present') }}" + key: "{{ lookup('file', item.1.value) }}" + manage_dir: "{{ item.1.manage_dir | default(true) }}" + exclusive: "{{ item.1.exclusive | default(false) }}" + loop: "{{ managed_users | default([]) | subelements('authorized_keys', skip_missing=True) }}" + loop_control: + label: "{{ item.0.name }} (file: {{ item.1.value }})" + when: + - item.1.type | default('github') == 'file' + - item.0.state | default('present') != 'absent' - name: Configure sudo for ansible user - template: + ansible.builtin.template: src: sudoers_ansible.j2 dest: /etc/sudoers.d/ansible mode: '0440' validate: 'visudo -cf %s' - name: Configure sudo for graham user - template: + ansible.builtin.template: src: sudoers_graham.j2 dest: /etc/sudoers.d/graham mode: '0440' validate: 'visudo -cf %s' - name: Enable passwordless sudo for %sudo - lineinfile: + ansible.builtin.lineinfile: path: /etc/sudoers state: present regexp: '^%sudo' line: '%sudo ALL=(ALL) NOPASSWD: ALL' validate: 'visudo -cf %s' - -- name: Install SSH keys from GitHub - ansible.posix.authorized_key: - user: "{{ item.username }}" - state: present - key: "https://github.com/{{ item.github }}.keys" - loop: "{{ github_users }}" \ No newline at end of file diff --git a/ansible/roles/ubuntu/tasks/main.yml b/ansible/roles/ubuntu/tasks/main.yml index c804158..ed768ed 100755 --- a/ansible/roles/ubuntu/tasks/main.yml +++ b/ansible/roles/ubuntu/tasks/main.yml @@ -17,28 +17,6 @@ autoremove: true autoclean: true -- name: Install required software - ansible.builtin.apt: - autoremove: true - autoclean: true - state: present - name: - - lsof - - htop - - mosh - - rsync - - tmux - - unattended-upgrades - - silversearcher-ag - - net-tools - - locate - - fail2ban - - munin-node - - nethogs - - pv - - fzf - - qemu-guest-agent - - name: Remove ubuntu advantage ansible.builtin.apt: autoremove: true @@ -47,12 +25,6 @@ name: - ubuntu-advantage-tools -- name: Create a user for andys - ansible.builtin.user: - name: andy - groups: - - sudo - state: present # - name: Allow SSH in ufw # community.general.ufw: # rule: limit