---
# System prepended metadata

title: 'Install, configure, manage and run Minion as non-root'

---

# Install, configure, manage and run Minion as non-root

This guide was designed for Meridian 2018 but should work on Horizon 22 or newer versions of OpenNMS. I'm assuming the operating system will be CentOS/RHEL 7, but all the commands should work in 8.

Any `yum` command will internally use `dnf` if you're using CentOS/RHEL 8.

## Installation

The installation process requires privileges. For this reason, the administrator must execute all the following steps as `root` or through `sudo`.

### User for Minion

Create the `minion` user before installing the OpenNMS RPMs (ensure that `/opt/minion` doesn't exist). If the RPMs are already installed (meaning the `/opt/minion` directory exists and it is populated), fix the `minion` user to work as a regular user.

The following code section illustrates the conditional behavior based on the existence of `/opt/minion`:

```bash=
USER_EXISTS=$(id -u minion > /dev/null 2>&1; echo $?)
if [ "$USER_EXISTS" == "1" ]; then
  if [ ! -d /opt/minion ]; then
    sudo useradd -d /opt/minion -m -c "OpenNMS Minion" minion
    sudo chmod 775 /opt/minion
  else
    sudo usermod -s /bin/bash minion
    sudo cp /etc/skel/.bash* /opt/minion/
    sudo chown minion:minion /opt/minion/.bash*
  fi
fi
```

Set the password for the `minion` user to let operators and administrator access the Minion server via SSH through that user and control the Minion application:

```bash=
MINION_PASSWORD="m1n10n;"
echo $MINION_PASSWORD | sudo passwd --stdin minion 
```

### Systemctl

Enable `sudo` access to execute `systemctl` and `journalctl` against the `minion` service for the `minion` user.

```bash=
sudo visudo -f /etc/sudoers.d/opennms
```

Inside the editor, add the following:

```=
Cmnd_Alias SYSTEMD = /usr/bin/journalctl, /usr/bin/systemctl start minion, /usr/bin/systemctl restart minion, /usr/bin/systemctl stop minion, /bin/systemctl status minion
minion ALL = NOPASSWD: SYSTEMD
```

Save the changes.

:::info
If operators are more familiar with the `service` command, update `Cmnd_Alias` to include those commands as well; for example `service start minion`.
:::

### Java

For Meridian 2018 and older, or Horizon 23 and older, it is recommended to use Oracle JDK 8 in production, even if it could work with OpenJDK. For Horizon 24 or newer and Meridian 2019 or newer, OpenJDK 11 is recommended.

The usual way to do this is by manually downloading the RPM from `java.oracle.com` (the latest version is 8u211 by the time this document was written). Unfortunately, this process cannot be automated as it is required to log in using your Oracle account to download Java 8 after the licensing change.

For Horizon 24+/Meridian2019+:

```bash=
if ! rpm -qa | grep -q java-11-openjdk-devel; then
  echo "Installing latest OpenJDK 11..."
  sudo yum install -y -q java-11-openjdk-devel
fi
```

### Network Privileges for ICMP

There are two valid methodologies to allow non-root users to send ICMP requests.

The first solution works on every Linux distribution, whereas the second one requires a fairly recent kernel.

#### Using netcap

:::info
The following procedure works regardless of the Kernel version used on your Linux Distribution, and it is the reason why the `ping` command works for unprivileged users.
:::

Update the network raw packets settings for the Java binary to execute ICMP requests as non-root.

For Oracle JDK:

```bash=
sudo setcap cap_net_raw+ep /usr/java/latest/bin/java
echo /usr/java/latest/lib/amd64/jli | sudo tee /etc/ld.so.conf.d/java-latest.conf
sudo ldconfig
```

For OpenJDK 8:

```bash=
sudo setcap cap_net_raw+ep /usr/lib/jvm/java-1.8.0/bin/java
echo  /usr/lib/jvm/java-1.8.0/lib/amd64/jli | sudo tee /etc/ld.so.conf.d/java-latest.conf
sudo ldconfig
```

For OpenJDK 11:

```bash=
sudo setcap cap_net_raw+ep  /usr/lib/jvm/java-11/bin/java
echo  /usr/lib/jvm/java-11/lib/jli | sudo tee /etc/ld.so.conf.d/java-latest.conf
sudo ldconfig
```

For more information check the documentation about [CAP_NET_RAW](http://man7.org/linux/man-pages/man7/capabilities.7.html).

:::warning
Please keep in mind that the `setcap` command has to be re-executed every time the JDK is upgraded.
:::

#### Using sysctl

:::warning
Using this methodology requires a fairly recent Kernel. For instance, this will work on CentOS/RHEL 8 (and modern versions of any Debian based distribution like Ubuntu). However, in CentOS/RHEL 7 it would work only for IPv4, as support for IPv6 was added in Kernel 3.11.
:::

```bash=
FILE="/etc/sysctl.d/99-zzz-non-root-icmp.conf"
echo "net.ipv4.ping_group_range=0 429496729" | sudo tee $FILE
sudo sysctl -p $FILE
```

About CentOS 7 or older. If you attempt to use this, you'll see errors like the following in the logs:

```=
2021-03-13 12:54:04,596 ERROR [Main] o.o.n.i.j.Jni6Pinger: Permission error received while attempting to open ICMP socket. See https://wiki.opennms.org/wiki/ICMP for information on configuring ICMP for non-root.
```

However, RRD/JDB files for ICMP response times seem to be updated, and ICMP outages are reported; but only for IPv4 interfaces.

This option is more strict than using `setcap`, and if you reduce the scope even more, reduce the group range to cover only the UID of the `opennms` user, as the above enables ICMP for all users in the system.

### Install Minion

Follow the usual procedure to install RPMs on CentOS/RHEL.

For Horizon:

```bash=
sudo yum install -y -q http://yum.opennms.org/repofiles/opennms-repo-stable-rhel7.noarch.rpm
sudo yum install -y -q jicmp jicmp6
sudo yum install -y -q opennms-minion
```

For Meridian 2018 and newer:

```bash=
MERIDIAN_USER="your_username_here"
MERIDIAN_PASSWORD="your_password_here"
MERIDIAN_REPO="2018/stable"

cat <<EOF | sudo tee /etc/yum.repos.d/meridian.repo
[meridian]
name=OpenNMS for Red Hat Enterprise Linux and CentOS 7
baseurl="https://$MERIDIAN_USER:$MERIDIAN_PASSWORD@meridian.opennms.com/packages/$MERIDIAN_REPO/rhel7"
enabled=1
gpgcheck=1
sslverify=false
gpgkey=http://yum.opennms.org/OPENNMS-GPG-KEY
EOF
sudo yum install -y -q jicmp jicmp6
sudo yum install -y -q meridian-minion
```

#### Move the `init` script

```bash=
sudo mv /etc/init.d/minion /opt/minion/bin/
```

:::warning
Due to a [known issue](https://issues.opennms.org/browse/LTS-231), a fixed version of `/etc/init.d/minion` is required when running Meridian 2018.1.5 or older. If this is the case, make sure to grab a copy of this script from the [Horizon RPM](http://yum.opennms.org/stable/common/opennms/opennms-minion-container-23.0.3-1.noarch.rpm). This is not required with latest version.
:::

#### Create a Systemd unit for Minion

The following is optional but recommended:

```bash=
cat <<EOF | sudo tee /etc/systemd/system/minion.service
[Unit]
Description=OpenNMS Minion server
Requires=network.target network-online.target
After=network.target network-online.target

[Service]
User=minion
LimitNOFILE=204800
LimitNPROC=63409

Type=forking
PIDFile=/opt/minion/karaf.pid

ExecStart=/opt/minion/bin/minion start
ExecStop=/opt/minion/bin/minion stop

[Install]
WantedBy=multi-user.target
EOF
sudo chown minion:minion /etc/systemd/system/minion.service
sudo systemctl daemon-reload
sudo systemctl enable minion
```

:::info
At this point, a user can login with the `minion` account, and should be able to manage the `minion` application through `systemctl`. All the content of `/opt/minion` is owned by the `minion` user, which makes the whole application fully configurable.
:::

### JVM Settings

Update `/etc/sysconfig/minion` for standard tuning and enable non-root nature.

```bash=
total_mem_in_mb=$(free -m | awk '/:/ {print $2;exit}')
mem_in_mb=$(expr $total_mem_in_mb / 2)
if [ "$mem_in_mb" -gt "8192" ]; then
mem_in_mb="8192"
fi
half_mem_in_mb=$(expr $mem_in_mb / 2)
java_opts="-XX:+UseG1GC"
sysconfig=/etc/sysconfig/minion
sudo sed -r -i "/export JAVA_MAX_MIM/s/^# //" $sysconfig
sudo sed -i -r "/export JAVA_MAX_MIM/s/=.*/=${half_mem_in_mb}M/" $sysconfig
sudo sed -r -i "/export JAVA_MAX_MEM/s/^# //" $sysconfig
sudo sed -i -r "/export JAVA_MAX_MEM/s/=.*/=${mem_in_mb}M/" $sysconfig
sudo sed -r -i "/export RUNAS/s/^# //" $sysconfig
sudo sed -i -r "/export RUNAS/s/=.*/=minion/" $sysconfig
sudo sed -r -i "/export JAVA_OPTS/s/^# //" $sysconfig
sudo sed -i -r "/export JAVA_OPTS/s/=.*/=\"$java_opts\"/" $sysconfig
```

:::info
The last entry is to avoid checking ICMP bits at kernel level, which won't work on CentOS/RHEL 7. The `setcap` on tha `java` binary is enough to perform ICMP requests through a non-root user on OS with old kernels.
:::

The following applies only when using Meridian 2018.1.5 or older:

```bash=
cat <<EOF | sudo tee -a /etc/sysconfig/minion
export PING_REQUIRED=FALSE
EOF
```

### Credentials

Configure the credentials to access the OpenNMS ReST API and the ActiveMQ broker (if apply).

By default, Minion will use the `admin` user with the `admin` password. The idea is create a user in OpenNMS called `minion`, and assign the `ROLE_MINION` to it. Then, update the credentials:

```bash=
MINION_USER="minion"
MINION_PASSWD="minion"
sudo /opt/minion/bin/scvcli set opennms.http $MINION_USER $MINION_PASSWD
sudo /opt/minion/bin/scvcli set opennms.broker $MINION_USER $MINION_PASSWD
```

> **WARNING**: `MINION_USER` and `MINION_PASSWD` must match the user created inside `users.xml` in the OpenNMS server.

### Permissions

If Minion was previously updated, fix the permissions

```bash=
sudo chown -R minion:minion /opt/minion
```

## Configuration

:::danger
The following content is not related to the non-root nature.
:::

:::warning
The following contains some general advice about how to configure Minion assuming that Kafka will be used for Sink. On Horizon 23 or newer (and for Meridian 2019+), Kafka is possible for RPC. For older versions, ActiveMQ should be used for RPC, which is what is outlined below as this guide was designed for Meridian 2018.
:::

This guide assumes that a centralized Kafka cluster will be shared across multiple OpenNMS instances, and will be used for the Sink API and for the Kafka Producer feature. On recent versions, as mentioned, it can also be used for the RPC API. because of this, the "Instance ID" must be used not only for Kafka but also for ActiveMQ if applies.

:::info
Make sure that OpenNMS has been configured as described [here](https://hackmd.io/0hICulQpSZm0J6OmnzfMvA?view#Configuration).
:::

1. Make sure that the Instance ID is properly defined on the Minions associated with the backend configured on step 1. Append the following content to `/opt/minion/etc/system.properties`:

```=
org.opennms.instance.id=XXX
```

On the above example, `XXX` is the chosen name for the instance ID, and that will be the prefix used on *all* the Topic/Queue names in Kafka and ActiveMQ.

:::info
It has to be be same content used for OpenNMS.
:::

2. Make sure to enable the Kafka Sink API for the Minions. Create a file called `/opt/minion/etc/featuresBoot.d/kafka.boot` with the following content:

```=
!opennms-core-ipc-sink-camel
opennms-core-ipc-sink-kafka
```

And configure the Kafka broker on `/opt/minion/etc/org.opennms.core.ipc.sink.kafka.cfg`, the content should look like this:

```=
bootstrap.servers=kafka1:9092
```

:::info
Use the same broker list used for OpenNMS. For more information, check [here](https://hackmd.io/0hICulQpSZm0J6OmnzfMvA).
:::

:::warning
Remember to change `kafka1` with the IP or FQDN of one of the Kafka broker. Alternatively, a list of brokers is also accepted; for instance, `kafka1:9092,kafka2:9092`.
:::

3. **For M2019+**. Enable the Kafka RPC API for the Minions.

Create a file called `/opt/minion/etc/featuresBoot.d/kafka.boot` with the following content:

```=
!opennms-core-ipc-rpc-camel
opennms-core-ipc-rpc-kafka
```

And the bootstrap servers to `/opt/minion/etc/org.opennms.core.ipc.rpc.kafka.cfg`. The content should look like this:

```=
bootstrap.servers=kafka1:9092
```

:::info
Use the same broker list used for OpenNMS. For more information, check [here](https://hackmd.io/0hICulQpSZm0J6OmnzfMvA).
:::

:::warning
Remember to change `kafka1` with the IP or FQDN of one of the Kafka broker. Alternatively, a list of brokers is also accepted; for instance, `kafka1:9092,kafka2:9092`.
:::

5. **For M2020+**: Enable the single-topic feature for RPC. Add the following content to `/opt/minion/etc/org.opennms.core.ipc.rpc.kafka.cfg` with the following content:

```=
single-topic=true
```

6. Configure the location, opennms-url and broker-url for Minion on org.opennms.minion.controller.cfg; for example:

```=
location=Apex
id=onms-minion.local
http-url=http://192.168.205.168:8980/opennms
broker-url=failover:tcp://192.168.205.168:61616
```

:::warning
Remember to change `192.168.205.168` with the IP or FQDN of the OpenNMS server. The above example assumes the usaged of the embedded AMQ within OpenNMS. Alternatively, An external AMQ server can be used. When using Kafka for RPC and Sink, `broker-url` can be omitted (it will be ignored).
:::

7. Configure Trap/Syslog reception on Minion

Add the following to `/opt/minion/etc/org.opennms.netmgt.trapd.cfg`:

```=
trapd.listen.interface=0.0.0.0
trapd.listen.port=1162

# controls how many traps are included in a single message sent to Kafka
trapd.batch.size=10

# limits how many messages are kept in memory if Kafka is unreachable
trapd.queue.size=10000
```

Add the following to `/opt/minion/etc/org.opennms.netmgt.syslog.cfg`:

```=
syslog.listen.interface=0.0.0.0
syslog.listen.port=1514

# controls how many traps are included in a single message sent to Kafka
syslog.batch.size=10

# limits how many messages are kept in memory if Kafka is unreachable
syslog.queue.size=10000
```

:::warning
The queue size must be consistent with the message/buffer limits at Kafka Broker level. This is important for Meridian 2018 when using Kafka, as with Horizon 24+ or newer, the messages are split into multiple chunks to avoid hitting Kafka limits.
:::

Speaking about Kafka Limits, the following is required for the Sink API:

```bash=
echo "max.request.size=5000000" >> /opt/minion/etc/org.opennms.core.ipc.sink.kafka.cfg
```

For RPC (not available on Meridian 2018):

```bash=
echo "max.request.size=5000000" >> /opt/minion/etc/org.opennms.core.rpc.sink.kafka.cfg
```

Minion would have to be restarted after the above changes.

That also requires changes on each Kafka broker and OpenNMS.

At Kafka broker level:

```bash=
cat <<EOF | tee -a /opt/kafka/conf/server.properties
message.max.bytes=5000000
replica.fetch.max.bytes=5000000
compression.type=producer
EOF
```

At OpenNMS level, for Sink:

```bash=
cat <<EOF | tee -a /opt/opennms/etc/opennms.properties.d/kafka.properties
org.opennms.core.ipc.sink.kafka.max.partition.fetch.bytes=5000000
EOF
```

And for RPC (not available on Meridian 2018):

```bash=
cat <<EOF | tee -a /opt/opennms/etc/opennms.properties.d/kafka.properties
# RPC Consumer (verify Kafka broker configuration)
org.opennms.core.ipc.rpc.kafka.max.partition.fetch.bytes=5000000
org.opennms.core.ipc.rpc.kafka.auto.offset.reset=latest
# RPC Producer (verify Kafka broker configuration)
org.opennms.core.ipc.rpc.kafka.max.request.size=5000000
EOF
```

8. Configure internal firewall (CentOS/RHEL with Firewalld is assumed)

Enable firewalld:

```bash=
sudo systemctl enable firewalld
sudo systemctl start firewalld
```

Open the UDP ports 162 and 514 on the local firewall, to allow the Minion Server to receive information through them:

```bash=
sudo firewall-cmd --zone=public --add-port=162/udp
sudo firewall-cmd --zone=public --add-port=162/udp --permanent

sudo firewall-cmd --zone=public --add-port=514/udp
sudo firewall-cmd --zone=public --add-port=514/udp --permanent
```

Enable port forwarding, in order to forward from UDP 162 to UDP 1162, and from UDP 514 to UDP 1514; to guarantee the Minion application receives the messages.

```bash=
sudo firewall-cmd --zone="public" --add-forward-port=port=162:proto=udp:toport=1162
sudo firewall-cmd --zone="public" --add-forward-port=port=162:proto=udp:toport=1162 --permanent

sudo firewall-cmd --zone="public" --add-forward-port=port=514:proto=udp:toport=1514
sudo firewall-cmd --zone="public" --add-forward-port=port=514:proto=udp:toport=1514 --permanent
```

:::warning
Do not associate or constraint the rules against `127.0.0.1` as the official documentation suggests, to be consistent with the `listen.interface` entry used on the configuration files. 
:::

To verify the firewall rules:

```bash=
sudo firewall-cmd --list-all --zone public
```

Or to check everything:

```bash=
sudo firewall-cmd  --list-all-zones
```

9. Start Minion

```bash=
systemctl start minion
```
