Basic HTTP авторизация для Nginx

В принципе это тривиальная операция описанная в документации к Nginx, но немного усложняет этот процесс необходимость создавать htpasswd файл, причем в самом Nginx средств для его создания нет (и это правильно), но предлагается создавать его используя утилиту htpasswd  входящую в состав Apache. Что самое смешное, на многих серверах, где используется Nginx, апача нет и не ожидается. Более того, большинство администраторов хостингов, “познавших” для себя Nginx начинают нескрываемо недолюбливать апач и верят, что наступит тот час, когда для Nginx наконец сделают аналог htaccess и апач, исправно служивший им долгие годы, наконец-то сгинет в небытие. Чтобы исправить сей досадный факт, добавлю в статью онлайн-формочку для генерации htpasswd фйла.

Давайте для примера создадим отдельный поддомен для хранения секретной информации с доступом по логину/паролю и в нагрузку в ней  будут директория для доступа к которой понадобится дополнительная пара логин/пароль и директория с включенным листингом файлов тоже с отдельным доступом. Т.е. структура примерно такая:
/ (доступ к файлам по _логин-пароль_1)
–otherpass/ (доступ к файлам по _логин-пароль_2)
–listed/ (включен автоиндекс файлов, т.е. можнго просмотреть какие файлы тут лежат, доступ к файлам по _логин-пароль_3)

Создадим виртуалхост Nginx

Допустим, для поддомена adminka.example.com. Для этого создадим файл

sudo nano /etc/nginx/sites-available/adminka.example.com

со следующим содержимым:

server {
	listen   80;
	server_name  adminka.example.com;
	access_log  /var/log/nginx/adminka_example_com.log;#можно написать /var/log/nginx/$host.log но придется заранее создать лог-файл и выставить на него правильные права
	root   /var/www/$host/htdocs;
	index  index.html index.htm index.php;
}

Ок, сохраняем, делаем этот хост доступным (включаем в конфиг нджинкса):

sudo ln -s /etc/nginx/sites-available/adminka.example.com /etc/nginx/sites-enabled/adminka.example.com

Создаем директории для htdocs

