Показаны сообщения с ярлыком безопасность. Показать все сообщения
Показаны сообщения с ярлыком безопасность. Показать все сообщения

7 окт. 2022 г.

Как меня подвела народная мудрость

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

Сегодня утром меня поднял срочный email от облачного провайдера. Так и так, мол, мы прикрываем вашу лавочку, потому как вы не пользователь, а какой-то интернет террорист, который атакует с наших серверов пол Европы. Следом пришло автоматическое уведомление, что хотя они и выделяют нам канал на 400 MBit/sec это не значит, что мы должны использовать прямо так 400 Mbit все время.

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

Хорошо, что перенесенный сервер работал в тестовом режиме и у меня была исходная копия. Пришлось полностью снести VPS, переустановить с нуля Ubuntu и начать все с начала.

Что произошло?

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

Вывод:

1. Всегда обновляйте библиотеки. Выделите на это регулярный день, например, утро понедельника.

2. Если проект стабильный, уже не развивается, тем не менее следите, что пишет гитхаб по поводу найденных в нем уязвимостей. И, если найдены с пометкой Critical или High -- сразу же обновляйте код.

3. Облачный сервер сегодня есть -- завтра его нет. И ни какие мольбы не помогут вернуть вам утраченные данные. Работать в облаке можно только при настроенной системе автоматического 100%-ного бэкапа с переносом данных на другой физический сервер, обязательно к другому провайдеру.

17 дек. 2017 г.

Каждой программе по своей "проблеме 2000 года"

Помните, мир стоял на ушах, ожидая как самолеты попадают и атомные станции повзрываются? К счастью, ничего такого не произошло благодаря тому, что все критическое ПО было вовремя проверено и исправлено.

В Гедымине есть своя "проблема  2000 года", а точнее проблема 32-х битного идентификатора бизнес-объекта. Чтобы быть еще более точным, не самого идентификатора а генератора GD_G_UNIQUE, с помощью которого идентификаторы добываются.

Данный генератор стартует со значения 147 000 000 и увеличивается по мере запроса новых идентификаторов. Причем, для сокращения количества запросов к серверу, увеличиение идет с шагом в 100, а неиспользованный на момент завершения программы интервал сохраняется в системном реестре.

Так как у нас ИД объекта -- это знаковое 32-х битное целое, то всего доступно чуть более 2-х миллиардов идентификаторов (мы не учитываем первые 147 миллионов, которые выделены под системные объекты платформы).

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

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

Что делать?

Теоретически есть два варианта решения проблемы. Первый -- это сдвинуть, утрамбовать все идентификаторы "вниз" на выявленные пустые пробелы. Затем изменить значение генератора в соответствии с максимальным ИД в базе.

Технически, для этого придется выполнить следующую последовательность шагов:
  1. Сохранить все существующие ИД в некоторой структуре.
  2. Построить таблицу соответствия старый ИД -- новый ИД.
  3. Отключить все внешние и первичные ключи.
  4. Обновить ВСЕ записи в базе данных, заменяя старые идентификаторы на новые.
  5. После предыдущего шага желательно выполнить бэкап-восстановление БД для чистки мусора.
  6. Восстановить все первичные и внешние ключи.
На базах размером свыше 100 Гб мы не представляем как можно выполнить указанную последовательность в доступное нам технологическое окно (обычно 8-10 часов).  И, если где-то в коде, используется привязка к ИД записи, вместо РУИД, то такой код перестанет работать. К тому же, надо будет как-то вычистить из реестров всех компьютеров сохраненные интервалы или одномоментно заменить все экзешники, скорректировав алгоритм кэширования.

Второй вариант:
  1. Создать таблицу для доступных интервалов идентификаторов GD_AVAILABLE_ID.
  2. При обращении к функции gdcBaseManager.GetNextID проверять не приблизились ли мы к опасной черте. 
  3. Если нет, то работать по-старому -- через генератор. 
  4. Если уже пора, то заполняем таблицу и по-мере необходимости берем очередной интервал из нее.
На практике проверено, что заполнение такой таблицы занимает пару часов даже на самой большой, доступной нам базе данных.

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

24 апр. 2016 г.

Задание на неделю #1. Firebird 3

Firebird Logo На этой неделе произошло событие, которого мы с нетерпением ждали шесть долгих лет -- вышла третья версия сервера Firebird. В двух словах предыстория такова: Interbase, от которого в 2000-м году отпочковался Firebird, был задуман в те времена, когда многопроцессорные системы с десятками гигабайт оперативной памяти встречались разве что в фантастических рассказах. Когда же научно-технический прогресс догнал и перегнал самые смелые фантазии, именно внутренняя архитектура сервера стала основным тормозом. По сути, в многопользовательском сценарии у системного администратора было два выбора: или использовать оперативную память для кэширования данных, но тогда запросы из всех подключений будут выполняться только на одном процессоре/ядре (архитектура SuperServer), или задействовать все процессоры/ядра, но тогда не будет общего кэша и скорость будет зависеть от производительности дисковой подсистемы (архитектура Classic). Тот случай, когда хрен редьки не слаще.

Изменения в архитектуре SuperServer движка Firebird 3 теперь позволяют последнему выполнять запросы сетевых клиентов параллельно на разных процессорах/ядрах, при доступе к общему кэшу, что теоретически делает SuperServer выбором по умолчанию при развертывании системы на предприятии. Как оно получится на практике -- зависит от надежности тройки. В начале 2000-х мы перевели всех клиентов с SuperServer на Classic по двум причинам: падение процесса SuperServer означает обрыв всех коннектов и потерю данных во всех открытых транзакциях у сетевых пользователей; выполнение тяжелого запроса одним из клиентов практически парализует работу всех остальных.

У одного нашего клиента база данных имеет размер 50 Гб, количество одновременных подключений 50-60, на сервере 128 Гб оперативной памяти и 20 физических ядер. План: использовать SuperServer, установить кэш размером 50 Гб и выделить под буфер сортировки 32 Гб. Учитывая, что дисковая подсистема построена на RAID контроллере с 2 Гб энергонезависимой памяти, теоретически, это позволит вообще исключить прямое обращение к дискам. Т.е. производительность сервера базы данных будет определяться процессором, памятью и скоростью обмена с RAID контроллером.

Напомним, что именно производительность дисковой подсистемы всегда возглавляла список факторов, влияющих на общую производительность СУБД. Мы ожидаем ускорения выполнения запросов минимум на порядок. О достигнутых результатах напишем в этом блоге.

Практически все нововведения из третьей версии найдут применение в Гедымине:

  • Передача НУЛЛ значений по сети битовой маской. Сейчас каждое поле с НУЛЛ значением занимает при передаче 4 байта + длина поля. Например, в таблице с бухгалтерскими проводками у нас десятки полей-аналитических признаков, которые могут быть не заполнены. Экономия может достигать нескольких сотен байт на каждую запись.
  • Возможность увеличить TCP буфер до 32 Кб и применить компрессию данных при передаче. В интернете есть свидетельства о том, что скорость возрастает десятикратно при подключении по сетям с большой латентностью. Например, к серверу в удаленном офисе по VPN каналу.
  • Шифрование файла базы данных. Теперь злоумышленник не сможет получить доступ к конфиденциальной информации просто переписав файл на сервер с чистой установкой Firebird и известным паролем к учетной записи SYSDBA.
  • Window функции в SQL запросах позволяют объединять вместе данные и агрегатные значения по заданным группам этих данных. До Firebird 3 такую задачу можно было решить либо подзапросами (крайне медленно, так как подзапрос будет выполняться для каждой записи), либо выполнением в цикле отдельных запросов для каждой группы и объединением их результатов в единый набор данных с помощью EXECUTE BLOCK или STORED PROCEDURE, либо алгоритмической обработкой на клиенте (например, внутри Fast Report при построении отчета).
  • “Linger” Database Closure for Superserver -- период времени, в течение которого сервер сохраняет в памяти все ресурсы, после закрытия последнего подключения к базе данных -- позволит ускорить загрузку пакета пространств имен, так как переподключение к базе данных выполняется после каждого ПИ, содержащего метаданные.
  • DDL триггеры позволят отказаться от выполнения процедуры at_p_sync при старте системы для синхронизации информации о структуре базы данных с содержимым AT_ таблиц.
  • Другие изменения и улучшения, на которых мы подробнее остановимся в следующих выпусках.
Исходный код Гедымина уже поправлен для совместимости с последней версией. После внутреннего тестирования мы опубликуем подробную инструкцию апгрейда для наших клиентов.

13 дек. 2015 г.

Выполнение скрипт-функции по расписанию

Как организовать выполнение скрипт-функции по расписанию?

Автозадачи решают данную проблему, но требуют постоянно загруженного gedemin.exe и открытого коннекта к базе данных. Начиная с версии 2.9.2 можно использовать планировщик операционной системы и два новых параметра командной строки:

gedemin.exe /run 147000555_256456378

Выполняет скрипт-функцию с заданным РУИДом после загрузки платформы, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы (если таковые имеются).

gedemin.exe /run 147000555_256456378 /exit

То же, что и выше, но после выполнения скрипт-функции программа будет завершена.

gedemin.exe /exit

Завершение выполнения gedemin.exe сразу после загрузки, подключения к базе данных, инициализации подсистемы автозадач и выполнения всех автозадач, назначенных на старт системы.

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

18 февр. 2011 г.

Проверка прав на уровне записи

Разборка одного случая, когда запрос под Администратором выполнялся значительно быстрее чем под обычным пользователем, показала, что вина лежит на дополнительном условии с вызовом функции G_SEC_TEST.

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

Слаще ли хрен редьки выяснить можно только экспериментальным путем.

Вот, если бы заранее знать долю записей на которые у текущего пользователя есть/отсутствуют права...

14 сент. 2009 г.

Таблица для классов

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

CREATE TABLE gd_class (
   id          dintkey,
   parent      dparent,
   name        dname,
   is_subtype  dboolean_notnull,
   aview       dsecurity,
   achag       dsecurity,
   afull       dsecurity
)

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

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

На вскидку, еще несколько областей, где таблица со списком классов была бы востребована:
  1. При определении какие поля атрибуты следует включать в запрос бизнес-объекта по-умолчанию. Сейчас соответствующая информация хранится в текстовом виде в поле OBJECTS таблицы AT_RELATION_FIELDS.
  2. Дерево классов в Проводнике Редактора скрипт-объектов можно было бы строить непосредственно по этой таблице.

25 авг. 2009 г.

Двоякий исследователь

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

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

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

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

Известная истина гласит: все новое — это хорошо забытое старое. Если мы выводим пиктограммки команд в двухмерной сетке, да еще и визуально разделяем их по областям, то почему бы не нарисовать связи между ними? Что бы начинающему пользователю сходу было понятно что к чему. Рисуем и получаем...
Центр управления™ из нашей старой доброй программы Анжелика Бухгалтер!

В свое время, для отрисовки Центра управления был написан renderer, который принимал на вход особый двоичный код. Код хранился в *.RC файле и компилировался прямо в выполняемый модуль. Не очень удобочитаемый был код, надо сказать:

22 GSTOOLBAR
BEGIN
28
111
20
0
529
$04
$00
1
0
0
$04
$01
0
1
102
$04
$00
2
2
333
$08
$00
3
3

...

PS: Ради справедливости стоит заметить, что идея центра управления была подсмотрена нами 15 лет назад в австралийской программе MYOB.