Skip to content

PPPoE Auth Issues

A subscriber dialed PPPoE, the router answered, then the session dropped with “Authentication failed” or “PPP LCP terminated by peer” in MikroTik’s log. This page walks the five causes ProxPanel rejects an auth request for, in the order the RADIUS server checks them.

  • MikroTik PPP log: <pppoe-user>: terminated, authentication failed
  • Customer phone call: “internet stopped working after I changed the password”
  • Subscribers page shows the user offline even though they say they’re trying to connect
  • radpostauth table fills with Access-Reject rows for the username
  • RADIUS log (docker logs proxpanel-radius) repeats Auth reject (<reason>): <username>
  1. Tail the RADIUS log while the user retries. Every reject is one line with a categorised reason — you usually have the answer before the user finishes redialling.

    Terminal window
    docker logs -f proxpanel-radius 2>&1 | grep -E "Auth (reject|request|success)"
  2. Match the reason string in the log against the sections below: not_found, inactive, expired, wrong_password, password_not_found, unknown NAS, mac_mismatch.

  3. Confirm with the database. Every rejection writes a row to radpostauth:

    Terminal window
    docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
    "SELECT username, reply, authdate FROM radpostauth WHERE username='USERNAME' ORDER BY authdate DESC LIMIT 5;"
  4. Apply the fix below for the matching cause. Most fixes don’t require a service restart — the next dial attempt picks up the new state.

The RADIUS server doesn’t have an entry in nas_devices matching the source IP of the request. Either a new MikroTik was added without being registered, or the BNG started sending RADIUS from a different interface.

Log line:

RADIUS auth: unknown NAS: <bng-private>

Diagnostic:

Terminal window
docker logs proxpanel-radius 2>&1 | grep "unknown NAS" | tail -20

Then list what NAS rows actually exist:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT id, name, ip_address, is_active FROM nas_devices ORDER BY id;"

Fix:

  1. Note the IP from the unknown NAS log line.

  2. In the UI: NAS / Routers → Add NAS — enter the IP, the shared secret, mark active.

  3. Or insert directly:

    Terminal window
    docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
    "INSERT INTO nas_devices (name, short_name, ip_address, secret, type, is_active) \
    VALUES ('BNG-1', 'bng1', '<bng-private>', 'StrongSharedSecret', 'mikrotik', true);"
  4. Reload secrets — the RADIUS server caches nas_devices at boot:

    Terminal window
    docker restart proxpanel-radius
  5. Confirm:

    Terminal window
    docker logs proxpanel-radius 2>&1 | grep "Loaded.*NAS"

The NAS IP must be the interface the MikroTik sources RADIUS from, not the router’s WAN. On RouterOS check with /radius print detail — the IP your server sees is the one set in /ip route for the route to the ProxPanel server.

The username sent in User-Name doesn’t match any row in subscribers (after realm stripping).

Log line:

Auth reject (user not found): john@example.net

Diagnostic:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT id, username, status, expiry_date FROM subscribers WHERE username ILIKE '%john%';"

If the username is in the table but the user is dialling something subtly different (typo, missing realm, extra space), Auth reject (user not found) is what you get. Compare the log line’s user string byte-for-byte against the DB row.

Fix:

  • Typo: the operator fixes it on the customer’s CPE, or in ProxPanel under Subscribers → Edit → Username.
  • Missing realm: check the NAS’s allowed_realms column. If the BNG is configured to strip a realm on the way in, the bare john must exist in the DB; if it isn’t stripping, john@example.net must exist.

The subscriber row exists but status != 'active'. Either the operator disabled the account, an automated suspension service ran (overdue invoice, sharing detection), or the reseller was suspended and cascaded down.

Log line:

Auth reject (inactive): john@example.net

Diagnostic:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT s.username, s.status, s.expiry_date, r.is_active AS reseller_active \
FROM subscribers s LEFT JOIN resellers r ON r.id = s.reseller_id \
WHERE s.username = 'john@example.net';"

Check the audit log for the most recent status change:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT created_at, user_name, action, description FROM audit_logs \
WHERE description ILIKE '%john%' ORDER BY created_at DESC LIMIT 10;"

Fix:

  1. If the account was intentionally suspended, this is working as designed — collect payment first.
  2. If it was suspended by the overdue-invoice service in error, Subscribers → Edit → Status → Active, then save.
  3. If reseller_active = false, the parent reseller is suspended. Reactivate the reseller under Resellers → Edit → Active — the subscriber stays as it was; you may still need to flip the subscriber back to active separately.

expiry_date < today. The subscriber row is active but past its term.

Log line:

Auth reject (expired): john@example.net

Diagnostic:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT username, status, expiry_date, NOW()::date - expiry_date AS days_overdue \
FROM subscribers WHERE username = 'john@example.net';"

Fix:

  • Subscribers → action menu → Renew — adds the service’s duration to expiry_date and writes a renewal transaction.
  • Or, Bulk action → Add Days for a free grace extension. Both push the new date through immediately; the next dial succeeds.

The username is valid and active, but the Cleartext-Password in radcheck (or the decrypted subscribers.password_plain) doesn’t match what was hashed/sent.

Log line:

Auth reject (MS-CHAPv2 failed): john@example.net

or for PAP:

Auth reject (wrong password - PAP): john@example.net

Diagnostic:

Terminal window
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT r.username, r.attribute, r.value FROM radcheck r \
WHERE r.username='john@example.net' AND r.attribute='Cleartext-Password';"

Make sure the radcheck Cleartext-Password matches the panel’s stored password. If radcheck has nothing, the RADIUS server falls back to security.DecryptPassword(subscriber.password_plain) — if both are blank, you get Auth reject (password not found) instead.

Fix:

  1. Subscribers → Edit — the password field shows the current value (decrypted server-side, sent over HTTPS). Compare against what the customer is typing.
  2. If the customer’s CPE has the wrong password configured, fix it there.
  3. If you want to reset: type a new password, save. The handler writes both subscribers.password_plain (encrypted) and radcheck.Cleartext-Password (plaintext for the RADIUS server). Next dial works.

The license server has flagged the customer as over their tier’s subscriber cap. New activations are blocked; existing subscribers keep authenticating, but creating one and then dialling fails because the row never got created.

Symptom in the log:

License: subscriber limit exceeded (count=1001, max=1000)

You’ll usually notice this on the create flow (the UI returns a 403 with a message about the tier limit) before it shows up as an auth failure. If someone is calling the API directly, you may see Auth reject (user not found) because the subscriber was never written.

Diagnostic:

Terminal window
docker exec proxpanel-api curl -s http://localhost:8080/health | jq

The /health payload includes the license tier and current count. Cross-check against the license server:

Terminal window
curl -s -X POST "https://license.proxrad.com/api/v1/license/validate" \
-H "Content-Type: application/json" \
-d '{"license_key":"PROXP-XXXXX-XXXXX-XXXXX-XXXXX","server_ip":"YOUR_IP"}' | jq

Fix:

Buy more capacity from your tier, or contact info@proxrad.com to upgrade. The block clears as soon as the license server’s max_subscribers is raised — no restart needed.

Cause 7 — MAC mismatch (optional, when MAC binding is enabled)

Section titled “Cause 7 — MAC mismatch (optional, when MAC binding is enabled)”

If the subscriber has save_mac = true and a stored mac_address, RADIUS will reject any session from a different CPE MAC.

Log line:

Auth reject (MAC mismatch): john@example.net, expected=AA:BB:CC:DD:EE:FF, got=11:22:33:44:55:66

Fix:

  • Customer replaced their router → Subscribers → action menu → Reset MAC clears the stored MAC; the next dial re-binds.
  • Or uncheck Save MAC on the edit page if MAC binding is more trouble than it’s worth for this customer.

Capture this and email info@proxrad.com:

Terminal window
# 1. Full RADIUS log around the failure window
docker logs proxpanel-radius --since=10m > /tmp/radius.log
# 2. The subscriber row
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT * FROM subscribers WHERE username='USERNAME';" > /tmp/sub.txt
# 3. Recent radpostauth
docker exec proxpanel-db psql -U proxpanel -d proxpanel -c \
"SELECT * FROM radpostauth WHERE username='USERNAME' ORDER BY authdate DESC LIMIT 20;" > /tmp/postauth.txt
# 4. Version + hardware ID
curl -s http://localhost:8080/health
  • RADIUS Timeouts — when the request never reaches the RADIUS server at all.
  • License Errors — when the license itself is blocking new auths.
  • NAS / Routers — where to add and edit NAS rows.
  • Subscribers — the edit page for status, expiry, password, MAC reset.