Version 1.47.0

Released: 2015-01-11 19 and exim.conf 4.2.3 (Manual changes) new

Update not required. These are optional changes.

They can also be managed with CustomBuild 2.0 (recommended manager)

Changes to the and exim.conf.

If you chose to update, uou must update BOTH your, and install a new exim.conf.

You can patch from 4.2.2 to 4.2.3.

If you're going from 4.2.1 or older, then do a fresh install of 4.2.2.


  • exim.conf 4.2.3 requires 19.

  • 19 requires exim.conf 4.2.3.

You must update them together.

New version 19:

This version has a few extra functions that the exim.conf can use (comparing to version 17)



block_cracking_notify - DA still needs to do something about it, but the mail.task.queue is notified


find_uid - only returns DA usernames.


We recommend using CustomBuild to mange the exim.conf and (note: this guide is for 4.3)

New exim.conf 4.2.3
  • set the disable_ipv6=false by default so CB can regex it based on the directadmin.conf ipv6 setting.

  • acl_smtp_auth to only allow 1 AUTH attempt per connection

  • /etc/virtual/blacklist_usernames (does not need to exist).

Must contain a DA Username, eg "fred" followed by the unix time it was added to the blacklist, eg:


(may change it to be fred:1409557166:more=info&in=the&url=format, which won't affect exim as it stops at the first : character)

Will drop authorized smtp connections if DA User is in that file for the given smtp auth id (username or

Also applies to script based sends.. email will not make it into the queue, will be discarded and message logged to mainlog.

  • acl_not_smtp = acl_script

Script based ACL, will check the User send count, and drop if at limit.

This is where the blacklist_usernames is checked as well.

  • For incoming email, will check if the local domain is supsended and give a proper message.

  • any relay_hosts relayed emails will now have the header added:

add_header = X-Relay-Host: $sender_host_address

  • removed the spamcheck_director, forcing people to use the file:


  • added .include_if_exists /etc/exim.dkim.conf, for easier dkim setup.

List of the major features:


We recommend using CustomBuild to install version 4.2: (note: guide is for 4.3)

BlockCracking notices and unblocking (TEMPLATES) (SKINS) new


BlockCracking is where exim keeps track of how many non-existent emails a sender tries to send within a given period of time, and blocks the account from sending if the count is too high.

Requires exim.conf 4.2.3+ and 19.

Can be installed with CustomBuild 2.0:

the changes listed here let you configure how DA manages the ability to unblock the account.

These changes are only a small portion of the feature, the bulk of it is in the Exim module, to be installed via CustomBuild.

  1. Unblocking for blocked smtp-auth Users:

Once an account is blocked, they're likely going to want to be unblocked, but the account must be deemed "safe" first.

New directadmin.conf variable:


1 means the standard password change will unblock the account.

Note that the account itself is not allowed to do this, it must go up one level, as the alleged spammer knows the password, and we don't want them just resetting the password to spam more.

So if a is blocked, the password must be changed from the User Level by a DirectAdmin User.

If a DirectAdmin User is block, then their creator or higher (a Reseller or Admin) must changed from the Reseller or Admin Levels.

However, the "Lost Password" tool on the 2222 Login Page will unblock the DA user upon password reset, as it's set to a new random value that the hacker (hopefully) cannot learn (assumes the email is not a local one)

Note that I say "change" password, which is what I expect you to do, as a compromised account is once were the smtp-auth password is known, thus must be changed.

However, if you change it to unblock it, there is nothing stopping you from setting it back to the original value. This is not recommended, as it will allow the spammer to send more spam.

Of course, if it's a false positive, then this should be fine (eg, you set a BlockCracking limit too low, etc..)

2 means, all rules of 1 apply, but 2 adds the extra "automatic unblocking" to happen after a give amount of time.

We really don't recommend you set this, but hopefully, Users will have changed their passwords on their own free will (we hope).. so the option frees up your time by having it done automatically.

directadmin.conf variable, set in minutes for the number of minutes before an account is unblocked:


0 doesn't do anything at this time, but we may reserve it to prevent unblocking... if someone would ever want that (me thinks not)

  1. Unblocking for blocked Paths

The same block_cracking_unblock variable is used to dertmine unblocking abilities for paths.

For a User to unblock a path, they can go to:

User Level -> E-Mail Accounts -> E-Mail Usage

and it will show all paths below /home/user (inclusive), which they can select and unblock.

Paths outside of /home/user, eg: /tmp or /home/otheruser will not show up in this table, and cannot be removed.

  1. Notices.

When a limit is reached, the adds the following to the /etc/virtual/mail_task.queue file:



DA then picks that up, and will send notices as applicable.

Who get notified depends on these already existent variables:

notify_on_mass_emailing=0|1                        # Global on/off switch for all the other notify variables

All 4 of these notify variables default to 1.

Once the "who" is figured out, assuming there are more than 0, the block_cracking_notice.txt template is used, and notices are delivered using the Message System.




contains the message to sent to the DA accounts.



Top added:



bottom added:


to list all php scripts that sent that day.

Added email quota and db quota to Admin Stats: \"Complete Usage Statistics\" new

CMD_ADMIN_STATS will now show the server totals for Email Disk Usage and Database Usage.

Ability to set Spam folder from INBOX.spam to Junk (TEMPLATES) new

Relating to this:

Ability to merge old inbox imap folders to new folders

new directadmin.conf option, internal default to:


if you set it to 0, all areas in DA will use "Junk" instead of INBOX.spam for paths.

Once you set:


it won't change any active data, you must use the id=1674 to sync the folders as desired.

But with DA 1.464+, then id=1674 guide will also include the reverse direction of the INBOX.spam to Junk.

If you switch, this would be a sample set of commands:

cd /usr/local/directadmin
echo spam_inbox_prefix=0 >> conf/directadmin.conf
/etc/init.d/directadmin restart
cd custombuild
./build update
./build set webapps_inbox_prefix no
./build roundcube
./build squirrelmail
cd ..
echo "action=convert&value=imap_inbox_prefix" >> data/task.queue; ./dataskq d2000
echo "action=rewrite&value=filter" >> data/task.queue; ./dataskq d2000




all instances of:


are now replaced with the token:


Ability to specify single domain with CMD_API_DOMAIN_OWNERS new

Relating to:


You can now specify a specific domain, eg:


which will make the CMD_API_DOMAIN_OWNERS filter out all other domains, giving you just:

Both Admin and Resellers can run this, but the Reseller can only do lookups on domains under their control (including their Users)

If a domain does not exist, an error is returned, eg:

error=1&text=Cannot find that domain&details=

Email deletion accountability logging new


will now show something like this, for each email account deleted:

2014:12:10-00:34:53: Email deleted : ip= user=fred : /CMD_EMAIL_POP

If the "login as" option was used, it will show:

2014:12:10-00:35:21: Email deleted : ip= login_as_master=admin user=fred : /CMD_EMAIL_POP

For API calls, "/CMD_EMAIL_POP" will show "API" instead (which the command is CMD_API_POP but it will just show API)

DA to manage domain IPS file for exim outbound IP/interfaces new

DA will manage this files:



for any domain created with an IP that is not the server IP.

helo_data will only get owned IPs.

Requires directadmin.conf setting:


internal default is:


This is for exim to use, to pick which IP to use when sending email, instead of sending from the server IP.

This can help isolate spamming domains from valid domains, so if an IP is blocked, domains that send from a different IP won't be blocked.

It relies on the "a" or "mx" values being set in the SPF record, so the IP added should be what they resolve to.

In the even that adding to the file fails, it will end up sending from the server IP, so the server IP should always be in the SPF/TXT record.

If you have multiple owned IPs assigned to a domain, the first value added will have priority, when in question.

If it's not what you want, delete then re-add the IP you want to have less priority from the User Level -> Domain's additional IP page.

If you need to disable the feature, then set add_domain_to_domainips=0 option, and delete the files:



exim.conf 4.3.1

adds these lines to the remote_smtp section:

interface = <; ${if exists{/etc/virtual/domainips}{${lookup{$sender_address_domain}lsearch*{/etc/virtual/domainips}}}}

helo_data = ${if exists{/etc/virtual/helo_data}{${lookup{$sending_ip_address}iplsearch{/etc/virtual/helo_data}{$value}{$primary_hostname}}}{$primary_hostname}}

and this one, just after #EDIT#1 (after #primary_hostname)

smtp_active_hostname = ${if exists{/etc/virtual/helo_data}{${lookup{$interface_address}iplsearch{/etc/virtual/helo_data}{$value}{$primary_hostname}}}{$primary_hostname}}

NOTE: current exim.conf 4.4.x already have these sections, so no need to edit your exim.conf.

For a LAN setup, DA assumes you've set the directadmin.conf option:


to the main LAN ip for the system that connects outbound,on the licensed/server IP.

For LAN ips that are not the lan_ip value, it gets more complicated.

DA takes the owned IP assigned to domain (External value), and assumes you've setup a linked LAN IP to this external IP.

It will go through the list of Linked IPs (probably going to be just the 1 value) and make an outbound call to:

while binding to the given linked-IP LAN ip value.

Basically, DA will force the local outbound connection to use that linked LAN IP..

The myip site will tell DA what the external value is for this IP... and if it matches the owned external IP assigned to the domain, then the given Linked LAN IP will be the value DA sets in the domainips file. A little overkill, but should be handy for LAN setups that use mutiple IPs who want sending IP segregation.


Add all domains and pointers to the domainips file:

echo "action=rewrite&value=domainips" >> /usr/local/directadmin/data/task.queue

same as above, but will start from an empty domainips file, handy if everything is out of sync, start over:

echo "action=rewrite&value=domainips&empty=yes" >> /usr/local/directadmin/data/task.queue

Adds just this domain (and it's pointers), but it will remove the previous values from the domainips first (unlike the mass rewrite)

This is better because it will remove server IP values, if they don't belong.. or fix any out-of-sync IPs.

echo "action=rewrite&value=domainips&" >> /usr/local/directadmin/data/task.queue

Similarly, for helo_data:


echo "action=rewrite&value=helo_data" >> /usr/local/directadmin/data/task.queue

Just for one IP:

echo "action=rewrite&value=helo_data&ip=" >> /usr/local/directadmin/data/task.queue
Sample /etc/virtual/helo_data:

where the server IP and main hostname do not need to be in this file.

The IP lookup on the left must be unique, matching the IP used for the incoming connection.

Sample /etc/virtual/domainips

where you can have duplicate IPs on the right, but the left side must be unique (matches

Manual control

You can set:


and manually manage these files.

Just rember to try and keep the forward and reverse dns lookups to match, eg: -> ->

Apache WordPress log scanning in BFM (disabled by default) (SKINS)(LANG) new

If you're looking for a shorter version that only explains how to use the feature and what to take note of, see documentation found here

Brute Force Monitor can now scan the Apache domain logs for WordPress wp-login.php attacks.

Internal default directadmin.conf setting:


There are 2 ways of running it (option 2 is likely going to be recommended for most people)

  1. to enable scanning of logs manually specified, set:

Next, for any domain you wish to have DA scan, add:


to the file:


With the brute_force_scan_apache_logs=1 set, the dataskq will be looking for logs that start with:


(whatever the directadmin.conf apachelogdir value is set to)

No need to worry about adding the pos= and size= value to the brute.conf, as the internally default to 0.

DA will crank them up as it goes.

  1. Option 2 for running it:

and DA itself will manage the logs in the brute.conf file for you (it adds all logs)

DA loads a loglist from /var/log/httpd/domains/*.log (excluding error.log).

Also loads the current /var/log/httpd/domains/*.log files currently saved in the brute.conf into the loglist.

For each of the loglist files, if it's missing or size=0, remove from the brute.conf (likely deleted at some point)

If size>0, parse it normally, and save to brute.conf.

This keeps the brute.conf up to date.

This is probably going to be the preferred method for most systems.

However, if you know exactly which websites run WordPress, then use the 1st option, to help keep the parsing load down.

If brute_force_scan_apache_logs=2 is used, then this variable comes into effect:


which is the number of minutes between the refresh of the apache log list.

Missing logs are always removed from the list, but new logs won't start scanning for this amount of time.

Time is in minutes, default is 10 minutes between refreshes.

It will store "last_refresh" in the brute.conf each time it's updated.


Note that apache does not know if a wp-login.php entry was a login failure or not..

All it knows is that a POST was made to the wp-login.php file.

Because of this, the Brute Force Monitor considers each POST access to wp-login.php a failed login, even if the correct password was used.

Keep this fact in mind when deciding on your IP block limit.

If you login and out many times over and over, then DA will consider this an attack, even if the correct IP was used each time.

So, make sure you don't add your domain log to the brute.conf until you're done with testing the login system.

Possible To-Do: Create a separate login counter max value for these types of logins, as the "failed" count doesn't really apply, logically.

useful command to quickly see which domains are being attacked, sorted by number of attempts:

cd /var/log/httpd/domains
grep -c wp-login.php /var/log/httpd/domains/*.log | grep -v :0 | awk -F':' '{print $2,$1}' | sort -n

And then if you want to monitor site sitse with the top 20 most number of attempts (be sure to look at the above output first, to decide), then, eg:

cp /usr/local/directadmin/data/admin/brute.conf /usr/local/directadmin/data/admin/brute.conf.backup

grep -c wp-login.php /var/log/httpd/domains/*.log | grep -v :0 | awk -F':' '{print $2,$1"="}' | sort -n | tail -n 20 | cut -d\\  -f2 >> /usr/local/directadmin/data/admin/brute.conf
Bonus optimization found:

While poking around in the code, I found a significant optimization to the code for "initial runs" where-by the internal list was previously being sorted after each log entry was found and added.

Code has been changed to fill up the list until everything is done, and then only sort it once.

For existing runs, it wouldn't change much (only scans changes since the last minute), but for initial runs where there could be hundreds of thousands of log entries found on the first go, sorting the whole list after each entry is added wasn't great (sorting after an entry is added to the container class is it's default behavior to prevent/overide duplicates entries, but doesn't apply in this case, as we know each log entry will be unique)



added triple radiobox for brute_force_scan_apache_logs=0|1|2

<td class=list>
Scan for WordPress attacks
<td class=list>
<input type=radio name=brute_force_scan_apache_logs value="2" |BF_SCAN_APACHE_LOGS_2|>All Logs&nbsp;&nbsp;
<input type=radio name=brute_force_scan_apache_logs value="1" |BF_SCAN_APACHE_LOGS_1|>Manual&nbsp;&nbsp;
<input type=radio name=brute_force_scan_apache_logs value="0" |BF_SCAN_APACHE_LOGS_0|>No
&nbsp;&nbsp;<a target=_blank href="">(?)</a>


LANG_PARSE_FOR_BFA=Parse service logs for brute force attacks
LANG_NOTIFY_AFTER_IP=Notify Admins after an IP has
LANG_NOTIFY_ADMIN_USER=Notify Admins after a User has
LANG_RESET_COUNT_AFTER=Reset count of IP/User failed attempts
LANG_CLEAR_BF_FROM_LOG=Clear failed login attempts from log
LANG_SCAN_APACHE_LOGS=Scan for WordPress attacks

Message System: Clear Messages (SKINS)(LANG) new

Related forum thread:

Ability to clear messages from the Message System based on the Subject and/or the Date of the message.

Specific code to only do this one task, so should be quite speedy.

Changes can be found on the bottom of the "Message System"

as well as in the Brute Force Monitor (same table, different default selected value)

Other optimization found which will greatly speed up the Message System.

Previous method would search each message ID directory for newest message or ticket date, for all files in that ticket # directory.

This should now only apply to a ticket, and the need for this for a "message" has been removed.

For messages, the newest message will always be the only message, and this st_mtime so happens to have already been loaded with the main 000.conf in the ConfigFile class.

Optimization for each "message" prevents the need for directory read of it's files, plus the fstat on each file in that directory.

That's all removed for each message ID which should greatly speed up any Message System that has many "Messages".. probably exponentially faster.

The above GUI system only applies to the ticket.list files.

To delete the actual data from disk (data/tickets/0000*/*), the following directadmin.conf options are related:


which is the default, 0 meaning "never delete".

To keep the disk clean, set the number of days you'd like each message type to last, in days.

When finding the date of a ticket, DA will use the time of the last reply.

So if you've got a ticket going on for 3 years, the time is based on the most recent message..

Meaning, if you replied yesterday to the ticket that was opened 3 years ago.. and the delete_ticket_days=365, then this ticket will not be deleted.

Note, if you delete the data from disk, and the ticket number is still in the tickets.list file, that's fine.

DA will now ignore missing tickets, and remove that bad entry from the tickets.list when noticed.

(saves having to hunt through all User tickets.list files for any message disk directory deletion)


New internal text:


Format is the same as the security_questions.txt where index/line 1 must have a value of the last index name.

Index/line 2 should be the wording for "-- anything --"

Index/line 3 should be the wording for "Brute-Force Attack detected in service log" (as the BFM has the table, and defaults to index 3)... which is hardcoded, so don't translate index 3 or it won't be found.


added table token:


also changed the "email a copy" form to be something like this:

<form action="CMD_TICKET" method="post">
<input type=hidden name=action value="email">
<input type=checkbox name=ON value="yes" |EMAILCHECKED|> Email a copy of all messages to <input size=30 type=text name=email value="|EMAIL|"><br>
Automatically delete tickets older than <input type=text size=1 name=delete_tickets_days value="|DELETE_TICKETS_DAYS|"> days. (0 = never)<br>
Automatically delete messages older than <input type=text size=1 name=delete_messages_days value="|DELETE_MESSAGES_DAYS|"> days. (0 = never)<br>
<input type=submit name=save value="Save">


add table token:



lang/en/internal/ticket.txt - up to 58


internal default change: ajax=1 new

Relating to the ajax feature:


The new internal default for all systems will now be enabled:


internal default change: user_can_set_email_limit=1 (SKINS)(LANG) new

Default change to be:


Relating to:

Per-Email send limit (SKINS)

If you wish to allow the Users to set values higher than the default 200, by leave 200 as the default, then chnage:


to be (for example):


where the default -1 tells DA to rely on the /etc/virtual/user_limit.

0 is unlimited, and anything above that is the max number a User can set.


When updating DA, if the file:


does not exist, the will set it to 0 (unlimted, same as not-existing)

The /etc/virtual/limit and /etc/virtual/limit_dausername will still be enforced.

New Installs:

For new installs, the default value for:


will be 200, set by

Keep this in mind when restoring accounts from different server.



add new values:

<input type=text name=per_email_limit value="|PER_EMAIL_LIMIT|" size=4> &nbsp;&nbsp;<a target=_blank href="">(?)</a>

<input type=radio name=user_can_set_email_limit value="yes" |USER_CAN_SET_EMAIL_LIMIT_YES|>|LANG_YES|&nbsp;&nbsp;&nbsp;<input type=radio name=user_can_set_email_limit value="no" |USER_CAN_SET_EMAIL_LIMIT_NO|>|LANG_NO| &nbsp;&nbsp;<a target=_blank href="">(?)</a>

<input type=text name=max_per_email_send_limit value="|MAX_PER_EMAIL_LIMIT|" size=4> &nbsp;&nbsp;-1=|PER_EMAIL_LIMIT|, 0=|LANG_UNLIMITED| &nbsp;&nbsp;<a target=_blank href="[Per-Email send limit (SKINS)](/changelog/version-1.42.0.html#per-email-send-limit-skins)">(?)</a>



LANG_PER_EMAIL_LIMIT=Daily limit per E-Mail Account
LANG_USER_CAN_SET_PER_EMAIL_LIMIT=User can set limit per E-Mail
LANG_MAX_PER_EMAIL=Max limit User can set per E-Mail

install default change: add_userdb_quota=1 new

Relating to this feature:

add_userdb_quota for dovecot quotas

Dovecot can support per-email quotas in the /etc/virtual/ files.

CustomBuild 2.0 can install the new dovecot.conf for you:

./buld update
./build set dovecot_conf yes
./build dovecot_conf

For new installs, this option will be enabled by default:


via the value in the data/templates/directadmin.conf file.

Existing installs will not be affected.

The internal default is still:


so the value must exist in the directadmin.conf in order to enable it.

Show User: domain settings (SKINS) new

On the "Show User" page, eg:


The Domains table will have one more column after "Suspended" called "Settings".

This column will show 3 values:


and those options will either be green, bold red, or orange, depending on if they're turned on or off.

Orange means it's off, but the user.conf is also off for that setting so not notable.



    COLOR: green;
    COLOR: red;
    FONT-WEIGHT: bold;
    COLOR: #DBA24D;
} + new

Relating to


You can now check all options the User is trying to save through DA in case you want to restrict the settings to certain values.

All GET and POST values will be in the evironment, plus "username".

For both scripts, exit with a non-zero value to abort the action.

Exit with 0 to allow the action.

echo'ed output is displayed if non-zero exit value.


required_hits=0.0|custom   (decimal number)

if required_hits=custom:, this is used:
high_score=7 (integer)
blacklist_from=textarea of emails
whitelist_from=textarea of emails


Similarly, if you want to prevent the User from disabling SA:


Note: There is nothing stopping a User from just deleting the user_prefs file.



mod_security templates (TEMPLATES) new


With CustomBuild 2.0, mod_security can be installed.

DA will need a few template changes and logs for mod_security.

With mod_ruid2, logging is done with user permissions in /var/log/modsec_audit/user/*

Without mod_ruid2, it's in /var/log/httpd or /var/log/nginx:



directadmin.conf new value, internal default:


Template changes for:

virtual_host2*.conf + user_virtual_host.conf:

<IfModule mod_security2.c>
SecAuditLogStorageDir |MODSEC_AUDIT_DIR|/|USER|

nginx templates will use a global mod_security setting.

You can disable any nginx server{} by using the following in the Custom Httpd Config:

ModSecurityEnabled off;



NGINX_MOD_SECURITY_ENABLE=include /etc/nginx/nginx-modsecurity-enable.conf;
NGINX_MOD_SECURITY_ENABLE="" (blank) if mod_security is not enabled.


DirectAdmin will create:

/var/log/modsec_audit root:root 711

and on each user httpd.conf write, if missing:

/var/log/modsec_audit user:user 700


Log Rotation

Uses the same apache log rotation rules.

It respects:


Rotation is done during the full tally, right after /var/log/httpd/domains/*.log are rotated.

And just before the HUP to apache (so only 1 HUP is given)

Rotation on /var/log/modsec_audit/user/* is done based on the filename date, so assumes the correct folder naming format, eg:


Where the logs_to_keep setting will compute the number of seconds before now" (5 days = 5 * 24 * 3600), and if the date in the 20141230 (Dec 30, 2014) is older than 5 day before now, then the folder is deleted.

The logs:



are rotated up, using the .1 .2 ... .5 format.

Table class: major speed improvement for large tables new

The internal code that assembles all table text into one string has been optimized.

The slowdown was discovered when there were ~2000 users in the BFM file:


because the BFM User table (top right) shows all rows on one page, allowing for a very large table.

The old code caused the process appending the many small string to the end slower and slower, then larger the string got.

The new code keeps track of the tail end, so the entire string size isn't re-computed each time a string segment is added.

Before the optimization, it took the table 70 seconds to build, and now it's instant (For the 2000 User test).

This will affect all dynamically generated tables in DA.

For most cases, if you're only looking at 50 rows, the table isn't big, so wouldn't be too noticeable.

But anytime you select "All" (Advanced Search) ... or a table is showing many rows on one page, that's when this change will really be noticeable.

Also re-wrote the title, cell (td) structs, by adding a "row" struct (tr), to use less ram.

Previously, each cell would hold variables for the row, duplicating that variable more than was needed.

The new row struct stores all of the per-row settings, and then each cell has the linked list form there, with just the info the cells need.

And also merged 3 int's into 1, by using binary masking, (checkbox settings for the row)

For many cases, a td cell "value" (for searching/sorting) was not what we actually wanted to display.

For example, a filesize, in bytes, but we want it to be human readable.

Or a unix timestamp, but we want the formatted day/month/year.

Previously, it use a sort of hidden a href hack:

<a href="value"></a>Actual String

to hide the value, and show the Actual String.

Another td cell change was the addition of a simpler override string.. so we don't need any of that mess, making the tables smaller.

For tests, I decided to ramp it up to a more extreme case to see how it would perform.

I added ~77,000 random row entries.

The sorting of that data only took 0.07 seconds.

And the string assembly took 0.55 seconds, creating a monster 29.1MB table string. (vs 70 seconds for 2000 rows)

Obviously, no table should show that many rows on one page, but regardless, I'm pleased with the results.

Can also use the BFM, "Avanced Search", on the log entires table, show "All" entries.

For me, a 26 page table (meaning 50 x 26 rows), now loads almost instantly when all entries are displayed on one page.

Using the Chrome "Network" debug page, the start of the download for this "All" view starts ~350ms after the request.

In comparison, the main / page started ~250ms after the request..

So reading in all brute data (IP, Users, 26 pages of logs into 1 page, skip list, blocked IPs), AND assembling all tables/skins only takes 100ms.

I didn't take stats on this from before, but I know it was a bit sluggish even with only the single page (50 entries) being shown.

Site Redirection loops if using same site. fixed

If a user redirects the path / on his domain to /somefolder, the .htaccess code that is added will append somefolder to itself over and over in a loop because .htaccess files are recursive to lower level directories so somefolder will also trigger a redirect.

The fix will be to do a check if the domain being redirected to is the same as the domain being redirected from. If it is, use this instead:

RedirectMatch ^/$

backup_roundcube.php and restore_roundcube.php to force utf-8 encoding in mysqli fixed

RoundCube uses UTF-8 encoding in the database, so we've changed the mysqli chrset to utf8 for the backup/restore.


CMD_LOGIN_KEYS ignoring user.conf setting fixed

user.conf value for:


was being ignored.

Only the directadmin.conf value of login_keys=1|0 was being used.

Changed to also check the user.conf.

If disabled, will show:

Login Keys are disabled

notify_admins_on_mass_emailings fixed

New feature, and bugfix.

Internal default:


to determine if Admins will get a message on mass emailing.

It was previously controlled by notify_admins_on_per_email_mass_emailings which wasn't correct.

Bugfix was that the Admins were not being added to the list, in either case.

subject= missing from new reseller.conf files fixed

Relating to this change:

Move the user.conf write before

Because the write was moved to within the User class, and not outside the object... with the use of inheritance, this means that the Reseller class didn't get written.

Fix was to add a write of the Reseller files within the Reseller creation.

Confirm bytes sent with writes fixed

Confirm bytes were actually sent with SSL_write, and retry if !SSL_get_error, accounting for offset/size changes for each attempt.

Remove linked IPs from dns during User IP change fixed

Bug where the "swap" of a zone (when changing the main User IP) does not take into account the linked IPs.

This causes the old values to be left in the zone, after the main IP has already been swapped.

Fix was to:

  1. Get the list of Linked IPs for the old IP (to be removed)

  2. And then from this list, remove any IPs that:

  • are going to still be in the domains ip.list file

  • any IPs that are the linked IPs for the mentioned ip.list file

  1. Once the final list of IPs to remove is assembed, remove any value from the zone, after the "swap", and before the addition of the new linked IPs, for the new main IP

"ssl_port" may be working when "port" isn't fixed

If DA is already running on port 2222, and ssl_port=2223 is set, it's possible that a "start" of DA will fail with the "cannot bind to 2222" error, but the ssl_port starts up fine.

This can prevent the dataskq from noticing directadmin isn't running on 2222, since it's running on 2223.

I'm not 100% sure on the exact scenario, but it is possible (it's been reported)

The fix is to have the master daemon kill the ssl deamon child if it fails.. bring everything down if 2222 doesn't work.

directadmin.conf option: request_timeout replaces connect_timeout, new handshake_timeout fixed

Previous default was:


connect_timeout has been deprecated and replaced with:


Also added new value:


which applies to SSL handshakes only.

Because connections to SSL without any data generates:

2014:12:08-22:26:10: Can't connect to ssl!
2014:12:08-22:26:10: ->syscall

which was the internal SSL timeout time, not DA's timeout.

and the timeout is too long (60 seconds).

The SSL handshake should happen very quickly for valid connections, so we allow 3 seconds for the SSL handshake, and then 20 seconds for the client to start sending the request after the handshake is done.

After the first bit of data is sent, then it goes to the timeout=60.

For data uploads, it will do a larger timeout, accordingly (won't disclose this, so as to prevent attacker from figuring out any sort of slow loris attack).

If you have connect_timeout=XX in your directadmin.conf, DA will give you the message in the error.log (and debug):

connect_timeout has been replaced with request_timeout. Please change your directadmin.conf. directadmin.conf option: request_timeout replaces connect_timeout, new handshake_timeout

and will set:


so your old value is still respected, but should be changed.

The wording of connect_timeout was not clear, as compared to the 2 new values, as "connect" was referring to both the handshake, and the reception of the first few bytes.

Now the handshake_timeout applies to SSl and it's negotiations, and then the request_timeout applies to the reception of the first few bytes of headers.

After the first few bytes are received, it then goes into the normal timeout= value for the download of the data.

Security: Database upload to use defaults-extra-file fixed

Create tmp file in /home/username/.my.cnf.XXXXXX for use by mysql for database upload/restore.

Credit to Patrik at for reporting the issue.

skip ~/.cagefs in backups fixed

home.tar.gz was including /home/user/.cagefs directory.

Added to the skip list.

unchecking E-Mail Data will also skip ~/Maildir fixed

Previously, unchecking the E-Mail Data checkbox only applied to /home/user/imap, in terms of actual email data.

The checkbox now controls adding/excluding /home/user/Maildir from the home.tar.gz file as well.

Related to this feature:

Admin Backup Options to specify E-Mail Data (SKINS)

Security: use mkstemp in various locations fixed

the copyFile function will now use mkstemp to create a file on the side first, then rename the destination file out of the way.

Doing a direct write to the destination was not as secure.

Report: DA-0298

New Certificate Requests should not have a challenge password fixed

CSR creation will now skip the challengePassword line in the config.

Reported that many major certificate issuers no longer accept CSRs that contain the value.

Tickets entries n tickets.list doubling each reply fixed

If you're a Reseller or User and you're replying to a ticket, this caused the list entries in the tickets.list to double due to a double read of the list.

Added a check for the 2nd read to prevent the doubling.

Last Updated: