Securing DirectAdmin

Secure DirectAdmin panel port 2222 with SSL

SSL is designed to keep an internet connection secure by safeguarding any sensitive data that is being sent between two systems, thereby preventing a third party from reading and modifying any information transferred, including protected personal information (PPI).

It's important to to use a secure connection for services and for the DirectAdmin panel itself: Secure DirectAdmin panel port 2222 with SSL

Security Questions or Two-Factor authentication after DirectAdmin login for an additional layer of security

To help increase the level of security with a DirectAdmin server, a feature was added which allows you to add questionsopen in new window or enable two-factor authenticationopen in new window after the login is already successful. These questions can be User specified, and any incorrect answer can then notify the User.

To enable the questions, or to add/modify the questions, log in as the user and go to:
Dashboard -> Change your Password -> Manage Security Questions

To enable two-factor authentication, log in as the user and go to:
Dashboard -> Change your Password -> Two-Step Authentication

The page also allows you to disable API access to the account using the same password used to login. Session Keys and Login Keys do not apply to this setting, as they're always allowed.

NOTE: Each failed 2FA/security question is counted in the 'failed_logins' file (where wrong passwords are counted). This means you must get a valid password and valid 2FA/Security Question within the "brutecount" limit, else you'll be put on the ip_blacklist (/usr/local/directadmin/data/admin/ip_blacklist), which is where IPs bruteforcing the DA login are blocked. To remove yourself from the blacklist, edit /usr/local/directadmin/data/admin/ip_blacklist and remove your IP from the list. You can also ensure you never become blocked by adding your IP to the file /usr/local/directadmin/data/admin/ip_whitelist.

Login Trigger changes regarding Two-step Authentication and Security Questions

Login triggers only trigger after both the user/pass login and 2FA/security questions are answered correctly. Previously, the trigger would happen after the valid login, before the 2FA/security questions were answered.

Triggers include:

  • checking to ensure the task.queue is being processed (random)
  • check for old custombuild/versions.txt (random)
  • clear the data/admin/ip_access/ folder
  • add to "login history"

Two-Step Authentication

The feature allows any DirectAdmin account to require the Two-Step Authentication using a time-based code from a smart-phone app (e.g., Google Authenticator, FreeOTP, Authy, etc.)

Access this feature from: Password Icon -> Two-Step Authentication

Once you have a "Secret" created, scan the barcode into your Authenticator App in your smart phone. Enable the feature, and future logins will require the temporary code generated from the App, entered on a 2nd page after a successful user/password login.

You can also optionally create one-time use "Scratch Codes", which can be printed and kept somewhere safe.

These scratch codes are used as a backup, in case your phone is not available to generate the code. A scratch code is only valid for one use, then is removed from the list of codes. The scratch code can be entered into the same "Code" field when logging into DA.

Information on the importance of Two-Factor Authentication can be found here: in new window

Applicable directadmin.conf settings include the following:

twostep_auth=0|1   default 1
block_ip_after_failed_twostep_auth=0|1|2  default 0

Where twostep_auth=1 enables two-step authentication, max_twostep_auth_attempts=5 permits 5 authentication attempts via this feature, and block_ip_after_failed_twostep_auth is whether or not to block the IP after failing two-step authentication, where setting to 0 (the default setting) means that no blocking will occur, 1 means that a warning will be given to the User that their IP will be blacklisted on their last attempt, and 2 means that no warning will be given when blocked. Of course, enabling the IP blocking requires that you have Blacklisting turned on in your Admin Settings.

The twostep_auth_discrepancy value should be a positive integer representing the number of 30 second time slices you can be off by (before or after). So the default of 1 allows a code to be valid before 30 seconds before, and 30 second after the phone's timer is done counting down.

DA's "Test Code" button may show you a message about the code being valid, but for a different time. If, for example, you see the time discrepancy as "-4", that means your code expired 2 minutes ago.

If you can't fix your server's time, then change this setting to be "4" (positive 4), and DA will allow a wider range of -4 to +4 for logins. Also useful if someone's phone time isn't accurate.

The twostep_auth_trust_days=30 value represents the duration that a trusted cookie is allowed to live in the client's browser. A trusted device can be un-trusted on the CMD_TWOSTEP_AUTH page. This may make your phone sad, as the server would no longer be its friend.

