Свежие заметки

Код состояния HTTP для неавторизованного пользователя

Среди официального списка кодов состояний HTTP есть такой код:

401 Unauthorized

The request requires user authentication.

И разработчики ошибочно начинают его применять в ответах на REST-запросы, чтобы сообщить браузеру о том, что текущий пользователь их приложения не авторизован. Это в корне неверно.

В описании кода 401 ясно указано, что вместе с таким ответом сервер должен передать заголовок WWW-Authenticate с перечнем условий. А браузер может повторить запрос, включив в него заголовок Authorization с требуемыми для аутентификации данными.

Чаще всего в реальной жизни этот код можно встретить, если доступ к ресурсу защищён паролем. Например, в Apache это можно сделать добавив в конфигурацию следующие строки:

AuthName "Secure Area"
AuthType Basic
AuthUserFile "/usr/local/apache/passwd/passwords"
Require valid-user

Браузер попросит пользователя ввести логин и пароль и будет в дальнейшем их использовать для доступа ко всем ресурсам, защищенным такой базовой аутентификацией. Замечу, что введённые данные будут каждый раз передаваться в открытом виде в заголовках запросов.

Очевидно, что такая проверка уровня доступа пользователя к запрашиваемому ресурсу по протоколу HTTP никак не связана с разграничением прав пользователей внутри сервера приложения.

А как лучше?

По моему мнению, разумнее отвечать неавторизованному пользователю кодом 403 Forbiden

403 Forbidden

The server understood the request, but is refusing to fulfill it.

А в теле ответа передать JSON с описанием проблемы.

Комментарии к заметке: 2

Знакомство с AWS Lambda

Amazon Web Services Lambda — это веб-сервис, запускающий ваш код на Node.js, Python или Java в ответ на определенные события и отвечающий за автоматическое выделение необходимых вычислительных ресурсов. Функции Lambda не хранят состояние, поэтому AWS Lambda может быстро запустить столько копий функции, сколько нужно для обработки входящих событий.

Оплата производится за количество вызовов функции и время, необходимое для исполнения кода этой функции. Изучение и маленькие домашние проекты для вас могут быть совершенно бесплатными в течение года с момента регистрации в AWS.

Создание пользователя

После регистрации в AWS нужно создать пользователя и назначить ему права доступа к сервисам.

Все инструкции вы найдёте в документации AWS Identity and Access Management «IAM Users». Сохраните файл с ключами — они потребуются при настройке CLI.

Только что созданный пользователь не имеет никаких прав. Чтобы он получил доступ к созданию и запуску функций Lambda, нужно назначить ему соответствующую политику. В разделе «Working with Policies» описано как добраться до списка политик. Нам нужно добавить для пользователя политику «AWSLambdaFullAccess». Позже аналогично можно будет добавить или удалить другие политики.

AWS Command Line Interface

Почти все функции AWS доступны через веб-интерфейс, но на практике удобнее использовать консольные команды или скрипты.

Инструкции по установке утилит вы найдёте на странице «AWS Command Line Interface». После того как вы проделаете все описанные там действия, запустите команду:

aws configure

и укажите ключи пользователя, которые вы получили при его создании. Я так же выбрал регион по-умолчанию «eu-central-1». В разных регионах доступны разные наборы сервисов.

Создание функции AWS Lambda

Для начала, напишем простой скрипт, который будет возвращать строку "Hello, world!". Так же в логах мы увидим с какими параметрами вызывалась функция.

'use strict';

exports.handler = (event, context, callback) => {
  console.log(JSON.stringify(event, null, 2));
  callback(null, 'Hello, world!');
};

Сохраним этот код в файл index.js.

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

Перед тем как загрузить функцию в AWS, нужно создать роль для неё. Если у вас уже существует подходящая роль, то этот шаг можно пропустить.

aws iam create-role \
  --role-name MyFirstLambda-Execution \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }]
  }' \
  --output text \
  --query 'Role.Arn'

В ответ вы получите строку вида:

arn:aws:iam::000000000000:role/MyFirstLambda-Execution

Так как наша функция будет вести логгирование, то нужно добавить ей соответствующую политику. Иначе лог не сохранится в CloudWatch.

aws iam attach-role-policy \
  --role-name MyFirstLambda-Execution \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Вот теперь можно загрузить нашу функцию в Lambda.

zip - index.js | \
aws lambda create-function \
  --function-name MyFirstLambda \
  --runtime nodejs4.3 \
  --role arn:aws:iam::000000000000:role/MyFirstLambda-Execution \
  --handler index.handler \
  --zip-file fileb:///dev/stdin

Код функции будет архивироваться и загружаться в AWS без создания временных файлов. Так же код можно загружать через S3.

В параметре --handler указываем точку входа — название модуля (index) и метод, который экспортирует модуль.

В параметре --role указываем роль, которую мы создали для запуска функции.

Выполнение функции

Запустить функцию можно из терминала

aws lambda invoke \
  --function-name MyFirstLambda \
  /tmp/out.json && cat /tmp/out.json

Результат сохранится в файл /tmp/out.json, содержимое которого мы затем выведем в терминале.

