Back to Index

Authenticating pfSense OpenVPN Against IBM i LDAP

Date:
Authenticating pfSense OpenVPN Against IBM i LDAP — featured image

Intro

When hosting services alongside IBM i, such as a VPN, unifying user authentication improves the end-user experience. By configuring the LDAP server on IBM i, users can log in to other services using their existing IBM i credentials.

Process

First, add user profiles to the System Distribution Directory. LDAP, like SMTP, uses this directory for user information. You can access it via the WRKDIRE command.

Note: Usernames are limited to 8 characters due to legacy constraints.

Instead of manually keying each user, I wrote a script to transfer all IBM profiles to the System Distribution Directory automatically.

import odbc from 'odbc'
import { Connection, CommandCall } from 'itoolkit'
import { parseString } from 'xml2js'

const config = {
  host: '<ip-address>',
  name: '<dsn-name>',
  username: '<user-name>',
  password: '<password>',
}

// set up ssh connection for running commands
const connection = new Connection({
  transport: 'ssh',
  transportOptions: {
    host: config.host,
    username: config.username,
    password: config.password,
  },
})

// connect via odbc using the DSN defined in odbc.ini
odbc.connect(`DSN=${config.name}`, (error, db) => {
  if (error) {
    throw error
  }

  // query the QSYS2 user info file
  db.query('SELECT USER_NAME,TEXT FROM QSYS2.USER_INFOB', (error, result) => {
    if (error) {
      throw error
    }

    // comb through the results
    result.forEach(({ USER_NAME, TEXT }) => {
      // disregard the IBM defined profiles
      if (USER_NAME.startsWith('Q')) {
        return false
      }

      // USER ID has an 8 character limit
      const USER_ID = USER_NAME.substring(0, 8)
      console.log({ USER_ID })
      // set up the command to run
      const command = new CommandCall({
        type: 'cl',
        command: `ADDDIRE USRID(${USER_ID} ${config.name}) USRD('${TEXT}') USER(${USER_NAME})`,
      })

      // add the command to the connection
      connection.add(command)
    })

    // after we added the commands let's run them
    connection.run((error, xmlOutput) => {
      if (error) {
        console.log({ error })
      } else {
        // parse the results
        parseString(xmlOutput, (parseError, result) => {
          if (parseError) {
            console.log({ parseError })
          }
          console.log({ result })
        })
      }
    })
  })
})

Managing LDAP

IBM integrates the Tivoli LDAP server, which is configurable via the Web Navigator. For detailed instructions, refer to the IBM i LDAP documentation. Like standard LDAP servers, it uses Common Names (CN) and Organization Names (ON) for authentication integration.

IBMi LDAP Properties

Implementation

I most commonly use this to authenticate pfSense OpenVPN users. Configuring pfSense to authenticate against the IBM i LDAP server is straightforward. Once set up, you can assign this authentication method to your OpenVPN configuration. This approach works for many other services that support LDAP.

PFSense LDAP

The full Node.js script for syncing profiles is available in the snippets above. If you are consolidating auth across IBM i and external services, LDAP is often the simplest bridge without adding a separate identity provider.