Applicable DA User files:

Applicable DA User user.conf settings:


NOTE: Each failed 2FA/security question is counted in the 'failed_logins' file (where wrong passwords are counted). This means you must get a valid password and valid 2FA/Security Question within the "brutecount" limit, else you'll be put on the ip_blacklist (/usr/local/directadmin/data/admin/ip_blacklist), which is where IPs bruteforcing the DA login are blocked. To remove yourself from the blacklist, edit /usr/local/directadmin/data/admin/ip_blacklist to remove your IP from the list. You can also ensure you never become blocked by adding your IP to the file /usr/local/directadmin/data/admin/ip_whitelist.

Limit admin level commands to certain IPs

Since the number of Admins on a server is usually limited to a small handful, limiting the access to Admin accounts to the IPs of those people might be something an Admin would want to do, in case the password fell into the wrong hands.

To do this, we can use all_pre.shopen in new window in order to check the IP of the caller to ensure they're allowed to be logged in. Also, it would be recommended to enable the on HTM filesopen in new window, not just as CMD files.

Create /usr/local/directadmin/scripts/custom/, and in it, add the code:

USERTYPE=`grep usertype= /usr/local/directadmin/data/users/${username}/user.conf | cut -d= -f2`

if [ "${USERTYPE}" = "admin" ]; then
   if [ "$caller_ip" = "" ]; then
       exit 0;

   if [ "$caller_ip" = "" ]; then
       exit 0;

   #repeat the check on the IP as many times as desired.

   echo "IP $caller_ip is not allowed to be logged in as an Admin";
   exit 1;
exit 0;

where and would be IPs that you want to allow to login as an Admin. You can add more checks for more IPs as needed. Make it executable:

chown diradmin:diradmin /usr/local/directadmin/scripts/custom/
chmod 700 /usr/local/directadmin/scripts/custom/

NOTE: if your IP changes, you must edit this file to add your new IP to the list or you won't be able to login as the Admin.

The same guide could be modified in many other ways, such as blocking all access to DirectAdmin, except to specific IPs. This can be done by simply removing the "if" statement that checks the USERTYPE, so that the IP check applies to all usertypes.

NOTE: there is also the custom script if you actually wish to block the accepted login from a specific IP. This would be a block at the login level (would be the same as wrong password), versus the, which simply restricts running any command after a login was successful, and session file created.

Using login_pre to only allow certain IPs

The login_pre.shopen in new window feature is a script that is called before each request (if it exists). The sample script in the above article describes how to allow one IP. If you wish to allow more than one IP, the script would need to be changed. Note that you can use PHP if you wish, and connect to an IP database, if you wanted to have a more versatile IP checking system.

The script below will describe how to create a text file to allow multiple IPs, with an easy-to-edit list.

  1. Similar to the sample script, we create /usr/local/directadmin/scripts/custom/, and in this script, add the code:

$user = getenv('username');
$ip = getenv('ip');

$ip_list = "/usr/local/directadmin/scripts/custom/ip_list.txt";

if ($user == 'demo_user' || $user == 'demo_reseller' || $user == 'demo_admin')
          //not worried about demos

$lines = file($ip_list);

foreach ($lines as $ip_val)
       $ip_val = trim($ip_val); //remove trailing newlines
       if ($ip == $ip_val)

echo "Invalid IP";


Save and make it executable:

chmod 755 /usr/local/directadmin/scripts/custom/
  1. As you may have guessed, the next step is to create the list itself. Edit /usr/local/directadmin/scripts/custom/ip_list.txt and add 1 IP per line for the IPs that you wish to allow into DA.

  2. This last step is optional. If you'd like to edit the ip_list.txt from within DA itself, add it to the edit files:

cd /usr/local/directadmin/data/templates/custom
cp ../edit_files.txt .
echo "/usr/local/directadmin/scripts/custom/ip_list.txt=user=root&group=root&permission=600&secure=yes" >> edit_files.txt

Which will add the file to Admin Level -> File Editor so that you can edit it through DA.

Prevent the creation of another admin account

