Building a FIPS-Compliant, Signed RPM for FlexLM on RHEL9

Discs

This guide walks through packaging a FlexLM license manager as an RPM on RHEL9, generating a FIPS-compliant GPG key to sign it, distributing the public key for verification, and wiring up a systemd service so the license daemon can start automatically.

Prerequisites

  • A RHEL9 build host (does not need to be the same host the RPM will be instaled on) that is in FIPS mode
  • Root or sudo access
  • The FlexLM vendor daemon and lmgrd binaries

1. Install RPM Build Dependencies

sudo dnf install rpm-build rpmdevtools rpmlint gnupg2 systemd-rpm-macros

After installing the required development tools, create a standard build tree in your home directory to work out of.

rpmdev-setuptree

This creates ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

2. Generate a FIPS-Compliant GPG Signing Key

RHEL9’s FIPS mode restricts you to FIPS-approved primitives. For GPG, that means RSA keys and SHA-256 or stronger for digests.

Confirm FIPS status first:

fips-mode-setup --check
# This should return Enabled. Enable FIPS if not enabled.

Set your GPG preferences to enforce FIPS-approved algorithms before key generation. Create or edit ~/.gnupg/gpg.conf

mkdir -p ~/.gnupg && chmod 700 ~/.gnupg
cat >> ~/.gnupg/gpg.conf << 'EOF'
personal-digest-preferences SHA256
cert-digest-algo SHA256
default-preference-list SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed
EOF

Now generate the key

gpg --full-generate-key

When prompted, chose the following:

  • Key Type: RSA And RSA (Default)
  • Key Size: 4096
  • Expiration: 1-2 years is common
  • Name/Email/Comment: Use identity tied to the packaging team
  • Passphrase: required, store it in a secrets manager for build credentials.

Verify the key was created with an approved algorithm and digest:

gpg --list-keys --with-colors | grep pub
gpg --edit-key "FlexLM Package Signing" showpref quit

You will want to see RSA4096 and SHA256/SHA512 in the output, not DSA or SHA-1.

3. Export and Distribute the Public Key

Export the public key so consuming systems can verify the RPM’s signature:

gpg --export -a "FlexLM Package Signing" > RPM-GPG-KEY-flexlm-packaging

On any system that will install the RPM, import the public key into the RPM database so rpm/dnf can validate the signature:

rpm --import RPM-GPG-KEY-flexlm-packaging

4. Configure rpmbuild to Sign With Your Key

Edit ~/.rpmmacros to add the following:

cat >> ~/.rpmmacros << EOF
%_signature gpg
%_gpg_name FlexLM Package Signing
%_gpg_path %{getenv:HOME}/.gnupg
%_gpgbin /usr/bin/gpg2
%__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor --digest-algo sha256 --pinentry-mode loopback --passphrase-fd 3 --no-secmem-warning -u "%{_gpg_name}" -sbo %{__signature_filename} %{__plaintext_filename}
EOF

The –digest-algo sha256 flag is the important part here because it forces the signature digest to a FIPS-approved algorithm redardless of the GPG’s defaults on the build host.

5. Stage the FlexLM Files

Lay out the source tree that the RPM package will need. A typical install looks like this:

mkdir -p ~/rpmbuild/SOURCES/flexlm-1.0/opt/flexlm/{bin,licenses,logs}

Copy in the vendor-supplied binaries and any supporting files.