aws lambda invoke \
  --function-name MyFirstLambda \
  --payload '{"foo":1,"bar":true}' \
  /tmp/out.json

Обновление кода и конфигурации функции

Обновляется код функции аналогично тому как она создавалась.

zip - index.js | \
aws lambda update-function-code \
  --function-name MyFirstLambda \
  --zip-file fileb:///dev/stdin

Если нужно обновить какие-то параметры конфигурации, то это делается командой update-function-configuration.

Заключение

С AWS Lambda можно легко получить масштабируемое окружение.

На практике функции вызываются по какому-либо событию. Например, запуск по расписанию, обновление таблицы в DynamoDB, появление файла в S3 или при поступлении HTTP-запроса в сервис API Gateway.

В свою очередь, код Lambda-функции может взаимодействовать с другими серверами AWS, внешними базами данных и веб-ресурсами.

Оставте свой комментарий

Настройка обратного прокси-сервера для поддержки протокола HTTP/2

Отдельный прокси-сервер может быть полезными, когда на штатном HTTP-сервере нет должной поддержки HTTP/2 или вы, по тем или иным причинам, не можете обновить версию сервера. Я попробовал некоторые из большого списка, существующих на данный момент, серверов.

nghttp2

Проект nghttp2 представляет собой библиотеку для работы с потоками данных и набор инструментов, написанных на базе этой библиотеки.

Процесс сборки несложен и подробно описан в документации. В результате компиляции вы получите:

  • nghttp — клиент HTTP/2;
  • nghttpd — сервер;
  • nghttpx — прокси-сервер;
  • h2load — инструмент нагрузочного тестирования.

Клиент по своей функциональности очень напоминает curl, а утилита нагрузочного тестирования — ab.

Прокси-сервер имеет несколько режимов работы и множество настроек. Тем не менее, его настройка в режиме обратного прокси оказалось очень простой.

$ nghttpx --frontend=*,443 \
    --backend=prod.noteskeeper.ru,80 \
    --tls-proto-list=TLSv1.2,TLSv1.1,TLSv1.0 \
    --dh-param-file=/etc/letsencrypt/live/noteskeeper.ru/dhparam.pem \
    /etc/letsencrypt/live/noteskeeper.ru/privkey.pem \
    /etc/letsencrypt/live/noteskeeper.ru/fullchain.pem

Обратный прокси будет слушать порт 443 на всех интерфейсах и передавать запросы на хост prod.noteskeeper.ru. Остальные параметры нужны для настройки TLS.

Ещё, возможно, будет полезен ключ --host-rewrite. Если он будет указан, то прокси заменит заголовок Host в запросе к бекэнду на значение из исходного запроса от клиента. Например, рассмотрим следующую ситуацию. Пусть на одном сервере будет настроен хостинг для моего блога по адресу noteskeeper.ru. На другом сервере я запускаю прокси. Теперь я должен заменить адрес в DNS-записи A на тот, который будет указывать на прокси и добавить ещё одну запись A на сайт, но с другим под-доменом (назовём его prod). Проблема в том, что хостинг знает только о сайте noteskeeper.ru и ничего не знает о prod.noteskeeper.ru. Из-за этого сайт начнёт отвечать ошибками с кодом 404. С таким же успехом мы могли бы в конфигурации бекэнда nghttpx указать просто IP-адрес, но домен всё же удобнее. Тут нам и поможет замена заголовка — клиент будет обращаться по адресу noteskeeper.ru и этот же адрес будет передаваться в запросе на сайт.

Минусом решения на базе nghttpx является то, что вам придётся запустить несколько его экземпляров с разными параметрами чтобы обслуживать запросы с или без www, а так же на 80 порту без шифрования и на 443 с шифрованием.

h2o

Проект h2o развивается как альтернатива Nginx и представляет собой высокопроизводительный веб-сервер с возможностью обратного прокси. Собирается он так же из исходников без каких-либо сложностей. В документации описаны все требуемые условия.

Сервер настраивается с помощью конфигурационного файла. В моём случае он получился вот таким:

hosts:
  "noteskeeper.ru:80":
    listen:
      port: 80
    paths:
      "/":
        redirect: https://noteskeeper.ru/
  "noteskeeper.ru:443":
    listen:
      port: 443
      ssl:
        key-file: /etc/letsencrypt/live/noteskeeper.ru/privkey.pem
        certificate-file: /etc/letsencrypt/live/noteskeeper.ru/fullchain.pem
        dh-file: /etc/letsencrypt/live/noteskeeper.ru/dhparam.pem
    paths:
      "/":
        proxy.reverse.url: "http://prod.noteskeeper.ru:80/"
        proxy.preserve-host: ON

# Включаем компрессию данных.
# Без этой настройки клиент получит данные в не сжатом виде,
# даже если они были сжаты на бекэнде.
gzip: ON

# Передавать быстрее файлы с большим приоритетом.
# Изначально к таким файлам относятся CSS и JS.
http2-reprioritize-blocking-assets: ON

# Включить проверку кешировани для файлов, которые
# были переданы с помощью server push.
http2-casper: ON

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

file.mime.addtypes:
  "application/x-javascript":
    extensions: [".js"]
    is_compressible: yes
    priority: highest

Запускается сервер командой:

$ h2o -c ./noteskeeper.conf

Заключение

Оба сервера, которые я протестировал, понимают заголовки ответа Link со значением rel=preload. Это позволит управлять очередью доставки контента пользователю. Ресурсы, которые вы указали, начнут отсылаться по сети ещё до того как браузер их запросит.

Для поддержки HTTP/2 вам придётся получить SSL-сертификат.

Ни одно из этих решений пока не выпущено в качестве собранного пакета. А это значит нужно будет повозиться с настройкой запуска демона.

Оставте свой комментарий

Конференции, на которых я был в 2015 году

Для меня сезон выступлений на конференциях начался только в мае.

В Москве в рамках фестиваля РИТ в этом году проходила большая конференция FrontendConf. Для неё я подготовил доклад «Пакуйте чемоданы. Грузите апельсины» (слайды). Модульная структура JS-приложения и инструменты для сборки уже стали признаком хорошего тона. Я сделал обзорный доклад, в котором рассказал о текущем состоянии технологий и инструментов для работы с JS-модулями.

В целом, у меня остались только приятные впечатления от организации самой конференции. Доклады были организованы в два параллельных потока. А по окончании первого дня, были устроены дискуссии, где докладчики и слушатели могли высказать своё мнение на заданную тему.

Практически в то же время, когда я подавал заявку на FrontendConf, меня пригласили выступить на UWDC с докладом «От простого к сложному» (слайды). В этом году уклон всей секции был сделал на разработку под мобильные устройства.

Пикантность ситуации заключалась в том, что в четверг и пятницу я был в Москве на FrontendConf, а в субботу уже должен был быть в Челябинске на UWDC. В тщательно продуманный план закралась неопределённость в виде московского метро. Я опоздал на нужный мне рейс аэроэкспресса. Следующий прибывал в аэропорт уже впритык ко времени посадки. Я зарегистрировался через приложение, но у меня не было распечатанного посадочного талона.

Несмотря ни на что, я успел на свой рейс! Эта ситуация меня ещё раз научила тому, что не стоит опускать руки, если ты в силах ещё что-то изменить.

Летом я выступал на митапах ChellyJS в Челябинске и 4front в Минске с докладом «Переходи на HTTPS!». В своё время меня очень заинтересовала эта тема. Казалось, что настройка безопасного соединения подвластна только бородатым сисадминам. Однако, на практике это совсем не так.

Встреча 4front для меня стала особенной из-за того, что я первый раз приехал в Минск. А ещё этот день совпал с днём моего рождения. Огромное спасибо ребятам, с которыми мы душевно посидели в кафешке после докладов.

В сентябре в Екатеринбурге уже традиционно проходит большая конференция FrontTalks. Мне очень нравится формат и подготовка этого мероприятия. Я всегда с удовольствием приезжаю на неё с каким-нибудь докладом. В этом году одним из трендов в веб-разработке стал webpack. В отличии от обзорного доклада, который я делал на FrontendConf, тут я решил рассказать о том, как именно webpack позволяет упростить некоторые рутинные задачи. Мой доклад «Глубокое погружение в webpack» доступен в записи или слайдах. Ведущие так тепло представили меня, что я даже растрогался и немного позабыл о чём хотел рассказать.

Кроме этого, весной я был на DUMP. Организаторы FrontTalks делают там секцию, просвещённую фронтенд разработке. Как и любая многопоточная конференция, она хороша тем, что всегда можно найти тему, которую было бы интересно послушать. С другой стороны, очень часто такие интересные доклады пересекаются.

А в октябре я уже четвёртый год подряд езжу на Fronteers в Амстердам. В этот раз я вёл текстовую трансляцию мероприятия для сообщества «Веб-стандарты». Логи доступны на Гитхабе — первый день, второй день. (Примечание: кликайте по ссылкам на pic.twitter.com — там очень часто я прикреплял несколько картинок, но выгрузились только первые из них).

И ещё, десятки часов я провёл за просмотром онлайн-трансляций других мероприятий. Могу с уверенностью сказать, что это время не было потрачено в пустую. Не стойте на месте, развивайтесь и совершенствуйтесь!

Комментарии к заметке: 2

Обновление сертификата Let’s Encrypt

Сертификаты Let’s Encrypt «живут» всего 90 дней. Такой короткий срок авторы проекта выбрали исходя из того, что выпущенные сертификаты легко обновить. Для этого нужно запустить инсталлятор с теми же параметрами, которые были использованы во время получения сертификата. Инсталлятор обнаружит, что на сервере уже есть сертификат для этих доменов и предложит обновить их.

После успешной загрузки обновлённых сертификатов инсталлятор сообщит дату окончания их действия.

Конфигурационные файлы сервера изменять при этом не нужно. На сервере хранятся все версии выпущенных сертификатов в папке /etc/letsencrypt/archive/, а в папке /etc/letsencrypt/live/ создаются симлинки на актуальные версии.

Запуск инсталлятора можно запланировать в crontab.

Оставте свой комментарий