If you only have 1 Admin account and want to make sure no other Admin account is created, you can use the in order to check the command being run, and block it if it's the creation of an Admin.

Create: /usr/local/directadmin/scripts/custom/ and add the code:

if [ "$command" = "/CMD_API_ACCOUNT_ADMIN" ]; then
    echo "Cannot use this command";
    exit 1;
if [ "$command" = "/CMD_ACCOUNT_ADMIN" ]; then
    echo "Cannot use this command";
    exit 1;
exit 0;

And make it executable:

chown diradmin:diradmin /usr/local/directadmin/scripts/custom/
chmod 700 /usr/local/directadmin/scripts/custom/

Detecting and preventing brute-force attacks

A common method of gaining access over a server is to use a technique called a brute force attack, or dictionary attack. What the attacker will do, is use a script to try and log in to an account with every possible password combination. This tends to require tens of thousands of login attempts, but eventually, the right combination will be found, and they can log in normally.

To prevent this, we can use a brute force login detection system. DirectAdmin has 2 such systems for these attacks.

  1. The original feature was created in DA 1.25.5, and will detect and block login attempts on DA itself (port 2222): in new window

This feature only applies to port 2222. It only blocks IPs on this port. It does not block IPs from other ports.

To enable this feature, go to Admin Level -> Admin Settings -> Blacklist IPs for excessive login attempts use a value around 10-20.

  1. The newer system works in tandem with the previous, and will scan the logs for the other services (Apache, Dovecot, Exim, ProFTPd, SSHd).

When an attack is detected, DA will notify the Admins on the box that the attack is in progress.

DA will not block the IPs since that would require a firewall, and DA doesn't manage firewalls (see below).

To enable the detection reporting, go to:
Admin Level -> Admin Settings -> Parse service logs for brute force attacks

The brute force monitor (BFM) page can be viewed at Admin Level -> Brute Force Monitor.

Hide brute-force notifications

As brute force attacks are fairly common, and the tools to prevent those attacks are fairly reliable, some admins do not wish to be told about every case of attacks.

Since the trigger of the only happens with notices, I've added an option to prevent the sending of the notices, but still call the scripts normally to block the IP.

The directadmin.conf option will be:


which is the internal default (option disabled, notifications shown).

If you wish to prevent notices from being sent out, but still have the IPs blocked, then set this in your directadmin.conf:


All servers should be running some sort of firewall to prevent them, e.g., </operation-system-level/securing/firewall#i-need-a-firewall-what-are-my-options>

The purpose of this guide is to hide those Message System messages once you've established that your blocking system is working correctly. There are several options and levels to suppress the messages.

  1. If you want to hide the messages from the "Message System", but continue to be notified via E-Mail: in new window

  2. If you want to send the high-volume attack E-Mails to some different address, like, you can add a user.conf variable: in new window


Brute-force monitor page generates a timeout

If you get a timeout message similar to the one below when accessing the Brute Force Monitor (BMF) page CMD_BRUTE_FORCE_MONITOR:

Your connection has timed out.

Either your request was invalid or the program hasn't completed your request.
Please notify the server admin.

This is usually caused by an over-sized file /usr/local/directadmin/data/admin/brute_log_entries.list. Check this file to see if it's large. If it is, you can shrink it down, but only keep the entries at the end of the file (the end of the file has the newest entries).

cd /usr/local/directadmin/data/admin
mv brute_log_entries.list brute_log_entries.list.backup
tail -n 1000 brute_log_entries.list.backup > brute_log_entries.list
chown diradmin:diradmin brute_log_entries.list

Where you can increase or decrease the 1000 lines as needed.

Other possible affected file are the and Of these 2 files, it's more likely the is causing the slowdown, because attacks typically happen on many Users from 1 IP address. If you need to empty the file, type:

cd /usr/local/directadmin/data/admin
chown diradmin:diradmin

We wouldn't recommend emptying the because this is what counts the attacks per IP, and is what is used to determine if an IP should be blocked.

Prevent BFM from reporting specific email

If you're using the Brute Force Monitor (BFM) and 1) have an email address that is repeatedly checking your server, but that account is invalid (either a wrong password, or a non-existent account), and 2) you know that the person doing it is not malicious, but only stubborn for not updating their settings, then you can tell the BFM to skip that email address from being checked.

