Пожалуй всем пользователям Linux известны такие файлы псевдо-устройств как /dev/random и /dev/urandom. Эти устройства являются интерфейсом к генератору случайных чисел ядра(RNG, random number generator).
Случайные числа(они же — непредсказуемый набор битов) очень важны в криптографии. Они являются базовыми кирпичиками для криптографических протоколов. От качества (неповторимости, непредсказуемости) этих чисел зависит стойкость всевозможных TLS-сертификатов, SSH и GPG ключей, сессионных симметричных TLS-ключей и т.д. Так же случайные числа являются основой для генерации UUID’ов, PID’ов, TCP sequence numbers и многого другого.
RNG генерит случайные числа на основе данных из пула энтропии(entropy pool) в ядре Linux. Наполнением этого пула так же занимается RNG и делается это на основе случайных событий в системе таких как: тайминги клавиатуры и дисков, движения мыши, прерывания(interrupts), сетевой трафик.
Пул энтропии имеет фиксированный объем 4096 bits:
cat /proc/sys/kernel/random/poolsize_x000D_ 4096
Размер пула нельзя изменить, это захардкожено в ядре.
Посмотреть текущий объем данных в пуле:
cat /proc/sys/kernel/random/entropy_avail_x000D_ 186
Доступный объем энтропии постоянно меняется, в зависимости от скорости пополнения и потребления соответственно.
Собственно через /dev/random и /dev/urandom приложения в user space получают эти самые случайные числаданные.
/dev/random является источником случайных данных наивысшего качества которые может предоставить ядро. Но при этом он блокирующийся, что означает, что приложение читающее из /dev/random повиснет в ожидании данных если пул энтропии окажется пустым.
/dev/urandom — unlimited random, не блокирующийся и приложения могут читать из него бесконечно. Предоставляет случайные данные такого же высокого качества что и /dev/random но до тех пор пока пул энтропии не опустеет. Когда пул будет пустым, /dev/urandom продолжит выдавать случайные данные но теоретически сильно меньшего качества.
Настоятельно рекомендуется для любых долго-живущих ключей, например для TLS-сертификатов использовать /dev/random т.к. только он гарантирует качество случайных чисел. Но, большинство приложений таких как Apache, Nginx, sshd и о Боже ssh-keygen, openssl используют /dev/urandom. Тут в принципе понятно Apache, Nginx, sshd не хотят блокироваться при генерации сессионных ключей. Но то, что ssh-keygen и openssl используют по умолчанию /dev/urandom меня поразило. Причем для openssl можно задать устройство при генерации ключей(ниже пример) а вот для ssh-keygen я возможности переопределить поведение не нашел.
В общем не важно откуда читают ваши приложения, нельзя допускать опустошение этого самого пула энтропии и тогда счастье будет и с /dev/urandom.
Прежде чем начать “майнить” энтропию, пара слов о ее корректном использовании из man /dev/random:
The amount of seed material required to generate a cryptographic key equals the effective key size of the key. For example, a 3072-bit RSA or Diffie-Hellman private key has an effective key size of 128 bits (it requires about 2^128 operations to break) so a key generator only needs 128 bits (16 bytes) of seed material from /dev/random.
Например openssl для генерации RSA ключа длиной 10240 bit использует всего 2048 исходного материала из /dev/random:
strace -e open openssl genrsa -rand /dev/random -out rootCA.key 10240_x000D_ …._x000D_ open("/dev/random", O_RDONLY) = 4_x000D_ 2048 semi-random bytes loaded_x000D_ Generating RSA private key, 10240 bit long modulus
Как заполнить пул энтропии?
Лучшее решение это использование специальных аппаратных средств(TPM, Trusted Platform Module) или инструкций процессора типа RDRAND(есть в Intel IvyBridge и Haswell процессорах).
Проверить наличие подобных устройств на сервере поможет утилита rngd из пакета rng-tools
rngd -v_x000D_ Unable to open file: /dev/tpm0_x000D_ can't open any entropy source_x000D_ Maybe RNG device modules are not loaded
Если rngd обнаружит поддерживаемые средства то вам повезло и вы можете запустить сервис rngd. В моем случае их нет)
Собственно задача rngd читать энтропию из аппаратных средств и наполнять ей пул энтропии ядра. Делается это через специальный ioctl вызов(RNDADDENTROPY) интерфейса /dev/random.
Если нет аппаратной поддержки
В интернете можно встретить рекомендации, где предлагают указывать /dev/urandom как источник энтропии для rngd. То есть источником энтропии для ядра по сути будет само ядро). Это довольно сомнительная идея, и я бы не стал так делать и вам не советую. Но ради эксперимента я провел тесты и результаты(которые ниже) тоже довольно не плохие.
Havegd
В основе лежит алгоритм HAVAGE который генерирует энтропию на основе счётчиков и состояний процессора. В силу сложного, многоуровневого устройства процессоров, один и тот же код всегда выполняется за разное время и это не постоянство является основой для алгоритма HAVAGE.
На практике, это user space демон который как и rngd наполняет пул энтропии ядра через ioctl интерфейс /dev/random. при этом не нуждается в источнике энтропии как rngd.
Для centos пакет доступен из epel.
После установки нужно просто запустить сервис. С параметрами по умолчанию haveged будет стараться держать пул энтропии ядра на уровне не ниже 1024.
Тестирование
Без rngd и haveged, команда(ниже будет понятно, что она делает):
cat /dev/random | rngtest -c 10000
Не завершилась за сутки!
rngd с /dev/urandom в качестве источника энтропии
(то, что я вам не рекомендовал)
Unit-файл rngd.service:
[Unit]_x000D_ Description=Hardware RNG Entropy Gatherer Daemon_x000D_ [Service]_x000D_ ExecStart=/sbin/rngd -f -r /dev/urandom -o /dev/random_x000D_ [Install]_x000D_ WantedBy=multi-user.target
Тест(худший из 3-х результат):
cat /dev/random | rngtest -c 10000_x000D_ _x000D_ rngtest 5_x000D_ Copyright (c) 2004 by Henrique de Moraes Holschuh_x000D_ This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE._x000D_ rngtest: starting FIPS tests..._x000D_ rngtest: bits received from input: 200000032_x000D_ rngtest: FIPS 140-2 successes: 9989_x000D_ rngtest: FIPS 140-2 failures: 11_x000D_ rngtest: FIPS 140-2(2001-10-10) Monobit: 0_x000D_ rngtest: FIPS 140-2(2001-10-10) Poker: 2_x000D_ rngtest: FIPS 140-2(2001-10-10) Runs: 6_x000D_ rngtest: FIPS 140-2(2001-10-10) Long run: 3_x000D_ rngtest: FIPS 140-2(2001-10-10) Continuous run: 0_x000D_ rngtest: input channel speed: (min=3.234; avg=7.202; max=7.981)Mibits/s_x000D_ rngtest: FIPS tests speed: (min=90.826; avg=142.151; max=146.719)Mibits/s_x000D_ rngtest: Program run time: 27833279 microseconds
Тут нужно смотреть на successes, failures, и на input channel speed.
При этом утилизация CPU процессом rngd: 57%
haveged
Тест(худший из 3-х результат):
cat /dev/random | rngtest -c 10000_x000D_ _x000D_ rngtest 5_x000D_ Copyright (c) 2004 by Henrique de Moraes Holschuh_x000D_ This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE._x000D_ rngtest: starting FIPS tests..._x000D_ rngtest: bits received from input: 200000032_x000D_ rngtest: FIPS 140-2 successes: 9993_x000D_ rngtest: FIPS 140-2 failures: 7_x000D_ rngtest: FIPS 140-2(2001-10-10) Monobit: 1_x000D_ rngtest: FIPS 140-2(2001-10-10) Poker: 0_x000D_ rngtest: FIPS 140-2(2001-10-10) Runs: 4_x000D_ rngtest: FIPS 140-2(2001-10-10) Long run: 2_x000D_ rngtest: FIPS 140-2(2001-10-10) Continuous run: 0_x000D_ rngtest: input channel speed: (min=1.780; avg=15.692; max=18.755)Mibits/s_x000D_ rngtest: FIPS tests speed: (min=79.473; avg=143.212; max=150.185)Mibits/s_x000D_ rngtest: Program run time: 13493892 microseconds
При этом утилизация CPU процессом haveged:12%
Виртуальные машины
Не рекомендуется использовать haveged внутри ВМ, т.к. там вроде как счетчики CPU не такие точные(типа округляются) и это сказывается на качестве энтропии.
Тру путь это использовать virt-ioRNG(qemu-kvm) — паравиртуальное устройство которое будет брать энтропию из пула хоста. Но, это уже совсем другая история…)
Помогла ли вам статья?