Пошаговая настройка SSL для Apache

Перевод статьи «Step by Step: Configuring SSL Under Apache» автора Juliet Kemp.

Вступление

Если у вас безопасный веб-сервер, пользователи, волнующиеся за безопасность своих данных, могут быть уверены, что запросы зашифрованы, поэтому их данные в безопасности. Лучшим путем для этого является использование Apache 2, лидирующего веб-сервера под Linux и Secure Sockets Layer, протокол безопасной передачи данных. Transport Layer Security (TLS) является развитием SSL, но они работают практически одинаково. Я буду ссылаться только на SSL.

SSL — протокол для безопасной передачи зашифрованных данных между веб-браузером и веб-сервером. В большинстве случаев, аутентификацию проходит сервер, что позволяет клиенту быть уверенным в том, что сервер является требуемым ему, а не наоборот. Как бы то ни было, когда соединение устанавливается, обе стороны находятся в безопасности, так как только клиент и сервер имеют доступ к ключу. Это работает в течении многих запросов, сервер не интересует, кем является клиент так долго, сколько он остается тем же клиентом на протяжении запроса. Если вас волнует аутентификация клиента, есть возможность использовать клиентские SSL сертификаты (или htaccess, Kerberos или другие, близкие к ним методы), но это не будет рассматриваться в этой статье.

Будучи на стороне клиента, вас волнует, тому ли человеку (серверу) вы посылаете какие-либо личные данные, которые хотите зашифровать. Поэтому сервер, а не клиент, аутентифицируется. Вас также беспокоит участие третей стороны в доступе к вашим данным в том виде, каком вы их посылаете. SSL предоставляет оба этих вида безопасности.

Протокол SSL работает следующим образом:

1. Клиент подключается к веб-серверу и дает список доступных кодов.

2. Сервер берет наиболее устойчивый код, который поддерживает и он, и клиент, и посылает сертификат со своим именем и ключ кодирования, подписанный доверенным Удостоверяющим Центром (Certificate Authority, далее — CA), таким как Verisign.

3. Клиент проверяет сертификат с помощью CA. На практике, хранят набор CA локально, поэтому это может быть сделано без контакта в реальном времени с CA, и поэтому более быстро.

4. Клиент посылает назад случайное число, зашифрованное с помощью публичного ключа сервера. Только клиент знает это число, и только сервер может его расшифровать (используя личный ключ); вот где реализуется безопасность от участия третей стороны.

5. Сервер и клиент используют случайное число для генерирования содержимого ключа, который используется на протяжении передачи данных.

Мы хотим сделать это на стороне клиента настолько прозрачным, насколько это возможно, чтобы сделать передачу данных настолько легкой, насколько возможно.

Настройка Apache для работы с SSL проста, но есть несколько необходимых шагов. Эта статья рассказывает, как получить сертификат, подписанный CA и как скомпилировать и настроить Apache для работы с SSL. Я использую Apache 2 с mod_ssl. ApacheSSL (реализация Apache с возможностями SSL) также доступен, но сейчас он уже устарел; mod_ssl намного лучше.

Создание сертификата.

Первый шаг — создание сертификата. Вы можете создать сертификат с паролем или без него. Главным неудобством использования пароля является тот факт, что он должен быть введен каждый раз при старте сервера. Поэтому он не может запускаться без присмотра или автоматически при загрузке, например, после выключения электричества. В зависимости от ваших установок, это может быть значимым фактом для вас, или нет.

Теоретически, преимуществом использования пароля являтся повышение защиты. Хотя, на практике пароли не дает такой большой защиты. Если кто-то сможет прочитать или скопировать приватный ключ, значит, у него уже есть доступ к системе и возможность получить пароль, например, используя такую программу, как кейлоггер. Пароль защитит от скрипт-кидди, но не от серьезного взломщика. Возможно, для большинства людей нет смысла его использовать.

Для тестовых целей, или для небольшой локальной сети, вы можете создать сертификат, подписанный вами. Вы можете сделать это, выполнив команду:

openssl req -new -x509 -days 365 -sha1 -newkey rsa:1024 \
 
-nodes -keyout server.key -out server.crt \
 
-subj '/O=Company/OU=Department/CN=www.example.com'

Давайте рассмотрим опции более подробно:

— -x509 означает, что сертификат обязателен, правильнее, чем просто запрос сертификата (смотри ниже)

— -days 365 устанавливает время истечения сертификата, равное году. Вы можете увеличить этот срок. Запомните дату истечения срока, чтобы обновить её при необходимости.

— -sha1 указывает, что будет использован SHA1 для шифрования.

— rsa:1024 делает ключ 1024 битным RSA.

— -nodes указывает отсутствие пароля.

— -keyout и -out указывают, где хранить сертификат и ключ. Ключ должен быть доступен для чтения только для root; сертификат может быть доступен для чтения для world, но должен быть доступным для чтения пользователю, который запускает Apache.

— -subj устанавливает имя компании, имя отделения компании и адрес веб-сайта. Если вы это пропустите, вас попросят это сделать. CN должен совпадать с адресом сайта, иначе сертификат не будет соответствовать, и пользователи будут получать предупреждение при подключении. Убедитесь, что вы не запрашиваете пароль.

Проблема с использованием сертификата, подписанного самостоятельно для сервера, работающего в реальном времени, заключается в том, что любой браузер, работающий с сайтом, не будет признавать сертификат правильным. Это значит, что пользователя будут запрашивать о подтверждении сертификата. Очевидно, что в большинстве случаев это не является оптимальным решением. Хотя, это нормально для целей теста, а для небольших локальных сетей было бы плохим вариантом платить за сертификат от CA.