To do this, create the /usr/local/directadmin/scripts/custom/ script and add the following code:

if [ "$type" = "User" ]; then
   if [ "$value" = "" ]; then
       echo "exempt";
       exit 1;
exit 0;

Make it executable:

chmod 755 /usr/local/directadmin/scripts/custom/

Note that this will only prevent reports for users/emails being logged. It will not prevent the blocking of the IPs, as the IP could be "attacking" some other account as well.

Found an XSS or CSRF hole in DirectAdmin?

We get these reports on occasion, but DirectAdmin has had a referer checking tool for quite some time now: in new window

Since this was added, none of the external XSS/CSRF reports have been valid.

People who think they've found one often are testing with a form in the DA skin, on port 2222 on the same IP. This means the form is in the skin, which is exactly where it lives usually for its intended purpose. So of course, the form submission passes, as one would expect. So this test is only possible if they've got access to editing the skins, which means they've already got root access on your server, so the XSS test doesn't mean anything.

An external form, using any different variable, either IP or port will fail the check_referer=1 test.

In DA debug mode, a sample might look like this, where is the server IP, and we actually put the hacking test on the same IP. In real life, the attacking IP would typically be on a different IP, but that only adds one more thing for the referer check to find. This just proves that even just the variance in the port is sufficient to stop the attack.

This test has with any standard form you want, DA on, with the client IP being

Checking referer to
Referer check failed for See error.log

And from the error.log:

2016:03:31-15:49:00: Referer port (80) does not match DA's (2222):
2016:03:31-15:49:00: Referer check failed for

So please, before submitting your security report, please check to ensure that you're not actually testing your form in 2222, since that's where it's going to be valid. Test with your form on any different port or IP.

I wish to have a so I can block IPs through DirectAdmin

Starting from DirectAdmin version 1.61.0, the CSF integration is done directly. New installs will have CSF automatically installed and BFM autoblock covered if installation isn't customized to disable them.

Existing installations after updating to 1.61.0 could have direct CSF integration upon deleting previously installed plugin files.

To activate, remove the following files:


Brute Force Monitoring for xmlrpc.php

DirectAdmin v1.590

The xmlrpc.php, a file included with WordPress, is an API file used for data transfers or actions between various things.

A stand-alone setup often does not need web access to this file, but any external connections, like iPhone apps that routinely do POSTs to it generating a 200 return code would require it.

This is also one of the major attack points for WordPress, used in attempts to determine the website's WordPress password.

As a result, our Brute Force Monitor (BFM), when "Scan for WordPress attacks" is enabled in the Admin Settings, will scan for POSTs to this file with a valid 200 return code (wordpress2 brute_filter.list).

This change is to increase the match count, which would have been the same as the Brute Force Monitor's count limit (set in Admin Settings), to now be 8x that value, to allow for possible valid usage, while still blocking attacks.

For example, if you set your ip_brutecount=100, aka:

"Notify Admins after an IP has: [100] login failures on any account"

This would mean that any User would be allowed 800 POST with a 200 response code to the xmlrpc.php file, based on the BFM's rules.

Note that the BFM's time window is sliding and is entirely based on a period of time where there are no attacks counted in order to reset the count to 0.

So if you have the following set in your Admin Settings,

"Reset count of IP/User failed attempts: [4] hours after last attempt"

A period of 4 hours must pass where there are 0 failed attempts from this IP address in order for the count up to 800 to reset to 0.

If even 1 POST to xmlrpc+200 is done per hour, then the count will never reset, and the 800 limit WILL eventually be hit.

There are plugins for WordPress, which can monitor for such attacks, saving the need for DA to do it, such as WordFence.

It's ultimately up to a website to be protecting itself, as the "Scan for WordPress attacks" is simply there to be a fall-back in case the website owner failed to secure WordPress.

The /usr/local/directadmin/data/templates/brute_filter.list contains options:

wordpress3=ip_after=&ip_until= -&text=] "POST /&text2=/wp-login.php&text3=" 302%20&count_multiplier=4