cp lmgrd ~/rpmbuild/SOURCES/flexlm-1.0/opt/flexlm/bin/
cp vendor_daemon ~/rpmbuild/SOURCES/flexlm-1.0/opt/flexlm/bin/
chmod 755 ~/rpmbuild/SOURCES/flexlm-1.0/opt/flexlm/bin/*

Create the systemd unit file in the same staging tree so it gets packaged as source:

mkdir -p ~/rpmbuild/SOURCES/flexlm-1.0/systemd
cat > ~/rpmbuild/SOURCES/flexlm-1.0/systemd/flexlm.service << 'EOF'
[Unit]
Description=FlexLM License Manager
After=network.target

[Service]
Type=forking
ExecStart=/opt/flexlm/bin/lmgrd -c /opt/flexlm/licenses/license.dat -l /opt/flexlm/logs/lmgrd.log
ExecStop=/opt/flexlm/bin/lmutil lmdown -c /opt/flexlm/licenses/license.dat -q
Restart=on-failure
User=flexlm
Group=flexlm

[Install]
WantedBy=multi-user.target
EOF

Be sure to adjust the user and group fields to what service account will be used (do not run as root).

Tar it up so it matches what the spec file’s %prep stage expects:

cd ~/rpmbuild/SOURCES
tar czf flexlm-1.0.tar.gz flexlm-1.0

6. Write the Spec File

Create ~/rpmbuild/SPECS/flexlm.spec

Name:           flexlm
Version:        1.0
Release:        1%{?dist}
Summary:        FlexLM License Manager

License:        Proprietary
URL:            https://internal.example.com/packaging/flexlm
Source0:        flexlm-1.0.tar.gz

BuildRequires:  systemd-rpm-macros
Requires:       systemd
Requires:       glibc
Requires(pre):  shadow-utils
%{?systemd_requires}

%description
Packages the FlexLM license manager (lmgrd and vendor daemon) along with
a systemd unit for automatic startup and management.

%prep
%setup -q

%install
mkdir -p %{buildroot}/opt/flexlm/{bin,licenses,logs}
mkdir -p %{buildroot}%{_unitdir}
cp -p opt/flexlm/bin/* %{buildroot}/opt/flexlm/bin/
cp -p opt/flexlm/licenses/* %{buildroot}/opt/flexlm/licenses/
install -m 644 systemd/flexlm.service %{buildroot}%{_unitdir}/flexlm.service

%pre
getent group flexlm >/dev/null || groupadd -r flexlm
getent passwd flexlm >/dev/null || useradd -r -g flexlm -d /opt/flexlm -s /sbin/nologin flexlm

%post
%systemd_post flexlm.service

%preun
%systemd_preun flexlm.service

%postun
%systemd_postun_with_restart flexlm.service

%files
%attr(755, flexlm, flexlm) /opt/flexlm/bin/*
%attr(644, flexlm, flexlm) /opt/flexlm/licenses/*
%dir %attr(755, flexlm, flexlm) /opt/flexlm/logs
%{_unitdir}/flexlm.service

%changelog
* Mon Aug 17 2026 Packaging Team <[email protected]> - 1.0-1
- Initial FlexLM RPM with systemd unit and FIPS-signed package

A few notes on this spec:

  • %{?systemd_requires} and the %systemd_post/%systemd_preun/%systemd_postun_with_restart macros handle enabling, disabling, and restarting the unit correctly across install, upgrade, and removal, you don’t need to call systemctl by hand in the scriptlets
  • %pre creates a dedicated system account so lmgrd never runs as root
  • Adjust Requires: to match whatever runtime libraries your specific vendor daemon links against, check with ldd vendor_daemon on the build host if you’re not sure

7. Build the RPM

cd ~/rpmbuild/SPECS
rpmbuild -ba flexlm.spec

This produces both the binary RPM and SRPM under ~/rpmbuild/RPMS/x86_64/ and ~/rpmbuild/SRPMS/

Lint it before signing:

rpmlint ~/rpmbuild/RPMS/x86_64/flexlm-1.0-1.*.rpm

8. Sign the RPM

rpm --addsign ~/rpmbuild/RPMS/x86_64/flexlm-1.0-1.*.rpm

You’ll be prompted for the key’s passphrase. Since the ~/.rpmmacros config from Step 4 was already established, the resulting signature is FIPS-compliant.

9. Verify the Signature

On the build host or any system that has the imported key:

rpm -K ~/rpmbuild/RPMS/x86_64/flexlm-1.0-1.*.rpm

You should see something like rsa sha256 (md5) pgp md5 OK or similar, confirming both the digest and signature check pass. If the importing system hasn’t run rpm –import on your public key, this will report the signature as unknown.

10. Install and Enable the Service

sudo dnf install ~/rpmbuild/RPMS/x86_64/flexlm-1.0-1.*.rpm
sudo systemctl enable --now flexlm.service
sudo systemctl status flexlm.service

At this point, FlexLM is packaged, signed with a FIPS-approved key and digest, installable through your normal RPM tooling, and managed by systemd like any other service on the box, including automatic start on boot if you leave it enabled.

Closing Remarks

It’s fair to ask why you would go through all of this instead of just dropping the vendor binaries on a box and running lmgrd by hand. The short answer is that wrapping FlexLM in an RPM turns a one-off manual install into something that’s repeatable, auditable, and manageable at scale.

  • Standardization. Every host gets FlexLM installed the same way, in the same location, owned by the same service account, with the same permissions. No drift between how one admin set it up versus another.
  • Simplified updates and rollbacks. A new vendor daemon or license file becomes a version bump and a dnf update, not a manual copy-and-hope. rpm -q gives you an instant answer to what version is running where, and downgrading is just as clean if a new release causes problems.
  • Consistent lifecycle management. The %pre, %post, %preun, and %postun scriptlets mean the service account, systemd unit, and file permissions get created, enabled, and cleaned up the same way every single time, whether it’s install, upgrade, or reomval.
  • Signature Verification. Because the package is signed, anyone installing it can verify it came from your packaging process untampered.
  • Easier compliance story. For FIPS and STIG-compliant environments, having a signed package with a documented build process is much easier to point to during an audit than “someone SCP’d the binaries over and ran a script”.
Prev