Не смотря на это, безусловно, лучше использовать сертификат, подписанный доверенным Удостоверяющим Центром, таким как Verisign (который имеет наибольший охват рынка), или меньшей организацией. Большинство браузеров уже имеют набор предустановленных сертификатов, заверенных CA, которые верифицируют сертификат вашего веб-сервера при подключении клиента. Это уменьшает количество трудностей для конечного пользователя и удостоверяет законность вашего сайта.

Чтобы получить сертификат, подписанный CA, прежде всего вы должны создать криптографическую пару и запрос сертификата:

openssl req -new -sha1 -newkey rsa:1024 -nodes \ 
 
-keyout server.key -out www.example.com.csr \ 
 
-subj '/O=Company/OU=Department/CN=www.example.com'

Этот пример работает так же, как и предыдущий, но на этот раз мы не используем опцию -x509. Выполнение этой команды приведет к генерации ключа и запроса сертификата, но не самого сертификата. Если вы ввели CN и так далее, вы не должны вводить адрес e-mail или пароль.

Ключ сервера (файл server.key, который, опять же, должен быть доступен для чтения только для root) остается на вашем веб-сервере; запрос (файл www.example.com.csr) отправляется в CA. Вы можете назвать файл запроса так, как вам вздумается, но назвав его по своему доменному имени, вы упростите задачу для CA.

Следующей стадией будет послать этот файл www.example.com.csr в CA, с вашей оплатой. Они должны вернуть его достаточно быстро, если вы предоставили всю требуемую информацию в вашем запросе. Выбранный вами CA объяснит их действия на своем сайте. Вам может понадобиться поменять формат файла на PEM, но, в случае Verisign, этого делать не придется.

Когда вы получите файл назад в PEM формате, переименуйте его в server.crt (это не является строгой необходимостью, но соответствует условиям Apache) и проверьте его:

    openssl verify -CAfile /path/to/trusted_ca.crt \
 
		-purpose sslserver server.crt

Затем проверьте, что результат выполнения этих двух команд одинаков, т.е., что сертификат соответствует приватному ключу:

    openssl x509 -noout -modulus -in server.pem | openssl sha1
 
    openssl rsa -noout -modulus -in server.key | openssl sha1

Теперь установите ваш ключ (сгенерированный выше как server.key) и сертификат (server.crt) в /etc/apache2/ssl или предпочитаемый вами каталог настроек, если он другой. Как указано выше, очень важно убедиться, что server.key доступен для чтения только для root, в то время как сертификат сервера может быть доступен для чтения для world, но принадлежать и быть доступным для записи только для root.

Компиляция Apache с SSL.

Итак, ваш сертификат сгенерирован. Теперь вам надо настроить свой сервер для его использования.

Для подавляющего большинства людей, лучшим способом установить и управлять Apache2 — модули, полученные через менеджер пакетов вашего дистрибутива. Apache2 из Debian идет вместе с модулем SSL, но он не включен по умолчанию. Для его включения вы должны выполнить команду: a2enmod ssl и перезапустить веб-сервер.

Чтобы сделать это, добавьте строчку

    Include /etc/apache2/mod_ssl.conf

в ваш /etc/apache2/apache2.conf (этот файл может также называться httpd.conf). Вы должны исправить её, обозначив соответствующее расположение файла mod_ssl.conf. Затем перезапустите веб-сервер.

Если вы хотите скомпилировать Apache2 из исходных кодов, в зависимости от того, какие установки вы до этого использовали, вы можете уже иметь или не иметь поддержку SSL. Проверьте это командой apache2 -l. Если перекомпиляция понадобится, запустите ./configure со всеми опциями, которые вы использовали до этого, добавив к ним —enable-ssl и —enable-setenvif (последняя нужна для совместимости с капризами Internet Explorer). Затем установите его как обычно, с помощью make;make install и проверьте правильность прав доступа.

Настройка Apache с SSL.

Следующим шагом будет настройка Apache2. Следующие инструкции приведут к запуску сервера как безопасного (порт 443) и как обычного веб-сервера (порт 80). Прежде всего, вам надо настроить сервер на принятие запросов на оба порта. Или отредактируйте /etc/apache2/ports.conf (в Debian, это входит в apache2.conf), или отредактируйте /etc/apache2/apache2.conf, включив строки:

Listen 80
 
Listen 443

Затем отредактируйте /etc/apache2/sites-enabled/yoursite для использования настроек SSL. Разделение настроек обычного и безопасного сервера с помощью VirtualHost — простейший способ из соображений условий эксплуатации. Любые настройки вне секций VirtualHost (например, установка ServerAdmin) будут применяться для обоих (и любых других) VirtualHost. Добавьте следующий текст в файл конфигурации:

# =================================================
 
# SSL/TLS settings
 
# =================================================
 
NameVirtualHost *:443
 
    DocumentRoot "/local/www/ssl_html"
 
    SSLEngine on
 
    SSLOptions +StrictRequire
 
        SSLRequireSSL
 
    SSLProtocol -all +TLSv1 +SSLv3
 
    SSLCipherSuite HIGH:MEDIUM:!aNULL:+SHA1:+MD5:+HIGH:+MEDIUM
 
    SSLRandomSeed startup file:/dev/urandom 1024
 
    SSLRandomSeed connect file:/dev/urandom 1024
 
    SSLSessionCache shm:/usr/local/apache2/logs/ssl_cache_shm
 
    SSLSessionCacheTimeout 600    
 
    SSLCertificateFile /etc/apache2/ssl/server.crt
 
    SSLCertificateKeyFile /etc/apache2/ssl/server.key
 
    SSLVerifyClient none
 
    SSLProxyEngine off
 
        AddType application/x-x509-ca-cert      .crt
 
        AddType application/x-pkcs7-crl         .crl
 
    SetEnvIf User-Agent ".*MSIE.*" \  
 
      nokeepalive ssl-unclean-shutdown \  
 
      downgrade-1.0 force-response-1.0

Несколько замечаний об этой конфигурации:

— SSLEngine должен быть включен, обозначая, что сервер использует SSL.

— DocumentRoot устанавливает корневой каталог виртуального хоста. Это значит, что вы можете отделять безопасное содержимое от обычного.

— SSLRequireSSL запрашивает использование SSL (на этом виртуальном сервере), то есть, пользователь не может подключиться к этому виртуальному хосту с помощью обычного HTTP-запроса. Вот зачем мы разделили безопасное и обычное содержимое.

— SSLProtocol отключает все протоколы, отличные от TLS v1.0 и SSL v3.0. Для современных браузеров все будет работать хорошо.

— SSLCipherSuite устанавливает использование только HIGH и MEDIUM шифров. SHA1 считается более безопасным, чем MD5, поэтому выбран он.

— SSLCertificateFile и SSLCertificateKeyFile указывают расположение файлов сертификата и ключа.

— SSLVerifyClient должна быть установлена как ‘none’, если не используется аутентификация примера.

Чтобы запускать обычный сервер на 80 порту, добавьте следующий текст в конфигурационный файл:

NameVirtualHost: *.80
 
    DocumentRoot "/local/www/html"

После сохранения отредактированного конфигурационного файла, перезапустите сервер. Если вы использовали пароль при генерации сертификата, вам понадобится ввести его при запросе.

Тестирование.

Создайте базовую страницу index.html в корневой директории вашего сервера, если у вас ещё нет содержимого там.

Затем направьте свой браузер на https://yoursite.com. Вы должны увидеть открытие SSL-соединения и загрузку страницы. Если вы используете сертификат, подписанный вами, ваш браузер даст предупреждение о том, что подлинность сервера не может быть проверена. Вы можете выбрать просмотр сертификата и принять его. Если вы используете внешний сертификат, все должно пройти без проблем.

Убедитесь, что не можете получить доступ к безопасному содержимому, используя http://. Если вы попробуете, вы должны получить сообщение об ошибке.

Устранение проблем.

Если это работает не так, как ожидалось, прежде всего, проверьте, что ваш сервер вообще запущен с помощью команды ps -a | grep apache. Если она не вернет результата, попробуйте перезапустить сервер и проверьте сообщения об ошибке в консоли.

Также проверьте, что права доступа к файлам сертификата и ключа установлены правильно (см. выше), также, как и права к тестовому HTML-файлу и директории, в которой он находится.

Затем, проверьте логи. Вы должны проверить как логи сервера, так и логи SSL, которые вы настроили в конфигурационном файле выше. Если вы не нашли там ничего полезного, попробуйте поменять значение LogLevel в файле конфигурации Apache2 на ‘debug’, перезапустите Apache2 и протестируйте снова. Это должно дать больше данных в логах.

Если вы запускаете веб-сервер на 80 порту, попробуйте запросить страницу с помощью http://, вместо https://, чтобы понять, заключается ли проблема в веб-сервере или в SSL-соединении. Учтите, что в приведенных выше установках, корневые каталоги веб-сервера разные для http:// и https://, так что вы не можете (или не должны!) получить доступ к тому же содержимому. Если ваша тестовая страница в корневом каталоге http:// работает, в то время, как тестовая страница в корневом каталоге https:// не работает, это поможет вам более точно указать на проблему.

Если проблема в SSL соединении, удобным инструментом будет s_client, который является диагностической утилитой для решения проблем в TLS/SSL-соединениях. Обычное его использование: /usr/bin/openssl s_client -connect localhost:443. Также существует множество других опций, которые вы можете узнать из документации. Если вы получили сообщения об ошибках, это должно вам помочь в определении проблемы.

Заключение.

Поздравляю! Теперь у вас есть рабочий безопасный сервер, с сертификатом, автоматически проверяемым большинством современных браузеров.

Обработка текстов в *nix

Очень часто администраторам *nix-систем приходится выполнять различные выборки, сортировки, группировки неструктурированных данных. Благо система имеет уйму утилит для этого. Рассмотрим в примерах выборку определённых полей файла. Надо отобрать из файла данные размещённые в первой, пятой и шестой колонках файла /etc/passwd.

Существует множество решений, ниже приведены лишь несколько из них:

   1. cat /etc/passwd | awk -F: '{print $1" "$5" "$6}'
 
      ...
      avahi Avahi mDNS daemon,,, /var/run/avahi-daemon
      haldaemon Hardware abstraction layer,,, /home/haldaemon
      ...
 
   2. cat /etc/passwd | awk -F: '{print $1":"$5":"$6}'
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...
 
   3. cat /etc/passwd | cut -d":" -f1,5,6
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...
 
   4. cut -d":" -f1,5,6 /etc/passwd
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...

Чем отличаются друг от друга эти варианты?

Первые два варианта: 1 и 2, — используют cat и awk. Первая утилита выводит содержимое файла /etc/passwd, вторая отбирает необходимые поля. «-F:» указывает на то, что разделителем полей в потоке служит двоеточие. Обратный слэш () перед двоеточием предписывает читать двоеточие, как двоеточие и не пытаться обработать его, как спецсимвол. В общем-то, обратный слэш используется в данном случае для перестраховки. Обратите внимание, что в первом случае мы вывели поля (нумеруются начиная с 1), разделённые пробелом, а во втором — двоеточием. Если вам необходимо модифицировать вывод, используйте эту связку, если вам нужно просто вывести требуемые поля пользуйтесь вариантами три или четыре.

