410 lines
11 KiB
Markdown
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
|