authelia: Inconsistencies in Argon2 memory configuration
Version
v4.37.5
Deployment Method
Docker
Reverse Proxy
NGINX
Reverse Proxy Version
1.24.0
Description
TL;DR
Issue
Setting a reasonable value for the argon2 memory
parameter in the Authelia configuration.yml
file results in extreme memory usage and very long processing times when checking or creating password hashes.
Fix
The --memory
argument for authelia hash-password
should take a value in KB, not MB. Maybe… See Solution Space below.
Context
I have a small Docker homelab that I’m setting up, and I’m using Authelia for authentication along side my reverse proxy (nginx). It’s almost definitely going to be just me using it, so I felt that the users_database.yml
method of user/hash store was adequate for my purposes.
I wanted to tune my Argon hashing to be light weight in terms of memory, so started with a memory limit of 4096kB, and tuned the iterations up, targeting around half a second user time (as I’ve seen recommended for good security).
Using this command:
$ time -v authelia crypto hash generate argon2 \
--iterations 150 \
--key-size 32 \
--memory 4096 \
--parallelism 4 \
--salt-size 16 \
--password password
Yields this output:
Digest: $argon2id$v=19$m=4096,t=150,p=4 ...
real 0m 0.36s
user 0m 0.52s
sys 0m 0.06s
This seems great, but when I plugged these Argon args into my configuration.yml
, I noticed that my Authelia login and password reset operations were hanging indefinitely, and that the Authelia process was sitting on +100% CPU, 512MB RAM (the limits I’ve configured in Docker during testing) and that the memory was spilling over into swap by the gigabytes.
The reason for this is that I was doing my timing tests (in the container) using this command:
Command 1: authelia crypto hash generate argon2 <args>...
And after a little digging through the source code, the internals of Authelia actually use this command when using values from configuration.yml
:
Command 2: authelia hash-password <args>...
Command 2 takes a memory parameter --memory
in MB, not kibibytes as everywhere else seems to suggest. This meant in my final configuration, I was actually asking Argon to do 150 iterations with 4GB of memory, in a container limited to 512MB. No wonder it was having a bad time…
Solution Space
My initial thought was that Command 2 should be changed to take the --memory
argument in kibibytes, which would be in line with the documentation, the default configuration, and most tutorials/documentation I’ve seen around.
However, this should probably not be done, since anyone using Authelia with the users_database.yml
authentication backend will all of a sudden have their Argon2 settings yield near instant hashing times (1 iteration of 1024KB), which would constitute a transparent reduction in security.
Some Speculation
I suspect the reason this hasn’t been noticed until now because most home users are using configuration.yml
settings like these:
password:
algorithm: argon2id
iterations: 1
key_length: 32
salt_length: 16
memory: 1024
parallelism: 8
I’ve seen this (or very similar) configuration recommended in several different Youtube tutorials, blogs and the like.
This setting of 1024MB for --memory
and just 1 iteration, yields a hash time circa 1 second (on my machine), which is mostly unnoticeable and will appear to work “nominally” apart from the >1GB memory usage spike during hashing operations. Even then, the memory spike would only really be noticeable if the user has a memory constraint on their Docker container less than 1GB, or, their system is close to using all available memory, and memory is spilling into swap. This would present as ~1-2 second authentication operations via the Authelia web front end, with the potential for intermittent very long or apparent hanging authentication operations.
Workaround
I am using the following configuration:
password:
algorithm: argon2id
iterations: 35
key_length: 32
salt_length: 16
memory: 32
parallelism: 4
memory: 32
is required to satisfy the memory >= parallelism * 8
requirement (reported by CLI if you give it less memory).
These settings give me consistent hash times around ~0.5s, meaning authentication in the web front end is very snappy 😃 It also means I can limit my Docker container to around ~64MB of memory.
Reproduction
The following is run inside a stock Authelia Docker container using the authelia/authelia:latest image.
- Run the following command:
time -v authelia crypto hash generate argon2 --iterations 1 --key-size 32 --memory 1024 --parallelism 4 --salt-size 16 --variant argon2id --password password
- Observe output similar to this:
Digest: $argon2id$v=19$m=1024,t=1,p=4$YsLnlyVc5YLmRUqtvjm3JQ$hNphHxGeB2ANspT+RUbc5mUT8YJvYCmnuUUgwBeVbo0
Command being timed: "authelia crypto hash generate argon2 --iterations 1 --key-size 32 --memory 1024 --parallelism 4 --salt-size 16 --variant argon2id --password password"
User time (seconds): 0.05
System time (seconds): 0.01
Percent of CPU this job got: 126%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0m 0.05s
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 160736
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 8270
Voluntary context switches: 353
Involuntary context switches: 115
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0
- Run the following alternate command:
time -v authelia hash-password --iterations 1 --key-length 32 --memory 1024 --parallelism 4 --salt-length 16 -- password
- Observe resulting output. In particular, note that the memory usage is now in the ~4GB range, and that the process took a lot longer to complete than in step 2.
Digest: $argon2id$v=19$m=1048576,t=1,p=4$SzLsq+XmS/8SH4oyaHUh4w$HaaS+8zmd2X8PH1UWEgZCs0j6m3nqV7Sct1Ok6bX/xM
Command being timed: "authelia hash-password --iterations 1 --key-length 32 --memory 1024 --parallelism 4 --salt-length 16 -- password"
User time (seconds): 1.15
System time (seconds): 0.39
Percent of CPU this job got: 184%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0m 0.84s
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 4488944
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 18192
Voluntary context switches: 377
Involuntary context switches: 6813
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0
Expectations
- The results of running the two commands described in the reproduction section should yield roughly the same results
- Using the configuration.yml snippet pasted below should yield results similar to those obtained via the
authelia crypto hash generate argon2
facility - The default value for memory in the
authelia hash-password
facility should be more appropriate. Right now it’s set to 65GB. Although without anchoring solutions, I believe this is a reasonable value if the--memory
arg were changed from MB to KB
Configuration (Authelia)
...
authentication_backend:
password_reset:
disable: false
refresh_interval: 5m
file:
path: /data/users_database.yml #this is where your authorized users are stored
password:
algorithm: argon2id
iterations: 150
key_length: 32
salt_length: 16
memory: 4096
parallelism: 4
...
Logs (Authelia)
N/A
Logs (Proxy / Application)
N/A
Documentation
N/A
Pre-Submission Checklist
-
I agree to follow the Code of Conduct
-
This is a bug report and not a support request
-
I have read the security policy and this bug report is not a security issue or security related issue
-
I have either included the complete configuration file or I am sure it’s unrelated to the configuration
-
I have provided all of the required information in full with the only alteration being reasonable sanitization in accordance with the Troubleshooting Sanitization reference guide
-
I have checked for related proxy or application logs and included them if available
-
I have checked for related issues and checked the documentation
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 19 (11 by maintainers)
Mostly correct, The old deprecated config is documented just in older versions of the docs (i.e. not in your face).
I think you missed the
--memory 1024
flag in your reproducer:$m=1048576
vs$m=1024
looks suspicious.Apparently the
hash-password
was removed in e3e31e3cbc2a2c030dc51f7cd6a15ccdfc956873 (the day following the 4.37.5 release), this issue might not be relevant anymore 🤷