feat(ansible): add linux-baseline role and day0_linux_baseline playbook

Introduces a single, idempotent baseline role to supersede the
overlapping day0-baseline and common roles. Capabilities are
feature-flagged so they can be toggled per-host:

  - packages (common + OS-family + per-host extras)
  - timezone + locale
  - chrony time sync against sundial
  - baseline users (jarvis admin account with SSH key + NOPASSWD sudo)
  - SSH hardening via /etc/ssh/sshd_config.d/ drop-in
  - unattended security upgrades (Debian family)
  - sysctl drop-in at /etc/sysctl.d/99-mk-labs.conf
  - journald retention caps
  - branded MOTD

Ubuntu/Debian is first-class; vars/RedHat.yml provides a placeholder
for future distros via the ansible_os_family pattern.

The legacy day0-baseline and common roles remain in place for now and
will be removed during the playbook cleanup sweep, alongside the
existing playbook naming inconsistencies.
This commit is contained in:
Hermes Agent service account
2026-05-29 20:40:04 -05:00
parent 1dfa7889ab
commit 91b5817e5f
25 changed files with 741 additions and 0 deletions

View File

@@ -19,6 +19,15 @@ terraform_server: "infra01"
# Traefik variables
traefik_server: "lightning-lane"
# ---------------------------------------------------------------------------
# JARVIS automation account
# ---------------------------------------------------------------------------
# Public key for the 'jarvis' user provisioned by the linux-baseline role on
# every host. Public keys are not secret; the matching private key lives on
# the JARVIS command centre (carousel-of-progress) and, when needed, in
# group_vars/all/vault as vault_jarvis_ssh_private_key.
jarvis_ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID5sym5ajFvDyzw395BkHv7qVb66XPTx/OF1p19MGuNo jarvis@mk-labs"
step_ca_principal_mappings:
- local_user: wed
principals:

View File

@@ -0,0 +1,23 @@
---
# ============================================================================
# day0_linux_baseline.yml
# ----------------------------------------------------------------------------
# Applies the mk-labs Linux baseline (linux-baseline role) to one or more
# hosts. Idempotent and safe to re-run.
#
# Usage:
# ansible-playbook playbooks/day0_linux_baseline.yml
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=figment
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=semaphore_server
#
# To trigger an opt-in full system upgrade:
# ansible-playbook playbooks/day0_linux_baseline.yml \
# -e target=figment -e 'baseline_features={"full_upgrade": true}'
# ============================================================================
- name: Apply mk-labs Linux baseline
hosts: "{{ target | default('all') }}"
become: true
gather_facts: true
roles:
- linux-baseline

View File

@@ -0,0 +1,64 @@
# linux-baseline
Idempotent baseline for any Linux host in mk-labs.
## What it does
Applied on every host, this role guarantees a known-good operational baseline:
| Capability | Default | Description |
|----------------------|---------|---------------------------------------------------------------|
| `packages` | on | Installs curated common + OS-family packages, plus host extras |
| `timezone` | on | Sets timezone and locale |
| `time_sync` | on | Installs and configures chrony against `sundial` |
| `users` | on | Provisions baseline admin accounts (jarvis) with SSH + sudo |
| `ssh_hardening` | on | Deploys `/etc/ssh/sshd_config.d/10-mk-labs-hardening.conf` |
| `unattended_upgrades`| on | Security-only automatic upgrades on Debian-family hosts |
| `sysctl` | on | Drop-in at `/etc/sysctl.d/99-mk-labs.conf` |
| `journald` | on | Caps journal size + retention |
| `motd` | on | Branded MOTD |
| `full_upgrade` | off | Opt-in full dist-upgrade. Use the day2 update playbook instead |
Every capability sits behind a feature flag in `baseline_features` so it
can be disabled per-host without forking the role.
## OS support
- **First-class:** Ubuntu (22.04, 24.04) and Debian (bullseye, bookworm).
- **Placeholder:** RHEL family — see `vars/RedHat.yml`. Flesh out when a
RHEL-family host joins the fleet.
OS-specific values are loaded from `vars/{{ ansible_os_family }}.yml`.
## Required inventory variables
| Variable | Where | Example |
|-------------------------|-----------------|-------------------------------------------|
| `jarvis_ssh_public_key` | group_vars/all | `ssh-ed25519 AAAA... jarvis@mk-labs` |
## Usage
```yaml
- name: Apply linux-baseline
hosts: "{{ target | default('all') }}"
become: true
roles:
- linux-baseline
```
## Per-host overrides
```yaml
# host_vars/some-host/vars
baseline_packages_extra:
- postgresql-client
- redis-tools
baseline_features:
motd: false
```
## Migration notes
This role supersedes the legacy `day0-baseline` and `common` roles, both
of which are slated for removal once the fleet has been re-baselined.