What this does, is after all log parsing is done, the Brute Force Monitor will count how many entries were triggered for that item (e.g., wordpress3) for that given IP.

Say there were 20 triggers. The multiplier means that instead of having a limit of 100, that item needs 400.

The actual logic in DA is actually backwards, as the total count uses other items, so we actually divide the number of wordpress3 counts by 4.

So instead of 20 hits, the count only see 5 towards the total. This also means that if there are only 3 hits, then 3/4 = 0, so no count would be triggered for that IP.

The entries will still show up in the log list, but count for that given IP (top table) would be lower.

The whole purpose for this is that the entry:

POST /wp-login.php HTTP/1.1" 302

could represent an attack, but could also just be a normal redirection. So we do want to count it, but it should be of lower significance.

Security Questions for extra layer of protection

DirectAdmin has an ability to enable Security Questions, which is where a valid login will take the client to another authentication page, asking for a valid answer to a pre-defined question. Forum threadopen in new window

The Security Questions page can be accessed from the "Password" icon, at the top of the page (where a DA User changes their password).

In the 'Security Questions options' (where you will enable the feature via a checkbox after adding your questions and answers), there is a checkbox to allow API connections to the account with this DA username with their password. If disabled (unchecked), no CMD_API call will work for this account when accessing the account with the usual password, so only disable API's if you know you don't need them.

This API checkbox does not apply to Login Keys, so you can disable APIs with the password, but APIs will still work when using a Login Key (they're so long that they won't likely be guessed).

There is no checkbox for Login Keys. If you don't want to allow Login Keys to access the API, don't create a Login Key.

This feature is an extra layer of security, in addition to the current Brute Force Attack monitor for port 2222.

The list of questions is stored in: /usr/local/directadmin/data/skins/enhanced/lang/en/internal/security_questions.txt.

The very first line will look something like:


where index 1 will always store the number of entries in the file. In this case, the last entry is index 22=.

For the sake of clarity, see the default file below:

# cat /usr/local/directadmin/data/skins/enhanced/lang/en/internal/security_questions.txt
2=What is the name of your favorite childhood friend?
3=In what city did you meet your spouse/significant other?
4=What was your childhood nickname?
5=What street did you live on in third grade?
6=What is your oldest sibling's birthday month and year? (e.g: January 1970)
7=What is the middle name of your oldest child?
8=What is your oldest sibling's middle name?
9=What school did you attend for sixth grade?
10=What was your childhood phone number including area code? (e.g: 000-000-0000)
11=What is your oldest cousin's first and last name?
12=What was the name of your first stuffed animal?
13=In what city or town did your mother and father meet?
14=Where were you when you had your first kiss?
15=What is the first name of the boy or girl that you first kissed?
16=What was the last name of your third grade teacher?
17=In what city does your nearest sibling live?
18=What is your oldest brother's birthday month and year? (e.g: January 1970)
19=What is your maternal grandmother's maiden name
20=In what city or town was your first job?
21=What is the name of the place your wedding reception was held?
22=What is the name of a college you applied to but didn't attend?

Basically, just take the highest number at the bottom, and use that (#1 skips itself automatically).

To add more entries, edit this file, and lock it from update overwrites:

chattr +i security_questions.txt

Language changes can have their own copy, in their own language.

Relative user.conf values:

  • security_questions=yes - Security questions will be required, if they're present.
  • api_with_password=yes - The API is allowed, using the current password. Login Keys and Session Keys are always allowed since this setting does not affect them.
  • notify_on_all_question_failures=yes - When enabled, all incorrect answers will generate a Message to the User (not to Admins). The "no" will still send a message to Admin and User after max_security_question_attempts attempts.

New directadmin.conf values, these are the internal defaults:


The block_ip_after_failed_security_questions option can be set to 1, and on the last attempt, a warning will be given to the User that their IP will be blacklisted.

If you set:


then no warning will be given.

Of course, this requires that you have the Blacklisting option turned on in your Admin Settings (if it's off, go turn it on now!).

Custom blacklisted IP page

An optional custom template file exists here:
If that file exists and the client IP is blacklisted, this content type text/html page will be sent to the client, instead of the hard-coded: "Your IP is blacklisted".
Read more.

Last Updated: