...

суббота, 27 октября 2018 г.

DNS over TLS — Шифруем наши DNS запросы с помощью Stunnel и Lua


источник изображения

После новости о том что "Google Public DNS тихо включили поддержку DNS over TLS" я решил попробовать его. У меня уже есть Stunnel который создаст шифрованный TCP туннель до гугла. Но программы обычно общаются с DNS по UDP протоколу. Поэтому нам нужен прокси который будет пересылать UDP пакеты в TCP поток и обратно. Мы напишем его на Lua.

Вся разница между TCP и UDP DNS пакетами:


4.2.2. TCP usage
Messages sent over TCP connections use server port 53 (decimal). The message is prefixed with a two byte length field which gives the message length, excluding the two byte length field. This length field allows the low-level processing to assemble a complete message before beginning to parse it.

RFC1035: DOMAIN NAMES — IMPLEMENTATION AND SPECIFICATION

То есть делаем туда:


  1. берём пакет из UDP
  2. добавляем к нему в начале пару байт в которых указан размер этого пакета
  3. отправляем в TCP канал

И в обратную сторону:


  1. читаем из TCP пару байт тем самым получаем размер пакета
  2. читаем пакет из TCP
  3. отправляем его получателю по UDP

Настраиваем Stunnel

Настройка простая. Пишем в stunnel.conf:

[dns]
client = yes
accept  = 127.0.0.1:53
connect = 8.8.8.8:853

То есть Stunnel:


  1. примет не шифрованное TCP по адресу 127.0.0.1:53
  2. откроет шифрованный TLS тунель до адреса 8.8.8.8:853 (Google DNS)
  3. будет передавать данные туда и обратно

Запускаем Stunnel

Работу тунеля можно проверить командой:

nslookup -vc ya.ru 127.0.0.1

Опция vc заставляет nslookup использовать TCP соединение к DNS серверу вместо UDP.

Результат:

*** Can't find server name for address 127.0.0.1: Non-existent domain
Server:  UnKnown
Address:  127.0.0.1

Non-authoritative answer:
Name:    ya.ru
Address:  (здесь IP яндекса)

Пишем скрипт

Я пишу на Lua 5.3. В нём уже доступны бинарные операции с числами. Ну и нам понадобится модуль Lua Socket.

Имя файла: simple-udp-to-tcp-dns-proxy.lua

local socket = require "socket" -- подключаем lua socket

--[[--

Напишем простенькую функцию которая позволит отправить дамп пакета в консоль. Хочется видеть что делает прокси.

--]]--

function serialize(data)
    -- Преобразуем символы не входящие в диапазоны a-z и 0-9 и тире в HEX представление '\xFF'
    return "'"..data:gsub("[^a-z0-9-]", function(chr) return ("\\x%02X"):format(chr:byte()) end).."'"
end

--[[--


UDP в TCP и обратно

Пишем две функции которые будут оперировать двумя каналами передачи данных.

--]]--

