Compare commits

...
21 Commits
Author SHA1 Message Date
Mike Reeves 52885e28c5 Name the reposync-side kernel repo securityonionkernelsync
The reposync section in repodownload.conf and the client repo assigned in
repo/client/oracle.sls both used the bare name securityonionkernel, colliding
across the two roles. Rename the reposync-side section (and its --repoid, the
so-repo-sync guard, and the so-kernel-upgrade presence check) to
securityonionkernelsync, mirroring the existing securityonion/securityonionsync
split for the main repo. The client repo stays securityonionkernel. Also give
the section its own name=Security Onion Kernel Repo repo.
2026-07-09 17:02:47 -04:00
Mike Reeves 9a71f64a35 Branch so-kernel-upgrade on the running kernel flavor
Only the RHCK->UEK flavor cross needs grubby --set-default; a UEK7->UEK8
update stays in the kernel-uek lineage and auto-promotes on its own. Detect
the running kernel and act accordingly:

- UEK8: already on target, no-op.
- UEK7: populate the repo and install UEK8, then verify it auto-promoted
  (warn with the manual grubby command if it did not) -- no grubby change.
- RHCK: install UEK8 and set the boot default explicitly, as before.

Also make an already-installed UEK8 skip the repo entirely so a disabled or
empty kernel repo can't block flipping the default, and correct the header
comment that claimed every transition needs grubby.
2026-07-09 15:10:13 -04:00
Mike Reeves 40c02b3149 Make so-kernel-upgrade populate the kernel repo and fail loudly
Three stages of the UEK8 path fail silently, and the script only handled
the last one:

1. Populate. so-repo-sync runs before the highstate deploys the
   [securityonionkernel] section into repodownload.conf, so the first
   kernel-aware soup skips the kernel sync. kernelrepo_init_empty then
   seeds valid-but-empty repodata, leaving an enabled repo with zero
   packages. dnf resolves it happily and installs nothing, no error.

2. Install. `dnf install kernel-uek` on a UEK7 node sees kernel-uek 5.15
   already installed, prints "Nothing to do" and exits 0 -- so the script
   sailed past the install and died later with a misleading grubby error.

3. Boot. Already handled: grubby only auto-promotes within the running
   kernel's flavor lineage, so 5.x -> 6.x UEK never promotes on its own.

Add ensure_kernel_repo(), which verifies the repo is enabled (necessary
because skip_if_unavailable=1 hides a broken repo) and that it can serve a
6.x kernel-uek. When it cannot, a manager runs so-repo-sync to populate
/nsm/kernelrepo and re-checks; a minion cannot fix it and exits non-zero
pointing the admin at the manager. Airgap managers bail, since their repo
comes from the ISO rather than a sync.

Install the explicit UEK8 NEVRA instead of the bare package name so the
"Nothing to do" exit-0 case cannot mask a no-op, and pin the repoquery to
securityonionkernel so a UEK7 kernel-uek in the main repo is never picked.

Still idempotent and still never reboots.
2026-07-09 14:21:08 -04:00
Mike Reeves 5fd5df54b4 Install UEK8 in so-kernel-upgrade when no UEK kernel is present
The script assumed the UEK8 kernel was already installed and only switched
the boot default to it. On a node running the EL9 stock kernel (RHCK 5.14)
there is no kernel-uek* package at all, so `dnf update` has nothing to
upgrade and UEK8 never lands -- the script just logged "nothing to do" and
exited 0.

When no 6.x UEK boot entry exists, install the kernel-uek metapackage (it
pulls kernel-uek-core plus the module subpackages, including
kernel-uek-modules-extra-netfilter) and then proceed with the grubby
switch. Fail loudly if securityonionkernel is not an enabled repo, since
that assignment is gated on the NIC-pin marker and the salt version match
and a silent no-op there is hard to diagnose.

Also point DEFAULTKERNEL at kernel-uek-core so later kernel updates stay on
the UEK line rather than falling back to RHCK.

Still idempotent and still never reboots.
2026-07-09 13:47:50 -04:00
Mike Reeves 339a5af4a3 Serve /kernelrepo through nginx so minions can reach the kernel repo
The /nsm/kernelrepo bind mount exposed the files, but without a matching
location block external requests to /kernelrepo/ fell through to the SOC
app and returned HTML, so minions hit 'repomd.xml parser error'. Add a
/kernelrepo/ location mirroring /repo/.
2026-06-26 12:02:49 -04:00
Mike Reeves 67a9abadf2 Gate so_kernel_repo on running salt matching the shipped version
During soup the grid is mid-salt-upgrade. Only assign the UEK8 kernel
repo once the node's grains.saltversion matches salt.minion.version from
minion.defaults.yaml, so the kernel repo and the update it enables don't
activate until the node is fully on the target salt.
2026-06-26 09:21:11 -04:00
Mike Reeves 94f31e1356 Add so-kernel-upgrade to switch the boot default to the UEK8 kernel
Installing kernel-uek-core adds a UEK8 (6.x) boot entry but doesn't make
it the default, because grubby only auto-promotes within the running
kernel's flavor lineage and we cross from a 5.x kernel to the new UEK8
flavor. so-kernel-upgrade finds the newest installed 6.x UEK kernel and
grubby --set-default's it (idempotent, verifies the change, no reboot).
2026-06-26 09:21:11 -04:00
Mike Reeves b0b022c3ad Seed an empty /nsm/kernelrepo so the manager repo is always valid
so-repo-sync only populates /nsm/kernelrepo after the highstate, so on a
manager the file:///nsm/kernelrepo repo could be assigned before any
repodata exists, failing every dnf op. Run createrepo on the dir when
repodata/repomd.xml is missing, leaving a synced repo untouched.
2026-06-24 13:23:25 -04:00
Mike Reeves 27c1c35e62 Mark kernel repo skip_if_unavailable so an empty repo can't brick dnf
When the kernel repo is assigned but /nsm/kernelrepo isn't populated
yet, its missing repomd.xml makes every dnf/pkg operation fail (e.g.
pkg.held for salt during highstate). The kernel repo is supplementary,
so set skip_if_unavailable=1 in both the salt-managed client repo and
the four install-time bootstrap repo files; dnf ignores it until it is
populated instead of aborting. The main repo stays strict.
2026-06-24 13:20:10 -04:00
Mike Reeves f45631af3a Guard kernel reposync on its config section existing
During soup, so-repo-sync runs before the highstate deploys the new
repodownload.conf. On the first upgrade to a kernel-aware version the
on-disk config lacks the [securityonionkernel] section, so dnf aborts
with "Unknown repo: 'securityonionkernel'" (set -e kills soup). Guard
the kernel reposync on the section being present; the next sync after
the highstate deploys it picks it up.
2026-06-24 12:15:10 -04:00
Mike Reeves 8e2753aeb8 Fix duplicate securityonionkernel repo definition
The install bootstrap appended the [securityonionkernel] section to the
shared /etc/yum.repos.d/securityonion.repo, but the salt state so_kernel_repo
(name securityonionkernel) manages its own canonical file
/etc/yum.repos.d/securityonionkernel.repo. At highstate both files defined the
same repo id, so dnf failed with "repository securityonionkernel is listed
more than 1 time".

Write the bootstrap kernel repo to /etc/yum.repos.d/securityonionkernel.repo
in all four securityonion_repo() branches so the id lives in exactly one file
and salt edits it in place. Mirrors how the main repo's runtime id matches its
file name.
2026-06-23 13:53:14 -04:00
Mike Reeves 698a746d6d Add UEK8 kernel repo support across install and grid
Mirror the kernel repo to full parity with the main package repo so the
grid can pull the Oracle UEK8 kernel:

- setup/so-functions: securityonion_repo() emits a [securityonionkernel]
  section in every branch (mirrorlist on non-airgap, https://$MSRV/kernelrepo
  for airgap/minion, file:///nsm/kernelrepo/ for manager); repo_sync_local()
  and create_repo() sync and build /nsm/kernelrepo.
- manager/init.sls: create /nsm/kernelrepo and deploy mirror-kernel.txt.
- nginx/enabled.sls: serve /nsm/kernelrepo at https://<repo_host>/kernelrepo.
- repo/client/oracle.sls: add so_kernel_repo, gated by
  onlyif test -e /opt/so/state/nic_names_pinned so the kernel repo is only
  assigned once NICs are pinned by MAC.
- update_packages(): run so-nic-pin before the dnf update that pulls the
  kernel, freezing interface names and dropping the pin marker so the kernel
  isn't downgraded then re-upgraded on the first highstate.
2026-06-23 13:19:56 -04:00
Josh PattersonandGitHub a9f9d8bd0d Merge pull request #15985 from Security-Onion-Solutions/soupmod2
allow manager two full highstates during soup, improve elastic script runtime
2026-06-22 17:02:02 -04:00
Jason ErtelandGitHub 953fdee3af Merge pull request #15984 from Security-Onion-Solutions/jertel/wip
Upgrade registry
2026-06-22 16:56:18 -04:00
Jason Ertel e2e3e690ca reset version 2026-06-22 16:52:29 -04:00
Jason Ertel bcc60a4ae0 kilo version 2026-06-22 13:07:49 -04:00
Jason Ertel b77103aa9f upgrade registry 2026-06-22 13:01:02 -04:00
Jorge ReyesandGitHub 63a2e20698 Merge pull request #15982 from Security-Onion-Solutions/reyesj2/wip
don't create stack trace when set -e is disabled
2026-06-18 15:25:41 -05:00
reyesj2 22d5c96bd5 don't create stack trace when set -e is disabled 2026-06-18 14:56:29 -05:00
Mike ReevesandGitHub 28fdd1eb6f Merge pull request #15970 from Security-Onion-Solutions/udev
Pin NIC names by MAC via udev (run-once) from the common state
2026-06-18 14:28:09 -04:00
Mike Reeves 80c39d612c Pin NIC names by MAC via udev (run-once) from the common state
Add so-nic-pin, which writes by-MAC persistent-net udev rules pinning each
physical NIC to its current name so a kernel upgrade can't renumber the
interfaces Security Onion binds by name (host:mainint, sensor:mainint, bond0).

Gated by the drop file /opt/so/state/nic_names_pinned: run-once on highstate,
and an admin can pre-create the marker to opt out. Wired into common/init.sls
as pin_nic_names, guarded by a matching unless.
2026-06-11 18:40:43 -04:00
13 changed files with 469 additions and 2 deletions
+11
View File
@@ -130,6 +130,17 @@ common_sbin:
- so-pcap-import
{% endif %}
# Pin physical NIC names by MAC (run-once) so a kernel upgrade can't renumber the
# interfaces SO binds by name. The marker keeps it a one-time setup; an admin can
# pre-create the marker to opt out.
pin_nic_names:
cmd.run:
- name: /usr/sbin/so-nic-pin
- unless: 'test -e /opt/so/state/nic_names_pinned'
- require:
- file: common_sbin
- file: statedir
common_sbin_jinja:
file.recurse:
- name: /usr/sbin
+243
View File
@@ -0,0 +1,243 @@
#!/bin/bash
#
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
# https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0.
#
# so-kernel-upgrade — install the UEK8 (6.x) kernel and make it the boot default.
#
# Security Onion is moving off the EL9 stock kernel (RHCK, 5.14) and UEK7 (5.15) onto UEK8
# (6.x). Three things have to happen, and the tool has to drive each one:
#
# 1. Populate. The manager mirrors the UEK8 packages into /nsm/kernelrepo via so-repo-sync,
# and serves them to the grid over https://<manager>/kernelrepo. Until that sync runs the
# repo is valid but EMPTY -- dnf resolves it happily and installs nothing, with no error.
# 2. Install. A node on RHCK has no kernel-uek* package at all, so there is nothing for
# 'dnf update' to upgrade. A node on UEK7 does have kernel-uek installed, so
# 'dnf install kernel-uek' reports "Nothing to do" and exits 0 without installing 6.x.
# Both cases need an explicit install of the UEK8 NEVRA.
# 3. Boot it. Whether a newly installed UEK8 kernel becomes the boot default depends on the
# RUNNING kernel's flavor. kernel-install/grubby (with UPDATEDEFAULT=yes) only auto-promote
# within the running kernel's flavor lineage:
# - From UEK7 (5.x, kernel-uek) the install stays in the kernel-uek lineage and IS
# auto-promoted, so no grubby change is needed -- just make sure the repo is populated
# and install UEK8.
# - From the stock EL9 kernel (RHCK, 5.14, no UEK) it is a flavor CROSS that is NOT
# auto-promoted, so the box keeps booting RHCK until grubby is told otherwise.
# This tool inspects the running kernel and only runs 'grubby --set-default' for RHCK.
#
# Every one of those failure modes is silent by default. This tool handles each case and fails
# loudly when it cannot, rather than reporting success while changing nothing.
#
# Manager vs minion: only the manager owns /nsm/kernelrepo, so only the manager can populate
# it. If the repo is empty here, a manager runs so-repo-sync itself; a minion has no way to
# fix it and exits non-zero telling the admin to sync the manager first.
#
# Idempotent: an already-installed, already-default UEK8 kernel is left alone. It only sets
# the boot default; it does NOT reboot -- the admin reboots the node on their own schedule.
. /usr/sbin/so-common
# Client-side repo id (what dnf enables on this node, from repo/client/oracle.sls) vs the
# reposync-side section in repodownload.conf that the manager mirrors from (mirrors the
# securityonion/securityonionsync split for the main repo).
KERNEL_REPO="securityonionkernel"
KERNEL_REPO_SYNC="securityonionkernelsync"
KERNEL_PKG="kernel-uek"
KERNEL_REPO_DIR="/nsm/kernelrepo"
REPOSYNC_CONF="/opt/so/conf/reposync/repodownload.conf"
GLOBAL_PILLAR="/opt/so/saltstack/local/pillar/global/soc_global.sls"
log() { echo "[so-kernel-upgrade] $*"; }
die() { echo "[so-kernel-upgrade] ERROR: $*" >&2; exit 1; }
command -v grubby >/dev/null 2>&1 || die "grubby not found"
command -v dnf >/dev/null 2>&1 || die "dnf not found"
ARCH="$(rpm -E '%{_arch}')"
is_airgap() {
[ -f "$GLOBAL_PILLAR" ] && grep -q 'airgap: *[Tt]rue' "$GLOBAL_PILLAR"
}
# Newest installed UEK8 (6.x) kernel known to the bootloader. UEK8 vmlinuz paths look like
# /boot/vmlinuz-6.12.0-204.92.4.2.el9uek.x86_64; UEK7 (5.15) and RHCK (5.14) won't match.
find_uek8() {
grubby --info=ALL 2>/dev/null \
| sed -n 's/^kernel="\(.*\)"$/\1/p' \
| grep -E '/vmlinuz-6\.[0-9]+.*uek' \
| sort -V | tail -1
}
# Classify the RUNNING kernel (uname -r) -- this, not what's installed, is what decides whether
# a UEK8 install auto-promotes to the boot default:
# uek8 6.x UEK already on the target line; nothing to do
# uek7 5.x UEK a UEK8 install stays in the kernel-uek lineage and auto-promotes (no grubby)
# rhck 5.14 EL9 crossing into the UEK flavor does NOT auto-promote (needs grubby --set-default)
running_flavor() {
case "$(uname -r)" in
6.*uek*) echo uek8 ;;
*uek*) echo uek7 ;;
*) echo rhck ;;
esac
}
# Newest UEK8 kernel-uek NEVRA offered by the kernel repo, empty if the repo has none.
# Restricted to the kernel repo so a UEK7 kernel-uek in the main repo can't be picked up,
# and filtered to 6.x so we never "succeed" by reinstalling the 5.15 we already have.
uek8_available() {
dnf -q repoquery --disablerepo='*' --enablerepo="$KERNEL_REPO" \
--arch="$ARCH" --latest-limit=1 \
--qf '%{name}-%{evr}.%{arch}\n' "$KERNEL_PKG" 2>/dev/null \
| grep -E "^${KERNEL_PKG}-6\." | tail -1
}
kernelrepo_rpm_count() {
find "$KERNEL_REPO_DIR" -maxdepth 1 -name '*.rpm' 2>/dev/null | wc -l
}
# The kernel repo starts life as valid-but-empty (kernelrepo_init_empty in
# salt/manager/init.sls) and is filled by so-repo-sync. During a soup, so-repo-sync runs
# BEFORE the highstate deploys the [securityonionkernelsync] section into repodownload.conf, so
# the first kernel-aware soup leaves the repo empty until the next nightly sync.
sync_kernel_repo() {
if is_airgap; then
log "airgap install: $KERNEL_REPO_DIR is populated from the airgap ISO, not by so-repo-sync."
return 1
fi
if ! grep -q "^\[${KERNEL_REPO_SYNC}\]" "$REPOSYNC_CONF" 2>/dev/null; then
log "$REPOSYNC_CONF has no [${KERNEL_REPO_SYNC}] section -- run a highstate to deploy it."
return 1
fi
log "populating $KERNEL_REPO_DIR with so-repo-sync (mirrors upstream; can take several minutes)"
su socore -c '/usr/sbin/so-repo-sync' || { log "so-repo-sync failed"; return 1; }
dnf -q clean expire-cache >/dev/null 2>&1
return 0
}
# Make the kernel repo actually able to serve a UEK8 package, or fail trying.
ensure_kernel_repo() {
# The repo is assigned by the repo.client highstate, and only once NICs are pinned by MAC
# (/opt/so/state/nic_names_pinned) so the kernel swap can't renumber interfaces SO binds
# by name. skip_if_unavailable=1 means a broken repo is silently ignored, so check first.
if ! dnf -q repolist --enabled 2>/dev/null | awk '{print $1}' | grep -qx "$KERNEL_REPO"; then
log "repo '$KERNEL_REPO' is not enabled on this node."
log "Run a highstate first; the repo is skipped until /opt/so/state/nic_names_pinned"
log "exists (run so-nic-pin) and this node's salt matches the version this release ships."
die "kernel repo unavailable"
fi
[ -n "$(uek8_available)" ] && return 0
log "repo '$KERNEL_REPO' is enabled but offers no UEK8 $KERNEL_PKG package"
if ! is_manager_node; then
log "This is a minion; it consumes the kernel repo from the manager and cannot populate it."
log "On the manager, run: su socore -c /usr/sbin/so-repo-sync"
log "then re-run this script here."
die "manager's kernel repo is empty"
fi
log "this is a manager and $KERNEL_REPO_DIR holds $(kernelrepo_rpm_count) rpm(s)"
sync_kernel_repo || die "could not populate $KERNEL_REPO_DIR"
[ -n "$(uek8_available)" ] \
|| die "so-repo-sync completed but $KERNEL_REPO still offers no UEK8 $KERNEL_PKG"
}
reboot_notice() {
[ "$(uname -r)" = "$(basename "$1" | sed 's/^vmlinuz-//')" ] \
|| log "REBOOT REQUIRED to start using the UEK8 kernel (currently running $(uname -r))."
}
# Keep future kernel updates on the UEK line rather than falling back to RHCK. Oracle ships
# /etc/sysconfig/kernel; only rewrite it when it's actually pointing somewhere else.
set_default_kernel_conf() {
if [ -f /etc/sysconfig/kernel ] && ! grep -q '^DEFAULTKERNEL=kernel-uek-core$' /etc/sysconfig/kernel; then
log "setting DEFAULTKERNEL=kernel-uek-core in /etc/sysconfig/kernel"
sed -i 's/^DEFAULTKERNEL=.*/DEFAULTKERNEL=kernel-uek-core/' /etc/sysconfig/kernel
fi
}
# Make sure a UEK8 kernel is installed, leaving its boot entry in INSTALLED_UEK8. If one is
# already present we leave the repo alone -- it may be disabled or empty and we don't need it
# just to flip the boot default. Otherwise install the explicit NEVRA, not the bare package
# name: on a UEK7 node 'dnf install kernel-uek' sees 5.15 already present, prints "Nothing to
# do" and exits 0 without installing 6.x.
ensure_uek8_installed() {
INSTALLED_UEK8="$(find_uek8)"
if [ -n "$INSTALLED_UEK8" ]; then
log "UEK8 kernel already installed: $INSTALLED_UEK8"
return 0
fi
ensure_kernel_repo
local nevra; nevra="$(uek8_available)"
log "installing $nevra from $KERNEL_REPO"
dnf -y install "$nevra" || die "failed to install $nevra"
INSTALLED_UEK8="$(find_uek8)"
[ -n "$INSTALLED_UEK8" ] || die "$nevra installed but no 6.x UEK boot entry appeared -- check 'grubby --info=ALL'"
log "installed UEK8 kernel: $INSTALLED_UEK8"
}
case "$(running_flavor)" in
uek8)
# Already on the 6.x UEK line. A plain 'dnf update' keeps this node current within the
# lineage and auto-promotes newer builds, so there is nothing for this tool to do.
log "already running a UEK8 kernel ($(uname -r)); nothing to do."
exit 0
;;
uek7)
# On a 5.x UEK kernel. Installing UEK8 stays inside the kernel-uek lineage, so dnf/grubby
# (UPDATEDEFAULT=yes) auto-promote it and we do NOT touch grubby. A node still on UEK7
# usually means the kernel repo was empty when it last updated, so populate it and install.
log "running UEK7 kernel ($(uname -r)); the kernel repo was likely not yet populated when"
log "this node last updated. Populating it and installing UEK8 -- the update stays on the"
log "kernel-uek line, so it becomes the boot default automatically (no grubby change needed)."
set_default_kernel_conf
ensure_uek8_installed
now="$(grubby --default-kernel 2>/dev/null)"
if [ "$now" = "$INSTALLED_UEK8" ]; then
log "boot default auto-promoted to UEK8 kernel: $INSTALLED_UEK8"
else
log "WARNING: expected the UEK8 kernel to auto-promote but the default is still"
log "'${now:-unknown}'. Run 'grubby --set-default=$INSTALLED_UEK8' to force it."
fi
reboot_notice "$INSTALLED_UEK8"
;;
rhck)
# On the stock EL9 kernel (5.14, no UEK installed). Crossing from RHCK into the UEK flavor
# does NOT auto-promote -- kernel-install/grubby only auto-promote within the running
# kernel's flavor lineage -- so after installing we must set the boot default explicitly.
log "running stock EL9 (RHCK) kernel ($(uname -r)); installing UEK8 and setting it as the"
log "boot default explicitly (a RHCK->UEK flavor change does not auto-promote)."
set_default_kernel_conf
ensure_uek8_installed
target="$INSTALLED_UEK8"
current="$(grubby --default-kernel 2>/dev/null)"
if [ "$current" = "$target" ]; then
log "UEK8 kernel is already the boot default: $target"
reboot_notice "$target"
exit 0
fi
log "current default kernel: ${current:-unknown}"
log "switching boot default to UEK8 kernel: $target"
grubby --set-default="$target" || die "grubby --set-default failed for $target"
# Verify the change actually took before claiming success.
now="$(grubby --default-kernel 2>/dev/null)"
[ "$now" = "$target" ] || die "default kernel is still '${now:-unknown}' after set-default"
log "boot default is now $target"
reboot_notice "$target"
;;
esac
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
#
# so-nic-pin — pin physical NIC names by permanent MAC via classic by-MAC udev
# rules, so a kernel upgrade can't renumber them.
#
# Security Onion binds its management and monitor interfaces BY NAME in pillar
# (host:mainint, sensor:mainint, and bond0 is built on a specific physical NIC).
# A kernel upgrade can change the kernel/systemd-udevd predictable-naming output
# and renumber those NICs (e.g. enp1s0 -> enp2s0), which breaks the grid: the
# pillar references a name that no longer exists and bond/bridge bring-up fails.
#
# This writes /etc/udev/rules.d/70-persistent-net.rules pinning each PHYSICAL NIC
# to its CURRENT name by its PERMANENT MAC, freezing the names across future kernel
# changes. It only writes the rules file; it does NOT live-trigger a rename (the
# rules apply on the next boot/kernel, and a live rename would be disruptive).
#
# Run-once: gated by the drop file /opt/so/state/nic_names_pinned. If the marker is
# present the script does nothing, so an admin can pre-create it to opt out. Invoked
# from the common state on every highstate; the marker keeps it a one-time setup.
NET_RULES_FILE="/etc/udev/rules.d/70-persistent-net.rules"
MARKER="/opt/so/state/nic_names_pinned"
log() { echo -e "[so-nic-pin] $*"; }
# Echo "<name> <permanent-mac>" for every PHYSICAL NIC. A physical NIC is backed by a
# real device (has device/driver), which excludes bond0/sobridge/docker0/veth*/lo whose
# MACs are dynamic and must never be pinned. The PERMANENT MAC is used (ethtool -P, with
# fallbacks), not the current one: an enslaved bond member's current MAC is rewritten to
# the bond's, so matching on it would be wrong/ambiguous.
physical_nics() {
local path n mac
for path in /sys/class/net/*; do
n="${path##*/}"
[ "$n" = "lo" ] && continue
[ -e "${path}/device/driver" ] || continue # real device only
mac="$(ethtool -P "$n" 2>/dev/null | awk '/Permanent address/{print $NF}')"
case "$mac" in ""|00:00:00:00:00:00) mac="$(cat "${path}/bonding_slave/perm_hwaddr" 2>/dev/null)" ;; esac
case "$mac" in ""|00:00:00:00:00:00) mac="$(cat "${path}/address" 2>/dev/null)" ;; esac
case "$mac" in ""|00:00:00:00:00:00) continue ;; esac
echo "$n $mac"
done
}
# Turn "<name> <mac>" lines on stdin into classic by-MAC persistent-net udev rules.
render_net_rules() {
echo "# Generated by so-nic-pin: pin NIC names by MAC so kernel upgrades can't renumber them."
echo "# Security Onion binds its management/monitor interfaces by name; do not hand-edit."
local n mac
while read -r n mac; do
[ -n "$n" ] || continue
printf 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", NAME="%s"\n' \
"$mac" "$n"
done
}
[ "$(id -u)" -eq 0 ] || exit 0 # salt runs us as root; bail quietly otherwise
[ -e "${MARKER}" ] && exit 0 # run-once guard (mirrors the state's unless)
nics="$(physical_nics)"
if [ -z "${nics}" ]; then
log "no physical NICs detected — nothing to pin (will retry on next highstate)"
exit 0 # do NOT drop the marker; let it retry later
fi
log "pinning physical NICs by permanent MAC:"
echo "${nics}" | sed 's/^/ /'
[ -f "${NET_RULES_FILE}" ] && cp -f "${NET_RULES_FILE}" "${NET_RULES_FILE}.bak"
echo "${nics}" | render_net_rules > "${NET_RULES_FILE}" || {
log "ERROR: failed to write ${NET_RULES_FILE}"
exit 1
}
mkdir -p "$(dirname "${MARKER}")" && touch "${MARKER}"
log "wrote ${NET_RULES_FILE} ($(grep -c '^SUBSYSTEM' "${NET_RULES_FILE}") NIC(s) pinned); dropped ${MARKER}"
+2
View File
@@ -0,0 +1,2 @@
https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8
https://repo-alt.securityonion.net/prod/3/oracle/9-uek8
+5
View File
@@ -11,3 +11,8 @@ name=Security Onion Repo repo
mirrorlist=file:///opt/so/conf/reposync/mirror.txt
enabled=1
gpgcheck=1
[securityonionkernelsync]
name=Security Onion Kernel Repo repo
mirrorlist=file:///opt/so/conf/reposync/mirror-kernel.txt
enabled=1
gpgcheck=1
+29
View File
@@ -86,6 +86,28 @@ repo_dir:
- group
- show_changes: False
kernelrepo_dir:
file.directory:
- name: /nsm/kernelrepo
- user: socore
- group: socore
- recurse:
- user
- group
- show_changes: False
# Ensure /nsm/kernelrepo is always a valid (if empty) repo before it is ever assigned to
# a client. Without repodata/repomd.xml an enabled file:///nsm/kernelrepo repo makes every
# dnf operation fail; so-repo-sync only populates it after the highstate, so seed an empty
# repo here. Only runs when repodata is missing, so it won't clobber a synced repo.
kernelrepo_init_empty:
cmd.run:
- name: createrepo /nsm/kernelrepo
- unless: 'test -e /nsm/kernelrepo/repodata/repomd.xml'
- require:
- file: kernelrepo_dir
- pkg: install_createrepo
manager_sbin:
file.recurse:
- name: /usr/sbin
@@ -122,6 +144,13 @@ so-repo-mirrorlist:
- user: socore
- group: socore
so-repo-kernel-mirrorlist:
file.managed:
- name: /opt/so/conf/reposync/mirror-kernel.txt
- source: salt://manager/files/mirror-kernel.txt
- user: socore
- group: socore
so-repo-sync:
{% if MANAGERMERGED.reposync.enabled %}
cron.present:
+11
View File
@@ -10,5 +10,16 @@ NOROOT=1
set -e
curl --retry 5 --retry-delay 60 -A "reposync/$(sync_options)" https://sigs.securityonion.net/checkup --output /tmp/checkup
dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/
createrepo /nsm/repo
# The kernel repo section is deployed to repodownload.conf by the manager highstate, which
# runs AFTER this script during soup. On the first upgrade to a kernel-aware version the
# on-disk config still predates the section, so guard on its presence to avoid dnf's
# "Unknown repo: 'securityonionkernelsync'" aborting the sync (set -e). The next sync after the
# highstate deploys the section will pick it up.
if grep -q '^\[securityonionkernelsync\]' /opt/so/conf/reposync/repodownload.conf; then
dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionkernelsync --download-metadata -p /nsm/kernelrepo/
createrepo /nsm/kernelrepo
fi
+2
View File
@@ -131,6 +131,8 @@ check_err() {
# Collect bash error context before passing off to check_err()
on_err() {
local exit_code=$?
# Ignore failures in blocks that explicitly disabled errexit with `set +e`.
[[ $- == *e* ]] || return $exit_code
# turn off xtrace to prevent added noise in debug log
set +x 2>/dev/null || true
+1
View File
@@ -59,6 +59,7 @@ so-nginx:
- /opt/so/conf/navigator/layers/:/opt/socore/html/navigator/assets/so:ro
- /opt/so/conf/navigator/config.json:/opt/socore/html/navigator/assets/config.json:ro
- /nsm/repo:/opt/socore/html/repo:ro
- /nsm/kernelrepo:/opt/socore/html/kernelrepo:ro
- /nsm/rules:/nsm/rules:ro
{% if NGINXMERGED.external_suricata %}
- /opt/so/rules/nids/suri:/surirules:ro
+10
View File
@@ -323,6 +323,16 @@ http {
autoindex_localtime on;
}
location /kernelrepo/ {
allow all;
sendfile on;
sendfile_max_chunk 1m;
autoindex on;
autoindex_exact_size off;
autoindex_format html;
autoindex_localtime on;
}
location /influxdb/ {
auth_request /auth/sessions/whoami;
rewrite /influxdb/api/(.*) /api/$1 break;
+1 -1
View File
@@ -16,7 +16,7 @@ include:
# Install the registry container
so-dockerregistry:
docker_container.running:
- image: ghcr.io/security-onion-solutions/registry:3.0.0
- image: ghcr.io/security-onion-solutions/registry:3.1.1
- hostname: so-registry
- networks:
- sobridge:
+30
View File
@@ -6,6 +6,10 @@
{% from 'repo/client/map.jinja' import REPOPATH with context %}
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% import_yaml 'salt/minion.defaults.yaml' as saltversion %}
{% set saltversion = saltversion.salt.minion.version %}
{% set INSTALLEDSALTVERSION = grains.saltversion %}
{% set role = grains.id.split('_') | last %}
{% set MANAGER = salt['grains.get']('master') %}
{% if grains['os'] == 'OEL' %}
@@ -57,6 +61,32 @@ so_repo:
- enabled: 1
- gpgcheck: 1
# Only assign the kernel repo once this node's running salt matches the version this
# SO release ships. During a soup the grid is mid-salt-upgrade; gating here keeps the
# UEK8 kernel repo (and the kernel update it enables) from activating until the node is
# fully on the target salt, the same way other states defer across the upgrade window.
{% if saltversion | string == INSTALLEDSALTVERSION | string %}
so_kernel_repo:
pkgrepo.managed:
- name: securityonionkernel
- humanname: Security Onion Kernel Repo
{% if GLOBALS.is_manager %}
- baseurl: file:///nsm/kernelrepo/
{% else %}
- baseurl: https://{{ GLOBALS.repo_host }}/kernelrepo
{% endif %}
- enabled: 1
- gpgcheck: 1
# Supplementary kernel repo: tolerate it being empty/unreachable (e.g. before the
# manager has populated /nsm/kernelrepo) so a missing repomd.xml can't make every
# dnf/pkg operation on the grid fail.
- skip_if_unavailable: 1
# Only assign the kernel repo once physical NIC names are pinned by MAC, so the
# UEK8 kernel update can't renumber interfaces SO binds by name (see pin_nic_names
# in salt/common/init.sls, which drops this marker via /usr/sbin/so-nic-pin).
- onlyif: 'test -e /opt/so/state/nic_names_pinned'
{% endif %}
{% endif %}
# TODO: Add a pillar entry for custom repos
+47
View File
@@ -886,6 +886,7 @@ create_repo() {
title "Create the repo directory"
logCmd "dnf -y install yum-utils createrepo_c"
logCmd "createrepo /nsm/repo"
logCmd "createrepo /nsm/kernelrepo"
}
@@ -1812,6 +1813,16 @@ securityonion_repo() {
echo "mirrorlist=file:///etc/yum/mirror.txt" >> /etc/yum.repos.d/securityonion.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8" > /etc/yum/mirror-kernel.txt
echo "https://so-repo-east.s3.us-east-005.backblazeb2.com/prod/3/oracle/9-uek8" >> /etc/yum/mirror-kernel.txt
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "mirrorlist=file:///etc/yum/mirror-kernel.txt" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
# Supplementary kernel repo: tolerate it being empty/unreachable so a missing
# repomd.xml can't make every dnf operation fail before the repo is populated.
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
logCmd "dnf repolist"
else
echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo
@@ -1820,6 +1831,13 @@ securityonion_repo() {
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=https://$MSRV/kernelrepo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
logCmd "dnf repolist"
fi
elif [[ ! $waitforstate ]]; then
@@ -1829,12 +1847,25 @@ securityonion_repo() {
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=https://$MSRV/kernelrepo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "sslverify=0" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
elif [[ $waitforstate ]]; then
echo "[securityonion]" > /etc/yum.repos.d/securityonion.repo
echo "name=Security Onion Repo" >> /etc/yum.repos.d/securityonion.repo
echo "baseurl=file:///nsm/repo/" >> /etc/yum.repos.d/securityonion.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonion.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonion.repo
echo "[securityonionkernel]" > /etc/yum.repos.d/securityonionkernel.repo
echo "name=Security Onion Kernel Repo" >> /etc/yum.repos.d/securityonionkernel.repo
echo "baseurl=file:///nsm/kernelrepo/" >> /etc/yum.repos.d/securityonionkernel.repo
echo "enabled=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "gpgcheck=1" >> /etc/yum.repos.d/securityonionkernel.repo
echo "skip_if_unavailable=1" >> /etc/yum.repos.d/securityonionkernel.repo
fi
logCmd "dnf repolist all"
if [[ $waitforstate ]]; then
@@ -1850,9 +1881,12 @@ repo_sync_local() {
# Sync the repo from the SO repo locally.
info "Adding Repo Download Configuration"
mkdir -p /nsm/repo
mkdir -p /nsm/kernelrepo
mkdir -p /opt/so/conf/reposync/cache
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9" > /opt/so/conf/reposync/mirror.txt
echo "https://repo-alt.securityonion.net/prod/3/oracle/9" >> /opt/so/conf/reposync/mirror.txt
echo "https://repo.securityonion.net/file/so-repo/prod/3/oracle/9-uek8" > /opt/so/conf/reposync/mirror-kernel.txt
echo "https://repo-alt.securityonion.net/prod/3/oracle/9-uek8" >> /opt/so/conf/reposync/mirror-kernel.txt
echo "[main]" > /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
echo "installonly_limit=3" >> /opt/so/conf/reposync/repodownload.conf
@@ -1866,12 +1900,18 @@ repo_sync_local() {
echo "mirrorlist=file:///opt/so/conf/reposync/mirror.txt" >> /opt/so/conf/reposync/repodownload.conf
echo "enabled=1" >> /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
echo "[securityonionkernel]" >> /opt/so/conf/reposync/repodownload.conf
echo "name=Security Onion Kernel Repo repo" >> /opt/so/conf/reposync/repodownload.conf
echo "mirrorlist=file:///opt/so/conf/reposync/mirror-kernel.txt" >> /opt/so/conf/reposync/repodownload.conf
echo "enabled=1" >> /opt/so/conf/reposync/repodownload.conf
echo "gpgcheck=1" >> /opt/so/conf/reposync/repodownload.conf
logCmd "dnf repolist"
if [[ ! $is_airgap ]]; then
curl --retry 5 --retry-delay 60 -A "netinstall/$SOVERSION/$OS/$(uname -r)/1" https://sigs.securityonion.net/checkup --output /tmp/install
retry 5 60 "dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/" >> "$setup_log" 2>&1 || fail_setup
retry 5 60 "dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionkernel --download-metadata -p /nsm/kernelrepo/" >> "$setup_log" 2>&1 || fail_setup
# After the download is complete run createrepo
create_repo
fi
@@ -2228,6 +2268,13 @@ update_sudoers_for_testing() {
}
update_packages() {
# Pin physical NIC names by MAC BEFORE pulling packages, so the UEK8 kernel that
# the update below installs can't renumber the interfaces SO binds by name. Doing
# it here (instead of waiting for the common highstate) also drops the
# /opt/so/state/nic_names_pinned marker that gates the kernel repo, so the kernel
# repo is assigned on the very first highstate and the kernel isn't downgraded and
# then re-upgraded. Run-once: so-nic-pin no-ops if the marker already exists.
logCmd "bash ../salt/common/tools/sbin/so-nic-pin"
logCmd "dnf repolist"
logCmd "dnf -y update --allowerasing --exclude=salt*,docker*,containerd*"
RMREPOFILES=("oracle-linux-ol9.repo" "uek-ol9.repo" "virt-ol9.repo")