mkdir /var/www/{adminka.example.com,adminka.example.com/htdocs,adminka.example.com/htdocs/{otherpass,listed}

Если кто не понял, эта команда делает то же, что и 4 следующие:

mkdir /var/www/adminka.example.com
mkdir /var/www/adminka.example.com/htdocs
mkdir /var/www/adminka.example.com/htdocs/otherpass
mkdir /var/www/adminka.example.com/htdocs/listed

Рестартим Nginx

sudo service nginx configtest
sudo service nginx restart

Проверяем: http://adminka.example.com/
Ага

403 Forbidden
nginx/0.7.64

Пароли не просит, листинг директории у нас не включен и index файла мы не создавали. То же самое для директорий otherpass и listed. Соответственно получаем 403 ошибку “доступ запрещен”.

Добавим листинг (если нужен)

Мы договорились, что в директории listed у нас разрешен просмотр содержимого директории… Редактируем конфиг

sudo nano /etc/nginx/sites-available/adminka.example.com
server {
	listen   80;
	server_name  adminka.example.com;
	access_log  /var/log/nginx/adminka_example_com.log;
	root   /var/www/$host/htdocs;
	index  index.html index.htm index.php;
	location /listed/ {
		autoindex on;
	}
}

Сохраняем, перезапускаем, пробуем: http://adminka.example.com/listed/
Ага, работает:

Index of /listed/
_________
../
_________

Но где же обещанные пароли? Спокойно, сейчас все будет!

Добавляем Basic HTTP authorization.

Пароли для разных директорий будем хранить в разных htpasswd файлах за пределами htdocs. Например так:

/var/www/adminka.example.com/
--/root_htpasswd
--/listed_htpasswd
--/otherpass_htpasswd
server {
	listen   80;
	server_name  adminka.example.com;
	access_log  /var/log/nginx/adminka_example_com.log;
	root   /var/www/$host/htdocs;
	index  index.html index.htm index.php;
	location / {
		auth_basic "Unauthorized"; # текст сообщения сервера с предложением ввести пароль
		auth_basic_user_file /var/www/$host/root_htpasswd; # путь к htpasswd файлу
	}
	location /listed/ {
		auth_basic "Unauthorized";
		auth_basic_user_file /var/www/$host/listed_htpasswd;
		autoindex on;
	}
	location /otherpass/ {
		auth_basic "Unauthorized";
		auth_basic_user_file /var/www/$host/otherpass_htpasswd;
		autoindex on;
	}
}

Создаем htpasswd файлы (см. далее), сохраняем, перезапускаем, проверяем!

Генерация хеша пароля

HTTP авторизация в Nginx работает так же как и в Apache, т.е. с использованием htpasswd файла, но с тем лишь ограничением, что для шифрования пароля можно использовать только стандартный алгоритм DES с двухсимвольной солью. В Linux для этого используется C-функция crypt() (см man 3 crypt).

Можно использовать утилиту htpasswd из комплекта поставки Apache

htpasswd -nd seriy
New password:
Re-type new password:
seriy:4lQ0JcanrGr9E

Вывод этой утилиты состоит из 3-х частей: первая, до двоеточия “seriy” – это имя пользователя, “4l” – это двухсимвольная соль для функции crypt() и остальная часть “Q0JcanrGr9E” – это сам хеш пароля. Единственная проблема такого подхода – придется устанавливать apache.

Вот пара примеров кода для генерации htpasswd (your_password – ваш пароль, salt – соль для пароля, должна содержать 2(!!!) символа из набора “./0-9A-Za-z”):
используя php:

php -r 'echo crypt("your_password", "salt");'

используя Python:

python -c 'import crypt; print crypt.crypt("your_password", "salt")'

Если кто предложит примеры на других языках – (perl, ruby) буду благодарен. Возможно для этого можно использовать утилиту mcrypt но я в ней не смог разобраться.

В завершение – вот простенькая веб-формочка для генерации htpasswd онлайн.

11 thoughts on “Basic HTTP авторизация для Nginx

  1. Pingback: я сервера pw « Мысли блогеров всего интернета

  2. rock

    Что тут писать-то?

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    from sys import argv
    from random import choice
    from string import ascii_letters, digits
    from getpass import getpass
    from crypt import crypt
    
    if __name__=="__main__":
            name = argv[1:2]
            if not name:
                    print "Введите имя пользователя:",
                    name = raw_input()
                    if not name:
                            print "ОШИБКА: Введите имя пользователя."
                            exit(1)
            else:
                    name = name[0]
    
            salt = ''.join(choice(ascii_letters.join(digits)) for char in range(2))
    
            passwd = getpass("Введите пароль: ")
            if not passwd == getpass("Введите пароль еще раз: "):
                    print "ОШИБКА: Пароль и подтверждение не совпадают."
                    exit(2)
    
            passwdHash = crypt(passwd, salt)
    
            print name + ':' + passwdHash
    
            exit(0)
    Reply
  3. Incognito

    у меня почему к listed постоянно пароль запрашивает, а к корневой один раз ввел в браузере и после этого открывается без запроса пароля, так и должно быть?
    как нибудь время жизни пароля задать можно?

    Reply
  4. Павел

    Апач устанавливать не нужно, достаточно установить apache2-utils.
    Для Debian Linux:
    aptitude install apache2-utils

    Reply
    1. Дмитро Раби

      Парсер лох, ковычки нужны одинарые или двайные “лапки”

      Reply
  5. mad_crack

    Каталоги можно сделать командой покороче, если использовать ключик -p

    mkdir -p /var/www/adminka.example.com/htdocs/{otherpass,listed}

    Reply

Leave a Reply to Павел Cancel reply

Your email address will not be published. Required fields are marked *