В третьем варианте используется связка cat и cut. Первый выводит, второй режет 🙂 Эту связку можно можифицировать так, как указано в варианте 4, так как cut может принимать на вход и поток (вариант три), и файл (вариант четыре). -d»:» — указывает считать разделителем полей двоеточие. Здесь разделитель указан в кавычках, поэтому использовать обратный слэш () нет необходимости. -f1,5,6 говорит о том, что нужно отобразить 1-ое, 5-ое и 6-ое поля. Имейте ввиду, что для cut порядок полей, пречисленных после -f, не имеет значения. Поля будут выводится так, как они расположены в файле. Другими словами, вы просто указываете какие поля вас интересуют, а не порядок вывода полей. Если вам необходимо изменить порядок полей, воспользуйтесь awk.Например, так:

cat /etc/passwd | awk -F: '{print $6" "$5" "$1}'
 
...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...

Кроме того, awk также умеет получать данные не только из потока, но и напрямую из файла. Т.е. последний пример мы можем переписать так:

awk -F: '{print $6" "$5" "$1}' /etc/passwd
 
...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...

Почему я чаще пользуюсь конструкцией «cat | awk» или «cat | cut»? Всё просто. Когда я пытаюсь отобрать поля, я не всегда помню их точное положение в файле. Тогда я делаю: cat /etc/passwd

Получаю полный список. После чего, уточнив нужные поля, нажимаю стрелочку вверх (в bash это вызов предыдущей команды) и дописываю | awk……..

Установка расширений PECL

Введение в установку PECL

» PECL — это репозиторий расширений PHP, которые доступны вам через систему » PEAR. Эта часть руководства предназначена для демонстрации того, как вы можете получить и установить расширения PECL.

Эти инструкции подразумевают, что /your/phpsrcdir/ является путем к каталогу с дистрибутивом исходного кода PHP, а extname — это имя расширения PECL. Поэтому, приведем в соответствие. Эти инструкции также подразумевают знакомство с » командой pear. Информация в руководстве PEAR для команды pear также применима для команды pecl.

Для того, чтобы расширение можно было использовать, оно должно быть собрано, установлено и загружено. Методы, описанные ниже, предоставляют вам различные рекомендации по поводу того, как собрать и установить расширения, но сами они не будут автоматически загружены. Расширения могут быть загружены через добавление директивы extension в файл php.ini, или путем использования функции dl().

В процессе сборки модулей PHP важно, чтобы у вас были правильные версии необходимых утилит (autoconf, automake, libtool и т.д.). Информацию об этих утилитах и их версиях можно посмотреть тут » Anonymous CVS Instructions.

Загрузка расширений PECL

Есть несколько вариантов для загрузки расширений PECL, в том числе:

  • » http://pecl.php.net/Вебсайт PECL содержит информацию о различных расширениях, которые предоставлены PHP Development Team. Информация, доступная на этом вебсайте, включает в себя: лог изменений, новости релизов, требования и другие подобные детали.
  • pecl download extnameРасширения PECL, которые были опубликованы на сайте PECL, доступны для скачивания и установки через » команду pecl. Также, могут быть также указаны особые ревизии.
  • CVSБольшинство расширений PECL, также, находятся в CVS. Веб-интерфейс для просмотра доступен по адресу » http://cvs.php.net/pecl/. Для загрузки напрямую из CVS используется следующая последовательность команд. Стоит отметить, что пользователь cvsread имеет пароль phpfi:
    $ cvs -d:pserver:cvsread@cvs.php.net:/repository login
    $ cvs -d:pserver:cvsread@cvs.php.net:/repository co pecl/extname
  • Загрузка для Windows Пользователи Windows могут найти скомпилированные расширения PECL путем скачивания Collection of PECL modules со страницы » PHP Downloads, либо через » PECL Snapshot, либо расширения DLL на странице » PECL4WIN. Для компиляции PHP под Windows прочитайте соответствующий раздел
  • PECL для пользователей Windows

    Как и в случае DLL расширения PHP, установка проста и заключается в копировании файла DLL расширения PECL в директориюextension_dir и подключением затем его через php.ini. Например, добавьте следующую строку в ваш php.ini:

    extension=php_extname.dll

    После выполнения этих действий, перезапустите web-сервис.

    Компиляция общих расширений с помощью команды pecl

    PECL позволяет легко создавать общие расширения PHP. Используя » команду pecl, выполните следующее:

    $ pecl install extname

    Эта команда загрузит исходный код для расширения extname, скомпилирует и установит extname.so в вашу директорию extension_dir. Файл extname.so может быть затем загружен в php.ini

    По умолчанию, команда pecl не будет устанавливать пакеты, отмеченные состоянием alpha или beta. Если нет доступных стабильных версий пакетов, вы можете установить beta версию пакета, используя следующую команду:

    $ pecl install extname-beta

    Также, вы можете установить определенную версию используя такой вариант:

    $ pecl install extname-0.1

    Замечание: После подключения расширения в php.ini необходимо перезапустить web-сервис для того, чтобы изменения вступили в силу.

    Компиляция общих расширений с помощью phpize

    Иногда, использование инсталятора pecl не подходит. Это может быть связано с тем, что вы находитесь за файерволом или из-за того, что расширение, которое вы хотите установить, недоступно в PECL-совместимом пакете (к примеру, расширения из CVS, которые еще не зарелизены). Если вам необходимо собрать такое расширение, вы можете использовать низкоуровневые утилиты для выполнения сборки вручную.

    Команда phpize используется для подготовки окружения для расширения PHP. В следующем примере директория, где находятся исходные коды расширения, называется extname:

    $ cd extname
    $ phpize
    $ ./configure
    $ make
    # make install

    В случае успешной установки будет создан файл extname.so и помещен в PHP extensions directory. Вам будет необходимо добавить строку extension=extname.so в php.ini перед тем, как вы станете использовать это расширение.

    Если в системе отсутствует phpize, но существует возможность установки заранее скомпилированных пакетов (типа RPM), убедитесь, что установлена соответствующая devel версия пакета PHP, так как они часто содержат команду phpize с подходящими файлами заголовоков для сборки PHP и его расширений.

    Используйте phpize —help для просмотра дополнительной информации.

    Компиляция расширений PECL статически в PHP

    Возможно, вы захотите собрать расширение PECL статично в ваш бинарйный файл PHP. Для этого необходимо поместить код расширения в директорию php-src/ext/ и вызвать перегенерацию конфигурационных скриптов через систему сборки PHP.

    $ cd /your/phpsrcdir/ext
    $ pecl download extname
    $ gzip -d < extname.tgz | tar -xvf -
    $ mv extname-x.x.x extname

    В результате будет создана следующая директория:

        /your/phpsrcdir/ext/extname

    После этого, выполните заново сборку конфигурационного скрипта PHP и затем соберите PHP как обычно:

    $ cd /your/phpsrcdir
    $ rm configure
    $ ./buildconf --force
    $ ./configure --help
    $ ./configure --with-extname --enable-someotherext --with-foobar
    $ make
    $ make install

    Замечание: Для запуска скрипта ‘buildconf’ вам необходимы autoconf версии 2.13 и automake версии 1.4+ (более новые версии autoconf могут работать, но это не поддерживается).

    Одна из двух опций —enable-extname или —with-extname используется в зависимости от расширения. Обычно, в случае, когда расширение не требует подключения внешних библиотек, используется —enable. Для того, чтобы убедиться в этом, выполните следующую команду после buildconf:

    $ ./configure --help | grep extname

    Проброс TCP соединения через ICMP туннель

    Утилита PingTunnel (http://www.cs.uit.no/~daniels/PingTunnel/) позволяет организовать
    TCP тунель поверх ICMP ‘echo’ или 53 UDP порта. Подобное может оказаться полезным для обеспечения
    работы клиента, для которого пакетным фильтром заблокирован весь трафик, кроме ICMP или 53 UDP порта.
    Для работы PingTunnel необходим запуск прокси-процесса на удаленной машине
    (не важно, под какой ОС, утилитой поддерживается даже Windows), имеющей выход в сеть.

    Ставим ptunnel.
    В Debian/Ubuntu:

       apt-get install ptunnel

    В RedHat/CentOS/Fedora:

       yum install ptunnel

    Во FreeBSD:

       cd /usr/ports/net/ptunnel && make && make install

    На внешней машине, имеющей выход в сеть, запускаем icmp-прокси («-x пароль» можно не указывать,

    но тогда пустит любого):

       ptunnel -x пароль

    На локальной машине, на которой ничего кроме ICMP не работает, поднимаем туннель:

       ptunnel -p хост_прокси -lp локальный_порт_туннеля -da адрес_дальнейшего_проброса \
          -dp порт_дальнейшего_проброса -x пароль

    Например:

        ptunnel -p proxy.testhost.ru -lp 2222 -da server.testhost.ru -dp 22 -x пароль

    На proxy.testhost.ru у нас должен быть запущен icmp-прокси.
    При коннекте на 2222 порт локальной машины мы будем переброшены на 22 порт хоста server.testhost.ru
    Например, для входа на server.testhost.ru по SSH нужно набрать:

       ssh -p 2222 localhost

    В случае проблем можно попробовать указать имя внешнего сетевого интерфейса через опцию «-c»,

    например «-c eth1».
    Для создания туннеля через 53 UDP порт на локальной и удаленной стороне нужно запустить ptunnel c опцией «-udp».

    Источник

    Советы для разработчика по работе с MySQL

    1. Оптимизируйте запросы для кэша запросов

    У большинства MySQL серверов включено кэширование запросов. Один из наилучших способов улучшения производительности — просто предоставить кэширование самой базе данных. Когда какой-либо запрос повторяется много раз, его результат берется из кэша, что гораздо быстрее прямого обращения к базе данных.
    Основная проблема в том, что многие просто используют запросы, которые не могут быть закэшированны:

          // запрос не будет кэширован
          $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
          // а так будет!
          $today = DATE("Y-m-d");
          $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

    Причина в том, что в первом запросе используется функция CURDATE(). Это относиться ко всем функциям, подобным NOW(), RAND() и другим, результат которых недетерминирован. Если результат функции может измениться, то MySQL не кэширует такой запрос. В данном примере это можно предотвратить вычислением даты до выполнения запроса.

    2. Используйте EXPLAIN для ваших запросов SELECT

    Используя EXPLAIN, вы можете посмотреть, как именно MySQL выполняет ваш запрос. Это может помочь вам избавиться от слабых мест производительности и других проблем в вашем запросе или в структуре таблиц.
    Результат EXPLAIN покажет вам, какие используются индексы, как выбираются и сортируются таблицы и т.д.
    Возьмите ваш SELECT запрос (он может быть сложным, с объединениями) и добавьте в начало ключевое слово EXPLAIN. Для этого вы можете использовать phpmyadmin. В результате вы получите очень интересную таблицу. Для примера, пусть я забыл добавить индекс в таблицу, которая участвует в объединении:

    Explain для неоптимизированного запроса

    После добавления индекса для поля group_id:
    Explain после оптимизации запроса

    Теперь вместо 7883 строк, выбираются только 9 и 16 строк из двух таблиц. Перемножение всех чисел в столбце rows даст число прямо пропорциональное производительности запроса.

    3. LIMIT 1, когда нужна единственная строка

    Иногда, обращаясь к таблице, вы точно знаете, что вам нужна только одна конкретная строка. Например, нужно получить одну уникальную строку или просто проверить существование записей, удовлетворяющих запросу WHERE.
    В этом случае, добавление LIMIT 1 в ваш запрос будет оптимальнее. Таким образом, база данных остановит выборку записей, после нахождения первой же, вместо того, чтобы выбрать всю таблицу или индекс.

          // есть пользователи в Alabama?
          // можно так:
          $r = mysql_query("SELECT * FROM user WHERE state = 'Alabama'");
          IF (mysql_num_rows($r) > 0) {
          // ...
          }
          // но так лучше:
          $r = mysql_query("SELECT * FROM user WHERE state = 'Alabama' LIMIT 1");
          IF (mysql_num_rows($r) > 0) {
          // ...
          }

    4. Индексируйте поля, по которым ищите

    Индекс это не только основной или уникальный ключ. Это так же любые столбцы в таблице, которые вы используете для поиска и их можно проиндексировать.

    Результат использования индекса

    Как вы можете заметить, это правило также применимо для части строк, например — «last_name LIKE ‘a%’». При поиске с начала строки, MySQL использует индекс этого столбца.
    Вы так же должны понимать, что это не сработает для регулярных выражений. Например, когда вы ищите слово (т.е. «WHERE post_content LIKE ‘%apple%’»), то от обычного индекса не будет никакого толку. Лучше будет использовать полнотекстовый поиск или создать вашу собственную систему индексации.

    5. Индексируйте поля для объединения и используйте для них одинаковые типы столбцов

    Если ваше приложение содержит много объединений таблиц, вам необходимо проиндексировать в обеих таблицах поля, используемые для объединения. Это повлияет на то, как MySQL делает внутреннюю оптимизацию объединений.
    Так же эти столбцы должны быть одного типа. Например, если вы объединяете столбец DECIMAL со столбцом INT из другой таблицы, MySQL не сможет использовать хотя бы один из индексов. Даже кодировки символов должны быть одного типа для строковых столбцов.

          // выборки компаний в штате пользователя
          $r = mysql_query("SELECT company_name FROM users
          JOIN companies ON (users.state = companies.state)
          users.id = $user_id");
          // обе колонки state должны быть проиндексированны
          // они обе должны иметь один тип данных и кодировку символов
          // а иначе MySQL сделает полную выборку из этих таблиц

    6. Не используйте ORDER BY RAND()

    (Имеется в виду выборка единственной строки. Примечание переводчика)

    Это одна из тех вещей, который выглядят очень хорошо на первый взгляд, но многие начинающие программисты попались на эту удочку. Вы даже не представляете, какое слабое место в производительности возникнет, если будете использовать это в запросах.
    Если вам действительно нужен случайный порядок строк в запросе, то есть лучшие способы сделать это. Конечно, это приведет к дополнительному коду, но позволит избавиться от слабого места в производительности, которое будет сужаться экспоненциально при увеличении данных. Проблема в том, что MySQL будет выполнять RAND() (а это нагрузка на процессор) для каждой строки при сортировке, выдавая только одну строку.

    Таким образом вы выберите случайный номер, который меньше количества строк и используете его для смещения в LIMIT.

    7. Избегайте SELECT *

    Чем больше данных считывается из таблицы, тем медленнее запрос. Это увеличивает время работы с хранилищем данных. Также, когда сервер базы данных установлен отдельно от web-сервера, будет большая задержка при передаче данных по сети.
    Прописывать, какие именно столбцы из запроса вам нужны — хорошая привычка.

          // не очень хорошо:
          $r = mysql_query(«SELECT * FROM USER WHERE user_id = 1»);
          $d = mysql_fetch_assoc($r);
          echo «Welcome {$d['username']}»;
     
          // лучше:
          $r = mysql_query(«SELECT username FROM USER WHERE user_id = 1»);
          $d = mysql_fetch_assoc($r);
          echo «Welcome {$d['username']}»;
     
          // разница более значительна при большем наборе данных.

    8. Старайтесь всегда создать поле ID

    В каждой таблице нужно поле id, которое будет PRIMARY KEY, AUTO_INCREMENT, а так же иметь тип INT. Так же неплохо, чтобы оно было UNSIGNED, т.к. вряд ли у идентификатора будут отрицательные значения.
    Даже если в вашей таблице пользователей есть уникальное поле username, не делаете его основным ключом. Использование поля VARCHAR, как основного ключа, очень медлительно. Да и структура вашего кода, относящаяся к пользователям, будет гораздо лучше, если у каждого пользователя будет свой внутренний идентификатор.
    Есть так же и внутренние операции MySQL, использующие первичный ключ. И это становиться очень важно для более сложных конфигураций базы данных (кластеры, распараллеливание и т.д.)
    Исключение из этого правила составляют «таблицы ассоциаций», используемые для связи «многие-ко-многим» между 2 таблицами. Например, таблица «posts_tags», содержит 2 поля: post_id, tag_id, который используется для объединения между двумя таблицами «Posts» и «Tags». Эта таблица будет иметь первичный ключ составленный из 2 полей.

    9. Используйте ENUM вместо VARCHAR

    ENUM — очень быстрый и компактный тип поля. Значения в нем храниться так же, как TINYINT, но отображаются как в строковом поле. Это делает его незаменимым в некоторых случаях.
    Если у вас есть поле, в котором будет вполне определенный набор значений, используйте ENUM вместо VARCHAR. Например, если есть поле «status», его значения могут быть «active», «inactive», «pending», «expired» и т.д.
    Можно даже получить от MySQL «совет» о том, как перестроить таблицу. Если у вас есть поле VARCHAR, MySQL может предложить заменить его на ENUM. Для этого используется PROCEDURE ANALYSE(), описанная ниже.

    10. Используйте подсказки от PROCEDURE ANALYSE()

    PROCEDURE ANALYSE() анализирует структуру вашей таблицы и данные в ней, и выдает возможные советы по оптимизации. Это возможно только при наличии реальных данных в таблице, т.к. анализ делается в основном на их основе.
    Например, если вы создали первичный ключ типа INT, а записей не очень много, MySQL может предложить заменить его на MEDIUMINT. Или, если используется VARCHAR в котором есть несколько уникальных значений, будет предложен ENUM.
    В phpmyadmin в структуре таблице есть ссылка «Анализ структуры таблицы», результат которой может быть, например, следующим:

    Подсказки в phpmyadmin от работы функции PROCEDURE ANALYSE()

    11. Используйте NOT NULL, если это возможно

    Если есть особые причины использовать NULL — используйте его. Но перед этим спросите себя — есть ли разница между пустой строкой и NULL (для INT — 0 или NULL). Если таких причин нет, используйте NOT NULL.
    NULL занимает больше места и, к тому же, усложняет сравнения с таким полем. Избегайте его, если это возможно. Тем не менее, бывают веские причины использовать NULL, это не всегда плохо.
    Из документации MySQL:
    «Столбцы NULL занимают больше места в записи, из-за необходимости отмечать, что это NULL значение. Для таблиц MyISAM, каждое поле с NULL занимает 1 дополнительный бит, который округляется до ближайшего байта».

    11. Используйте NOT NULL, если это возможно

    Если есть особые причины использовать NULL — используйте его. Но перед этим спросите себя — есть ли разница между пустой строкой и NULL (для INT — 0 или NULL). Если таких причин нет, используйте NOT NULL.
    NULL занимает больше места и, к тому же, усложняет сравнения с таким полем. Избегайте его, если это возможно. Тем не менее, бывают веские причины использовать NULL, это не всегда плохо.
    Из документации MySQL:
    «Столбцы NULL занимают больше места в записи, из-за необходимости отмечать, что это NULL значение. Для таблиц MyISAM, каждое поле с NULL занимает 1 дополнительный бит, который округляется до ближайшего байта».

    12. Prepared Statements

    Есть множество преимуществ в использовании prepared statements, как для безопасности, так и для улучшения производительности. Prepared statements фильтруют значения данных, добавляемых в запрос, что защищает запросы от SQL инъекций. Конечно, вы можете фильтровать переменные вручную, но тут может сказаться человеческая забывчивость и невнимательность. Конечно, это не столь важно при использовании какого-либо фреймворка или ORM.
    Поскольку статья посвящена оптимизации, отмечу также выгоды для нее. Они проявляются, когда запрос выполняется много раз в приложении. Вы можете использовать для prepared statement разные значения, но MySQL будет разбирать запрос только один раз.
    Кроме того, последние версии MySQL компилируют prepared statements в бинарную форму, что позволяет повысить эффективность.
    Раньше многие программисты избегали prepared statements по одной единственной причине — они не кэшировались MySQL, но с версии 5.1 это не так.
    Посмотрите mysqli extension для использования prepared statements или воспользуйтесь абстракцией базы данных, например, PDO.

          // создаем a prepared statement
          IF ($stmt = $mysqli->PREPARE("SELECT username FROM user WHERE state=?")) {
          // привязываем значения
          $stmt->bind_param("s", $state);
          // выполняем
          $stmt->EXECUTE();
          // привязываем результат
          $stmt->bind_result($username);
          // получаем данные
          $stmt->fetch();
          printf("%s is from %s\n", $username, $state);
          $stmt->close();
          }

    13. Небуферизованные запросы

    Обычно, делая запрос, скрипт останавливается и ждет результата его выполнения. Вы можете изменить это, используя небуферизованные запросы.
    Хорошее описание есть в документации функции mysql_unbuffered_query():

    «mysql_unbuffered_query() отправляет SQL-запрос в MySQL, не извлекая и не автоматически буферизуя результирующие ряды, как это делает mysql_query(). С одной стороны, это сохраняет значительное количество памяти для SQL-запросов, дающих большие результирующие наборы. С другой стороны, вы можете начать работу с результирующим набором срезу после получения первого ряда: вам не нужно ожидать выполнения полного SQL-запроса»

    Однако есть определенные ограничения. Вам придется считывать все записи или вызывать mysql_free_result() прежде, чем вы сможете выполнить другой запрос. Так же вы не можете использовать mysql_num_rows() или mysql_data_seek() для результата функции.

    14. Храните IP в UNSIGNED INT

    Многие программисты хранят IP адреса в поле типа VARCHAR(15), не зная что можно хранить его в целочисленном виде. INT занимает 4 байта и имеет фиксированный размер поля.
    Убедитесь, что используете UNSIGNED INT, т.к. IP можно записать как 32 битное беззнаковое число.
    Используйте в запросе INET_ATON() для конвертирования IP адреса в число, и INET_NTOA() для обратного преобразования. Такие же, такие функции есть и в PHP — ip2long() и long2ip() (в php эти функции могут вернуть и отрицательные значения. замечание от хабраюзера The_Lion).

          $r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id";

    15. Таблицы фиксированного размера (статичные) — быстрее

    Если каждая колонка в таблице имеет фиксированный размер, то такая таблица называется «статичной» или «фиксированного размера». Пример колонок не фиксированной длины: VARCHAR, TEXT, BLOB. Если включить в таблицу такое поле, она перестанет быть фиксированной и будет обрабатываться MySQL по-другому.
    Использование таких таблицы увеличит эффективность, т.к. MySQL может просматривать записи в них быстрее. Когда надо выбрать нужную строку таблицы, MySQL может очень быстро вычислить ее позицию. Если размер записи не фиксирован, ее поиск происходит по индексу.
    Так же эти таблицы проще кэшировать и восстанавливать после падения базы. Например, если перевести VARCHAR(20) в CHAR(20), запись будет занимать 20 байтов, вне зависимости от ее реального содержания.
    Используя метод «вертикального разделения», вы можете вынести столбцы с переменной длиной строки в отдельную таблицу.

    16. Вертикальное разделение

    Вертикальное разделение — означает разделение таблицы по столбцам для увеличения производительности.
    Пример 1. Если в таблице пользователей хранятся адреса, то не факт что они будут нужны вам очень часто. Вы можете разбить таблицу и хранить адреса в отдельной таблице. Таким образом, таблица пользователей сократиться в размере. Производительность возрастет.
    Пример 2. У вас есть поле «last_login» в таблице. Оно обновляется при каждом входе пользователя на сайт. Но все изменения в таблице очищают ее кэш. Храня это поле в другой таблице, вы сведете изменения в таблице пользователей к минимуму.
    Но если вы будете постоянно использовать объединение этих таблиц, это приведет к ухудшению производительности.

    17. Разделяйте большие запросы DELETE и INSERT

    Если вам необходимо сделать большой запрос на удаление или вставку данных, надо быть осторожным, чтобы не нарушить работу приложения. Выполнение большого запроса может заблокировать таблицу и привести к неправильной работе всего приложения.
    Apache может выполнять несколько параллельных процессов одновременно. Поэтому он работает более эффективно, если скрипты выполняются как можно быстрее.
    Если вы блокируете таблицы на долгий срок (например, на 30 секунд или дольше), то при большой посещаемости сайта, может возникнуть большая очередь процессов и запросов, что может привести к медленной работе сайта или даже к падению сервера.
    Если у вас есть такие запросы, используйте LIMIT, чтобы выполнять их небольшими сериями.

          while (1) {
          mysql_query("DELETE FROM logs WHERE log_date <= '2009-10-01' LIMIT 10000");
          IF (mysql_affected_rows() == 0) {
          // удалили
          break;
          }
          // небольшая пауза
          usleep(50000);
          }

    18. Маленькие столбцы быстрее

    Для базы данных работа с жестким диском, возможно, является самым слабым местом. Маленькие и компактные записи обычно лучше с точки зрения производительности, т.к. уменьшают работу с диском.
    В документации к MySQL есть список требований к хранилищам данных для всех типов данных.
    Если ваша таблица будет хранить мало строк, то не имеет смысла делать основной ключ типом INT, возможно лучше будет сделать его MEDIUMINT, SMALLINT или даже TINYINT. Если вам не нужно хранить время, используйте DATE вместо DATETIME.
    Однако будьте осторожны, что бы не вышло как с Slashdot.

    19. Выбирайте правильный тип таблицы

    Два основных типа таблиц — MyISAM и InnoDB, у каждого есть свои плюсы и минусы.
    MyISAM хорошо считывает из таблиц большое количество данных, но он плох для записи. Даже если вы изменяете всего одну строку, блокируется вся таблица, и ни один процесс не может ничего из нее прочитать. MyISAM очень быстро выполняет запросы типа SELECT COUNT(*).
    У InnoDB более сложный механизм хранения данных, и он может быть медленнее, чем MyISAM, для маленьких приложений. Но он поддерживает блокировку строк, что более эффективно при масштабировании. Так же поддерживаются некоторые дополнительные функции, такие операции как транзакции.
    Подробнее:MyISAM Storage EngineInnoDB Storage Engine

    20. Используте ORM

    Используя ORM, можно получить определенную оптимизацию работы. Все, что можно сделать с помощью ORM, можно сделать и вручную. Но это требует дополнительной работы и более высокого уровня знаний.
    ORM замечателен для «ленивой» загрузки данных. Это означает выборку данных по мере необходимости. Но необходимо быть осторожным, т.к это может привести к появлению множества маленьких запросов, что приведет к снижению производительности.
    ORM также может объединять несколько запросов в пакеты, вместо отправки каждого отдельно.
    Моя любимая ORM для PHP — Doctrine. Я уже писал статью об установке Doctrine в CodeIgniter.

    21. Будьте осторожны с постоянными соединениями

    Постоянные соединения предназначены для уменьшения расходов на установление связи с MySQL. Когда соединение создается, оно остается открытым после завершения работы скрипта. В следующий раз, этот скрипт воспользуется этим же соединением.mysql_pconnect() в PHPНо это звучит хорошо только в теории. Из моего личного опыта (и опыта других), использование этой возможности не оправдывается. У вас будут серьезные проблемы с ограничением по числу подключений, памятью и так далее.
    Apache создает много параллельных потоков. Это основная причина, почему постоянные соединения не работаю так хорошо, как бы хотелось. Перед использованием mysql_pconnect() посоветуйтесь с вашим сисадмином.

    Источник