Version 1.58.2

Released: 2019-08-27

One-Time Login Hash URL new

You can now create a one-time use URL which will automatically log you in as the specified User.

NOTE: Must have Login Keys enabled for the given User in their user.conf.

For example, to send someone a login URL to be logged in as admin, type:

/usr/local/directadmin/directadmin --create-login-url user=admin

which will output something similar to:


where the hash value is a randomly long length from 120 to 148 characters.

  1. The hash is stored on disk the the global file:

but it will be crypted as the "left-side" index, with details about this hash on the right-side.

Thus, a lookup of a given hash must cycle through each item, testing the crypt until found.

It does create a Login Key under this User, but insetad of a crypted key, it saved "key=hash", signifying that it's a login url, standing by.

  1. Once a valid hash has been accepted, the login key is swapped with a true crypted password, and that password is set in the sessions file.

The password for this key is never seen.

The original login hash is removed from the global login_hashes file.

Cookie is sent, and the login works just like any other Login Key.


By Default the Login Hashes live for 3 days, including the end of the Login Key time.

So you have up to 3 days to login and logout. (it's not extended upon hash-to-key conversion)

You can set a different time by adding:


for example, to the --create-login-url options list.

Valid time units are:


and ARE case sensitive.


IPs: You can list one or many IPs or ranges by adding this to the options:

Commands: Allow + Deny

Similar to the Login Keys, you can control which CMDs are allowed or denied by doing something like:


which would block all Admin Level functions for this URL hash.

Just be careful, if you block ALL_ADMIN as it's difficult NOT to make Admin Level calls, for some things like ajax counts, etc.


When logged in with a login hash, upon clicking "Logout" (CMD_LOGOUT), it will destroy the session but will also delete this Login Key, so it doesn't hang around after.

It should get delete eventually, after the expiry hits during various cleanup operations.


You can also get json output by calling it with json=yes:

/usr/local/directadmin/directadmin --create-login-url user=admin json=yes

which would output something like:

        "allow_htm": "yes",
        "clear_key": "yes",
        "expiry_timestamp": "1566096767",
        "hash": "QTyjeGyhIDpZLit4....abZ2UJCczm1U",
        "keyname": "HASHURLvicJDn5L",
        "max_uses": "0",
        "url": ""

Suspend/Unsuspend Users/Domains from command line new

New directadmin command line options let you suspend/unsuspend accounts from ssh as root:

Suspend account (any account type):

/usr/local/directadmin/directadmin --suspend-user user=fred

Unsuspend account:

/usr/local/directadmin/directadmin --unsuspend-user user=fred

Suspend domain:

/usr/local/directadmin/directadmin --suspend-domain

Unsuspend domain:

/usr/local/directadmin/directadmin --unsuspend-domain

One-Click login to any RoundCube account from DirectAdmin (SKINS) new

UPDATE: Please follow this guide to enable this feature:

One click login to roundcubeopen in new window


NOTE: RoundCube 1.3.10 requires version 0.2 of the direct_login module.

If you're having issues, please:

./build update
./build roudcube

Issue report:

New feature, found on the E-Mail Accounts page of the User Level, where the "Login" column will show extra characters (arrow and letter) to signify that the one-click login method is enabled.

By default it's disabled (for now), with the internal default being:


to enable it, run:

cd /usr/local/directadmin
./directadmin set one_click_webmail_login 1
service directadmin restart
cd custombuild
./build update
./build dovecot_conf
./build exim_conf
./build roundcube

Requires CustomBuild build script at least rev 2148.



  1. When the "Login" URL is clicked for the given email account, a javascript function will post to:
  1. This user and a new random crypted pass is setup in /etc/virtual/, eg:

with only the created timestamp as additional information, as the is only used as the dovecot passdb, not the user info (which still uses the passwd file)

  1. DA also creates a token file for the RoundCube login:


Where the TOKENHASH file contains the encoded email,password,client IP, and creation time.

  1. DA outputs an auto-submitting form to <host>/roundcube/direct_login/index.php

with token=TOKENHASH

  1. The direct_login/index.php reads in the TOKENHASH to ensure it's all correct.

There is a 10 second window from the time in the token or the token is denied.

The TOKENHASH file should be delete regardless if it worked or not.

Only the IP used to create the token through DA/2222 is allowed to use this token.

  1. The direct_login/index.php then logs the User into RC and creates the cookies, etc.

  1. The fully tally run in dataskq will check/clear old RoundCube tokens, and old passwd_alt entries.

The direct_login code in RC only allows tokens to live for 10 seconds.

The tally check cleans them up if they're more than an hour old (only runs once a day, so they could sit a while if the request didn't go through)

passwd_alt entries are cleaned up if they're older than 16 hours old.

Any changes/additions to a passwd_alt by DA would do the check to clear them out sooner, but the task.queue would still clean them up daily if they're more than 16 hours old at the time of the check.

This means your login is at most 16 hours. Should be plenty.



You shouldn't need to know this, but RoundCube, Dovecot, and the do require changes for this to work.

CustomBuild will be able to do this for you, just run:

./build update
./build dovecot_conf
./build roundcube

after enabling the directadmin.conf setting.

Very rough (not for anyone to use, just as a reference)

wget -O /etc/dovecot/conf/alternate_passwd.conf

cd /var/www/html/roundcube
tar xvzf roundcube_direct_login-0.1.tar.gz
chown -R webapps:webapps direct_login
chmod 711 direct_login
chmod 700 direct_login/tokens

/etc/ needs to be version 28 or higher, to parse the passwd_alt files.




added JS+form to be triggered by the login column:

<script type="text/javascript">
<!-- // start preload code
function webmail_login(email)
document.getElementById("webmail_email").value = email;
// done with preload code -->
<form id='webmail_form' action='CMD_WEBMAIL_LOGIN' method='POST'>
<input id='webmail_email' type='hidden' name='email' value=''>



You'll see this token:


which can be used to POST the above form to CMD_WEBMAIL_LOGIN as needed.

Ability to reset all User zones new

If you need to reset every User-created zone back to the default (same as the "Reset Defaults" button when viewing a zone, you can use the following task.queue command to do it:

cd /usr/local/directadmin

echo "action=reset&value=all_zones" >> data/task.queue; ./dataskq d2000

Note, it only resets domains which are listed in /etc/virtual/domainowners

It does not reset other zones.

It's highly recommended you backup all of your zones prior to the reset, in case it does not give you the desired result.

Default change: Restore with LOCAL nameservers new

The old default was to restore with the NS records from the backups.

This change will have new installs default to the local NS records, overriding the zones.

Existing installs should not be affected as long as you've already done a backup/restore.

If unsure, please check your backup settings to ensure they're correct:

Admin Level -> Admin Backup/Transfers -> Backup/Restore Settings


Reseller Level -> Manage User Backup -> Backup/Restore Settings

Evolution: "Restore with local NameServers."

Enhanced: "Restore with Local NameServers. (unchecked: Use NS values from backup)"

Default: letsencrypt_multidomain_cert from 2 to 3 new

The letsencrypt_multidomain_cert=2 was the previous default.

It would list all domains and pointers under the User.

Listing other domains doesn't make much sense unless you're on an owned IP, and this is the main domain.

New internal default:


But the need for owned IPs these days is low, with the use of SNI everywhere.

CMD_API_SSL include key in CSR request output new

When creating a request to generate a CSR, you can now include:


in the POST request for request=yes, and in addition to the API output generating the "request=", it wll also include the "key=".

DNSSEC subdomain to push DS to remote DA server when in two-way DNS fixed

Relates to this feature:

dnssec_add_subdomain_ds_to_parent to check Multi-Server Setup zones


Where should have:

  • on box A

  • on box B

when signing on B, the DS records get added to's zone on A

This is all fine, except when A and B are in two-way clustering.

A -> B

B -> A

The issue was that before deciding to push the DS records to the parent zone, DA figures out if that zone is local or not.

Because A had pushed it's copy of over to B, the lookup on B of "is the parent local" returns true, thus the remote push is not attempted.

The local write then fails as on B is a raw dnssec file not meant to be read by DA, stored on B in /var/named/ (rather than on A)

The fix is to simply add another check when doing the "is this domain local" lookup, so also exclude any zone that is dnssec signed (the larger zone on B with more DNSSEC data).

So "is parent domain local and not the dnssec from some other server", which now returns "false" for on B, thus DA goes to the cluster to push the DS records into on box A.


FileManager: Reset Owner: needed POST (SKINS) fixed

The "Reset Owner" and "Recursively" buttons in the Enhanced FileManager were using GET requests, but needed POST.

Using GET produced:

"The requested command requires POST but GET was used"

Solution is to have a form, submitted via javascript when given path is clicked.





<form id='reset_owner_form' action='/CMD_FILE_MANAGER' method='POST'>
<input id='reset_path' type='hidden' name='path' value=''>
<input type='hidden' name='action' value='resetowner'>
<input id='reset_method' type='hidden' name='method' value=''>
<script type="text/javascript">
function reset_owner(f, recursive)
if (recursive == "1")
document.getElementById("reset_method").value = "recursive";
document.getElementById("reset_path").value = f;

As the hrefs in the table will now call:

reset_owner('/full/path/file.txt', '1');

where '1' for recursive and '0' for just that file.

Invalid json out in FileManager for names with backslash fixed

Filenames that contains a \ character generated invalid json in the related request:


json_encoded the indexes solved it.

Compile time: Aug 24 2019 at 17:05:29


disable axfr (SECURITY) fixed

During a fresh DirectAdmin install, if there is no named.conf, DirectAdmin will install one for you.

The provided named.conf from us has always included allow-transfer { none; }; This is a good thing.

However, if you have a named.conf before DirectAdmin is installed, it would previously use the default allow-transfer setting, whatever it might have been.

This change is to check to see if there is no trace of allow-transfer anywhere in your named.conf (or included options files), and if there are zero traces of that key-word, it will be added.

Both new installs an existing installs will check and update the named config options{} section for this, and lock it down if it's not present.

If you already have the setting, have the setting set to do something else (allow other IPs), etc, then no change will be done.

If you DO want axfr transfers enabled, then add any variation of the desired allow-transfer setting to the options{} area of your named.conf, and future updates won't bother you again.


Francisco @

Logrotate: php-fpm to use USR1 fixed

The /etc/logrotate.d/php-fpm file was using signal USR2 to reload the logs, but that's a full reload of the entire php-fpmXX daemon.

We only need a re-open the logs, so switch to USR1.

Only affects new installs.

If you would like the lighter reload for existing installs (if you use php-fpm), grab a fresh logrotate file:

wget -O /etc/logrotate.d/php-fpm

Access-Control-Allow-Origin: only on client Origin header fixed

Related to this:


You can now set multiple values in the access_control_allow_origin setting, eg:


using a comma separated list.

for example.

The Access-Control-Allow-Origin header will now only be shown if:

  1. There is an incoming Origin header from the client

  2. That header exact matches on of the items in the list, including port, etc.. (extract string match)

The comma separated entries are trimmed, so whitespace before/after is ok.

Users can cause root race condition (SECURITY) fixed

A root level issue was discovered that allows local Users to perform a certain action at a very specific time, skipping over a check that DirectAdmin had just performed.

This could cause root files to be overwritten, resulting in damage to the system.

This cannot be performed by an external attacker, and we have no known reports of any compromised systems. We strongly suggest updating as a means of neutralizing the threat.

To prevent promoting this exploit and attracting attacks, further details will be released at a later date, to allow enough time for upgrades to complete.


Bartosz Kwitniewski

Max DB length is less per underscore fixed

If the mysql.db table has a db column with length 64, the actual max db name length is 63 because the _ character for this column is escaped to \_ where \_ is actually stored in that column.

Also, if you allow underscores in your DB names (which we highly discourage), this is included in the size count for each underscore present. (please try and move away from using this option)

MySQL seems to allow over-length database names, but the column used to display them is limited, so this needed to be capped to prevent the broken case.

The result would be the number of users shows -1 when there are in fact 2 (system account + db_name)



will now include:

"MAX_DB_LENGTH": "63",

which is directly based on the mysql.db.db table/row size less 1, since all DB names have the _ character, thus \_

You'd need to subtract the username length+1 from this value to know the true length allowed.


Last Updated: