How to Configure Monit to Run Processes as Specific Non-Root Users


2 views

Monit's default behavior of running as root creates security concerns when managing services like MySQL, Apache, or custom applications. While this gives Monit the necessary permissions to control system processes, it's not ideal for actually running those services.

The solution lies in Monit's start program directive. Here's the basic syntax:

check process mysqld
    matching "mysqld"
    start program = "/etc/init.d/mysql start" as uid "mysql" and gid "mysql"
    stop program = "/etc/init.d/mysql stop"

For more complex scenarios, you might need to combine user context with environment variables:

check process rails_app
    matching "mongrel"
    start program = "/bin/bash -c 'cd /apps/rails/current && bundle exec mongrel_rails start -e production -p 8000 -a 127.0.0.1 -d'"
        as uid "deploy" and gid "deploy"
        with timeout 60 seconds
    stop program = "/bin/bash -c 'cd /apps/rails/current && bundle exec mongrel_rails stop'"
        as uid "deploy" and gid "deploy"

When switching users, environment variables might be lost. Use the with clause to preserve them:

check process sidekiq
    matching "sidekiq"
    start program = "/usr/local/bin/sidekiq -C /apps/config/sidekiq.yml"
        as uid "appuser" and gid "appgroup"
        with HOME "/home/appuser"
        with PATH "/usr/local/bin:/usr/bin:/bin"
        with RAILS_ENV "production"

Remember that the target user must have appropriate permissions:

  • Execution rights on the binary
  • Read access to configuration files
  • Write access to PID files and log directories

If processes fail to start, check:

# Verify user switching
sudo -u username -g groupname /path/to/command

# Check Monit logs
tail -f /var/log/monit.log

# Test process startup manually
su - username -c "/full/path/to/command"

When configuring Monit to manage services, a common security concern arises: Monit typically executes as root, but we need certain processes (like MySQL, Apache, or custom applications) to run under specific user accounts. Here's how to properly implement this while maintaining system security.

The key to solving this lies in Monit's process control directives:

check process mysqld
    matching "mysql"
    start program = "/etc/init.d/mysql start" as uid "mysql" and gid "mysql"
    stop program = "/etc/init.d/mysql stop" as uid "mysql" and gid "mysql"
    if failed port 3306 protocol mysql then restart
    if 5 restarts within 5 cycles then timeout

For different service types, the implementation varies slightly:

For Systemd Services

check process nginx
    matching "nginx"
    start program = "/bin/systemctl start nginx" as uid "www-data" and gid "www-data"
    stop program = "/bin/systemctl stop nginx"
    if failed port 80 protocol http then restart

For Direct Binary Execution

check process custom_app
    matching "custom_app"
    start program = "/usr/bin/sudo -u appuser /path/to/custom_app" as uid "root"
    stop program = "/usr/bin/killall custom_app" as uid "root"
    if failed port 9000 then restart

When dealing with more complex scenarios:

check process redis
    matching "redis-server"
    start program = "/usr/bin/sudo -u redis -g redis /usr/bin/redis-server /etc/redis/redis.conf" as uid "root"
    stop program = "/usr/bin/sudo -u redis /usr/bin/redis-cli shutdown" as uid "root"
    if failed port 6379 then restart
    if cpu > 80% for 3 cycles then alert

When implementing user context changes:

  • Always verify the target user has minimal required permissions
  • Use dedicated system accounts for services (never regular user accounts)
  • Consider filesystem permissions for any resources the service needs to access
  • Test stop/start operations thoroughly before relying on Monit

If services fail to start properly:

  1. Check Monit logs (/var/log/monit.log)
  2. Verify the user exists (id username)
  3. Test the start command manually with the target user (sudo -u username command)
  4. Review process capabilities and SELinux contexts if applicable

For complex startup requirements, consider using wrapper scripts:

#!/bin/bash
# /usr/local/bin/start_custom.sh
sudo -u appuser -g appgroup /path/to/application --config /etc/app.conf

Then reference in Monit:

start program = "/usr/local/bin/start_custom.sh" as uid "root"