# 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 ISTI-CNR, Pisa, Italy