Задать вопрос
  • Как превратить ПК с Linux и прокси в точку доступа Wi-Fi?

    Vamp
    @Vamp
    Для этого нужно:
    1. Поднять прокси-сервер на 0.0.0.0, вместо 127.0.0.1.
    2. Настроить все устройства на использование этого прокси так же как вы настроили браузер. Только указать локальный IP адрес вашего ноутбука.
    Ответ написан
    Комментировать
  • Как пробросить сервер за NAT провайдера?

    Vamp
    @Vamp
    У cloudflare есть решение для этой задачи - cloudflare tunnel.

    Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (cloudflared) creates outbound-only connections to Cloudflare's global network.
    Ответ написан
    1 комментарий
  • Как исправить проблему с mail php и заголовками для gmail?

    Vamp
    @Vamp
    Заголовки To и Subject подставляются автоматически функцией mail. Вам не нужно добавлять их самостоятельно. Просто удалите строки:

    $headers .= "To: $to\r\n";
    $headers .= "Subject: $subject\r\n";
    Ответ написан
    1 комментарий
  • Как деплоить php приложение вместе с docker?

    Vamp
    @Vamp
    Это типичный вариант развёртывания приложения в докере. Единственный его минус - некоторое время даунтайма между моментом когда опустился старый контейнер и поднялся новый. Для решения этой проблемы есть разные техники. Например, blue-green deployment. Это когда есть два контейнера, условно называемые green и blue. И трафик льётся только на один. Скажем, на green. В момент деплоя обновляется blue контейнер и после того как он полностью будет готов - переключаем трафик на blue. В следующий раз деплой начнётся с обновления green.

    Переключение трафика делается разными способами. Самый безопасный - переписать конфиг nginx и релоаднуть его. Или менее безопасный, зато без необходимости править конфиг, - добавить оба конейнера в одну upstream группу и опустить green контейнер после обновления blue.

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

    Другой вариант развёртывания - контейнер с пхп поднят постоянно, но код проекта монтируется в контейнер через volume. Далее ci джоба по sftp заливает новый код в соседнюю папку и переключает симлинк. Симлинк переключается атомарно и контейнер сразу начинает работать с новым кодом. Ничего больше делать не надо. Я рекомендую такой подход когда на проекте менее 50 серверов.
    Ответ написан
    4 комментария
  • Как работать с DI-контейнером?

    Vamp
    @Vamp
    В самом контейнере задаются только примитивные типы данных?

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

    А все объектные зависимости решаются с помощью автовайринга через Reflection API?

    Можно и так. А можно руками связи выставить. Или аннотациями. Разные способы бывают, смотря какие поддерживаются выбранной вами реализацией DI.

    К примеру, у меня есть контроллер.

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

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

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

    Модель принимает класс соединения с БД, а БД принимает массив с настройками подключения.

    В контейнере можно зарегистрировать объект Settings, в котором хранятся данные для подключения к базе и прочие глобальные настройки, а затем инжектить его в класс для работы с базой. Ещё контейнер может сам выступать хранилищем настроек и инжектить их (в том числе примитивные). Так сделано в DI компоненте Symfony.

    Есть еще класс пагинации, он тоже должен быть в контроллере и его конструктор принимает три параметра типа int.

    Опять же, если эти три параметра не зависят от запроса, то можно зарегистрировать пагинатор в DI. Если зависят, то можно из конструктора перенести эти параметры в метод и вызывать его в контроллере. Например: $page = $this->paginator->calulatePage($one, $two, $three)
    Ответ написан
    2 комментария
  • Как отправить длинное SMS с помощью SMPP?

    Vamp
    @Vamp
    Отправляйте текст через message_payload, а не short_message. В этом случае делением на сегменты будет заниматься провайдер.

    Пример Данилы лучше не использовать. Нельзя втупую резать текст по 153 символа. А ещё там косяк с кодировкой.

    Я не вижу никаких ошибок, потому что либа даже не отправляет мое SMS.

    Дебажить SMPP - это боль и страдания (не зависит от используемого языка). Лучше всего снять дамп трафика и смотреть на сырые пдухи в wireshark. В идеале отправлять смски через другой протокол. Многие провайдеры предоставляют HTTP API.
    Ответ написан
  • Какой софт поставить на домашний файловый сервер из древнего железа?

    Vamp
    @Vamp
    Я на свой древневековый ноутбук с 32-битным процессором (intel atom) поставил openmediavault. На слабом железе хорошо работает.

    Не верю в стабильность внешних ресурсов.

    А в стабильность хлама значит верите? По опыту эксплуатации хлама скажу, что лучше его выкинуть/продать и взять что-нибудь свежее взамен. Посмотрите мини-пк или одноплатники. Будет надёжнее, меньше места занимать, меньше энергии потреблять. Я в итоге заменил ноутбук на raspberry pi с m.2 модулем, куда вставил терабайтный SSD.
    Ответ написан
  • Какой оптимальный способ попасть в домашнюю сеть снаружи?

    Vamp
    @Vamp
    Самый удобный вариант - развернуть частный VPN типа zerotier или tailscale.
    Ответ написан
  • Как запустить парсер NBBC под PHP8?

    Vamp
    @Vamp
    Нужно ещё конструкторы в классах переименовать в __construct() и тогда останется только два теста, которые заваливаются из-за того, что htmlspecialchars по умолчанию применяет ENT_QUOTES.
    Ответ написан
    Комментировать
  • Низкая скорость отдачи с VPS на конкретный хост?

    Vamp
    @Vamp
    Разница почти наверняка из-за неудачного маршрута PC2->PC1. Маршрут можно посмотреть командой traceroute или mtr (в ubuntu пакет называется mtr-tiny). Далее можно попробовать написать письмо в техподдержку хостера PC2, приложив трейсы (с обоих сторон) и результаты iperf3 (так же с обоих сторон). Тогда хостер может подправить маршрутизацию и скорость восстановится.
    Ответ написан
    5 комментариев
  • Как правильно должно быть развернута инфрастуктура с Docker, Kubernetes?

    Vamp
    @Vamp
    1. Потому что докер объединил уже имеющиеся в линуксе способы изоляции процессов (chroot, cgroups, namespaces) в удобное для использования приложение.

    2. Гипервизором выступает ядро линукса. Поэтому накладные расходы на контейнеризацию значительно ниже, чем на виртуализациию.
    Ответ написан
  • Как избавиться от требования обработки исключения в Project Reactor?

    Vamp
    @Vamp
    неужели есть только один вариант это обрабатывать с поощью try/catch внутри каждого блока что бы избежать Unhandled exceptions?

    Вобщем-то да. Можно вынести try/catch в отдельный универсальный метод, который оборачивает исключение в RuntimeException:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> clazz.getDeclaredConstructor(Server.class, Channel.class))
            .map(constructor -> ex(() -> constructor.newInstance(this.manager.getServer(), channel)))
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    
    private <T> T ex(Callable<T> code) {
        try {
            return code.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    Конкретно в вашем примере можно вызов конструктора переместить в callable, из которого делается Mono, так как из Callable разрешено стрелять проверяемыми исключениями, в отличии от Function, который принимает map:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> {
                var constructor = clazz.getDeclaredConstructor(Server.class, Channel.class);
                return constructor.newInstance(this.manager.getServer(), channel);
            })
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    Ответ написан
    Комментировать
  • Как правильно добавить точку монтирования в docker compose для elasticsearch?

    Vamp
    @Vamp
    Нужно монтировать /usr/share/elasticsearch/data

    volumes:
          - /home/user/green:/usr/share/elasticsearch/data
    Ответ написан
    Комментировать
  • Как на самом деле работает параллелизм?

    Vamp
    @Vamp
    Вопрос 1. Два конвейера позволяют только сократить накладные расходы на context switch. Параллелизм это не увеличивает, так как вычислительное ядро по-прежнему одно.

    Вопрос 2. Верно. Разделите понятия процесс и поток. Думайте о процессе как о контейнере для потоков, а не как об активной сущности, которая исполняет код. В каждом процессе есть как минимум 1 поток. Все без исключения программы стартуют как однопоточный процесс, который имеет возможность превратиться в многопоточный, заспавнив ещё потоков. Ну и, соответственно, ОС шедулит потоки, а не процессы.

    Вопрос 3. Нет. Процессор сам подгружает инструкции из кеша/основной памяти. Потоки не совершают никаких дополнительных действий для этого. Всё происходит прозрачно для потока.

    Вопрос 4. Kernelspace и userspace потоки отличаются только тем, в каких кольцах они исполняются. Kernel потоки исполняются в привилегированных кольцах, а user потоки в непривилегированных. Во всём остальном эти потоки ничем друг от друга не отличаются. Кольца ограничивают набор процессорных инструкций и системных вызовов, которые поток может использовать. Например, чтение файла с диска - это привилегированная операция (так как требует прямого доступа к девайсу), поэтому когда обычный userspace поток хочет прочитать файл, ОС приостанавливает userspace поток и передаёт управление kernelspace потоку, у которого достаточно привилегий на выполнение данной операции.

    Кооперативную многозадачность можно сделать и самому. Концепция называется green threads. В языках программирования встречается уже готовая реализация грин тредов, только называется иначе: корутины (python), горутины (go), виртуальные потоки (java). Все они делают одно и то же - реализуют кооперативную многозадачность в userspace. При большом желании можно написать и свою реализацию грин тредов без использования уже готовых языковых инструментов. Хотя практического смысла в этом мало (кроме нарабатывания опыта).

    Вопрос 5. Думаю, ответ на этот вопрос очевиден из ответа на предыдущий.

    И всё же под "kernel" обычно подразумевают ядро ОС, а не ядро процессора (которое называют core, а не kernel). Единственный способ запустить поток в kernelspace - написать модуль/драйвер ядра. Обычные программы так не могут.

    Финальный вопрос. Правильно понимаете. Вот только "пошаговая стратегия" - это про многозадачность, а не про параллелизм. Процессор с одним ядром многозадачным быть может. Параллельным - нет.
    Ответ написан
    7 комментариев
  • Стоит ли фильтровать пароль на символы?

    Vamp
    @Vamp
    У вас классическая проблема обработки пользовательского ввода. Решается она по-разному в зависимости от контекста, в котором используются данные. Так как вы не указываете контекст, то и ответ будет соответствующе широким.

    1. Запрос первоначально поступает в node.js (конкретно в движок v8, отвечающий за сетевое взаимодействие). Здесь возможны уязвимости, при которых специальным образом сформированный запрос может привести к отказу в обслуживании (DoS), несанкционированному доступу к памяти (вспоминается эпичнейшая уязвимость heartbleed) и иным спецэффектам.

    2. Далее запрос поступает в express, где он парсится и преобразовывается в объект req. Здесь так же могут быть уязвимости, эксплуатируемые путем отправки специально сформированного запроса.

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

    3. Далее ваш код занимается обработкой данных. Важный момент в этом - санация и валидация данных.

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

    Валидация - это проверка данных на корректность. Например, вы ожидаете номер телефона в поле phone. После санации останется проверить, что длина номера не нулевая и не больше 15.

    Если данные предполагается передавать дальше, скажем, на удалённый JSON REST сервис, то вы должны будете корректно экранировать строку перед добавлением её в JSON, иначе случайная кавычка сломает ваш запрос.

    Либо если данные будут вставляться в базу данных, то необходимо экранировать данные (либо вставлять данные через подготовленные запросы) чтобы не получить SQL injection уязвимость.

    Вот тут как раз и появляется зависимость от контекста. В контексте базы данных - один способ экранирования. В контексте json - другой. В контексте HTML (при выводе данных пользователю) - третий. В ещё каком-нибудь другом контексте будут свои способы обезопасить данные.

    Если рассматривать конкретно пароль, то с ним всё просто. Как правило, пароли не хранятся на сервере в открытом виде и предварительно подвергаются преобразованию в криптографически стойкий хеш (например, argon2 или scrypt). Сам хеш уже имеет фиксированный размер и известный набор символов. Поэтому санировать пароль не нужно, а при валидации просто проверить минимально допустимую длину, несовпадение с логином, email и может ещё какие-нибудь проверки, навязанные бизнесом или применяемыми стандартами.
    Ответ написан
    6 комментариев
  • Как в контейнере сделаться рутом?

    Vamp
    @Vamp
    Ваш образ создан from scratch. Это значит, что в нём только приложение. И больше ничего лишнего нет. В том числе и бинарника /bin/bash, путь к которому вы указываете в /etc/passwd

    Возьмите образ на основе альпины: anycable/anycable-go:1.2-alpine
    Там есть /bin/sh, пакетный менеджер и ещё всяких разных утилит по мелочи.
    Ответ написан
    1 комментарий
  • Как работать с сырыми результатами SELECT MySQL CLI в PHP CLI?

    Vamp
    @Vamp
    Вам придется самостоятельно парсить $res в более удобный массив или объект. Можно слегка упростить вывод mysql, добавив аргумент --batch.
    Ответ написан
    1 комментарий
  • Как авторизовавшись на одном сайте в "сети сайтов", быть автоматически авторизованным и на других?

    Vamp
    @Vamp
    Видел однажды такую реализацию.

    При успешном прохождении авторизации кидает на страницу, где через тег img подключается "картинка" с каждого домена, участвующего в sso тусовке.

    <h2>Вы успешно вошли</h2>
    <img src="https://example.com/auth.php?key=1hB7fa014bbXCDg920">
    <img src="https://example.net/auth.php?key=hVVFD41looas8730MM">
    <img src="https://example.org/auth.php?key=QnaVBhj7mqwaBK9xq6">

    При авторизации создаётся и записывается в базу пачка одноразовых временных токенов, привязанных к только что авторизованному пользователю. При загрузке auth.php проверяется этот токен (передается через key в примере выше) и если токен ещё не просрочился, то выдаётся однопиксельный прозрачный gif с авторизационным кукисом. Этот кукис привязывается к домену, с которого загружается картинка.
    Ответ написан
    1 комментарий
  • Зачем освобождать память в C?

    Vamp
    @Vamp
    Потому что неосвобождённая память остаётся занятой даже после выхода указателя за пределы области видимости. В итоге если вы будете только аллоцировать память, но не освобождать её, то рано или поздно программа сожрёт всю память в системе.
    Ответ написан
    1 комментарий
  • Что означает синтаксис dockerfile (php-fpm)?

    Vamp
    @Vamp
    что такое dev суфикс (freetype и freetype-dev) и как понять какую версию ставить (я ставил методом тыка, когда получал ошибки и гуглил)

    Dev суффикс означает, что в данном пакете присутствуют файлы, необходимые для компиляции кода, использующего устанавливаемую библиотеку. Устанавливать нужно обе версии. Обычная версия "freetype" содержит саму библиотеку в so файле, которую gd будет подключать в рантайме. Dev версия "freetype-dev" нужна для сборки самого gd, так как модуль собирается из исходников.

    почему, например, для mycrypt мне сперва нужно загрузить именно libmcrypt-dev, потом через pecl скачать mcrypt, а потом через алиас docker-php-ext-enable включить (экспериментировал и не работает с другими сценариями), а если смотреть на opcache, тот тут сразу docker-php-ext-install и все, а xdebug еще один сценарий: pecl -> docker-php-ext-enable - почему все по разному, как это понять?

    Для разных модулей требуются разные сторонние библиотеки. Для gd это freetype (для работы со шрифтами), libpng (для поддержки формата png), libjpeg-turbo (для поддержки jpeg). У opcache таких зависимостей нет, поэтому ничего дополнительно устанавливать не требуется.

    в чем отличие этих сценариев установки расширений (что делает каждая команда, простыми словами)?

    У php есть модули, которые поставляются с самим php. Например: zip, opcache, pdo_mysql, intl. Такие модули устанавливаются специально предусмотренным для этого скриптом docker-php-ext-install. Этот скрипт распаковывает исходники php, собирает запрошенные вами модули, копирует их в /usr/local/lib/php/extensions/no-debug-non-zts-20190902 (в других образах php путь может отличаться), прописывает загрузку модуля в ini и удаляет распакованные ранее исходники. То есть под капотом скрипт docker-php-ext-install вызывает скрипты docker-php-source, docker-php-ext-configure, docker-php-ext-enable.

    А есть модули, которые хостятся отдельно на сайте pecl.php.net. Это официальный репозиторий, где хостятся всякие разные сторонние модули типа mcrypt, imagick, xdebug. Для их установки нужно использовать утилиту pecl. Так как pecl автоматически не включает модули в ini, из-за этого необходимо самому включать их при помощи скрипта docker-php-ext-enable.

    нагуглил что
    docker-php-source - создает /usr/src/php

    Ещё распаковывает туда исходники самого php и поставляемых с ним модулей.

    docker-php-ext-install - скачивает (ОТКУДА?)

    Из архива /usr/src/php.tar.xz

    docker-php-ext-configure- конфигурирует перед установкой (что это значит?)

    Этот скрипт настраивает настройки, с которыми будет скомпилирован модуль. Например, тот же gd можно собрать без поддержки jpeg если сконфигурировать без опции --with-jpeg. Нужно ли запускать docker-php-ext-configure так же зависит от того, что написано в документации. Очень мало модулей требует предварительной конфигурации перед установкой. Мне на ум приходит только gd.

    В целом, официальная документация php вам поможет понять каким способом нужно устанавливать модули. Смотрим раздел requirements. Если написано "This extension is bundled with PHP", значит нужно устанавливать при помощи скрипта docker-php-ext-install. Если написано что-то вроде "This PECL extension is not bundled with PHP", значит нужно устанавливать через pecl.

    Независимо от способа установки, нужно всегда устанавливать библиотеки, от которых зависят конкретные нужные вам модули. Например, в том же imagick написано "ImageMagick >= 6.2.4 is required". Это значит, что нужно установить библиотеку imagemagick. В вашем базовом образе на основе alpine 3.16 в репозиториях ОС присутствует версия 7.1.0, которая подходит под условие >= 6.2.4:

    /var/www/html # apk info imagemagick
    imagemagick-7.1.0.35-r0 description:
    Collection of tools and libraries for many image formats
    
    imagemagick-7.1.0.35-r0 webpage:
    https://imagemagick.org/
    
    imagemagick-7.1.0.35-r0 installed size:
    4664 KiB


    Для intl нужна "ICU library, version 4.0.0 or newer". Такая библиотека тоже присутствует в репозиториях альпины:

    /var/www/html # apk info icu
    icu-71.1-r2 description:
    International Components for Unicode library
    
    icu-71.1-r2 webpage:
    https://icu.unicode.org/
    
    icu-71.1-r2 installed size:
    796 KiB


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