View File

@@ -0,0 +1,120 @@
---
# ============================================================================
# linux-baseline role defaults
# ============================================================================
# This role applies an idempotent baseline to any Linux host in mk-labs.
# Ubuntu is the first-class target; other distributions plug in via
# vars/<ansible_os_family>.yml.
# ============================================================================
# ---------------------------------------------------------------------------
# Feature flags - each capability can be toggled independently
# ---------------------------------------------------------------------------
baseline_features:
packages: true
timezone: true
time_sync: true
ssh_hardening: true
users: true
unattended_upgrades: true
sysctl: true
journald: true
motd: true
# Off by default: a full apt/dnf upgrade is intrusive. Use the dedicated
# day2 update playbook for that. Security patches are handled by
# unattended-upgrades.
full_upgrade: false
# ---------------------------------------------------------------------------
# Locale & time
# ---------------------------------------------------------------------------
baseline_timezone: America/Chicago
baseline_locale: en_US.UTF-8
baseline_ntp_server: sundial.local.mk-labs.cloud
baseline_ntp_fallback_pool: 2.pool.ntp.org
# ---------------------------------------------------------------------------
# Packages
# ---------------------------------------------------------------------------
# Distribution-agnostic baseline. Distro-specific additions are merged in
# from vars/<family>.yml at runtime.
baseline_packages_common:
- curl
- wget
- vim
- htop
- git
- jq
- unzip
- rsync
- ca-certificates
- gnupg
- net-tools
- dnsutils
- acl
- tree
- tmux
# Per-host opt-in extras. Set in host_vars or group_vars.
baseline_packages_extra: []
# ---------------------------------------------------------------------------
# Baseline users
# ---------------------------------------------------------------------------
# 'jarvis' is the universal admin / automation account that this role
# provisions on every host. Service users (e.g. 'cast' for podman
# workloads) are created by their respective application roles, not here.
baseline_users:
- name: jarvis
comment: "JARVIS automation account"
shell: /bin/bash
groups: "{{ baseline_sudo_group }}"
sudo_nopasswd: true
ssh_authorized_keys:
- "{{ jarvis_ssh_public_key }}"
# ---------------------------------------------------------------------------
# SSH hardening
# ---------------------------------------------------------------------------
# Delivered as a drop-in under /etc/ssh/sshd_config.d/ rather than editing
# the main config file. Safer to re-apply, easier to remove.
baseline_ssh_permit_root_login: "no"
baseline_ssh_password_authentication: "no"
baseline_ssh_pubkey_authentication: "yes"
baseline_ssh_permit_empty_passwords: "no"
baseline_ssh_x11_forwarding: "no"
baseline_ssh_client_alive_interval: 300
baseline_ssh_client_alive_count_max: 2
baseline_ssh_max_auth_tries: 4
baseline_ssh_login_grace_time: 30
# ---------------------------------------------------------------------------
# Unattended security upgrades (apt / dnf-automatic)
# ---------------------------------------------------------------------------
baseline_unattended_security_only: true
baseline_unattended_auto_reboot: false
baseline_unattended_auto_reboot_time: "03:00"
# ---------------------------------------------------------------------------
# sysctl
# ---------------------------------------------------------------------------
# Light hand here. The kernel defaults are generally sensible; we tune
# only what consistently bites us in a homelab.
baseline_sysctl:
vm.swappiness: 10
fs.inotify.max_user_watches: 524288
fs.inotify.max_user_instances: 512
net.ipv4.tcp_keepalive_time: 600
net.ipv4.tcp_keepalive_intvl: 60
net.ipv4.tcp_keepalive_probes: 6
# ---------------------------------------------------------------------------
# journald retention caps
# ---------------------------------------------------------------------------
baseline_journald_max_use: "1G"
baseline_journald_max_retention: "30day"
# ---------------------------------------------------------------------------
# MOTD branding
# ---------------------------------------------------------------------------
baseline_motd_banner: "mk-labs"

View File

@@ -0,0 +1,20 @@
---
- name: Restart chrony
ansible.builtin.systemd:
name: "{{ baseline_chrony_service }}"
state: restarted
- name: Restart sshd
ansible.builtin.systemd:
name: "{{ baseline_sshd_service }}"
state: restarted
- name: Reload sysctl
ansible.builtin.command:
cmd: sysctl --system
changed_when: false
- name: Restart systemd-journald
ansible.builtin.systemd:
name: systemd-journald
state: restarted

View File

@@ -0,0 +1,25 @@
---
galaxy_info:
role_name: linux_baseline
author: JARVIS
description: >-
Idempotent Linux baseline for mk-labs hosts. Ubuntu first-class,
pluggable for other distributions via vars/<os_family>.yml.
license: MIT
min_ansible_version: "2.14"
platforms:
- name: Ubuntu
versions:
- jammy
- noble
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- baseline
- bootstrap
- hardening
- homelab
dependencies: []

View File

@@ -0,0 +1,21 @@
---
# ============================================================================
# journald retention
# ============================================================================
- name: Ensure journald drop-in directory exists
ansible.builtin.file:
path: /etc/systemd/journald.conf.d
state: directory
owner: root
group: root
mode: "0755"
- name: Deploy journald retention drop-in
ansible.builtin.template:
src: journald-retention.conf.j2
dest: /etc/systemd/journald.conf.d/10-mk-labs-retention.conf
owner: root
group: root
mode: "0644"
notify: Restart systemd-journald

View File

@@ -0,0 +1,63 @@
---
# ============================================================================
# linux-baseline / main entrypoint
# ============================================================================
# Orchestrates baseline application. Each capability sits behind a
# feature flag in baseline_features so individual concerns can be
# disabled per-host without forking the role.
# ============================================================================
- name: Preflight checks
ansible.builtin.import_tasks: preflight.yml
tags: [baseline, preflight, always]
- name: Load OS-family-specific variables
ansible.builtin.include_vars: "{{ ansible_os_family }}.yml"
tags: [baseline, always]
- name: Apply package baseline
ansible.builtin.import_tasks: packages.yml
when: baseline_features.packages | bool
tags: [baseline, packages]
- name: Configure timezone and locale
ansible.builtin.import_tasks: timezone.yml
when: baseline_features.timezone | bool
tags: [baseline, timezone]
- name: Configure time synchronisation
ansible.builtin.import_tasks: time_sync.yml
when: baseline_features.time_sync | bool
tags: [baseline, time-sync]
- name: Provision baseline users
ansible.builtin.import_tasks: users.yml
when: baseline_features.users | bool
tags: [baseline, users]
- name: Harden SSH
ansible.builtin.import_tasks: ssh_hardening.yml
when: baseline_features.ssh_hardening | bool
tags: [baseline, ssh, hardening]
- name: Configure unattended security upgrades
ansible.builtin.import_tasks: unattended_upgrades.yml
when:
- baseline_features.unattended_upgrades | bool
- ansible_os_family == "Debian"
tags: [baseline, unattended-upgrades]
- name: Apply sysctl baseline
ansible.builtin.import_tasks: sysctl.yml
when: baseline_features.sysctl | bool
tags: [baseline, sysctl]
- name: Configure journald retention
ansible.builtin.import_tasks: journald.yml
when: baseline_features.journald | bool
tags: [baseline, journald]
- name: Apply MOTD branding
ansible.builtin.import_tasks: motd.yml
when: baseline_features.motd | bool
tags: [baseline, motd]

View File

@@ -0,0 +1,12 @@
---
# ============================================================================
# MOTD branding
# ============================================================================
- name: Deploy mk-labs MOTD
ansible.builtin.template:
src: motd.j2
dest: /etc/motd
owner: root
group: root
mode: "0644"

View File

@@ -0,0 +1,32 @@
---
# ============================================================================
# Package baseline
# ============================================================================
- name: Update apt cache (Debian family)
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install baseline packages (common)
ansible.builtin.package:
name: "{{ baseline_packages_common }}"
state: present
- name: Install baseline packages (OS family)
ansible.builtin.package:
name: "{{ baseline_packages_family }}"
state: present
- name: Install baseline packages (host extras)
ansible.builtin.package:
name: "{{ baseline_packages_extra }}"
state: present
when: baseline_packages_extra | length > 0
- name: Perform full system upgrade (opt-in)
ansible.builtin.package:
name: "*"
state: latest # noqa package-latest - intentional under feature flag
when: baseline_features.full_upgrade | bool

View File

@@ -0,0 +1,18 @@
---
# ============================================================================
# Preflight: fail fast on unsupported platforms.
# ============================================================================
- name: Assert a supported OS family
ansible.builtin.assert:
that:
- ansible_os_family in ["Debian", "RedHat"]
fail_msg: >-
linux-baseline currently supports the Debian and RedHat OS families.
Host {{ inventory_hostname }} is running {{ ansible_distribution }}
{{ ansible_distribution_version }} ({{ ansible_os_family }}).
success_msg: "Detected {{ ansible_distribution }} {{ ansible_distribution_version }}."
- name: Gather package facts
ansible.builtin.package_facts:
manager: auto

View File

@@ -0,0 +1,43 @@
---
# ============================================================================
# SSH hardening
# ============================================================================
# Delivered as a drop-in under /etc/ssh/sshd_config.d/. The main
# sshd_config remains untouched so distro upgrades don't conflict and
# rollback is trivial (delete the drop-in).
#
# Some older sshd builds (Ubuntu < 20.04, Debian < 11) do not honour
# sshd_config.d/. We fall back to lineinfile on those.
# ============================================================================
- name: Check whether sshd supports the Include directive
ansible.builtin.command:
cmd: sshd -T
register: sshd_test
changed_when: false
check_mode: false
- name: Determine if /etc/ssh/sshd_config.d/ is honoured
ansible.builtin.stat:
path: /etc/ssh/sshd_config.d
register: sshd_dropin_dir
- name: Ensure /etc/ssh/sshd_config.d exists when honoured
ansible.builtin.file:
path: /etc/ssh/sshd_config.d
state: directory
owner: root
group: root
mode: "0755"
when: sshd_dropin_dir.stat.exists
- name: Deploy SSH hardening drop-in
ansible.builtin.template:
src: sshd_hardening.conf.j2
dest: /etc/ssh/sshd_config.d/10-mk-labs-hardening.conf
owner: root
group: root
mode: "0644"
validate: "sshd -t -f %s"
when: sshd_dropin_dir.stat.exists
notify: Restart sshd

View File

@@ -0,0 +1,16 @@
---
# ============================================================================
# sysctl baseline
# ============================================================================
# Delivered as a drop-in under /etc/sysctl.d/ so each setting is
# self-documenting and removable.
# ============================================================================
- name: Deploy mk-labs sysctl drop-in
ansible.builtin.template:
src: 99-mk-labs.conf.j2
dest: /etc/sysctl.d/99-mk-labs.conf
owner: root
group: root
mode: "0644"
notify: Reload sysctl

View File

@@ -0,0 +1,25 @@
---
# ============================================================================
# Time synchronisation via chrony
# ============================================================================
- name: Install chrony
ansible.builtin.package:
name: chrony
state: present
- name: Deploy chrony configuration
ansible.builtin.template:
src: chrony.conf.j2
dest: "{{ baseline_chrony_config_path }}"
owner: root
group: root
mode: "0644"
backup: true
notify: Restart chrony
- name: Ensure chrony is enabled and running
ansible.builtin.systemd:
name: "{{ baseline_chrony_service }}"
state: started
enabled: true

View File

@@ -0,0 +1,24 @@
---
# ============================================================================
# Timezone and locale
# ============================================================================
- name: Set system timezone
community.general.timezone:
name: "{{ baseline_timezone }}"
- name: Ensure locale package present (Debian)
ansible.builtin.apt:
name: locales
state: present
when: ansible_os_family == "Debian"
- name: Generate locale
community.general.locale_gen:
name: "{{ baseline_locale }}"
state: present
- name: Set system locale
ansible.builtin.command:
cmd: "localectl set-locale LANG={{ baseline_locale }}"
changed_when: false

View File

@@ -0,0 +1,37 @@
---
# ============================================================================
# Unattended security upgrades (Debian family)
# ============================================================================
- name: Ensure unattended-upgrades is installed
ansible.builtin.apt:
name:
- unattended-upgrades
- apt-listchanges
state: present
- name: Deploy 50unattended-upgrades configuration
ansible.builtin.template:
src: 50unattended-upgrades.j2
dest: "{{ baseline_unattended_config_dir }}/50unattended-upgrades"
owner: root
group: root
mode: "0644"
backup: true
- name: Enable periodic upgrade timers
ansible.builtin.copy:
dest: "{{ baseline_unattended_config_dir }}/20auto-upgrades"
owner: root
group: root
mode: "0644"
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
- name: Ensure unattended-upgrades timer is running
ansible.builtin.systemd:
name: unattended-upgrades
state: started
enabled: true

View File

@@ -0,0 +1,51 @@
---
# ============================================================================
# Baseline users
# ============================================================================
# Creates each user defined in baseline_users with their primary group,
# SSH key(s), and optional passwordless sudo.
# ============================================================================
- name: Create primary group for baseline users
ansible.builtin.group:
name: "{{ item.name }}"
state: present
loop: "{{ baseline_users }}"
loop_control:
label: "{{ item.name }}"
- name: Create baseline users
ansible.builtin.user:
name: "{{ item.name }}"
comment: "{{ item.comment | default('') }}"
group: "{{ item.name }}"
groups: "{{ item.groups | default(omit) }}"
append: true
shell: "{{ item.shell | default('/bin/bash') }}"
create_home: true
state: present
loop: "{{ baseline_users }}"
loop_control:
label: "{{ item.name }}"
- name: Deploy SSH authorized keys for baseline users
ansible.posix.authorized_key:
user: "{{ item.0.name }}"
key: "{{ item.1 }}"
state: present
loop: "{{ baseline_users | subelements('ssh_authorized_keys', skip_missing=True) }}"
loop_control:
label: "{{ item.0.name }}"
- name: Grant passwordless sudo to baseline users
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ item.name }}"
content: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL\n"
owner: root
group: root
mode: "0440"
validate: "visudo -cf %s"
loop: "{{ baseline_users }}"
loop_control:
label: "{{ item.name }}"
when: item.sudo_nopasswd | default(false) | bool

View File

@@ -0,0 +1,26 @@
// {{ ansible_managed }}
// mk-labs unattended-upgrades policy
Unattended-Upgrade::Allowed-Origins {
{% if baseline_unattended_security_only %}
"${distro_id}:${distro_codename}-security";
"${distro_id}ESMApps:${distro_codename}-apps-security";
"${distro_id}ESM:${distro_codename}-infra-security";
{% else %}
"${distro_id}:${distro_codename}";
"${distro_id}:${distro_codename}-security";
"${distro_id}:${distro_codename}-updates";
{% endif %}
};
Unattended-Upgrade::Package-Blacklist { };
Unattended-Upgrade::DevRelease "auto";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Remove-Unused-Dependencies "false";
Unattended-Upgrade::Automatic-Reboot "{{ baseline_unattended_auto_reboot | string | lower }}";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";
Unattended-Upgrade::Automatic-Reboot-Time "{{ baseline_unattended_auto_reboot_time }}";
Unattended-Upgrade::SyslogEnable "true";

View File

@@ -0,0 +1,5 @@
# {{ ansible_managed }}
# mk-labs sysctl baseline
{% for key, value in baseline_sysctl.items() %}
{{ key }} = {{ value }}
{% endfor %}

View File

@@ -0,0 +1,28 @@
# {{ ansible_managed }}
# Managed by Ansible - linux-baseline role
# Local edits will be overwritten.
# Primary NTP source: mk-labs internal NTP server (sundial)
server {{ baseline_ntp_server }} iburst prefer
# Fallback: public NTP pool
pool {{ baseline_ntp_fallback_pool }} iburst maxsources 2
driftfile /var/lib/chrony/chrony.drift
# Step the clock if the adjustment is >1s on the first 3 corrections
makestep 1.0 3
# Sync RTC from system clock
rtcsync
# Allow chronyd to use the keyfile for hardware refclock
{% if ansible_os_family == "Debian" %}
keyfile /etc/chrony/chrony.keys
{% else %}
keyfile /etc/chrony.keys
{% endif %}
# Log selective events
logdir /var/log/chrony
log measurements statistics tracking

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
# mk-labs journald retention policy
[Journal]
SystemMaxUse={{ baseline_journald_max_use }}
MaxRetentionSec={{ baseline_journald_max_retention }}
Storage=persistent
Compress=yes
ForwardToSyslog=no

View File

@@ -0,0 +1,11 @@
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ {{ baseline_motd_banner }} - {{ inventory_hostname }}{{ ' ' * (50 - (baseline_motd_banner | length) - (inventory_hostname | length)) }}║
║ ║
║ {{ ansible_distribution }} {{ ansible_distribution_version }} ({{ ansible_kernel }}){{ ' ' * (53 - (ansible_distribution | length) - (ansible_distribution_version | length) - (ansible_kernel | length)) }}║
║ ║
║ Managed by Ansible. Local changes will be overwritten. ║
║ ║
╚═══════════════════════════════════════════════════════════════╝

View File

@@ -0,0 +1,19 @@
# {{ ansible_managed }}
# mk-labs SSH hardening drop-in
# Managed by Ansible - linux-baseline role
PermitRootLogin {{ baseline_ssh_permit_root_login }}
PasswordAuthentication {{ baseline_ssh_password_authentication }}
PubkeyAuthentication {{ baseline_ssh_pubkey_authentication }}
PermitEmptyPasswords {{ baseline_ssh_permit_empty_passwords }}
X11Forwarding {{ baseline_ssh_x11_forwarding }}
ClientAliveInterval {{ baseline_ssh_client_alive_interval }}
ClientAliveCountMax {{ baseline_ssh_client_alive_count_max }}
MaxAuthTries {{ baseline_ssh_max_auth_tries }}
LoginGraceTime {{ baseline_ssh_login_grace_time }}
# Prefer modern key exchange and ciphers
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

View File

@@ -0,0 +1,22 @@
---
# ============================================================================
# Debian-family overrides (Ubuntu, Debian)
# ============================================================================
# Package facts module needs python3-apt on the target.
baseline_packages_family:
- python3-apt
- apt-transport-https
- software-properties-common
- lsb-release
- unattended-upgrades
baseline_sudo_group: sudo
# Service names
baseline_chrony_service: chrony
baseline_chrony_config_path: /etc/chrony/chrony.conf
baseline_sshd_service: ssh
# Unattended-upgrades file locations
baseline_unattended_config_dir: /etc/apt/apt.conf.d

View File

@@ -0,0 +1,19 @@
---
# ============================================================================
# RedHat-family overrides (Rocky, AlmaLinux, RHEL) - placeholder
# ============================================================================
# This file exists to make the OS abstraction visible. It is not exercised
# today because mk-labs is all-Ubuntu. Flesh out when a RHEL-family host
# enters the fleet.
baseline_packages_family:
- dnf-automatic
- python3-dnf
baseline_sudo_group: wheel
baseline_chrony_service: chronyd
baseline_chrony_config_path: /etc/chrony.conf
baseline_sshd_service: sshd
baseline_unattended_config_dir: /etc/dnf/automatic.conf.d