-- здесь пакеты из UDP пересылаются в TCP поток
function udp_to_tcp_coroutine_function(udp_in, tcp_out, clients)
    repeat
        coroutine.yield() -- возвращаем управление главному циклу
        packet, err_ip, port = udp_in:receivefrom() -- принимаем UDP пакет
        if packet then
            -- > - big endian
            -- I - unsigned integer
            -- 2 - 2 bytes size
            tcp_out:send(((">I2"):pack(#packet))..packet) -- добавляем размер пакета и отправляем в TCP
            local id = (">I2"):unpack(packet:sub(1,2))    -- читаем ID пакета
            clients[id] = {ip=err_ip, port=port}          -- записываем адрес отправителя
            print(os.date("%c", os.time()) ,err_ip, port, ">", serialize(packet)) -- отображаем пакет в консоль
        end
    until false
end

-- здесь пакеты из TCP потока пересылаются к адресату по UDP
function tcp_to_udp_coroutine_function(tcp_in, udp_out, clients)
    repeat
        coroutine.yield() -- возврашяем управление главному циклу
        -- > - big endian
        -- I - unsigned integer
        -- 2 - 2 bytes size
        local packet = tcp_in:receive((">I2"):unpack(tcp_in:receive(2)), nil) -- принимаем TCP пакет
        local id = (">I2"):unpack(packet:sub(1,2))                            -- читаем ID пакета
        local client = clients[id]                                            -- находим получателя
        if client then
            udp_out:sendto(packet, client.ip, client.port) -- отправляем пакет получателю по UDP
            clients[id] = nil                              -- очищаем ячейку
            print(os.date("%c", os.time()) ,client.ip, client.port, "<", serialize(packet)) -- отображаем пакет в консоль
        end
    until false
end

--[[--

Обе функции сразу после запуска выполняют coroutine.yield(). Это позволяет первым вызовом передать параметры функции а дальше делать coroutine.resume(co) без дополнительных параметров.


main

А теперь main функция которая выполнит подготовку и запустит главный цикл.

--]]--

function main()
    local tcp_dns_socket = socket.tcp() -- подготавливаем TCP сокет
    local udp_dns_socket = socket.udp() -- подготавливаем UDP сокет

    local tcp_connected, err = tcp_dns_socket:connect("127.0.0.1", 53) -- соединяемся с TCP тунелем
    assert(tcp_connected, err) -- проверяем что соединились
    print("tcp dns connected") -- сообщаем что соединились в консоль

    local udp_open, err = udp_dns_socket:setsockname("127.0.0.1", 53) -- открываем UDP порт
    assert(udp_open, err)      -- проверяем что открыли
    print("udp dns port open") -- сообщаем что UDP порт открыт

    -- пользуемся тем что таблицы Lua позволяют использовать как ключ что угодно кроме nil
    -- используем как ключ сокет чтобы при наличии данных на нём вызывать его сопрограмму
    local coroutines = {
        [tcp_dns_socket] = coroutine.create(tcp_to_udp_coroutine_function), -- создаём сопрограмму TCP to UDP
        [udp_dns_socket] = coroutine.create(udp_to_tcp_coroutine_function)  -- создаём сопрограмму UDP to TCP
    }

    local clients = {} -- здесь будут записываться получатели пакетов

    -- передаём каждой сопрограмме сокеты и таблицу получателей
    coroutine.resume(coroutines[tcp_dns_socket], tcp_dns_socket, udp_dns_socket, clients) 
    coroutine.resume(coroutines[udp_dns_socket], udp_dns_socket, tcp_dns_socket, clients)

    -- таблица из которой socket.select будет выбирать сокет готовый к получению данных
    local socket_list = {tcp_dns_socket, udp_dns_socket} 

    repeat -- запускаем главный цикл
        -- socket.select выбирает из socket_list сокеты у которых есть данные на получение в буфере
        -- и возвращает новую таблицу с ними. Цикл for последовательно возвращает значения из новой таблицы  
        for _, in_socket in ipairs(socket.select(socket_list)) do
            -- запускаем ассоциированную с полученным сокетом сопрограмму
            local ok, err = coroutine.resume(coroutines[in_socket])
            if not ok then
                -- если сопрограмма завершилась с ошибкой то
                udp_dns_socket:close() -- закрываем UDP порт
                tcp_dns_socket:close() -- закрываем TCP соединение
                print(err) -- выводим ошибку
                return     -- завершаем главный цикл
            end
        end
    until false
end

--[[--

Запускаем главную функцию. Если вдруг будет закрыто соединение мы через секунду установим его заново вызвав main.

--]]--

repeat
    coroutine.resume(coroutine.create(main)) -- запускаем main
    socket.sleep(1) -- перед рестартом ждём одну секунду
until false

проверяем


  1. Запускаем stunnel


  2. Запускаем наш скрипт

    lua5.3 simple-udp-to-tcp-dns-proxy.lua
    

  3. Проверяем работу скрипта командой

    nslookup ya.ru 127.0.0.1
    

    На этот раз без '-vc' так так мы уже написали и запустили прокси который заворачивает UDP DNS запросы в TCP тунель.


Результат:

*** Can't find server name for address 127.0.0.1: Non-existent domain
Server:  UnKnown
Address:  127.0.0.1

Non-authoritative answer:
Name:    ya.ru
Address:  (здесь IP яндекса)

Если всё нормально можно указать в настройках соедидения как DNS сервер "127.0.0.1"


заключение

Теперь наши DNS запросы под зашитой TLS.


ссылки


  1. RFC1035: DOMAIN NAMES — IMPLEMENTATION AND SPECIFICATION
  2. DNS поверх TLS
  3. simple-udp-to-tcp-dns-proxy.lua

Let's block ads! (Why?)

[Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 13: «Сетевые протоколы», часть 2

Массачусетский Технологический институт. Курс лекций #6.858. «Безопасность компьютерных систем». Николай Зельдович, Джеймс Микенс. 2014 год


Computer Systems Security — это курс о разработке и внедрении защищенных компьютерных систем. Лекции охватывают модели угроз, атаки, которые ставят под угрозу безопасность, и методы обеспечения безопасности на основе последних научных работ. Темы включают в себя безопасность операционной системы (ОС), возможности, управление потоками информации, языковую безопасность, сетевые протоколы, аппаратную защиту и безопасность в веб-приложениях.

Лекция 1: «Вступление: модели угроз» Часть 1 / Часть 2 / Часть 3
Лекция 2: «Контроль хакерских атак» Часть 1 / Часть 2 / Часть 3
Лекция 3: «Переполнение буфера: эксплойты и защита» Часть 1 / Часть 2 / Часть 3
Лекция 4: «Разделение привилегий» Часть 1 / Часть 2 / Часть 3
Лекция 5: «Откуда берутся ошибки систем безопасности» Часть 1 / Часть 2
Лекция 6: «Возможности» Часть 1 / Часть 2 / Часть 3
Лекция 7: «Песочница Native Client» Часть 1 / Часть 2 / Часть 3
Лекция 8: «Модель сетевой безопасности» Часть 1 / Часть 2 / Часть 3
Лекция 9: «Безопасность Web-приложений» Часть 1 / Часть 2 / Часть 3
Лекция 10: «Символьное выполнение» Часть 1 / Часть 2 / Часть 3
Лекция 11: «Язык программирования Ur/Web» Часть 1 / Часть 2 / Часть 3
Лекция 12: «Сетевая безопасность» Часть 1 / Часть 2 / Часть 3
Лекция 13: «Сетевые протоколы» Часть 1 / Часть 2 / Часть 3

Студент: клиент не может расшифровать этот билет, потому что он зашифрован с помощью ключа сервиса.

Профессор: да, это действительно умно, не правда ли? У нас есть ключ Kс,s который может получить клиент, но здесь, в билете Tс,s есть еще одна копия этого ключа, зашифрованная с помощью Ks.

Причина, по которой это сделано, состоит в том, что сервер Kerberos на самом деле пытается обеспечить безопасность общения клиента с другим парнем. Поэтому Керберос создает случайный ключ Kс,s и даёт одну копию клиенту, а другую серверу, с которым клиент собирается поговорить. Представьте, что Kerberos просто бы обратился к сервису со словами: «эй, сервис, этот парень хочет поговорить с тобой, вот ключ для этого»! Это было бы прискорбно, потому что сервер Kerberos обращался бы к сервису снова и снова при каждом запросе. Так что KDS создаёт 2 копии сессионного ключа: одну для клиента, а другую для TGS.

Поэтому вместо этого разработчики придумали хороший трюк, где они предоставляют клиенту этот билет, и тот не может с ним ничего сделать, кроме как обратиться с ним к правильному сервису. И если этот сервис имеет правильный ключ Ks, он его расшифрует и скажет: «ага, это тот самый ключ, который я должен использовать, чтобы поговорить с этим клиентом». Таким образом, оба участника соединения, клиент и сервис, установят общий ключ для защиты своего соединения.

Студент: так что же такое TGS?

Профессор: существует два взгляда на то, что представляет собой TGS. С точки зрения клиента, это просто еще одна услуга, для пользования которой вы можете получить билет. Чем больше функций предоставляет этот сервис, тем больше билетов он предоставляет. Фактически это сервис по выдаче билетов.

Студент: извините, я имел в виду, что у нас билет называется TGS.

Профессор: о, да, извините, надпись tgs под стрелкой на этой схеме является всего лишь сокращением для всего блока записи, кроме индекса s в параметре Tс,s, который означает фактическое имя этого сервиса — TGS. Вы можете представить, что у нас есть сервер Kerberos, есть этот сервис TGS и есть реальный сервис, до которого вы хотите добраться. Так что сначала вам придётся попросить Kerberos предоставить вам билет для получения доступа к определённому сервису.

Вы бы могли попросить Kerberos дать вам билет непосредственно на файловый сервер, и это могло бы сработать. Но для этого вам бы понадобился ваш Kс для расшифровки и на всё остальное время пользования сервером. Вместо этого вы получаете билет для специального сервиса TGS. Он выглядит так же, как и другие службы, за исключением того, что размещён в отдельном боксе. И он с удовольствием даст вам позже больше билетов без повторного предоставления вашего первоначального ключа клиента Kc.

Студент: то есть его идея в том, что как только вы получите билет TGS, вы можете просто избавиться от своего ключа Kc?

Профессор: да, самое классное в этом то, что как только вы получите этот билет Tс,s от сервиса TGS, вы избавляетесь от пароля и ключа Kc. Таким образом, как только вы авторизуетесь в рабочей станции Athena и через пару секунд получите билет Tс,s, ваш пароль удаляется из памяти. Так что даже если кто-то схватит тебя, отберёт компьютер и убежит с ним, все, что у него будет – это твой билет. Хорошо, если он сможет получить доступ к вашей информации на 10 часов, или на период действия билета, но не дольше, потому что пароль не сохранился и при следующем входе в «Афину» его потребуется вводить заново.

Единственный момент, когда нужен пароль – это когда вы посылаете запрос серверу Kerberos, вы получаете этот ответ с билетом и расшифровываете его. После этого можно забыть про пароль. Но конечно, вы не можете отбросить пароль, прежде чем используете его для расшифровки.
Таким образом, первый, верхний интерфейс C на нашей схеме используется для получения билета с начальным ключом Kc, а второй, нижний интерфейс S – для доступа к сервисам, но уже без необходимости получения начального ключа Kc.

Итак, мы уже поговорили о двух конкретных проблемах протокола Kerberos, которые как бы встроены в него, что причиняло определённые неудобства. Во-первых, создатели предполагали, что шифрование также обеспечит аутентификацию или целостность сообщений, но этого не произошло. Этот недостаток исправлен в Kerberos версии 5, где выполняется явная проверка подлинности сообщений. Во-вторых, для произвольных клиентов у них существовала возможность угадывать пароли других людей.

Как это можно исправить? Как предотвратить атаки способом угадывания пароля в протоколах такого вида? Что мы можем попробовать?

Студент: я не уверен, но можно попробовать «посолить» пароль.

Профессор: «соление» просто означает, что клиент, возможно, должен разными способами хешировать пароль. Но это не помешает попробовать его подобрать. Так что, возможно, дороже будет построить словарь.

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

Профессор: да, еще одна хорошая идея — сделать процесс хеширования очень дорогим. Возможно, это разумно. Поэтому, если эта хэш-функция занимает секунду вычислительного времени, как вы, ребята, делали это во второй лабораторной работе, то в этом случае подбор паролей стал бы очень дорогим занятием. Так что это кажется разумным планом – использовать комбинацию «соления» и усложнения процесса отгадывания.

Еще одним способом защиты может стать усложнение ответа. Вы слышали, что в первых версиях протокола сервер Kerberos не имел ни малейшего представления, правильный клиент к нему обращается или нет. Что вы могли бы сделать – это предоставить доказательства, что вы правильный клиент, то есть зашифровать текущую метку времени с хешем пароля, или что-то в этом роде. Затем сервер Kerberos мог бы просто проверить, правильность этих вещей, и если они совпадают, предоставить вам билет.

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

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

Студент: можно просто ограничить выдачу билетов, если серверы фиксируют слишком много запросов на их предоставление.

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

Студент: как атакующий может послать запрос серверу Kerberos?

Профессор: я думаю, он мог бы воспроизвести сообщение правильного пользователя, то есть увидеть его, скопировать, отправить и так же получить ответ с сервера Kerberos. Если хакер просматривает сеть, он может перехватить сообщение во время передачи. Так что ограничение количества запросов – временная мера, лишь немного повышающая безопасность. Но, конечно, если вы просматриваете чужую сеть, то вы увидите, как этот пакет возвращается с сервера независимо от того, что произошло на этапе формирования Tс,s. Так что хакер может увидеть ответ сервера клиенту и попытаться напасть на него.

Вероятно, существуют более сложные схемы, которые вы могли бы разработать, но я не думаю, что Kerberos 5 реализует нечто более сложное, чем рассмотренный нами план, который кажется достаточно хорошим, чтобы не позволить случайным людям пытаться сломать что-либо или использовать brute-force для взлома пароля.

Студент: предположим, что можно предусмотреть аутентификацию или ещё что-то, чтобы установить общий ключ. И тогда вы сможете зашифровать эту вещь и общий ключ с помощью Kc.

Профессор: да, это так. Если вы действительно делаете это правильно, то для этого существует протокол под названием Password Authenticated Key Exchange, PAKE, который выполняет аутентификацию по паролю. Именно это и происходит в Kerberos.

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

Это протоколы, которые в значительной мере полагаются на криптографию, поэтому трудно объяснить на доске, почему они работают.

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

Профессор: да, есть много странных требований, которые учитывали эти ребята. Конечно, на практике, эти серверы могут принимать как соединения Kerberos, так и соединения не-Kerberos. И для не-Kerberos соединений, вы получаете картину, как будто кто-то подключается к почтовому серверу, но не использует рабочую станцию Athena. Он просто хочет отправить свой пароль.

И тогда почтовый клиент здесь, скажем так, собирается взять этот пароль и получить билет от вашего имени только для его проверки, что позволит вам использовать этот почтовый клиент. Так что, вероятно, вы всё-таки захотите, чтобы проверку паролей Kerberos осуществлял сам Kerberos. Я не думаю, что это исключается, потому что, конечно, Kerberos 5 развертывает это хеши меток времени и всё такое.

Я думаю, что еще одна вещь, на которую вы должны обратить внимание в материалах лекции, это то, что разработчики Kerberos 4 выбрали одну схему шифрования — DES, наиболее популярный алгоритм шифрования того времени. Это симметричный блочный шифр, довольно быстрый. В то время он обеспечивал достаточную безопасность, и они просто встроили его в протокол.

Все в Kerberos должно было использовать только DES, или, по крайней мере, все в Kerberos версии 4. Это стало проблематичным, потому что теперь, 25-30 лет спустя, шифрование DES легко поддаётся взлому методом brute-force, так как ключи шифрования имеют очень маленький размер – всего 56 бит.

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

Kerberos версии 5 поддерживает несколько различных схем шифрования, включая AES и другие криптографические алгоритмы. Так что это кажется гораздо лучшим способом обеспечить безопасность. С другой стороны, MIT продолжал поддерживать DES ещё 2 года назад, но теперь от этого отказался, так что на сегодня ваш ректор защищен, по крайней мере, от такого типа атак. Теперь давайте рассмотрим, что происходит в сервисе TGS, от которого вы получаете билет. Взаимодействие с этим сервисом выглядит немного по-другому.

С одной стороны, как клиент, вы собираетесь говорить с ним так, как если бы вы говорили с любым другим сервисом с поддержкой Kerberos. Рассмотрим, как вы выполняете собственную аутентификацию с билетом к какой-то машине. Но ответ, который вы собираетесь вернуть — это просто билет к какому-то другому принципу, на основе которого вы будете общаться, например, с файловым сервером.

Поэтому сообщения уровня протокола будут выглядеть таким образом – справа я нарисую TGS, а слева — клиента. У клиента уже есть билет для TGS, полученный с помощью протокола, изображённого вверху.

Теперь клиент собирается отправить некие комбинации сообщений, которые доказывают, что он правильный клиент, и эти сообщения имеют отношение к выдаче запроса по какому-то конкретному принципу через TGS.

Итак, клиент собирается отправить в TGS такое послание: S – это сервис, с которым он собирается общаться дальше, это может быть почтовый или файловый сервер, затем сюда включается билет клиента Tc, который он получил для tgs, зашифрованный с помощью ключа K tgs и аутентификатор, который зашифрован ключом Kc,tgs общим для клиента и сервиса TGS. Вот так выглядит сообщение, которое вы собираетесь отправить в TGS – оно говорит: «посмотрите на это сообщение, сделайте с ним что-нибудь и ответьте билетом на этот новый сервис S». Ответ здесь выглядит почти так же, как на рисунке вверху, и на самом деле это то же самое – это билет между клиентом и этим новым сервисом, зашифрованным с помощью Ks. Но теперь здесь стало немного по-другому.

Вместо шифрования ключом Kс, который клиент, наверное, забыл с тех пор, теперь шифрование происходит с помощью общего ключа Kc,tgs между клиентом и службой TGS.

Как же сервер выясняет, чего хочет клиент и как сервер выполняет проверку подлинности клиента? Сервер TGS знает свой собственный ключ Ktgs, поэтому сначала он собирается расшифровать это послание Tc,tgs, заглянуть внутрь билета и выяснить, что происходит. Зачем же нам нужны все эти поля в билете? Почему так важно иметь в билете имя сервера S? Что бы пошло не так, если бы у нас не было S?

Студент: если бы его не было, тогда можно было бы потенциально получить разрешение на использование любого сервера.

Профессор: да, это так. Вообще это хорошая идея — сделать в сетевых протоколах так, чтобы можно было точно сказать, что означает это сообщение. В случае, если вы опустите S, вы можете опираться на то, что если вы используете билет для неправильного S, тогда, возможно, у вас будет другой ключ Ks, и он не сможет выполнить расшифровку или что-то вроде этого. Так что кажется, что это хорошая идея – включить название сервиса для того, чтобы убедиться, что сервер, который получает эти билеты, расшифровывает их и проверяет — это билет для меня или для кто-то еще?

Студент: что делает клиент с полученным ключом Ktgs?

Профессор: хороший вопрос! Клиент понятия не имеет, что это. Потому что это сверхсекретный ключ. Если бы вы знали его, то были бы в состоянии взломать весь Kerberos. Так что клиент понятия не имеет, что такое Ktgs.

Студент: откуда же берётся этот Ktgs?

Профессор: сам сервер Kerberos генерирует для вас всё это послание, в котором уже есть Tc,tgs и Ktgs, вы не создаёте его сами, а просто копируете отсюда.

Так для чего же так важно имя клиента? Это легко сообразить. Если вы не помещаете имя клиента в билет, тогда сервер получает это послание, но понятия не имеет, с кем он пытается поговорить. Он не знает, для кого должен выдать билет – для вас или для кого-то ещё.

А как обстоят дела с другими полями? Почему разработчики вставляют в билет Tc,s адрес addr? Это ведь просто IP-адрес клиента, так почему бы его не использовать напрямую?

Я думаю, что смысл такого решения состоит в желании разработчиков повысить безопасность. Они хотели убедиться, что если клиент вошел в систему с какого-то IP адреса, то все остальное по тому же билету происходит с того же IP-адреса. Например, если вы вошли с какого-то IP-адреса, например 18.26.4.9, то каждое соединение с файловым или почтовым сервером должно быть с того же IP-адреса. В противном случае сервер должен отклонить ваше соединение, так как может предположить, что кто-то украл ваш билет. Таким образом мы защищаемся здесь от использования украденных билетов. Если у вас ещё есть тот же билет — хорошо, но если вы не используете тот же IP-адрес, у вас ничего не получится.

На данный момент это выглядит заблуждением, и Kerberos 5 все ещё использует подобный подход, хотя это необязательно. Действительно, вы должны просто полагаться на криптографию вместо обеспечения безопасности IP-адреса.

А в чём смысл временной метки timestamp и времени жизни life в билете? Для чего они нужны и чем полезны?

Студент: они нужны для предотвращения атак повторного воспроизведения.
Профессор: атаки повторного воспроизведения нам помогает предотвратить аутентификатор, потому что эта штука генерируется каждый раз, когда вы делаете новый запрос. Но с другой стороны, билет остается прежним, так что это, конечно, не мешает атакам повторного воспроизведения.

Студент: это мешает кому-то украсть ваш билет, а затем использовать его в своих целях.
Профессор: да, это просто ограничивает время, в течение которого билет действителен, благодаря чему ущерб от его кражи уменьшается. Временная метка – это время, когда вы получили билет, а время жизни показывает, сколько этот часов билет действителен от начальной метки времени. Поэтому, если вы попытаетесь использовать его слишком рано или слишком поздно, то по протоколу Kerberos любой сервер должен отклонить такой билет. Это означает, что каждый сервер должен синхронизировать свои часы.

Студент: ранее вы говорили, что клиент может выбросить свой начальный ключ отбрасывает Kс, но должен хранить Kс,s, полученный от TGS.

Профессор: да, это так, клиент отбрасывает Kс после входа в систему, но должен хранить Kс,s.

Студент: итак, если кто-то крадет Kс,s, то у него появляется доступ…

Профессор: да, давайте рассмотрим, насколько это плохо? Почему лучше, чтобы хакер раскрыл этот Kc,tgs, чем Kс?

Студент: если вы получите возьмете Kс,s, то вы сможете просто украсть сеанс между этими двумя собеседниками, но если вы украдёте Kс, то сможете выдать себя за клиента.

Профессор: совершенно верно. Поэтому единственный способ защиты это то, что Kc,s — это на самом деле новый ключ, который создается каждый раз, когда вы входите в систему. Это хорошо, потому что у вас есть билет Tc,tgs, который к нему прилагается. Если вы потеряете этот билет или он станет не действителен, то у вас есть ещё эти 56 бит в этом ключе Kc,s. Но никто не собирается их использовать. Весь смысл этих битов в том, что благодаря им билет Tc,tgs говорит, что этот ключ Kc,s действителен в данный момент, и в нём есть какое-то ограничение.

Студент: поэтому, если кто-то сможет украсть оба эти элемента — Tc,tgs, зашифрованный ключом Kc,tgs, и сам ключ Kc,tgs, зашифрованный Kc — то он не будет ничем ограничен.

Профессор: да, если кто-то украдет обе эти вещи, то сможет выдать себя за вас или, например, войти в файловый или почтовый сервер в течение времени жизни билета, которая может составлять пару часов или 10 часов. Украденным Kc можно будет пользоваться без временных ограничений, пока вы не измените свой пароль.

Итак, похоже, что на сегодня все эти поля в билете вроде как важны, кроме представления IP-адреса, который не особо влияет на безопасность. Мы наконец-то получаем в ответ этот билет, который изображён внизу схемы, и поскольку мы знаем Kc,tgs, то можем расшифровать ответ от сервера TGS. Теперь у нас есть билет на любой сервер — файловый сервер, почтовый сервер, какой угодно сервер, к которому мы хотели подключиться.

Теперь давайте рассмотрим, как вы можете, наконец, использовать всё это в протоколе уровня приложений. Предположим, что я говорю с почтовым сервером, чтобы получить свои сообщения. Поэтому моя клиентская рабочая станция собирается отправить TGS запрос на получения билета для доступа к почте, например, PO12, и TGS вернет ей билет с доступом к почте PO12. И внутри этого билета, или внутри этого ответа, у меня теперь есть общий ключ Kc,s для связи между мной и почтовым сервером. Я покажу этот билет почтовому серверу, и он убедится в том, что я правильный клиент и всё в этом ключе соответствует правильным принципам. Так что у нас с почтовым сервером состоится зашифрованный разговор с использованием этого нового ключа Kc,s.

Что я мог бы сделать как клиент, это сначала отправить сообщение на почтовый сервер, которое включает в себя билет на почтовый сервер Tc,mail, зашифрованный ключом почтового сервера Kmail, и вместе с ним отправить сообщение о том, что мне нужно сделать с почтой, например, удалить сообщение 5 – DELETE 5, зашифрованное ключом Kc,mail.

Итак, что же происходит в этом протоколе на почтовом сервере mail? В первую очередь почтовый сервер использует для расшифровки этого билета свой секретный ключ Kmail, а потом заглянет внутрь и найдёт две важные вещи -основное имя того, кто с ним говорит, и ключ Kc,s, который он должен использовать для расшифровки всего последующего трафика и аутентификации его в Kerberos 5. После этого он сможет расшифровать ваше сообщение и сказать: «ага, пользователь C пытается удалить пятое сообщение, поэтому я выполню эту команду».

Студент: сервер Kerberos изначально отправляет билет Tc,tgs и ключ Kc,s. Где здесь содержится аутентификатор?

Профессор: Идентификаторы Ac фактически генерируются клиентом. Обратите внимание, что клиенту для генерации аутентификатора нужен только ключ Kc,s, и клиент может делать это столько раз, сколько захочет. Таким образом, общий план для аутентификаторов или причина использования аутентификаторов состоит в предотвращении атаки повторного воспроизведения.

Таким образом, в Kerberos 4 каждый раз, когда клиент посылает новый запрос, будет создаваться новый аутентификатор, говорящий, что это новый запрос, клиент создаёт его только теперь и он отличается от всех предыдущих запросов, поэтому нужно сделать то, что просит клиент.

Общий план состоял в том, что сервер должен был хранить кэш этих аутентификаторов, которые были отправлены, например, в течение последних пяти минут. Поэтому если он видел дубликат аутентификатора, то воспринимал его как попытку атаки повторного воспроизведения и отвергал его. Так же он поступал, если видел аутентификатор, который находится за пределами пятиминутного времени жизни и которого у него не было в кэше. Сервер смотрел на метку времени в этом аутентификаторе, видел, что он устарел, и отклонял просьбу, потому что она была слишком старая. Так что если клиенту нужно получить доступ, пусть отправит новый запрос.
Таков общий план использования аутентификаторов. Как и многие вещи из первой версии Kerberos, в последствии они были немного изменены, по крайней мере, в Kerberos 4. Потому что этот аутентификатор на самом деле ничего не говорит о вашем послании, это просто какое-то дополнение к нему.

Таким образом, способ его использования, например, в этом протоколе почтового сервера, по крайней мере, в Kerberos 4, состоит в том, что вы генерируете аутентификатор, берёте его и так же зашифровываете ключом Kс,mail. И почтовый сервер будет отслеживать, отправили ли вы этот аутентификатор заранее или нет.

Но здесь нет ничего, что соединяет аутентификатор с сообщением, которое вы посылаете. Для первого сообщения это было здорово, но когда через секунду вы посылаете следующее сообщение, вы создаёте второй аутентификатор. При этом кто-то в сети может сказать: «ага, у меня есть ваш новый аутентификатор, я могу использовать его вместе с соединением со старым сообщением DELETE и тем самым заставить дважды удалить сообщение номер 5, даже если вторая команда предназначена для какой-то другой операции».

Таким образом, Kerberos 5 предоставляет вам право вставить в аутентификатор что-то, что касается отдаваемой вами команды. Разработчики, конечно, могли бы изначально использовать такой механизм, но понадобилось время, чтобы они поняли, как правильно должен работать протокол.

Студент: откуда клиент берёт Kс,mail?

Профессор: клиент получает его из этого ответа.

Потому что клиент, посылая TGS запрос на билет, имеет в виду, что это S – имя сервиса mail, и подстрочный индекс S в полученном билете Tc,s – это тоже mail, и индекс S в ключе Kc,s — это тоже имя mail. Так что этот Kc,s на самом деле Kс,mail. Вот так клиент узнает о ключе, который является общим для него и файлов на почтовом сервере.

Студент: а как почтовый сервер получает Kс,mail?

Профессор: да, как же почтовый сервер получает этот общий ключ? Ведь почтовый сервер, возможно, никогда раньше не слышал о вас и о вашем соединении. Откуда же Kс,mail приходит на почтовый сервер?

Студент: разве это не часть билета?

Профессор: да, правильно, так что это классная вещь! Вы отправляете этот билет на почтовый сервер и почтовый сервер узнаёт собственный секретный ключ Kmail, который он использует для расшифровки билета Tс,mail, и этот общий ключ находится там вместе с именем того, с кем он делится данным ключом. Вот так сервер узнает, что разговаривает с тем парнем, с которым должен использовать общий ключ.

Итак, что вот что представляет собой основной план использования данного протокола при работе с приложениями. Но с этим всё ещё имеется куча проблем. Так что о Kerberos хорошо читать на бумаге, однако в нём имеются проблемы, о которых эти ребята не знали 30 лет назад.
Поэтому неизбежно, что у нас возникают проблемы, которые вы должны решить. Так, одна интересная проблема Kerberos 4 касается способа шифрования и аутентификации сообщений для приложений. Она заключается в том, что здесь используется один и тот же ключ для шифрования сообщений от клиента к серверу и для ответных сообщений от сервера клиенту.

54:00 мин

Курс MIT «Безопасность компьютерных систем». Лекция 13: «Сетевые протоколы», часть 3


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

Спасибо, что остаётесь с нами. Вам нравятся наши статьи? Хотите видеть больше интересных материалов? Поддержите нас оформив заказ или порекомендовав знакомым, 30% скидка для пользователей Хабра на уникальный аналог entry-level серверов, который был придуман нами для Вас: Вся правда о VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps от $20 или как правильно делить сервер? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps до декабря бесплатно при оплате на срок от полугода, заказать можно тут.

Dell R730xd в 2 раза дешевле? Только у нас 2 х Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 ТВ от $249 в Нидерландах и США! Читайте о том Как построить инфраструктуру корп. класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки?

Let's block ads! (Why?)

Минтруд: тестовое задание — это трудовые отношения

Изначальная статья "Что всё-таки не так с наймом в IT?" многим порвала шаблон мышления месяц назад. Не все смогли адекватно воспринять суть трудового права. От шуток к делу, теперь слово берёт Министерство труда и социальной защиты Российской Федерации.

Ниже дословно:


В соответствии со статьей 64 Трудового кодекса Российской Федерации (далее — ТК РФ) необоснованный отказ в заключении трудового договора запрещается, если это не связано с деловыми качествами работника.
В целях проверки его соответствия поручаемой работе в трудовом договоре по соглашению сторон может быть предусмотрено условие об испытании работника (статья 70 ТК РФ).

Поэтому тестовое задание без трудового договора — это сразу ст.5.27. КоАП РФ:


  • Соответственно 30-50 тыс. рублей за первое, и 50-70 тыс. рублей за каждое последующее нарушение для работодателя.
  • Если работодатель отказывается признавать трудовые отношения, то ещё:
    • 50-100 тыс. рублей для работодателя, 100-200 тыс. рублей за повтор;
    • 3-5 тыс. рублей для конкретного работника, который выдал тестовое задание, 5-7 тыс. рублей за повтор.
  • Невыплата зарплаты за тестовое задание — 30-50 тыс. рублей, 50-100 тыс. рублей за повтор.

Разбираемся под катом.

В качестве пруфа предоставляется ответ как он есть страница 1 и страница 2. Логика правого аспекта изложена в изначальной статье. Поэтому повторяться не будет.


Так откуда ветер дует?

Начнём с того, что трудовое законодательство множества стран находится под прямым влиянием принимаемых под эгидой Международной организации труда (МОТ) конвенций и рекомендаций. Отсюда растут ноги наличия независимой инспекции труда, унифицированного максимального времени работы в 40 часов в неделю, требования бесплатного предоставления услуг кадровых агентств для соискателей работы и много чего ещё за целый век функционирования.

В ТК РФ есть пробелы, которые закрыты в ратифицированных конвенциях или общепринятых принципах. Процедура тестирования кандидатов никак не отражена в ТК РФ по причине того, что она идёт вразрез с международными принципами. Эти принципы были изначально заложены для НЕ интеллектуальных тружеников с целью полного исключения дискриминации и обеспечения всех минимальным заработком.

Именно поэтому выходит именно так, как было сказано в изначальной статье — работодатель ОБЯЗАН заключить трудовой договор, если есть вакансия, фиксированная оплата и соискатель утверждает, что он соответствует требованиям. Максимум допускается ПРОВЕРИТЬ ДОКУМЕНТЫ о наличии той или иной квалификации и провести собеседование (от слово БЕСЕДА), где на работодателе лежит обязанность рассказать о предстоящих условиях труда, а не пытать соискателя тестированием. Тонкий момент вскрытия самозванцев не закладывался в принципы. По сути отказать по деловым качествам возможно только, если кандидат не утверждает об их наличии или это явно открывается на собеседовании.


Так какая должна быть процедура найма?

Есть дисциплина сравнительного правоведения. Давайте посмотрим на одну из ближайших соседок — Латвию. В законе о труде Латвийской Республики есть такие положения (приведена суть по этапам):



ст.32. Объявление о работе


  • (1) Запрет на дискриминацию по полу.
  • (2) Запрет на дискриминацию по возрасту.
  • (3) Обязательное указание регистрационных данных лица, производящего отбор кандидатов — чтобы было ясно на кого жаловаться и кого привлекать к ответственности.

Примечание: мои знакомые однажды поимели общение с трудовой инспекцией после объявления с "молодой стройной девушкой". Спасло только использование номера телефона с анонимной симки.

ст.33. Трудовое интервью


  • (1) Интервью — это устный или письменный ОПРОС (т.е. ТИПОВАЯ АНКЕТА).
  • (2) Запрещается задавать вопросы, которые не связаны с исполнением работы. Явно запрещается спрашивать дискриминационные вопросы, в т.ч.:
    • о беременности,
    • о семье и семейном положении,
    • о судимости,
    • о религиозной принадлежности,
    • о партийной принадлежности и политических взглядах,
    • о национальной или этнической принадлежности.
  • (3) Работодатель обязан рассказать об условиях труда и порядках, которые имеют значение для заключения трудового договора.
  • (4) Претендент обязан дать информацию о состоянии здоровья и профессиональной подготовленности лишь на столько, на сколько это необходимо для заключения трудового договора и выполнения трудовых обязанностей.

ст.34. Нарушение запрета дискриминации при трудоустройстве


  • (1) Претендент имеет право оспорить отказ в трудоустройстве в судебном порядке в течение трёх месяцев.
  • (2) Если отказ не на основе дискриминации, то у претендента нет права требовать принудительное заключение трудового договора.

ст.35. Необходимые документы для заключения трудового договора


  • (1) Удостоверение личности и иные предусмотренные нормативными актами документы.
  • (2) Документы об образовании и квалификации, если для выполнения работы требуются особые знания или навыки.
  • (3) Разрешение на работу (для иностранцев).

ст.36. Проверка здоровья


  • (1) Работодатель в праве потребовать проверку здоровья, если это необходимо для выполнения работы (т.е. если медик, моряк, машинист и т.д.)
  • (2) Доктор указывает только ту информацию, которая имеет значение для конкретной деятельности.
  • (3) Работодатель оплачивает все расходы, если только претендент не предоставил заведомо ложные сведения о состоянии своего здоровья.

ст.39. Договорённость работника и работодателя

Трудовой договор считается ЗАКЛЮЧЕННЫМ с момента когда работник и работодатель договорились об оплате и работник согласился соблюдать трудовой порядок и распоряжения работодателя.

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

ст.40. Форма трудового договора


  • (1) Трудовой договор заключается письменно до начала работы.
  • (2) Обязательные детали...
  • (3)-(12) Прочая бюрократия.

ст.46. Назначение испытания


  • Примерно так же, как в РФ и других странах под зонтиком МОТ.

ст.47. Результат испытания


  • (1) Обе стороны вправе разорвать трудовой договор письменно без объяснения причин за три дня до завершения трудовых отношений.
  • (2) Иначе, испытание пройдено.

Примечание: делаешь всё по процедуре — ничего объяснять не требуется!

ст.48. О дискриминации при испытании

Ну, вы поняли — есть право обжаловать в суде в течение одного месяца.



Как все могут увидеть, процедура найма расписана по шагам и прямо отражает международно признанные принципы трудового права. К великому сожалению, некоторые маргинальные компании в Латвии нарушают и местное, и международное трудовое законодательство даже при явном указании на это. В России не лучше. Лирика: хоть одна компания с корпоративном блогом на Хабре может заявить, что не даёт тестовых заданий?

По дискриминации в Латвии: 1-3 месяца на обжалование в суде связано лишь с восстановлением в трудовых правах, а вот сроки давности по уголовному кодексу за отдельную статью о дискриминации никто не отменял. Поэтому отказ в трудоустройстве по причине отказа претендента делать тестовое задание без трудового договора может рассматриваться как дискриминация по политическим взглядам и степени законопослушности, п.2.ст.149(1) — до трёх лет лишения свободы для работников предприятия (HR). Лирика: нужно ведь с чего-то начинать правоприменительную практику для этой статьи.


Почему требуется трудовой договор для тестового задания?

Для тех, кто всё ещё не понимает зачем же эта бюрократия с трудовым договором.

Представьте ситуацию: водителю дали тестовое задание на площадке, у транспортного средства оказалась неисправна тормозная система, произошла авария с жертвами. Претендент говорит, что выдали неисправное ТС, а работодатель, что водитель совершил угон.

Вариант второй: программисту или дизайнеру дали тестовое задание, но у него банально отсутствуют свои средства производства или лицензии на необходимое ПО (причина — только откинулся, пожар, кража и т.п.). Работодатель обязан предоставить это, а вот без трудового договора выходит дискриминация — человек не может устроиться на работу из-за своего социального/материального положения.

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


Понимает ли автор "утопию" правового подхода?

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

Процедура простая и эффективная:


  1. Изначальный отсев по CV.
    • никакой системы баллов или оценки качества составления CV,
    • упор только на опыт и ПОНИМАНИЕ фактической деятельности в указанном опыте — HR вообще не годится.
  2. Короткий звонок: интерес кандидата, фильтр по зарплате, запись на интервью.
  3. Сначала 30 минут на вопросы на бумаге в одиночестве.
    • менее 15 вопросов разного уровня сложности без лишней писанины,
    • составлено в типовой анкете и не меняется годами — упор лишь на фундаментальные знания,
    • результат не имеет особого значения, условный порог прохождения всего 30% — это грубый фильтр на "самозванцев".
  4. Непосредственно интервью, используя опыт работы из резюме и предоставленные ответы — 15-30 минут.
    • результат теста — лишь фундамент для вопросов устной части,
    • любая ложь или жульничество всплывает сразу — типовой HR не может так эффективно вскрывать,
    • "дно" в различных направлениях нащупывается лишь для понимания уровня кандидата,
    • слабый результат теста вытягивается наводящими вопросами — часто кандидат банально мандражирует и это сказывается на результате,
    • в конечном итоге оценка даётся не квалификации кандидата, а достоверности предоставленных сведений.
  5. Формальная часть интервью — 10-30 минут.
  6. Принятие решения по конкретному человеку и согласование позиции через корпоративную процедуру (в некоторых случаях до месяца).
  7. Заключение трудового договора с испытательным сроком.
  8. Тестовое задание на реальном проекте в самостоятельном режиме.
    • Знакомство с реальной деятельностью, не мешая команде.
    • Максимум 1 неделя с ежедневной проверкой успеваемости.

В результате, за один рабочий день (8 часов) вполне реально проинтервировать более 10 человек. Каждый кандидат тратит час-полтора своего времени. 4-6 интервью в день было нормой в аврал и набирались дружные боевые команды. Примерно так же работал первый начальник автора и эта преемственная модель проверена уже десятилетиями. Если у вас на предприятии текучка кадров и вы не вылезаете из интервью, то проблема скорее совсем в другом.

Таким образом набирается 90% персонала и это здравый подход. Искать лучших из лучших, как и "пускать кровь" ежегодно — это утопия. Заполнение отдельных уникальных позиций происходит путём роста изнутри, либо целенаправленного предложения конкретным кандидатам без отбора.

Что мы видим сейчас — это форменная идиотия и неумение HR учиться на собственных ошибках. В XX веке уже пытались фильтровать людей по IQ — результат оказался негативным. Сейчас в начале XXI века пытаются автоматизировать наём многоступенчатой "тупой" оценкой человека. Вот это и есть утопия. А заботиться о своём времени, презирая время кандидатов — это прямое неуважение.

Хотите нанять действительно хорошую команду в любую область — допускайте к оценке только профильных спецов со смежными способностями в кадровом деле. То же относится и к управлению в целом. Если у кого-то только одна ответственность "нанимать", то нет ничего удивительного в бездарном изображении деятельности. Многие вопросы психологической совместимости в отборе — это уже дискриминационный моветон, т.к. не в разведку и не на подлодку идут. В цивилизованном обществе такие проблемы нивелируются. Туда же и высосанные из пальца оценки эффективности персонала. Оценивать нужно команду проекта, а команда должна разбираться и определять внутренние и внешние тормоза самостоятельно.

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

Если уже совсем паранойя по самозванцам, то на испытательный срок возможно указать в договоре особый порядок начисления зарплаты. Служба внутренней безопасности имеет целых три месяца для проверки человека, в т.ч. по поведению на рабочем месте. Новый человек не получает "все ключи" и от него не должно быть потенциальной опасности сильно больше чем от простого посетителя или внешнего обслуживания. Если это не так, то проблема совсем не в потенциальной угрозе.


Почему это так важно для развития общества?

Откатимся на значительное время назад. Открытое рабство — это норма. Никто особо не возникал, всех нарушителей злобно карали. Население, в т.ч. сами рабы, всячески этому способствовали. Сейчас открытая форма рабства всё ещё существует даже в России, но жёстко пресекается при раскрытии, но исправительная система всё равно построена на издевательском лишении свободы.

Откатимся поближе. Расовая сегрегация — это норма на западе, которая в наши дни жёстко пресекается вплоть до уголовной ответственности. Хотя, чем дальше на восток — тем больше присутствие многих элементов расизма. Развитая Япония совсем не исключение.

Наши дни, скрытая форма рабства — это норма. Рабы… Работники боятся отстаивать свои права, большая часть даже не знает о своих правах. Рабовладельцы… Работодатели этим всячески пользуются. Как и с формой открытого рабства, конформизм отдельных работников приводит к проигрышу всех работников в целом. Теория игр во всей красе.

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


Заключение

По тому, как Минтруд выбирает выражения и открещивается от официальности своего мнения, ясно, что они сами попали в ступор и балансируют между интересами рабовладельцев и трудовым правом. Высшие судебные органы, на решение которых идёт неявная отсылка, тоже "молодцы". В такой же ступор попали многие юристы, а журналисты похоже вообще не поняли сути. Правозащитные организации ангажированы политическими темами. Пока ещё не ответила Общественная палата РФ, но вряд ли будет ли хоть какое-то результативное шевеление.

Хоть у автора нет особых иллюзий, но есть желание верить, что в России зарегулируют процедуру найма, что исключат издевательские многочасовые тестовые задания, что у девушек не будут спрашивать о детородных планах на собеседованиях, что во всех объявлениях о найме будет указаны реквизиты. Может хоть одной проблемой станет меньше и не добавится новых для добросовестных.

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

Для тех, кто готов на более решительные действия — бомбите инспекцию труда и прокуратуру жалобами на недобросовестных работодателей, и жалобами в вышестоящие организации при бездействии, вплоть до администрации Президента. Обжалуйте в суде — это крайне действенно и элементарно. Организуйтесь со своими юристами и качайте ранее описанную схему через суд, 1-3 месячные зарплаты за несколько часов возни на потоке.

Только так возможно что-то сдвинуть с мёртвой точки.


Ваше мнение

В России есть признанная площадка для петиций, но сначала давайте проведём соцопрос. Не стесняйтесь обосновать ваше мнение в комментариях. Может общество и заслуживает такое положение дел...

Let's block ads! (Why?)

Серийные 8K OLED телевизоры LG ждём в июне 2019-го, возможно выпуск затянется

Согласно свежим сообщениям www.ajudaily.com и www.whathifi.com, компания LG технологически готова к тому, чтобы запустить серийный выпуск 8K OLED телевизоров в первой половине 2019-го. По заявлениям CEO LG Display Хана Сан-бома (Han Sang-beom), сделанным в Сеуле, массовое производство устройств планируется начать уже в мае, а в июне представить продукцию на рынке. Под катом немного подробней об анонсе массового производства, конкуренции и перспективах 8K в ближайшем будущем.


Текущее состояние дел с 8К


Вероятно, многие в курсе, что 8К — это приблизительно 8000 пикселей по горизонтали, стандарт 8K UHD (4320p) предполагает разрешение 7680×4320 (33,2 мегапикселя). Еще сравнительно недавнего многие считали, что создание 8K устройств для бытового применения нецелесообразно. Мол, не заметит зритель разницы и, кроме как выставочные концепты, 8K-устройства не рассматривались. Однако с развитием технологий представления производителей и пользователей быстро изменились.

Ранее СМИ сообщали о том, что прототипы OLED панелей от LG c 88-дюймовым экраном были представлены на IFA 2018. Кроме того, многие уже знают, что основной конкурент LG компания Samsung выпустила свою первую коммерчески доступную 8K QLED панель — QE85Q900R TV. В России она уже доступна по предзаказу.

Таким образом можно сказать, что LG стремится “догнать и перегнать” своих соотечественников. При этом необходимо отдать им должное, они “идут очень плотно” с Samsung, заметно уступая отраслевое лидерство лишь эпизодически и ситуативно.

В теории от эпичной битвы корейских титанов, к которой периодически присоединяются их коллеги из Японии и КНР, выигрывает рынок, партнёры компаний и более всех потребитель, который получит вожделенную инновацию раньше и дешевле, чем в случае, если бы её разработкой занимался монополист. Но это в теории…

Контент, которого нет


Несмотря на оптимизм представителей LG и других компаний, которые стремятся поскорее выпустить на рынок 8К, существуют проблемы. В частности, в LG не оторваны от реальности и понимают проблему с контентом. Хан Сан-бом согласен с тем, что сколько-нибудь адекватного количества контента катастрофически мало, его практически нет. Более того, на данный момент существуют ощутимые сложности даже с 4К контентом, к которому пользователь только начал привыкать. А большинство существующих 8K записей — демонстрационные.

Вероятно, что, как и в случае с Samsung, LG будет опираться на данные предзаказов и в зависимости от их количества будет рассчитывать массовость серий. Не исключено, что, как и в случае с 4K, маркетологи компании сумеют сыграть на престижности факта обладания устройством с характеристиками, заведомо превосходящими возможности контента. Характерно, что масштабирование по 4К, причем с не слишком высокой интенсивностью, происходит только сейчас.

Наш опыт


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

Есть еще одна любопытная деталь: внушительная часть наших клиентов стремилась приобрести 4K панели по рекомендации знакомых. Часто эти люди не имели представления о том, зачем им 4K разрешение. Можно говорить, что умелая раскрутка 4К как технологического тренда “для всех” была настолько жесткой, что потребители просто воспринимали информацию о разрешении, как синоним крутости устройства, что не всегда соответствовало действительности.

Зачем нужен 8K сегодня?


Авторы обзора whathifi.com на первый в мире серийный 8K телевизор Samsung QE85Q900R отмечали, что по детализации, резкости, яркости, динамичности, цветовой вибрации и в целом по кинематографичности изображения — этот телевизор превосходит всё, что они видели ранее. Важно также понимать, что выводы делались по демо-контенту, который специально создавался для этого телевизора.

Для FullHD и 4K-картинки в QE85Q900R применяется алгоритм (аппликатор), построенный на основе искусственного интеллекта, который использует машинное обучение для анализа изображения и повышения разрешения. Не исключено, что нечто подобное будет использоваться в новинках от LG.

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

Надо четко понимать, что в мире, где нет соответствующего контента, 8K может существовать только в таком достроенном «додуманном» алгоритмом виде, что частично нивелирует преимущества этого разрешения. С другой стороны очевидно, что рано или поздно контент таки появится.

Итог


Очевидно, что будущее за 8K, при этом остается открытым вопрос, насколько далеко от нас это будущее. О реализации планов LG мы узнаем достаточно скоро, но неизвестно станет ли технология востребованной сразу. Буду рад вашим мнениям и прогнозам в комментариях.

Джинса
В нашем каталоге пока нет 8К панелей, но представлено множество телевизоров, панелей, проекторов и другой электроники для воспроизведения видео.

Let's block ads! (Why?)

Лечим проблему FHRP asymmetric routing

Что такое “FHRP asymmetric routing”?

Состояние маршрутизации, при котором трафик в пределах одной сессии уходит через один маршрутизатор FHRP(VRRP/HSRP) master, а возвращается — через второй.

image


Что в этом плохого?

Если все маршрутизаторы находятся в пределах одной LAN — скорее всего, ничего.
Проблемы начинаются, если сетевая топология выглядит так:

image


1. MTU discovery — if the smallest MTU of the two paths differ, end-point MTU path discovery could result in a largest of the two MTU, which in turn will result in dropping of maximum size packets. For example, if one path goes through a VPN tunnel and the other does not, the VPN tunnel will have a smaller MTU. ping will work fine, but transferring large files will fail consistently.
2. Connectivity trouble shooting will be more difficult if one of the two path is broken but the other is not. Good old «traceroute» will be no help at all, as it will not be able to detect the reverse path intermediate points, unless it is exercised from both sides of the connection, which requires an out-of-band management channel.

Источник


Почему так получается?

У вышестоящего маршрутизатора (core-r-1) отсутствует информация о ролях нижестоящих маршрутизаторов в FHRP.

Решение о выборе маршрута принимается автономно, исходя из метрик протокола динамической маршрутизации или PBR.


Как это исправить?

С точки зрения прохождения трафика: трафик должен уходить и возвращаться через тот же маршрутизатор и VPN-туннель

image

С точки зрения маршрутизации:
Вышестоящие маршрутизаторы должны получать информацию о состоянии FHRP.

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

image

Как это работает?

image


Тестовый стенд (GNS3, MikroTik, BGP, VRRP).

image


  1. Ссылка для скачивания
  2. Router credentials:
    A. Login: admin
    B. Pass: нет

© Идея — webfox, статья и сборка стенда — maniak

Let's block ads! (Why?)

[Из песочницы] Codable для API запросов и как навести в коде порядок

Привет, Хабр!

Начиная со Swift 4 нам доступен новый протокол Codable, который позволяет легко кодировать/декодировать модели. В моих проектах очень много кода для API вызовов, и за последний год я проделал большую работу по оптимизации этого огромного массива кода во что-то очень легкое, лаконичное и простое путем убивания повторяющегося кода и использования Codable даже для multipart запросов и url query параметров. Так получилось несколько отличных на мой взгляд классов для отправки запросов и парсинга ответов от сервера. А также удобная структура файлов представляющая из себя контроллеры для каждой группы запросов, которая мне привилась при использовании Vapor 3 на бэкенде. Несколько дней назад я выделил все свои наработки в отдельную библиотеку и назвал ее CodyFire. О ней мне и хотелось бы рассказать в этой статье.

Дисклеймер


CodyFire базируется на Alamofire, но это несколько больше чем просто обертка над Alamofire, это целый системный подход к работе с REST API для iOS. Именно поэтому я не переживаю, что в Alamofire пилят пятую версию в которой будет поддержка Codable, т.к. это не убьет моё творение.

Инициализация


Начнем немного издалека, а именно с того, что часто мы имеем три сервера:

dev — для разработки, то что запускаем из Xcode
stage — для тестирования перед релизом, обычно в TestFlight или InHouse
prod — продакшн, для AppStore

И многие iOS разработчики конечно же знают о существовании Environment Variables и о схемах запуска в Xcode, но за мою (8+ лет) практику 90% разработчиков руками прописывают нужный сервер в какой-нибудь константе пока тестируют, или перед сборкой, и это то, что мне хотелось бы исправить показав хороший пример как надо делать правильно.

CodyFire по умолчанию автоматически определяет в каком окружении сейчас запущено приложение, делает оно это очень просто:

#if DEBUG
    //DEV environment
#else
    if Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt" {
        //TESTFLIGHT environment
    } else {
        //APPSTORE environment
    }
#endif


Это конечно же под капотом, а в проекте в AppDelegate вам нужно всего лишь прописать три URL
import CodyFire

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        let dev = CodyFireEnvironment(baseURL: "http://localhost:8080")
        let testFlight = CodyFireEnvironment(baseURL: "https://stage.myapi.com")
        let appStore = CodyFireEnvironment(baseURL: "https://api.myapi.com")
        CodyFire.shared.configureEnvironments(dev: dev, 
                                              testFlight: testFlight,
                                              appStore: appStore)
        return true
    }
}


И можно было бы просто этому порадоваться и больше ничего не делать.

Но в реальной жизни нам часто нужно в Xcode тестировать dev, stage и prod сервера, и для этого я призываю использовать схемы запуска.

image

Совет: в разделе Manage schemes не забудьте каждой схеме поставить галочку `shared` чтобы они были доступны всем разработчикам в проекте.

В каждой схеме нужно прописать переменную окружения `env` которая может принимать три значения: dev, testFlight, appStore.

image

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

CodyFire.shared.setupEnvByProjectScheme()

Более того, часто босс или тестировщики вашего проекта могут просить о переключении сервера «на лету» где-нибудь на LoginScreen. С CodyFire вы сможете легко это реализовать переключая сервер одной строкой изменив окружение:
CodyFire.shared.environmentMode = .appStore

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

Структура файлов и контроллеры


Теперь можно рассказать о моем видении структуры файлов для всех API вызовов, это можно назвать идеологией CodyFire.

Давайте сразу посмотрим как это в итоге выглядит в проекте

image

А теперь посмотрим листинги файлов, начнем с API.swift.

class API {
    typealias auth = AuthController
    typealias post = PostController
}


Здесь перечислены ссылки на все контроллеры, чтобы их было легко вызывать через `API.controller.method`.
class AuthController {}


API+Login.swift
extension AuthController {
    struct LoginResponse: Codable {
        var token: String
    }
    
    static func login(email: String, password: String) -> APIRequest<LoginResponse> {
        return APIRequest("login").method(.post)
                                  .basicAuth(email: email, password: password)
                                  .addKnownError(.notFound, "User not found")
    }
}


В этом декораторе мы декларируем функцию обращения к нашему API:

— указываем endpoint
— HTTP метод POST
— используем враппер для basic auth
— деклариируем желаемый текст для определенного ответа от сервера (это удобно)
— и указываем модель по которой будут декодированы данные

Что осталось скрытым?

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

200 ОК это статус код по умолчанию ожидаемый CodyFire для всех запросов, в случае которого идет декодинг данных и вызывается callback, что всё хорошо, вот ваши данные.
Далее где-то в коде для вашего LoginScreen вы сможете просто вызывать
API.auth.login(email: "test@mail.com", password: "qwerty").onKnownError { error in
    switch error.code {
    case .notFound: print(error.description) //выведет: User not found
    default: print(error.description)
    }
}.onSuccess { token in
    //TODO: сохраняем auth token в надежном месте
    print("Received auth token: "+ token)
}


onKnownError и onSuccess это только малая часть колбэков, которые может возвращать APIRequest, поговорим о них позже.

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

API+Signup.swift

extension AuthController {
    struct SignupRequest: JSONPayload {
        let email, password: String
        let firstName, lastName, mobileNumber: String
        init(email: String,
             password: String,
             firstName: String, 
             lastName: String, 
             mobileNumber: String) {
            self.email = email
            self.password = password
            self.firstName = firstName
            self.lastName = lastName
            self.mobileNumber = mobileNumber
        }
    }
    
    struct SignupResponse: Codable {
        let token: String
    }
    
    static func signup(_ request: SignupRequest) -> APIRequest<SignupResponse> {
        return APIRequest("signup", payload: request).method(.post)
                .addKnownError(.conflict, "Account already exists")
    }
}


В отличие от входа, при регистрации мы передаем большое количество данных.

В данном примере мы имеем модель SignupRequest которая соответствует протоколу JSONPayload (таким образом CodyFire понимает тип payload), чтобы body нашего запроса было в виде JSON.

В итоге вы получаете простую функцию которая принимает модель payload

API.auth.signup(request)

и которая в случае успеха вернет вам определенную модель ответа.

По-моему уже круто, да?

А что если multipart?


Давайте рассмотрим пример когда можно создать некий Post.

Post+Create.swift

extension PostController {
    struct CreateRequest: MultipartPayload {
        var text: String
        var tags: [String]
        var images: [Attachment]
        var video: Data
        init (text: String, tags: [String], images: [Attachment], video: Data) {
            self.text = text
            self.tags = tags
            self.images = images
            self.video = video
        }
    }
    
    struct Post: Codable {
        let text: String
        let tags: [String]
        let linksToImages: [String]
        let linkToVideo: String
    }
    
    static func create(_ request: CreateRequest) -> APIRequest<CreateRequest> {
        return APIRequest("post", payload: request).method(.post)
    }
}


Данный код сможет отправить multipart форму с массивом файлов картинок и с одним видео.
Посмотрим как вызвать отправку. Тут самый интересный момент про Attachment.
let videoData = FileManager.default.contents(atPath: "/path/to/video.mp4")!
let imageAttachment = Attachment(data: UIImage(named: "cat")!.jpeg(.high)!, 
                                 fileName: "cat.jpg",
                                 mimeType: .jpg)
let payload = PostController.CreateRequest(text: "CodyFire is awesome", 
                                           tags: ["codyfire", "awesome"],
                                           images: [imageAttachment],
                                           video: videoData)
API.post.create(payload).onProgress { progress in
    print("прогресс выгрузки: \(progress)")
}.onKnownError { error in
    print(error.description)
}.onSuccess { createdPost in
    print("пост успешно создан: \(createdPost)")
}


Attachment это модель в которой помимо Data передается также имя файла и его MimeType.

Если вы хоть раз отправляли multipart форму из Swift с использованием Alamofire или голого URLRequest я уверен вы оцените простоту CodyFire.

Теперь более простые, но не менее классные примеры GET вызовов.

Post+Get.swift

extension PostController {
    struct ListQuery: Codable {
        let offset, limit: Int
        init (offset: Int, limit: Int) {
            self.offset = offset
            self.limit = limit
        }
    }
    
    static func get(_ query: ListQuery? = nil) -> APIRequest<[Post]> {
        return APIRequest("post").query(query)
    }
    
    static func get(id: UUID) -> APIRequest<Post> {
        return APIRequest("post/" + id.uuidString)
    }
}


Самый простой пример это
API.post.get(id:)

который в onSuccess вернет вам Post модель.

А вот более интересный пример

API.post.get(PostController.ListQuery(offset: 0, limit: 100))

который принимает на вход ListQuery модель,
которую в итоге APIRequest конвертирует в URL-path вида
post?limit=0&offset=100

и вернет в onSuccess массив [Post].

Вы конечно можете и по-старинке писать URL-path, но теперь-то вы знаете, что можно тотально Codable'зироваться.

Последний пример запроса будет DELETE

Post+Delete.swift

extension PostController {
    static func delete(id: UUID) -> APIRequest<Nothing> {
        return APIRequest("post/" + id.uuidString)
              .method(.delete)
              .desiredStatusCode(.noContent)
    }
}


Здесь два интересных момента.

— возвращаемый тип APIRequest, в нем указан generic тип Nothing, который является пустой Codable моделью.
— мы явно указали, что ожидаем получить 204 NO CONTENT, и CodyFire только в этом случае вызовет onSuccess.

Как вызывать этот endpoint из вашего ViewController'a вы уже знаете.

Но тут два варианта, первый с onSuccess, а второй без. На него и посмотрим

API.post.delete(id:).execute()

То есть если вам неважно отработает ли запрос, то можете просто вызвать у него .execute() и всё, иначе он запустится после декларации onSuccess хендлера.

Доступные функции


Авторизация каждого запроса


Для подписи каждого API запроса какими-либо http-headers используется глобальный хэндлер, который вы можете задать где-нибудь в AppDelegate. Более того на выбор можно использовать классический [String: String] или Codable модель.

Пример для Authorization Bearer.

1. Codable (рекомендую)

CodyFire.shared.fillCodableHeaders = {
    struct Headers: Codable {
        //NOTE: если nil, то не добавиться в headers
        var Authorization: String?
        var anythingElse: String
    }
    return Headers(Authorization: nil, anythingElse: "hello")
}


2. Классика [String: String]
CodyFire.shared.fillHeaders = {
    guard let apiToken = LocalAuthStorage.savedToken else { return [:] }
    return ["Authorization": "Bearer \(apiToken)"]
}


Выборочное добавление некоторых http-headers в запрос


Это можно сделать при создании APIRequest, например:
APIRequest("some/endpoint").headers(["someKey": "someValue"])

Обработка неавторизованных запросов


Вы можете обрабатывать их глобально, например в AppDelegate
CodyFire.shared.unauthorizedHandler = {
    //выбросить пользователя на WelcomeScreen
}


или местно в каждом запросе
API.post.create(request).onNotAuthorized {
    //пользователь не авторизован
}


Если сеть не доступна

API.post.create(request). onNetworkUnavailable {
    //нет связи с интернетом, либо авиарежим, либо проблемы с сетью
}


иначе в onError вы получите ошибку -1109 или в onKnownError это enum значение ._notConnectedToInternet
Кстати, onKnownError имеет приоритет перед onError. Если вы объявили и onError и onKnownError и ошибка одна из известных, то будет вызван только onKnownError.

Запуск чего-либо перед тем как запрос запустится


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

Как отключить/включить вывод логов глобально

CodyFire.shared.logLevel = .debug
CodyFire.shared.logLevel = .error
CodyFire.shared.logLevel = .info
CodyFire.shared.logLevel = .off


Как отключить вывод логов для одного запроса

.avoidLogError()

Обрабатывать логи по-своему

CodyFire.shared.logHandler = { level, text in
    print("Ошибка в CodyFire: " + text)
}


Как задать ожидаемый http-код ответа сервера


Как я уже говорил выше, по умолчанию CodyFire ожидает получить 200 OK и если получает, начинает парсить данные и вызывает onSuccess.

Но ожидаемый код можно задать в виде удобного enum, например для 201 CREATED

.desiredStatusCode(.created)


или даже можно задать кастомный ожидаемый код
.desiredStatusCode(.custom(777))


Отмена запроса

.cancel()


и можно узнать, что запрос отменен объявив .onCancellation хендлер
.onCancellation {
    //запрос был отменен
}


иначе будет вызван onError или в onKnownError

Установка таймаута для запроса

.responseTimeout(30) //ставим таймаут в 30 секунд


на событие таймаута тоже можно повесить хендлер
. onTimeout {
    //запрос завершился по таймауту
}


иначе будет вызван onError или в onKnownError.

Установка интерактивного дополнительного таймаута


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

Идея в том, что он хотел, чтобы проверка email/password длилась 2 секунды или более. И если она длится только 0.5 секунды, значит нужно накинуть еще 1.5 и только тогда вызвать onSuccess. А если занимает ровно 2 или 2.5 секунды, то вызывать onSuccess сразу же.

.additionalTimeout(2) //минимум 2 секунды будет выполняться запрос


Свой Date encoder/decoder


В CodyFire есть свой DateCodingStrategy enum, в котором три значения

— secondsSince1970
— millisecondsSince1970
— formatted(_ customDateFormatter: DateFormatter)

DateCodingStrategy можно задать в трёх вариантах и отдельно для decoding и encoding
— глобально в AppDelegate

CodyFire.shared.dateEncodingStrategy = .secondsSince1970
let customDateFormatter = DateFormatter()
CodyFire.shared.dateDecodingStrategy = .formatted(customDateFormatter)


— для одного запроса
APIRequest("some/endpoint")
    .dateDecodingStrategy(.millisecondsSince1970)
    .dateEncodingStrategy(.secondsSince1970)


— или даже отдельно для каждой модели, просто нужно чтобы модель соответствовала CustomDateEncodingStrategy и/или CustomDateDecodingStrategy.
struct SomePayload: JSONPayload, CustomDateEncodingStrategy, CustomDateDecodingStrategy {
   var dateEncodingStrategy: DateCodingStrategy
   var dateDecodingStrategy: DateCodingStrategy
}


Как добавить в проект


Библиотека доступна на GitHub под MIT лицензией.

Установка пока доступна только через CocoaPods

pod 'CodyFire'

Я очень надеюсь, что CodyFire будет полезна другим iOS-разработчикам, упростит для них разработку, и вообще сделает мир чуточку лучше, а людей добрее.

Вот и всё, спасибо, что уделили время.

Let's block ads! (Why?)

Дрон-съемка, «грабли», лайфхаки, саморазвитие и карьера фотографа / видеографа: новый подкаст «GLPH»

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

Сегодня мы представляем вашему вниманию текстовый транскрипт второго выпуска подкаста [первый про IT-редакцию нового типа — здесь], в котором с нами согласился пообщаться Вадим Щербаковпрофессиональный аэровидеограф, фотограф и независимый арт-директор.

Мы обсудили ход карьеры Вадима и его подходы к саморазвитию:


На фото: Вадим Щербаков, профессиональный аэровидеограф, фотограф и независимый арт-директор

dmitrykabanov: Обычно спрашивают, сколько ты зарабатываешь или сколько часов в неделю ты работаешь. Я хочу поговорить о том, из чего состоит твой рабочий процесс.

Ты занимаешься аэровидеосъемкой и фотографией архитектуры — можешь рассказать предысторию, как ты к этому пришел? Я тебя помню с очень старого Dribbble-митапа 2013 года, который прошел в «Рабочей станции» возле Парка Горького.



В: Если ты помнишь, тогда я выступал как арт-директор, рассказывал про интерактивный дизайн, чем я до сих пор занимаюсь. Параллельно я делаю то, на что хочу переключиться полностью, — съемкой, фото и видео. Это — архитектура, landscape и cityscape.

Д: Как осуществлялся этот переход? Он до сих пор идет? Получается, что все это время ты работал и сейчас работаешь как независимый специалист?

В: Да. И тогда, и сейчас, и в будущем я хочу работать независимо. Речь идет не об открытии какой-то своей компании, а об абсолютном фрилансе — тогда и сейчас.

Если говорить о фото, видео и дрон-съемке, снимать я начал более 10 лет назад. Начинал, как все, с пленочного фотоаппарата, но достаточно быстро перешел на «цифру». Помню свои первые камеры Canon. Ими я пользуюсь до сих пор. Долгое время я снимал все подряд, как все. Буквально: что вижу, то и снимаю. Получалось ужасно, это даже не обсуждается.



Д: Это было больше твоим увлечением: интересные гаджеты, пленочные фотоаппараты?

В: Да, пленочные, потом цифровые. Но получалось плохо.
Потом меня посетила «гениальная мысль»: чем больше я куплю дорогой техники и оборудования, тем лучше станут мои фото и видео. Это была идиотская идея, которая не воплотилась в жизнь. В итоге я покупал ND-фильтры, линзы, новые фотоаппараты, но продолжал быть на том же уровне мастерства. Я не понимал, в чем проблема.



Д: Можешь ли сказать, какую сумму ты потратил, чтобы понять, что из творческого застоя нужно выходить с помощью наработки мастерства, а не дорогой техники?

В: Боюсь, что сейчас уже не вспомню. Это было давно и длилось около трех лет. Мне казалось, что с переходом на «цифру» фотографии стали лучше, и захотелось большего.

Я подумал, что это «большее» нарастет с новой линзой, новым ND-фильтром, новым каким-то гаджетом, которым я даже не умею пользоваться. Эту ошибку делают очень многие.

Точную цифру не могу сказать, потому что не помню, сколько тогда стоил, например, Canon EOS 350D. Это привело меня к тому, что я покупал дорогие вещи, но качество не становилось лучше.

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



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

В: Отсутствие людей — только часть проблемы. Еще у меня не было информации о том, как правильно снимать. Профессионального контента на YouTube тогда толком не было.

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

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

Я просто понял, что хочу снимать: не людей и зверей, а именно пейзажи.

И я захотел понять, как делать это правильно.



Д: Как подготовиться к поездке такого уровня?

В: Тогда подготовки не было. Мы ездили в Исландию как туристы. Было просто: «О, едем в Исландию, классно. Надо взять побольше одежды». Сейчас это — совсем другое дело.
Я думаю не о количестве одежды, а о том, сколько взять оборудования, как его уместить. Тогда я поехал с вещами и фотоаппаратом с одной линзой. Сейчас я беру два фотоаппарата или два дрона, много линз, и оставшееся место — для вещей. Совершенно разные подходы.

Когда я приехал обратно, стало ясно, что снимки получились лучше, чем те, что я делал раньше. Поэтому я стал действовать. Покупал больше фотожурналов, которые рассказывают о том, как снимать. Смотрел больше туториалов, где люди пошагово показывали, как и что делать. Я был настолько неуч, что даже не знал какие-то базовые вещи вроде «Golden hour», «Blue hour».


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

В итоге всё сложилось воедино. Я понял, что хочу посвятить этому [фото и видео] большую часть времени и плавно перейти с одного фриланса на другой. Для этого я стал смотреть больше туториалов, изучать материалы именно о композиционной съемке, узнавать, когда, как и чем снимать. Это начало нарастать, как снежный ком, и привело меня на существующий уровень.



Д: Что ты делал с первыми материалами, которые посчитал достаточно качественными, чтобы выставить на продажу? Кем были первые клиенты? Чем ты их зацепил?

Чем выделился среди остальных?



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

Первые клиенты стали появляться, когда я начал заниматься съемкой с дрона, композитной и time blending-съемкой. Это было не так давно, и я шел к этому медленнее, чем хотелось бы.

Я просто стал выкладывать свои работы на стоки: Creative Market, iStockphoto и другие. Здесь нет секретов, все стоки известны и их можно пересчитать по пальцам. Но у меня есть одна особенность. Обычно клиенты сами находят меня через мое портфолио и фильмы.

Поэтому выпуск дрон-фильмов был для меня самым правильным шагом. Он стал моим инструментом развития и продаж. Например, меня случайно нашли American Airlines и попросили использовать один фрагмент. Я не знаю, как они меня нашли.

Мой фильм был размещен только на YouTube и на Vimeo. Получается, что если дрон-фильм набирает достаточную популярность, студии могут его найти и обратить внимание.



Недавний кейс — через дрон-фильм меня нашли Pro Film, которые делали ролик для ВТБ.

Также я выставляю свои фильмы в Aerial Entertainment Studios (AES). Этот сервис создал американец, который живет в Гонконге. AES занимает определенную нишу на рынке — продает видео крупным зарубежным киностудиям: Warner Bros., Fox, MGM и так далее. У этого сервиса есть один недостаток — 80% запросов размещают на съёмку американских локаций. Это понятно: все фильмы, сериалы чаще всего снимают там.

Поэтому чаще покупают либо нейтральные вещи: пролет над горами или озером без привязки вроде «пролет над озером штата Мичиган», либо конкретные тематические вещи, которые просто не найти. Например, сериал «Homeland» заказал съемку Москвы. Им нужно было видео, в котором ты как будто взлетаешь на самолете.



Д: Я хотел бы спросить про оборудование. Ты говоришь, что задачи очень разные. Например, сегодня нужно лететь на дроне одной категории, завтра — лететь в другой стране на другом оборудовании. Какие здесь есть нюансы и потенциальные риски, в том числе и с регулированием, которое действует в Европе и недавно появилось у нас?

В: Любое оборудование можно арендовать, поэтому я особо не заморачиваюсь. Например, у меня есть клиент из Индии, где до 1-го декабря запрещено летать на дронах, ввозить их и вывозить. Поэтому я знаю, что не возьму свой арсенал. Клиент сам арендует оборудование уже на месте. Для этой конкретной съемки нам будет достаточно DJI Phantom 4 Pro.

Для съемок у меня есть только DJI Mavic и Phantom 4 для моих личных нужд. Всё остальное я могу арендовать. И в поездку я беру либо оба дрона, либо ничего.

Если говорить о фотооборудовании, то тут всё зависит от задачи. Я беру в поездки одну или две камеры и набор линз. Как и многие другие фотографы я выбрал свою «линейку» линз для постоянного использования. Плюс у меня есть несколько линз от партнеров, которые я должен и хочу брать с собой. Поэтому обычно я беру полный арсенал, плюс-минус одна линза.



Д: На тех фотографиях, что ты выкладываешь, я заметил, что у тебя круто оборудована домашняя студия. Рабочее место — важная вещь, когда ты не просто производишь одну единицу контента в месяц или раз в полгода отправляешься в поездку, а стараешься работать в темпе, даже с учетом различия задач. Что ты думаешь на этот счет?

В: Мне помогают перемены. Поэтому я не на сто процентов ушел от интерактивного дизайна. Об этом почти никто не знает, но даже когда я работал в интерактивном дизайне, я делал также интерактивные приложения. Мне нравится переключаться между разными задачами.
Пока я делаю одно дело, то немного забываю про другое. Мне так легче работать. Появляется свежий взгляд на вещи. Например, сегодня я снимаю, завтра делаю какой-то проект, послезавтра что-то еще и так далее. Больше всего меня убивает рутина, а смена деятельности меня всегда подбадривала и выручала.


На фото: кабинет Вадима (короткое видео в Facebook)

Мой кабинет (студия) — моя отдушина. Раньше у меня чего-то такого не было. Я работал в более «бухгалтерских» офисах. Это меня тяготило. Я же представитель креативной профессии. Поэтому рабочее место тоже должно быть креативным. Например, по fashion-дизайнеру сразу видно, что он — fashion-дизайнер. И с фотографами в какой-то степени так же.



Д: Так или иначе, когда проекты растут и становятся сложнее, приходится взаимодействовать с коллегами из индустрии или «технарями», которые помогают настраивать оборудование или ловить нужный ракурс. Есть ли у тебя команда?

В: Я стараюсь всегда рассчитывать на свои силы.

Д: Но в титрах же несколько человек?

В: Да, но эти люди помогают мне не в технических вопросах. Например, на съемки я часто езжу с женой. Она тоже увлечена этим делом и действительно помогает мне снимать. Иногда я «взлетаю» и прошу ее поснимать. Она замечает то, что я могу не увидеть, предлагает новые смелые идеи и ракурсы. Бывает, что ее «footage» покупают быстрее, чем мой.


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

Д: Сколько часов в день ты работаешь?

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

Д: Всё происходит естественно. Так, как ты привык за много лет.

В: Да, но иногда я перерабатываю. Тогда я останавливаюсь и несколько дней почти ничего не делаю. Обычно у меня все в гармонии, а работа распределяется почти на целый день.



Д: Поговорим о контенте, с которым ты работаешь. Ты упомянул журналы, туториалы. Что еще есть в твоей сфере? Что нужно знать, на что обратить внимание и что лучше смотреть, чтобы понимать индустрию, узнавать о трендах и новых техниках?

В: Я всё это определяю термином «насмотренность». И это — мой главный совет. Это нужно не для того, чтобы копировать, а чтобы понимать, что происходит в индустрии. Что не надо делать, где и как искать новые пути. Поэтому «насмотренность» — большой плюс для человека, который работает в любой индустрии.

Я советую любые онлайн-ресурсы, которые продвигают фотографию. Это Pexels, Fstoppers, Skypixel (для дронов) или любые другие комьюнити, которые вы найдете. Главное, чтобы в них размещали действительно хорошие фотографии. Если вы хотите отправлять свои работы на конкурсы и дрон-фестивали — следите за работами, которые там выигрывают.

По поводу техник съемки фото и видео — всю информацию можно найти на YouTube. С дронами сложнее. Пока не существует единого туториала от того, как человек впервые открывает коробку, до того, как сдает фильм клиенту или отправляет его на фестиваль.

Всю информацию нужно искать по отдельности: начиная с того, как запускать дрон, до количества кадров в секунду для конкретной съемки. Я давно думаю о создании детального руководства и, возможно, когда-то его выпущу.




Д: Как работают профессиональные медиа с контентом в этой индустрии? Как они взаимодействуют с авторами? Они просто пишут тебе, что им нравится твой материал, забирают его себе и выплачивают гонорар? Или вы с ними договариваетесь сотрудничать на постоянной основе, как это делают колумнисты? Или еще как-то?

В: В крупные стоки присылать свои фото и видео нужно самостоятельно. Этого материала сейчас столько, что к тебе сам никто не придет. Есть немало микростоков (например, азиатских), которые могут увидеть фильм и предложить сотрудничество. Крупные ресурсы так не делают.

Есть имиджевые медиа. Они находят тебя сами, но не платят за размещение фото. Они могут выплатить гонорар за статью или туториал, но основное преимущество от такого сотрудничества — построение бренда. Остальные клиенты — единичные покупки.

В моем случае таким клиентом стали American Airlines. Им не нужны постоянные пролеты над условным городом или горой. Они приходят раз в 2–3 года за каким-то уникальным контентом. Нет такого, что они готовы сотрудничать с одним автором на постоянной основе. Они работают с глобальными компаниями, которые производят контент.

Если у таких компаний нет того, что им нужно, они обращаются к авторам.




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

В: Точно. Более того, видеограф должен разбираться не только в съемке, но и знать, что потом делать с отснятым материалом. Себя нужно показать. Для этого можно выпустить уникальное видео, но чтобы тебя заметили и купили то, что ты сделал, нужен высокой уровень.

Поэтому важно вкладываться не только в поездки, но и в последующую обработку и продвижение своего продукта. Это — глобальная задача, но она вполне осуществима.



Заметки к этому выпуску:

Let's block ads! (Why?)