ansible-role-os-bootstrap/README.md

410 lines
11 KiB
Markdown

# os-bootstrap
An Ansible role that performs early-stage OS bootstrapping for Linux servers. It runs before any role dependencies (rsyslog, firewall, NTP, etc.) and establishes the baseline system state: locale, timezone, hostname, package prerequisites, network configuration, disk management, sysctl tuning, and PKI infrastructure.
This role is designed to be the **first dependency** of higher-level roles such as [basic-system-setup](https://gitea-s2i2s.isti.cnr.it/ISTI-ansible-roles/ansible-role-basic-system-setup).
## Requirements
- Ansible >= 2.9
- Python 3 on target hosts (this role installs additional Python packages needed by subsequent Ansible tasks)
### Ansible Collections Required
```bash
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install community.general
```
## Supported Platforms
- Ubuntu 20.04 (Focal), 22.04 (Jammy), 24.04 (Noble)
- Debian 11 (Bullseye), 12 (Bookworm)
- RHEL / CentOS Stream / Rocky Linux / AlmaLinux 8, 9, 10
## Task Descriptions
Tasks run in the following order:
| Task File | Description | Condition |
| --------- | ----------- | --------- |
| `http_client_proxy.yml` | Configures system-wide HTTP/HTTPS proxy environment variables | `enable_env_proxy` |
| `ansible-python3-pkgs.yml` | Installs Python 3 packages required by Ansible modules | always |
| `hostname.yml` | Sets the system hostname from inventory | `explicitly_set_hostname` |
| `locale.yml` | Generates and configures system locales | always |
| `timezone.yml` | Sets the system timezone | always |
| `etchosts-customizations.yml` | Adds custom entries to `/etc/hosts` | always |
| `network-interfaces.yml` | Configures additional network interfaces via Netplan (Ubuntu) | `ubuntu_configure_additional_interfaces` |
| `additional_disks.yml` | Partitions, formats, and mounts additional disks | `additional_disks` |
| `swap_device.yml` | Configures a swap device | `swap_device` |
| `external_repos_el.yml` | Installs EPEL and optional elrepo repositories | RedHat family only |
| `basic_setup_el.yml` | EL/RedHat package install, SELinux configuration, service management | RedHat family only |
| `deb_general.yml` | Debian/Ubuntu package install, apt proxy, unattended upgrades, service cleanup | Debian family only |
| `sysctl.yml` | Writes custom kernel parameters to `/etc/sysctl.d/` | always |
| `grub_cmdline_parameters.yml` | Appends parameters to `GRUB_CMDLINE_LINUX` | `configure_grub_cmdline_parameters` |
| `pki_dir.yml` | Creates the PKI directory hierarchy | always |
| `self_signed_certificate.yml` | Generates a self-signed certificate with mkcert | `letsencrypt_acme_install` is defined and true |
| `trusted_ca.yml` | Installs Let's Encrypt and custom CA certificates into the system trust store | always |
| `certificate_from_private_ca.yml` | Requests a certificate from a private CA (mkcert) | `mkcert_create_certificate` and no Let's Encrypt |
### Sub-tasks for `deb_general.yml`
| Task File | Description | Condition |
| --------- | ----------- | --------- |
| `apt_proxy.yml` | Configures APT proxy in `/etc/apt/apt.conf.d/02proxy` | `use_apt_proxy` |
| `dist_upgrade.yml` | Performs a full distribution upgrade | `dist_upgrade` |
| `packages_deb.yml` | Installs common and additional packages | always |
| `remove_unneeded_pkgs.yml` | Removes unwanted packages (exim, snapd, lxd, etc.) | always |
| `pubkeys.yml` | Manages root SSH authorized keys | `manage_root_ssh_keys` |
| `unattended_upgrades.yml` | Configures `unattended-upgrades` for automatic security updates | always |
| `disable_services.yml` | Disables unwanted services | `disable_some_not_needed_services` |
## Role Variables
### Timezone and Locale
```yaml
timezone: Europe/Rome
default_locale_lang: en_US.UTF-8
default_deb_locale_messages: C.UTF-8
default_el_locale_messages: en_US.UTF-8
locales_list:
- { name: "{{ default_locale_lang }}" }
- { name: en_US.UTF-8 }
- { name: en_US }
- { name: it_IT.UTF-8 }
- { name: it_IT }
```
### Hostname and /etc/hosts
```yaml
explicitly_set_hostname: true
domain_name: "{{ ansible_domain }}"
# Inline block of hosts entries, e.g.:
# "192.168.1.10 host1.example.com host1"
custom_etc_hosts_entries: ""
custom_etc_hosts_entries_adjunct: ""
```
### Network Interfaces (Ubuntu / Netplan)
```yaml
ubuntu_configure_additional_interfaces: false
ubuntu_configure_additional_int_dhcp_overrides: true
ubuntu_configure_additional_ints_list: []
# Example:
# ubuntu_configure_additional_ints_list:
# - name: eth1
# dhcp4: true
disable_ipv6: false
ipv6_sysctl_value: 1
ipv6_sysctl_file: /etc/sysctl.d/10-ipv6-disable.conf
```
### Sysctl
```yaml
sysctl_custom_file: /etc/sysctl.d/90-custom-values.conf
sysctl_opts_reload: true
sysctl_custom_file_state: present
# Only name and value are mandatory
sysctl_custom_options: []
# - name: 'net.nf_conntrack_max'
# value: '32768'
# sysctlfile: '{{ sysctl_custom_file }}'
# sysctl_reload: '{{ sysctl_opts_reload }}'
# sysctlfile_state: '{{ sysctl_custom_file_state }}'
```
### GRUB
```yaml
configure_grub_cmdline_parameters: false
grub_cmdline_additional_parameters: ""
# Example: "intel_iommu=on quiet"
```
### Additional Disks
```yaml
additional_disks: false
disks_and_mountpoints_list: []
# - mountpoint: '/data'
# device: 'xvda3'
# fstype: 'xfs'
# opts: 'noatime'
# state: 'mounted'
# create_filesystem: true
```
### Swap Device
```yaml
swap_device: false
swap_device_name: /dev/vdxxxxx
```
### HTTP Client Proxy
```yaml
enable_env_proxy: false
env_proxy_http_host: localhost
env_proxy_http_port: "3128"
env_proxy_http_protocol: http
env_proxy_https_protocol: "{{ env_proxy_http_protocol }}"
env_proxy_http_url: "{{ env_proxy_http_protocol }}://{{ env_proxy_http_host }}:{{ env_proxy_http_port }}"
env_proxy_https_url: "{{ env_proxy_http_url }}"
env_proxy_use_authentication: false
env_proxy_username: ""
env_proxy_password: ""
no_proxy_targets:
- ::1
- 127.0.0.1
- localhost
```
### Python 3 Packages for Ansible
```yaml
ansible_python3_debs:
- python3-lxml
ansible_python3_el:
- python3-lxml
```
### PKI Directory and Certificates
```yaml
pki_dir: /etc/pki
pki_subdirs:
- certs
- keys
pki_install_a_custom_ca: false
# Self-signed certificate paths (used when letsencrypt is not available)
self_signed_cert: "{{ pki_dir }}/selfsigned/cert"
self_signed_fullchain: "{{ pki_dir }}/selfsigned/fullchain"
self_signed_key: "{{ pki_dir }}/selfsigned/privkey"
self_signed_subject: /CN={{ ansible_fqdn }} self signed
# Certificate from private CA (mkcert)
mkcert_create_certificate: false
mkcert_cert_name: "{{ ansible_fqdn }}.pem"
mkcert_cert_dest_path: "{{ pki_dir }}/certs"
mkcert_key_name: "{{ ansible_fqdn }}-key.pem"
mkcert_key_dest_path: "{{ pki_dir }}/keys"
mkcert_dsn_and_ip_list: "{{ ansible_fqdn }} {% for ip in ansible_all_ipv4_addresses %}{{ ip }} {% endfor %}"
mkcert_ca_host: localhost
```
### Trusted CA Certificates
```yaml
trusted_ca_el_anchors_path: /etc/pki/ca-trust/source/anchors
trusted_ca_deb_path: /usr/local/share/ca-certificates
# Let's Encrypt intermediate CAs (normally already trusted by the OS)
trusted_ca_letsencrypt_install: false
trusted_ca_letsencrypt_ca_certificates_url: https://letsencrypt.org/certs
# Additional custom CAs to install
trusted_ca_additional_ca_files: []
# - { ca_url: 'https://example.com/foo-ca.pem', ca: 'foo-ca.pem', name: 'foo-ca' }
```
### EL/RedHat — External Repositories
```yaml
centos_install_epel: true
centos_epel_repo_url: epel-release
centos_install_release_scl: false
rh_install_elrepo: false
```
### EL/RedHat — Basic Setup
```yaml
centos_pkg_state: latest
centos_packages_cleanup: true
# SELinux
selinux_policy_type: targeted
selinux_policy_state: enforcing
centos_selinux_daemons_dump_core: false
# Services
centos_disable_avahi: true
centos_remove_avahi: false
centos_disable_networkmanager: false
centos_remove_networkmanager: false
centos_services_to_be_disabled:
- acpid
# DNS (optional, for static resolver configuration)
centos_set_dns_servers: false
dns1: 208.67.220.220
dns2: 208.67.222.222
# Root SSH keys
manage_root_ssh_keys: true
```
### Debian/Ubuntu — Package Management
```yaml
use_apt_proxy: false
apt_proxy_url: http://localhost:3128
dist_upgrade: false
pkg_state: present
common_packages:
- acl
- curl
- wget
- htop
- vim-tiny
- psmisc
- tcpdump
- lsof
- strace
- rsync
- unzip
- tree
- bash-completion
- sudo
- less
# ... see defaults/main.yml for the full list
# Additional packages (define in your playbook or group_vars)
# additional_packages:
# - pkg1
# - pkg2
```
### Debian/Ubuntu — Package Cleanup
```yaml
cleanup_base_packages: true
base_packages_to_remove:
- ppp
- at
- snapd
cleanup_exim_email_server: true
disable_apport_service: true
ubuntu_remove_lxd: true
```
### Debian/Ubuntu — Unattended Upgrades
```yaml
unatt_allowed_origins:
- ${distro_id}:${distro_codename}-security
unatt_autofix: "true"
unatt_minimalsteps: "false"
unatt_install_on_shutdown: "false"
unatt_email_on_error: "false"
unatt_autoremove: "true"
unatt_autoreboot: "false"
unatt_autoreboot_time: now
```
### Debian/Ubuntu — Service Management
```yaml
disable_some_not_needed_services: false
services_to_be_disabled:
- rpcbind
- atd
- acpid
```
## Dependencies
None. This role is intentionally dependency-free so it can run before any other role.
## Example Playbook
Normally you do not invoke `os-bootstrap` directly — it is pulled in automatically as a dependency. If you need to run it standalone:
```yaml
---
- hosts: servers
become: true
roles:
- role: adellam.os_bootstrap
vars:
timezone: Europe/Rome
explicitly_set_hostname: true
disable_ipv6: false
sysctl_custom_options:
- name: net.nf_conntrack_max
value: "65536"
```
### With proxy and extra disk
```yaml
---
- hosts: servers
become: true
roles:
- role: adellam.os_bootstrap
vars:
enable_env_proxy: true
env_proxy_http_host: proxy.example.com
env_proxy_http_port: "3128"
additional_disks: true
disks_and_mountpoints_list:
- mountpoint: /data
device: sdb1
fstype: xfs
opts: noatime
state: mounted
create_filesystem: true
```
### EL with custom SELinux and EPEL
```yaml
---
- hosts: el_servers
become: true
roles:
- role: adellam.os_bootstrap
vars:
centos_install_epel: true
selinux_policy_state: enforcing
selinux_policy_type: targeted
centos_pkg_state: latest
```
## Testing
```bash
source ~/ansible/ansible6/bin/activate
ansible-lint
```
Basic test playbook is in `tests/test.yml`.
## License
EUPL-1.2
## Author Information
Andrea Dell'Amico <andrea.dellamico@isti.cnr.it>
ISTI-CNR, Pisa, Italy