[{"content":"В предыдущих реинкарнациях этот сайт уже бывал запущен в виде html файлов за nginx, статического сайта на s3 и даже в виде сайта в ipfs. В этот раз попробую разместить его на Cloudflare pages. Будем маленький туториал, а заодно и оценю сложность этого размещения.\nПодключаем репозиторий Gitlab к Cloudflare Первым делом мы переходим на страницу через меню Compute, далее Workers \u0026amp; Pages:\nНа странице нужно выбрать Create application:\nСоздаем Cloudflare pages Для создания нужно выбрать не сильно заметную ссылку внизу Continue to Pages:\nА на открывшейся странице Import an existing Git repository:\nТут нужно будет подключить Gitlab (в моем случае, либо GitHub, если нужно) и выбрать соответствующий репозиторий. Для упрощения, есть окошко с поиском:\nНастройка сборки Далее нужно настроить сборку. Я беру команду из своего .gitlab-ci.yml и немного ее правлю, так как домен будет доступен в переменной окружения при вызове сборки:\nВот такая команда сборки у меня получается:\n1 hugo --enableGitInfo --minify -e production -b $CF_PAGES_URL Больше ничего не меняю, для сборки выбрал ветку main, именно в ней лежит готовый к проду сайт.\nОтладка билда Первый билд у меня завершился с ошибкой. В выводе есть возможность посмотреть лог и/или скачать его. Основная ошибка, как видно на скрине, это недостаточная версия Hugo.\nНужную версию можно \u0026ldquo;зафиксировать\u0026rdquo; через переменную окружения HUGO_VERSION:\nВерсию вписываем именно числовую, так как при сборке ссылка сама будет сформирована на extended версию. В моем случае на сейчас, это версия 0.164.0. Только числом, без дополнительных v или extended.\nСборка и загрузка После удачной сборки, будет выполнена загрузка файлов:\nИ вот сообщение, что сайт доступен на специальном поддомене:\nGitlab В интерфейсе Gitlab Pipeline так же будет указан отдельный блок, который по клику будет вести в сборочный процесс на Cloudflare:\nCloudflare pages В интерфейсе Cloudflare pages будет присутствовать информация о пройденных сборках и доступных тех доменах:\nТак же тут можно привязать свой домен, так как он уже был на Cloudflare, то его замена происходит в пару кликов:\nА в настройках поменять или поправить то, что настраивалось по шагам в мастере:\nЧто получаем в итоге Теперь выкладывая эту запись, не придется ничего делать. Нужно только подождать, когда Cloudflare pages соберут с нуля Hugo сайт и зальют на тех домен. Так же это работает для не основной ветки, то есть можно посмотреть \u0026ldquo;preview\u0026rdquo; на отдельном техническом домене:\nКоторый будет реально доступен:\nНо тут нужно быть внимательным! ТЕХНИЧЕСКИЕ ДОМЕНЫ НЕ УДАЛЯЮТСЯ САМИ ПО СЕБЕ. Поэтому нужно либо добавить их удаление через CI GitLab, либо руками удалять их Cloudflare pages.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/cloudflare-pages/","summary":"\u003cp\u003eВ предыдущих реинкарнациях этот сайт уже бывал запущен в виде html файлов за nginx, статического сайта на s3 и даже в\nвиде сайта в \u003ccode\u003eipfs\u003c/code\u003e. В этот раз попробую разместить его\nна \u003ca href=\"https://www.cloudflare.com/products/pages/\"\u003eCloudflare pages\u003c/a\u003e. Будем маленький туториал, а заодно и оценю сложность\nэтого размещения.\u003c/p\u003e","title":"Cloudflare Pages"},{"content":"Вроде бы прошло уже прилично времени, но я только сейчас разгрузился от переноса рабочих проектов. Теперь буду переносить это сайт. Буду описывать все проблемы и особенности, с которыми столкнусь \u0026ldquo;по ходу пьесы\u0026rdquo;.\nЗамена переменных для GitLab CI Здесь все относительно просто. При первоначально настройке деплоя сайта опирался на docker образ amazon/aws-cli. Так что пришлось только обновить переменные в настройках репозитория.\nТут все как у всех, но стоит обратить внимание на AWS_ACCESS_KEY_ID, он состоит из двух частей \u0026lt;tenant_id\u0026gt;:\u0026lt;key_id\u0026gt;. Ключи key_id получается как везде, через сервисный аккаунт. А вот tenant_id нужно найти в консоли сервиса. Найти его можно по пути \u0026ldquo;Хранение данных\u0026rdquo;, \u0026ldquo;Object Storage\u0026rdquo;, \u0026ldquo;Параметры работы с API\u0026rdquo;:\nТут же есть регион. В моем случае он отличался от Яндекса, в Яндексе регион был ru-central1, в Cloud.ru же он имеет значение ru-central-1. Так же указываем новый AWS_ENDPOINT_URL_S3 как https://s3.cloud.ru. Остальные настройка просто копируются из данных сервисного аккаунта и названия бекета.\nСтатический сайт Тут есть как плюс, так и минус. Имя для статического сайта можно указать отличное от имени бакета. Но оно не может содержать ничего кроме латинских букв, цифр и знаки дефиса:\nТак же оно будет ТОЛЬКО в виде [доменное имя].s3.cloud.ru. Как задать ему свой домен, пока-что не понятно. А значит придется на уровне Cloudflare поправить обработку домена. Так же это создает проблему: сайт будет всегда доступен по этому домену, значит нужно настраивать доступ ТОЛЬКО с ip адресов Cloudflare.\nОграничение доступа для всех, кроме Cloudflare Получить список адресов Cloudflare можно либо на странице https://www.cloudflare.com/ips/, либо по апи https://api.cloudflare.com/client/v4/ips. Оттуда мы берем весь список и обновляем правило доступа (ip адреса на 21 октября 2026 года):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 { \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Sid\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Deny\u0026#34;, \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;NotPrincipal\u0026#34;: {}, \u0026#34;Action\u0026#34;: \u0026#34;s3:GetObject\u0026#34;, \u0026#34;NotAction\u0026#34;: [], \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:s3:::[имя бакета]/*\u0026#34;, \u0026#34;NotResource\u0026#34;: [], \u0026#34;Condition\u0026#34;: { \u0026#34;NotIpAddress\u0026#34;: { \u0026#34;aws:SourceIp\u0026#34;: [ \u0026#34;173.245.48.0/20\u0026#34;, \u0026#34;103.21.244.0/22\u0026#34;, \u0026#34;103.22.200.0/22\u0026#34;, \u0026#34;103.31.4.0/22\u0026#34;, \u0026#34;141.101.64.0/18\u0026#34;, \u0026#34;108.162.192.0/18\u0026#34;, \u0026#34;190.93.240.0/20\u0026#34;, \u0026#34;188.114.96.0/20\u0026#34;, \u0026#34;197.234.240.0/22\u0026#34;, \u0026#34;198.41.128.0/17\u0026#34;, \u0026#34;162.158.0.0/15\u0026#34;, \u0026#34;104.16.0.0/13\u0026#34;, \u0026#34;104.24.0.0/14\u0026#34;, \u0026#34;172.64.0.0/13\u0026#34;, \u0026#34;131.0.72.0/22\u0026#34;, \u0026#34;2400:cb00::/32\u0026#34;, \u0026#34;2606:4700::/32\u0026#34;, \u0026#34;2803:f800::/32\u0026#34;, \u0026#34;2405:b500::/32\u0026#34;, \u0026#34;2405:8100::/32\u0026#34;, \u0026#34;2a06:98c0::/29\u0026#34;, \u0026#34;2c0f:f248::/32\u0026#34; ] } } }, { \u0026#34;Sid\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;NotPrincipal\u0026#34;: {}, \u0026#34;Action\u0026#34;: \u0026#34;s3:GetObject\u0026#34;, \u0026#34;NotAction\u0026#34;: [], \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:s3:::[имя бакета]/*\u0026#34;, \u0026#34;NotResource\u0026#34;: [], \u0026#34;Condition\u0026#34;: {} } ] } Вместо [имя бакета] нужно подставить свой бакет. В конце должно быть правило Allow. Потому что если запрос не попал вод первое правило (имел правильный ip адрес), то без второго он окажется не попавшим ни в одно правило и все равно будет заблокирован.\nНастройки самого Cloudflare После простого прописывания домена из Cloud.ru в виде CNAME мы получаем ошибку:\nЭто из-за того, что в настройках у нас до сих пор стоит строгая проверка сертификата домена. Но это уже не Yandex, и сертификат отдается для работы с сайтом на s3. Тут нужно первым делом подменить hostname при запросе от Cloudflare, что бы Cloud.ru хотя бы поняло, на какой статический сайт идет запрос. И тут случается затык, так как функции переписывания hostname и/или sni - работают ТОЛЬКО в Enterprise версии:\nИтог На текущий момент решить это все можно только поставив свой собственный nginx (или другой прокси на любой вкус). Средствами же Cloud.ru и Cloudflare это сделать нельзя. В следующий раз попробуем использовать Cloudflare pages.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/neudachnyj-pereezd-iz-yandex-cloud-s3-v-cloudru-s3/","summary":"\u003cp\u003eВроде бы прошло уже прилично времени, но я только сейчас разгрузился от переноса рабочих проектов. Теперь буду\nпереносить это сайт. Буду описывать все проблемы и особенности, с которыми столкнусь \u0026ldquo;по ходу пьесы\u0026rdquo;.\u003c/p\u003e","title":"Неудачный переезд из Yandex Cloud s3 в Cloud.ru s3"},{"content":"Проблема возникла внезапно, только вчера все запускаловь и вот после очереднного обновления Fedora 44 проект, в котором используеться MySQL сервер внутри Docker контейнера с пробросом папки с данными в проект отказался запускаться.\nСимптомы При попытке поднятия контейнера получаем его вывод:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.29-1.el8 started. [Note] [Entrypoint]: Switching to dedicated user \u0026#39;mysql\u0026#39; [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.29-1.el8 started. [Note] [Entrypoint]: Initializing database files [Warning] [MY-010918] [Server] \u0026#39;default_authentication_plugin\u0026#39; is deprecated and will be removed in a future release. Please use authentication_policy instead. [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.29) initializing of server in progress as process 42 [Warning] [MY-013242] [Server] --character-set-server: \u0026#39;utf8\u0026#39; is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. [Warning] [MY-013244] [Server] --collation-server: \u0026#39;utf8_unicode_ci\u0026#39; is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. [System] [MY-013576] [InnoDB] InnoDB initialization has started. [Warning] [MY-012638] [InnoDB] Retry attempts for writing partial data failed. [ERROR] [MY-012639] [InnoDB] Write to file ./ibdata1 failed at offset 0, 1048576 bytes should have been written, only 0 were written. Operating system error number 5. Check that your OS and file system support files of this size. Check also that the disk is not full or a disk quota exceeded. [ERROR] [MY-012640] [InnoDB] Error number 5 means \u0026#39;Input/output error\u0026#39; [ERROR] [MY-012267] [InnoDB] Could not set the file size of \u0026#39;./ibdata1\u0026#39;. Probably out of disk space [ERROR] [MY-012929] [InnoDB] InnoDB Database creation was aborted with error Generic error. You may need to delete the ibdata1 file before trying to start up again. [ERROR] [MY-010020] [Server] Data Dictionary initialization failed. [ERROR] [MY-013236] [Server] The designated data directory /var/lib/mysql/ is unusable. You can remove all files that the server added to it. [ERROR] [MY-010119] [Server] Aborting [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.29) MySQL Community Server - GPL. Еще вчера все было хорошо и база поднималась без проблем. Но да, вчера в конце дня было выполнено обновление:\n1 2 3 ❯ dnf history list | head -2 ID Command line Date and time Action(s) Altered 520 dnf upgrade --refresh 2026-07-07 18:59:31 88 Среди прочего там было обновлено как kernel, так и selinux-policy:\n1 2 3 4 5 ❯ dnf history info 520 | grep \u0026#39;container-selinux\\|kernel-0\u0026#39; Install kernel-0:7.1.3-200.fc44.x86_64 External User updates Upgrade container-selinux-4:2.250.0-1.fc44.noarch Dependency updates Remove kernel-0:7.0.12-201.fc44.x86_64 User @System Replaced container-selinux-4:2.249.0-1.fc44.noarch Dependency @System Наученный горьким опытом обновлений политик SELinux было решено что дело в контексте и метках. Но добавление ключей в docker-compose.yml файл не решило проблему, даже временное отключение через sudo setenforce 0 не спасло (даже до этого дошло).\nРешение Так как это точно не SELinux пошел гуглить такие проблемы, вдруг я не один такой. Действительно не один, только тут человек был с другой файловой системой. Там был ZFS, а у меня btrfs.\nПосле прописывания параметров запуска:\n1 2 3 4 db: container_name: some-db image: mysql:8.0.29 command: --innodb-use-native-aio=0 --innodb-flush-method=fsync Говорят, что поможет отключение CoW, но завести без флагов с отключенным CoW так и не получилось.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/problema-zapuska-docker-kontejnera-mysql-na-fedora/","summary":"\u003cp\u003eПроблема возникла внезапно, только вчера все запускаловь и вот после очереднного обновления Fedora 44 проект, в\nкотором используеться MySQL сервер внутри Docker контейнера с пробросом папки с данными в проект отказался запускаться.\u003c/p\u003e","title":"Проблема запуска Docker контейнера MySQL на Fedora"},{"content":"Не успел я порадоваться всему, что было сделано в рамках предыдущей статьи. Как, проверяя новый файл, я обнаружил неприглядную картину. А именно совершенно нечитаемый контент, как показано в баннере к этой заметке.\nПоиск и диагностика Первым делом я, конечно, полез в DevTools браузера:\nВ браузере вроде бы все хорошо, но смущает одно: нигде нет данных по кодировке. На остальные страницы сайта это не влияет из-за того, что картинки бинарные, а html имеет встроенный заголовок. Проверить заголовки, с которыми Yandex. Cloud S3 отдает файлы можно через их cli:\n1 2 3 4 5 6 7 ❯ yc storage s3api head-object --bucket ********************* --key llms.txt etag: \u0026#39;\u0026#34;************************947e1f8b\u0026#34;\u0026#39; request_id: **************** accept_ranges: bytes content_length: \u0026#34;7470\u0026#34; content_type: text/plain last_modified_at: \u0026#34;****-**-**T**:**:**Z\u0026#34; Да, контент тайп есть, но он без кодировки. Видимо, после упаковки в gzip и передачи через CloudFlare браузеру \u0026ldquo;угадать\u0026rdquo; кодировку контента уже не удалось, к сожалению. А вот curl угадал:\n1 2 3 4 5 6 7 8 9 10 11 ❯ curl https://spwsmr.website/llms.txt -s | head # SpwSmr ## Публикации - [Добавил llms.txt на этот сайт](https://spwsmr.website/posts/dobavil-llmstxt-na-etot-sajt/) - [Установка неподписанного пакета на Fedora 44](https://spwsmr.website/posts/ustanovka-nepodpisannogo-paketa-na-fedora-44/) - [Потерянный docker-init в Fedora](https://spwsmr.website/posts/poteryannyj-docker-init-v-fedora/) - [Анализ системы с помощью инструментов командной строки: free и vmstat](https://spwsmr.website/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-free-vmstat/) - [Как создать установочную флешку Windows 11](https://spwsmr.website/posts/kak-sozdat-ustanovochnuyu-fleshku-windows11/) - [Проблемы с КриптоПро после обновления на Fedora 42](https://spwsmr.website/posts/problemy-s-kriptopro-posle-obnovleniya-na-fedora/) - [Принудительно очищаем данные из Sentry](https://spwsmr.website/posts/prinuditelno-ochishaem-dannye-iz-sentry/) Но так как файл предназначен для LLM моделей, то лучше, что бы все работало сразу и для всех.\nРешение В GitLab CI используется образ amazon/aws-cli. И вроде бы Yandex.CLoud S3 должен быть совместим с AWS S3. Значит нужно глянуть документацию по sync. Именно aws s3 sync используеться мной. В ней говорится, что для загрузки можно использовать --content-type \u0026quot;text/plain; charset=utf-8\u0026quot;. Но тогда все файлы которые отправляются через sync будут text/plain. Это очень неудобно для html файлов и невозможно для svg или png файлов. Значит идет по другому пути: загружаем нужный нам файл отдельно ото всех и с ручным указанием контент тайпа. Вместо лаконичного:\n1 aws s3 sync ./public/ \u0026#34;$AWS_S3_BUCKET_NAME\u0026#34; --delete будет:\n1 2 aws s3 sync ./public \u0026#34;$AWS_S3_BUCKET_NAME\u0026#34; --delete --exclude \u0026#34;llms.txt\u0026#34; aws s3 cp ./public/llms.txt \u0026#34;$AWS_S3_BUCKET_NAME/llms.txt\u0026#34; --content-type \u0026#34;text/plain; charset=utf-8\u0026#34; Да, можно было бы оба вызова сделать через sync, но пока-что файал только один и так будет нагляднее. После обновления .gitlab-ci.yml можно насладиться результатом:\n1 2 3 4 5 6 7 ❯ yc storage s3api head-object --bucket ********************* --key llms.txt etag: \u0026#39;\u0026#34;************************947e1f8b\u0026#34;\u0026#39; request_id: **************** accept_ranges: bytes content_length: \u0026#34;7470\u0026#34; content_type: text/plain; charset=utf-8 last_modified_at: \u0026#34;****-**-**T**:**:**Z\u0026#34; ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/fiks-zagruzki-txt-fajlov-s-kirilicej-v-yandex-s3/","summary":"\u003cp\u003eНе успел я порадоваться всему, что было сделано в рамках \u003ca href=\"/posts/dobavil-llmstxt-na-etot-sajt/\"\u003eпредыдущей статьи\u003c/a\u003e.\nКак, проверяя новый файл, я обнаружил неприглядную картину. А именно совершенно нечитаемый контент, как показано в\nбаннере к этой заметке.\u003c/p\u003e","title":"Фикс загрузки txt файлов с кирилицей в Yandex S3"},{"content":"Решил добавить llms.txt на этот сайт. Вроде бы как это должно добавить удобства для работы с сайтом большим языковым моделям. Тем более для моего шаблона PaperMod в одном из обновлений появился шаблон. Да и версия hugo позволяет.\nНастройки Hugo У меня используется конфигурационный файл в формате toml. Так что все настройки будут в этом формате.\nДобавляем описание формата в outputFormats:\n1 2 3 4 5 [outputFormats.LLMS] mediaType = \u0026#34;text/plain\u0026#34; baseName = \u0026#34;llms\u0026#34; isPlainText = true notAlternative = true То есть мы создаем формат, который является обычным текстом (text/plain), с именем llms. Расширение будет браться из mediaType, что в итоге даст нам llms.txt. Так же отключаем экранирование html сущностей (isPlainText). И последним параметром мы отключаем добавление этого формата вывода как альтернативного (notAlternative), это уберет ссылку на данный файл из \u0026lt;header\u0026gt; как \u0026lt;link rel=\u0026quot;alternate\u0026quot; ...\u0026gt;.\nТеперь добавляем генерацию этого файла:\n1 2 [outputs] home = [\u0026#39;HTML\u0026#39;, \u0026#39;RSS\u0026#39;, \u0026#39;JSON\u0026#39;, \u0026#39;LLMS\u0026#39;] Добавляем это формат вывода для главной страницы, именно для нее будет создан файл в корне сайта с путем /llms.txt.\nШаблон Так как в моей теме шаблон уже появился, то этот шаг можно пропустить. Но если шаблона нет, можно взять базовый шаблон:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # {{ .Site.Title }} \u0026gt; {{ .Site.Params.description }} {{ with .Site.Params.llms.intro }}{{ . }} {{ end -}} ## Pages {{ range where .Site.RegularPages \u0026#34;Section\u0026#34; \u0026#34;\u0026#34; -}} - [{{ .Title }}]({{ .Permalink }}): {{ with .Description }}{{ . }}{{ else }}{{ .Summary | plainify | truncate 120 }}{{ end }} {{ end -}} {{ range .Site.Sections -}} ## {{ .Title }} {{ range .RegularPages -}} - [{{ .Title }}]({{ .Permalink }}): {{ with .Description }}{{ . }}{{ else }}{{ .Summary | plainify | truncate 120 }}{{ end }} {{ end }} {{ end -}} ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/dobavil-llmstxt-na-etot-sajt/","summary":"\u003cp\u003eРешил добавить \u003ca href=\"https://llmstxt.org/\"\u003ellms.txt\u003c/a\u003e на этот сайт. Вроде бы как это должно добавить удобства для работы с\nсайтом большим языковым моделям. Тем более для моего шаблона \u003ca href=\"https://github.com/adityatelange/hugo-PaperMod/\"\u003ePaperMod\u003c/a\u003e\nв одном из\nобновлений \u003ca href=\"https://github.com/adityatelange/hugo-PaperMod/commit/3bb0ca281fd17eff8e3489011a444f326d7c4c72\"\u003eпоявился шаблон\u003c/a\u003e.\nДа и версия \u003ca href=\"https://gohugo.io/\"\u003ehugo\u003c/a\u003e позволяет.\u003c/p\u003e","title":"Добавил llms.txt на этот сайт"},{"content":"В одно чудесное утро не удалось зайт в Диадок по сертификату. Сервис ругался и требовал обновить ПО:\n\u0026ldquo;Для работы с сертификатами обновите необходимые компоненты.\u0026rdquo;\nНо оказалось, что не все так просто.\nПроблема Вот такая страница открылась, при попытке войти:\nКазалось бы: скачать, установи и радуйся. Но после скачивания компонента по ссылке, установить его не получиться. Так как полученный rpm файл не подписан. Видимо, последнее или предпоследнее обновление добавило глобальную проверку подписи всех пакетов, даже локальных. Не подскажу, так как обновляю эту часть системы редко (вот бы был репозиторий для этого компонента, да, Контур?).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ❯ sudo dnf install ./diag.plugin-3.1.2.425-1.x86_64.002622.rpm Обновление и загрузка репозиториев: Репозитории загружены. Пакет Арх. Версия Репозиторий Размер Обновление: diag.plugin x86_64 0:3.1.2.425-1 @commandline 990.8 KiB замена diag.plugin x86_64 0:3.1.0.210-1 @commandline 73.1 KiB Сводка транзакции: Обновление: 1 пакета Замена: 1 пакета Общий размер входящих пакетов составляет 1 MiB. Необходимо загрузить 0 B. После этой операции будут использоваться дополнительные 918 KiB (установка 991 KiB, удаление 73 KiB). Is this ok [y/N]: y Выполнение транзакции Сбой транзакции: Сбой транзакции rpm. Предупреждение: пропущены проверки OpenPGP для 1 пакета из репозитория: @commandline - package diag.plugin-3.1.2.425-1.x86_64 does not verify: no digest Решение Поискав бегло в интернете, нашел множество вариантов: от временного отключения опций проверки, до указания на нереальные ключи, которые не работают. Ключи в сам dnf --nogpgcheck и --no-gpgchecks не помог. Временное отключение опции проверки --setopt=localpkg_gpgcheck=0 и --setopt=localpkg_gpgcheck=False тоже не помогли. Рабочим вариантом оказался такой запуск:\n1 2 3 4 5 6 ❯ sudo rpm -Uvh --nodigest --nofiledigest --force ./diag.plugin-3.1.2.425-1.x86_64.002622.rpm Подготовка... ################################# [100%] Обновление / установка... 1:diag.plugin-3.1.2.425-1 ################################# [ 50%] Очистка / удаление... 3:diag.plugin-3.1.0.210-1 ################################# [100%] Тут по порядку:\nU - обновить пакет, чтобы удалить старую версью (если это первая установка, то i); --nodigest - не проверять подпись заголовка и самого пакета; --nofiledigest - не проверять сам файл с пакетом; --force - позволяет перезаписывать файл от другого пакета, переустанавливать туже или более старую версию. Так же пришлось обновить kontur.plugin:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ❯ dnf info kontur.plugin Обновление и загрузка репозиториев: Репозитории загружены. Установленные пакеты Имя : kontur.plugin Этап : 0 Версия : 4.13.0.4561 Выпуск : 1 Архитектура : x86_64 Размер установки : 32.7 MiB Источник : kontur.plugin-4.13.0.4561-1.src.rpm Из репозитория : \u0026lt;неизвестно\u0026gt; Сводка : Kontur.Plugin URL-адрес : https://help.kontur.ru/plugin/ Лицензия : Proprietary Описание : Kontur.Plugin from SKB Kontur : Kontur.Plugin is an extension for web browsers that allows users to : perform cryptographic operations in SKB Kontur services. Поставщик : AO PF SKB Kontur По итогу установки мы получаем:\nТеперь можно зайти в Диадок и наконец-то отправить документы.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/ustanovka-nepodpisannogo-paketa-na-fedora-44/","summary":"\u003cp\u003eВ одно чудесное утро не удалось зайт в \u003ca href=\"https://diadoc.kontur.ru/\"\u003eДиадок\u003c/a\u003e по сертификату. Сервис ругался и требовал\nобновить ПО:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ldquo;Для работы с сертификатами обновите необходимые компоненты.\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eНо оказалось, что не все так просто.\u003c/p\u003e","title":"Установка неподписанного пакета на Fedora 44"},{"content":"В какой-то момент понадобилось мне запустить новый docker-compose.yml файл. Процесс загрузки образов и настройки окружения контейнеров прошел хорошо, даже контейнеры создались, но далее процесс не пошел.\nПолучаем ошибку Что бы получить ошибку достаточно выполнить следующую команду:\n1 docker run -it --rm --init fedora bash Вместо того, что бы оказаться в bash контейнера с Fedora, получаем такую ошибку:\n1 docker: Error response from daemon: exec: \u0026#34;docker-init\u0026#34;: executable file not found in $PATH Точно такая же ошибка падала при развертывании docker-compose.yml.\nПоиски причины Так как в Fedora у меня установлен moby-engine, проверил что есть в официальном пакете с докером:\n1 2 3 4 5 6 7 8 9 ❯ rpm -qlp https://download.docker.com/linux/fedora/43/x86_64/stable/Packages/docker-ce-29.1.4-1.fc43.x86_64.rpm предупреждение: https://download.docker.com/linux/fedora/43/x86_64/stable/Packages/docker-ce-29.1.4-1.fc43.x86_64.rpm: Header OpenPGP V4 RSA/SHA512 signature, key ID c52feb6b621e9f35: NOKEY /etc/docker /usr/bin/docker-proxy /usr/bin/dockerd /usr/lib/systemd/system/docker.service /usr/lib/systemd/system/docker.socket /usr/libexec/docker/docker-init /usr/share/man/man8/dockerd.8.gz Да, действительно там есть файл /usr/libexec/docker/docker-init на который сейчас жалуется докер. А есть ли этот файл где-то в другом пакете в Fedora?\n1 2 3 4 ❯ dnf provides \u0026#39;*/docker-init\u0026#39; Обновление и загрузка репозиториев: Репозитории загружены. No matches found. If searching for a file, try specifying the full path or using a wildcard prefix (\u0026#34;*/\u0026#34;) at the beginning. К сожалению найти такой файл (в моих подключенных репах) не получилось. Но среди зависимостей moby-engine есть tini-static:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ❯ dnf repoquery --requires moby-engine Обновление и загрузка репозиториев: Репозитории загружены. /bin/sh container-selinux containerd docker-cli = 28.4.0-1.fc43 docker-cli = 29.1.3-1.fc43 iptables libc.so.6(GLIBC_2.34)(64bit) libnftables.so.1()(64bit) libnftables.so.1(LIBNFTABLES_1)(64bit) libseccomp libsystemd.so.0()(64bit) libsystemd.so.0(LIBSYSTEMD_209)(64bit) moby-filesystem = 28.4.0-1.fc43 moby-filesystem = 29.1.3-1.fc43 nftables pigz rtld(GNU_HASH) systemd tar tini-static xz Именно tini и должен использоваться вместо docker-init, но почему-то не хочет использоваться. Проверим, что же указано в moby-engine:\n1 2 3 4 ❯ dnf repoquery -l moby-engine | grep docker.service Обновление и загрузка репозиториев: Репозитории загружены. /usr/lib/systemd/system/docker.service Так, есть в нем такой файл, для запуска сервиса с докером. И в этом файл прописано:\n1 2 3 4 5 6 7 8 ❯ cat moby-engine-29.1.3-1.fc43.x86_64/usr/lib/systemd/system/docker.service | grep ExecStart -A6 ExecStart=/usr/bin/dockerd \\ -H fd:// \\ --containerd=/run/containerd/containerd.sock \\ --selinux-enabled \\ --userland-proxy-path /usr/bin/docker-proxy \\ --init-path /usr/bin/tini-static ExecReload=/bin/kill -s HUP $MAINPID Теперь проверяем что же systemd сейчас делает при запуске докера:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ❯ systemctl cat docker.service | head -n 20 # /etc/systemd/system/docker.service [Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=docker.socket network-online.target firewalld.service Requires=docker.socket Wants=network-online.target [Service] Type=notify EnvironmentFile=-/etc/sysconfig/docker # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not support the cgroup feature set required # for containers run by docker ExecStart=/usr/bin/dockerd \\ --host=fd:// \\ --exec-opt native.cgroupdriver=systemd \\ $OPTIONS ExecReload=/bin/kill -s HUP $MAINPID # Having non-zero Limit*s causes performance problems due to accounting overhead Оказывается что файл, который сейчас используеться в systemd лежит в совсем другом месте: /etc/systemd/system/docker.service и нечего не знает про --init-path /usr/bin/tini-static. Если попробовать найти чей это файл, то выясняется:\n1 2 3 4 5 6 7 ❯ dnf provides /etc/systemd/system/docker.service Обновление и загрузка репозиториев: Репозитории загружены. No matches found. If searching for a file, try specifying the full path or using a wildcard prefix (\u0026#34;*/\u0026#34;) at the beginning. ❯ ls -al /etc/systemd/system/docker.service -rw-r--r--. 1 root root 1313 мар 27 2023 /etc/systemd/system/docker.service Решение Файл /etc/systemd/system/docker.service лежит с марта 2023 года и по логике загрузки systemd - заменяет собой тот, что есть в /usr/lib/systemd/system. Так что удаляем кастомный файл и перечитываем все сервис файлы:\n1 2 3 4 ❯ sudo rm /etc/systemd/system/docker.service [sudo] пароль для user: ❯ systemctl daemon-reload После чего еще раз пробуем запустить:\n1 2 ❯ docker run -it --rm --init fedora bash [root@410a1e428d08 /]# Наблюдаем что все теперь работает.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/poteryannyj-docker-init-v-fedora/","summary":"\u003cp\u003eВ какой-то момент понадобилось мне запустить новый \u003ccode\u003edocker-compose.yml\u003c/code\u003e файл. Процесс загрузки образов и настройки\nокружения контейнеров прошел хорошо, даже контейнеры создались, но далее процесс не пошел.\u003c/p\u003e","title":"Потерянный docker-init в Fedora"},{"content":"В пятой статье из цикла \u0026ldquo;Системные данные с помощью инструментов командной строки\u0026rdquo; мы рассмотрим free и vmstat - две небольшие утилиты, которые раскрывают удивительно много информации о состоянии вашей системы Linux. free дает вам мгновенный снимок того, как используются оперативная память и подкачка. vmstat ( статистический отчет о виртуальной памяти) предоставляет отчеты в реальном времени о памяти, процессоре и активности ввода-вывода (I/O).\nК концу этой статьи буферы и кэш станут для вас \u0026ldquo;пространством для маневра\u0026rdquo;, вы сможете с уверенностью читать загадочную колонку доступно (available) и выявлять утечки памяти или перегрузку ввода-вывода.\nКраткий обзор free Основы использования 1 2 3 4 $ free -h total used free shared buff/cache available Mem: 23Gi 14Gi 575Mi 3,3Gi 12Gi 8,8Gi Swap: 8,0Gi 6,6Gi 1,4Gi free анализирует /proc/meminfo и выводит итоги о физической памяти, подкачке (swap), а также буферах ядра и кэше. Используйте -h для человекочитаемых единиц, -s 1 для обновления каждую секунду и -c N для остановки после N выборок. Это удобно для отслеживания потребления памяти при параллельной работе. Например, free -s 60 -c 1440 дает 24-часовую запись в формате CSV без установки дополнительных демонов мониторинга.\nСвободная память относится к ОЗУ, которая полностью не занята. Она не используется никаким процессом или для кэширования. В серверных системах я склонен рассматривать это как бесполезную память, поскольку неиспользуемая память не влияет на производительность. В идеале, после того как система проработала некоторое время, это число должно стремиться к минимально возможному.\nС другой стороны, доступная память представляет собой оценку того, сколько памяти могут использовать новые или уже запущенные процессы, не прибегая к подкачке. Она включает свободную память, а также части кэша и буферов, которые система может быстро вернуть при необходимости.\nПо сути, различие в Linux заключается в следующем: свободная память простаивает и не используется, в то время как доступная память включает в себя как действительно свободное пространство, так и память, которую можно легко освободить, чтобы система оставалась отзывчивой без подкачки. Не проблема иметь мало свободной памяти, обычно стоит беспокоиться о доступной памяти.\nВыявление проблем с free Быстрое сокращение доступной памяти в сочетании с ростом используемого подкачки указывает на реальную нехватку памяти. Большие скачки загрузки/выгрузки указывают на перегруженность или неконтролируемый расход памяти. vmstat – отчет о статистике виртуальной памяти vmstat (статистика виртуальной памяти) отображает процессы, память, подкачку, блочный ввод-вывод, прерывания, переключения контекста и использование ЦП в одной строке. Что бы проследить тенденции, запустите vmstat с интервалом ( вывод, показанный ниже, был разделен на три части для лучшей читаемости):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ vmstat 1 3 procs -----------memory---------- r b swpd free buff cache 2 0 7102404 1392528 36 12335148 0 0 7102404 1392560 36 12335188 0 0 7102404 1373640 36 12349928 ---swap-- -----io---- si so bi bo 8 21 130 724 0 0 0 0 0 0 8 48 -system-- -------cpu------- in cs us sy id wa st gu 2851 19 15 7 77 0 0 0 5779 7246 14 10 77 0 0 0 5141 6525 12 9 79 0 0 0 Разберем вывод Из справочника vmstat(8):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Procs r: Количество запущенных процессов (работающих или ожидающих времени выполнения). b: Количество процессов, заблокированных в ожидании завершения ввода-вывода. Memory На них влияет опция --unit. swpd: объем используемой памяти подкачки. free: объем неиспользуемой памяти. buff: объем памяти, используемый в качестве буферов. cache: объем памяти, используемой в качестве кэша. inact: объем неактивной памяти. (-a опция) active: объем активной памяти. (-a опция) Swap На них влияет опция --unit. si: Объем памяти, загружаемой с диска (в сек.). so: Объем памяти, выгруженной на диск (в сек.). IO bi: Кибибайт получен от блочного устройства (KiB/s). bo: Кибибайт отправлен на блочное устройство (KiB/s). System in: Количество прерываний в секунду, включая часы. cs: Количество переключений контекста в секунду. CPU Это проценты от общего времени ЦП. us: Время, потраченное на выполнение неядерного кода. (время пользователя, включая nice) sy: Время, затраченное на выполнение кода ядра. (системное время) id: Время, проведенное в режиме ожидания. До Linux 2.5.41 сюда входит время ожидания ввода-вывода. wa: Время, потраченное на ожидание ввода-вывода. До Linux 2.5.41, включено в простой. st: Время потреченное на виртуальной машине. До Linux 2.6.11, неизвестно. gu: Время, потраченное на запуск гостевого кода KVM (гостевое время, включая гостевое время). Практическая диагностика Раздел Ключевые поля Что смотреть Раздел Ключевые поля Что смотреть Раздел Ключевые поля Что смотреть Процессы r (run-queue), b (blocked) r \u0026gt; ядер процессора = конкуренция Память swpd, free, buff, cache Подъем swpd с падением free = давление памяти Подкачка si, so Ненулевое значение so означает, что ядро выгружается (swapping) Ввод/вывод bi, bo Высокий bo + высокий wa указывает на интенсивную запись на блочное устройство Система in, cs Внезапные всплески могут указывать на interrupt storm Процессор us, sy, id, wa, st Высокая wa (ожидание ввода-вывода) = узкое место в системе хранения Обнаружение утечки памяти Запустите vmstat 500 в одном терминале, пока приложение вызывающее подозрения работает в другом. Если free продолжает падать и si/so последовательно растет, физическая оперативная память исчерпывается. Ядро начинает выгружаться в систему подкачки (swapping), что является классическим поведением утечки.\nНахождение насыщения ввода-вывода Когда wa (ожидание ввода-вывода) и bo (запись на блочное устройство) растут, а r остается скромным, ЦП простаивает и застрял в ожидании диска. Рассмотрите возможность добавления более быстрого хранилища или настройки параметров планировщика ввода-вывода.\nОбнаружение чрезмерной загрузки процессора Устойчивый r, который вдвое больше числа логических ядер с низким wa и большим количеством свободного пространства, означает, что узким местом является ЦП, а не память или ввод-вывод. Используйте top или htop, чтобы найти самые загруженные процессы, или масштабируйте рабочие нагрузки соответствующим образом.\nЗаключение Освоение free и vmstat дает вам возможность взглянуть на использование памяти, активность подкачки, задержку ввода-вывода и загрузку ЦП. Для повседневной отладки: начните с free, чтобы проверить, действительно ли вашей системе не хватает памяти, а затем используйте vmstat, чтобы выявить причину, будь то утечки памяти, узкие места на диске или перегрузка ЦП.\nОставайтесь с нами для следующей части нашей серии \u0026ldquo;Анализ системы с помощью инструментов командной строки\u0026rdquo; и удачного устранения неполадок Fedora!\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-free-vmstat/","summary":"\u003cp\u003eВ пятой статье из\nцикла \u0026ldquo;\u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lscpu-i-lsusb/\"\u003eСистемные\u003c/a\u003e \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-dmidecode-i-lspci/\"\u003eданные с помощью\u003c/a\u003e\n\u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lsof-lsblk/\"\u003eинструментов\u003c/a\u003e \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lslogins-lsmod/\"\u003eкомандной строки\u003c/a\u003e\u0026rdquo;\nмы рассмотрим \u003cem\u003efree\u003c/em\u003e и \u003cem\u003evmstat\u003c/em\u003e - две небольшие утилиты, которые раскрывают удивительно много информации о состоянии\nвашей системы Linux. \u003cem\u003efree\u003c/em\u003e дает вам мгновенный снимок того, как используются оперативная память и подкачка. \u003cem\u003evmstat\u003c/em\u003e (\nстатистический отчет о виртуальной памяти) предоставляет отчеты в реальном времени о памяти, процессоре и активности\nввода-вывода (I/O).\u003c/p\u003e\n\u003cp\u003eК концу этой статьи буферы и кэш станут для вас \u0026ldquo;пространством для маневра\u0026rdquo;, вы сможете с уверенностью читать загадочную\nколонку \u003cem\u003eдоступно\u003c/em\u003e (\u003cem\u003eavailable\u003c/em\u003e) и выявлять утечки памяти или перегрузку ввода-вывода.\u003c/p\u003e","title":"Анализ системы с помощью инструментов командной строки: free и vmstat"},{"content":"Так уж сложилось, что давно не приходилось ставить Windows никуда. Linux-овая привычка \u0026ldquo;дэдэш (dd) iso прямо в диск\u0026rdquo;, к сожалению, подвела. Оказалось, что для установки с флешки Windows 11 нужно эту самую флешку \u0026ldquo;правильно\u0026rdquo; отформатировать и расположить там файлы.\nНачальные данные:\nесть образ диска (например, /tmp/windows.iso); есть флешка (например, /dev/sdb). Для работы понадобятся следующие программы:\nwipefs (пакет util-linux); mkfs.vfat (пакет dosfstools); mkfs.ntfs (пакет ntfsprogs); udisksctl (пакет udisks2). Сначала отформатируем флешку:\n1 # wipefs -a /dev/sdb Далее создаем структуру файловой системы на флешке:\n1 2 3 4 5 # parted /dev/sdb (parted) mklabel gpt (parted) mkpart BOOT fat32 0% 1GiB (parted) mkpart INSTALL ntfs 1GiB 100% (parted) quit Тут мы создали 2 раздела на gpt диске:\nBOOT под файлы загрузчика; INSTALL под все остальные файлы (в примере был взять весь объем флешки, можно указать размер вручную заменим 100% на нужный). Далее форматируем каждый из разделов в нужную файловую систему и монтируем его:\n1 2 3 4 5 6 7 # mkfs.vfat -n BOOT /dev/sdb1 # mkdir /mnt/vfat # mount /dev/sdb1 /mnt/vfat/ # mkfs.ntfs --quick -L INSTALL /dev/sdb2 # mkdir /mnt/ntfs # mount /dev/sdb2 /mnt/ntfs Монтируем iso образ:\n1 2 # mkdir /mnt/iso # mount /mnt/windows.iso /mnt/iso/ Далее копируем в BOOT раздел все файлы из iso кроме папки sources, но включая файл sources/boot.wim:\n1 # rsync -r --progress --include sources/boot.wim --exclude sources/* --delete-before /mnt/iso/ /mnt/vfat/ И полный образ в INSTALL раздел:\n1 # rsync -r --progress --delete-before /mnt/iso/ /mnt/ntfs/ После этого ждем сброс данных на флешку, размонтируем все разделы и извлекаем ее:\n1 2 3 4 5 6 7 # sync # umount /mnt/ntfs # umount /mnt/vfat # umount /mnt/iso # udisksctl power-off -b /dev/sdb Теперь с флешки можно загрузиться и выполнить установку Windows.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/kak-sozdat-ustanovochnuyu-fleshku-windows11/","summary":"\u003cp\u003eТак уж сложилось, что давно не приходилось ставить Windows никуда. Linux-овая привычка \u0026ldquo;дэдэш (\u003ccode\u003edd\u003c/code\u003e) iso прямо в\nдиск\u0026rdquo;, к сожалению, подвела. Оказалось, что для установки с флешки Windows 11 нужно эту самую флешку \u0026ldquo;правильно\u0026rdquo;\nотформатировать и расположить там файлы.\u003c/p\u003e","title":"Как создать установочную флешку Windows 11"},{"content":"После обновления с Fedora 41 на новую версию Fedora 42 приключилась беда: пропал доступ к токену. Не получается воспользоваться им ни для авторизации, ни просто увидеть носитель в приложении КриптоПро CSP 5.\nВ чем проблема Из-за недоступности носителя, нет возможности авторизоваться ни в одном сервисе, где это было возможно ранее (для меня это Контур Диадок или личный кабинет в налоговой). Все время вылазит окно:\nВ самой КриптоПро CSP так же токен не отображается:\nПочему так получилось Если попробовать переустановить КриптоПро CSP, то появится куча ошибок. Суть их в том, что нет пакета, который бы предоставлял исполняемый файл /usr/sbin/pcscd. Пример вывода ошибки:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 Не удалось разрешить транзакцию: Проблема 1: конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 2: пакет cprocsp-rdr-rustoken-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-rustoken-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 3: пакет cprocsp-rdr-rosan-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-rosan-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 4: пакет cprocsp-rdr-infocrypt-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-infocrypt-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 5: пакет cprocsp-rdr-cpfkc-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-cpfkc-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 6: пакет cprocsp-rdr-jacarta-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-jacarta-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 7: пакет cprocsp-rdr-rutoken-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-rutoken-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 8: пакет cprocsp-rdr-edoc-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-edoc-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 9: пакет cprocsp-rdr-novacard-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-novacard-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 10: пакет cprocsp-rdr-mskey-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-mskey-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 11: пакет cprocsp-rdr-kst-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-kst-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 12: пакет cprocsp-rdr-inpaspot-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-inpaspot-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Проблема 13: пакет cprocsp-rdr-emv-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026gt;= 5.0, но ни один из поставщиков не может быть установлен - пакет cprocsp-rdr-emv-64-5.0.13455-7.x86_64 из @commandline требует cprocsp-rdr-pcsc-64 \u0026lt; 6.0, но ни один из поставщиков не может быть установлен - конфликтующие запросы - ничто не предоставляет /usr/sbin/pcscd, необходимый для cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64 из @commandline Если поискать этот файл в пакетах rpm (спасибо сервису https://rpmfind.net/), то мы увидим, что этого файла действительно нет для Fedora 42:\nНо виден пакет, который его предоставляет. В Fedora 42 этот пакет, внезапно, присутствует. И даже предоставляет это бинарник, но \u0026ldquo;есть нюанс\u0026rdquo;:\n1 2 3 4 ❯ dnf repoquery -l pcsc-lite | grep \u0026#39;bin/pcscd\u0026#39; Обновление и загрузка репозиториев: Репозитории загружены. /usr/bin/pcscd Файл теперь лежит в каталоге /usr/bin, а не /usr/sbin. Как оказывается об это предупреждали https://fedoraproject.org/wiki/Changes/Unify_bin_and_sbin. Теперь каталоги унифицированы, и добавлен симлинк. Вот только пакеты КриптоПро CSP пока-что (16 апреля 2025 года) собраны с зависимостью от /usr/sbin. Так как при обновлении пакеты переустанавливаются, часть из них не может быть больше \u0026ldquo;использована\u0026rdquo;. \u0026ldquo;Сломанные\u0026rdquo; пакеты удаляются и больше стандартным методом их поставить нельзя: нет зависимости - установка прерывается.\nРешение На текущий момент проще всего установить нужные пакеты вручную. При автоматической установке в консоль попадают сообщения, в том числе со списком устанавливаемого:\n1 2 3 4 5 6 7 8 Найдены устройства PC/SC: для установки отмечены пакеты для их поддержки. Удаление пакетов КриптоПро CSP... Пакеты КриптоПро CSP успешно удалены Установка ./lsb-cprocsp-base-5.0.13455-7.noarch.rpm ./lsb-cprocsp-rdr-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-kc1-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-capilite-64-5.0.13455-7.x86_64.rpm ./cprocsp-curl-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-ca-certs-5.0.13455-7.noarch.rpm ./cprocsp-rdr-gui-gtk-64-5.0.13455-7.x86_64.rpm ./cprocsp-cptools-gtk-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-emv-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-inpaspot-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-kst-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-mskey-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-novacard-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-edoc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rutoken-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-jacarta-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cloud-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cpfkc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-infocrypt-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rosan-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cryptoki-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rustoken-64-5.0.13455-7.x86_64.rpm ./cprocsp-pki-cades-64-2.0.15400-1.amd64.rpm ./cprocsp-pki-plugin-64-2.0.15400-1.amd64.rpm ./lsb-cprocsp-pkcs11-64-5.0.13455-7.x86_64.rpm ... Обновление и загрузка репозиториев: Репозитории загружены. Не удалось разрешить транзакцию: ... Самым простым методом на текущий момент будет ручная установка без проверки зависимостей:\n1 sudo rpm -i --nodeps ./lsb-cprocsp-base-5.0.13455-7.noarch.rpm ./lsb-cprocsp-rdr-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-kc1-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-capilite-64-5.0.13455-7.x86_64.rpm ./cprocsp-curl-64-5.0.13455-7.x86_64.rpm ./lsb-cprocsp-ca-certs-5.0.13455-7.noarch.rpm ./cprocsp-rdr-gui-gtk-64-5.0.13455-7.x86_64.rpm ./cprocsp-cptools-gtk-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-pcsc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-emv-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-inpaspot-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-kst-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-mskey-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-novacard-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-edoc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rutoken-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-jacarta-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cloud-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cpfkc-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-infocrypt-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rosan-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-cryptoki-64-5.0.13455-7.x86_64.rpm ./cprocsp-rdr-rustoken-64-5.0.13455-7.x86_64.rpm ./cprocsp-pki-cades-64-2.0.15400-1.amd64.rpm ./cprocsp-pki-plugin-64-2.0.15400-1.amd64.rpm ./lsb-cprocsp-pkcs11-64-5.0.13455-7.x86_64.rpm После этого все начинает работать как нужно. Носитель видно в КриптоПро CSP:\nИ больше не появляется окно \u0026ldquo;Вставьте носитель\u0026rdquo;.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/problemy-s-kriptopro-posle-obnovleniya-na-fedora/","summary":"\u003cp\u003eПосле обновления с Fedora 41 на новую версию \u003ca href=\"https://fedoramagazine.org/whats-new-fedora-workstation-42/\"\u003eFedora 42\u003c/a\u003e\nприключилась беда: пропал доступ к токену. Не получается воспользоваться им ни для авторизации, ни просто увидеть\nноситель в приложении \u003ca href=\"https://www.cryptopro.ru/products/csp\"\u003eКриптоПро CSP 5\u003c/a\u003e.\u003c/p\u003e","title":"Проблемы с КриптоПро после обновления на Fedora 42"},{"content":"Если Sentry установлена у нас (On-Premise) иногда трудно предугадать нужные объемы диска для безболезненной работы. Но есть возможность выполнить очистку данных руками. Основной источник занимаемого места - это база PostgreSQL.\nВ Sentry есть встроенная команда, которая чистит данные:\n1 sentry cleanup --deys 14 --project main-site При этом можно указать несколько параметров:\n--days - количество дней, после которого нужно убрать записи (в примере выше - все записи старше двух недель), по умолчанию 30 дней; --project - проект из которого нужно удалить старые записи (в примере выше - main-site), по умолчанию все проекты; --concurrency - количество обработчиков для очистки (по умолчанию 1). Далее нужно только выполнить вакуум внутри БД, что бы место было освобождено с диска фактически (на примере docker-compose варианта Sentry):\n1 docker-compose exec postgres bash Далее логинимся в PostgreSQL:\n1 psql -U postgres И выполняем (внимание, во время VACUUM таблицы окажутся заблокированными):\n1 VACUUM FULL; ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/prinuditelno-ochishaem-dannye-iz-sentry/","summary":"\u003cp\u003eЕсли \u003ca href=\"https://sentry.io/\"\u003eSentry\u003c/a\u003e установлена у нас (On-Premise) иногда трудно предугадать нужные объемы диска для\nбезболезненной работы. Но есть возможность выполнить очистку данных руками. Основной источник занимаемого места - это\nбаза PostgreSQL.\u003c/p\u003e","title":"Принудительно очищаем данные из Sentry"},{"content":"Многие пользователи доверяют различным решениям VPN для надежной защиты своей конфиденциальности или доступа к личным или корпоративным ресурсам при подключении из ненадежной сети. Это может быть сеть WiFi в аэропорту или кафе. В этой статье описывается, как использовать NetworkManager что бы улучшить эту безопасность.\nНесколько месяцев назад исследователи раскрыли уязвимость TunnelVision с идентификатором CVE-2024-3661. CVE описывает способ перенаправления трафика за пределы зашифрованного канала большого количества VPN-решений. Атака тривиальна для вредоносных агентов, имеющих доступ к сети, к которой подключается пользователь. Им даже не нужно эксплуатировать какую-либо неизвестную ошибку или скрытую уязвимость. Им нужно только перенастроить одну стандартную функцию протокола DHCP.\nАтака основана на установке через DHCP некоторых маршрутов в вашей системе. Давайте начнем с изучения того, что такое DHCP и маршрутизация и как атака их использует. Если вы просто хотите узнать решение, перейдите к разделу «Политики маршрутизации спешит на помощь». Перейдите к разделу «Настройка NetworkManager», чтобы увидеть, как эффективно смягчить последствия в вашей системе при настройке VPN с помощью NetworkManager.\nDHCP DHCP это Dynamic Host Configuration Protocol (протокол динамической конфигурации хоста). Это протокол для автоматического назначения IP-адресов и другой конфигураций, связанной с IP, хостам, которые подключаются к сети. Когда вы подключаетесь к домашней сети и автоматически получаете частный IP-адрес, вместо того, чтобы настраивать его вручную, весьма вероятно, что это происходит благодаря DHCP-серверу, работающему на вашем маршрутизаторе, поскольку DHCP является наиболее широко используемым методом для этой цели.\nТипичные части конфигурации, которые хост получает от DHCP, это IP-адрес хоста, сетевая маска и шлюз. Этих трех настроек обычно достаточно, чтобы выяснить, как достичь других хостов в той же частной сети и в Интернете.\nSLAAC обычно заменяет DHCP для IPv6, но работает аналогично.\nБазовое объяснение маршрутизации Маршрутизация это то, как ваш хост решает, куда отправить каждый пакет в зависимости от IP-адреса назначения. С помощью команды ip route вы можете увидеть некоторые маршруты, которые использует ваша система. Они выглядят примерно так ( упрощенно):\n1 2 3 4 192.168.1.0/24 dev eth0 src 192.168.1.10 172.16.0.0/16 dev eth1 src 172.16.1.2 10.0.0.0/8 via 192.168.1.25 dev eth0 src 192.168.1.10 default via 192.168.1.1 dev eth0 src 192.168.1.10 Примечание: определение подсетей в формате 192.168.1.0/24 соответствует нотации CIDR. Смотрите здесь, если вы не знаете, что это значит.\nСначала мы видим прямой маршрут, указывающий на отправку пакетов, направленных на IP-адреса в подсети 192.168.1.0/24, через eth0. Затем прямой маршрут, указывающий на отправку пакетов, направленных на IP-адреса в подсети 172.16.0.0/16, через eth1. Прямые маршруты используются для пакетов, направленных в ту же подсеть, что и хост, поскольку они напрямую доступны. Ядро использует маршруты, чтобы решить, какое устройство является правильным для отправки пакета. Было бы ошибкой отправлять пакет, направленный на 192.168.1.20, через интерфейс eth1, который подключен к другой физической сети.\nТретий маршрут содержит via 192.168.1.25. Это следующий переход (next hop), он нужен для отправки пакетов в физически не доступные нашей системе пункты назначения. Тут указано, что пакеты должны отправляться через промежуточное устройство с IP-адресом 192.168.1.25. Это должен быть маршрутизатор с возможностью пересылки пакета в другие сети, где пункт назначения доступен.\nПоследний известен как маршрут по умолчанию. Ядро использует его для отправки пакетов, которые не соответствуют никакому другому маршруту. Обычно он используется для настройки шлюза, адреса следующего перехода. На него следует направлять все пакеты, маршрут к которым мы явно не знаем. Именно так наши пакеты попадают в Интернет.\nКак работает атака Большинство клиентов VPN являются VPN на основе маршрутизации. Этот тип клиентов устанавливает туннель к сети VPN, создавая виртуальное устройство. Обычно оно называется tun0 или как-то похоже. Программа VPN шифрует весь трафик, проходящий через это виртуальное устройство, и отправляет его на другой конец туннеля. Идея заключается в том, что, хотя трафик все еще проходит через потенциально скомпрометированную сеть, теперь он делает это как зашифрованные данные.\nЧтобы выбрать, какой трафик отправлять через интерфейс туннеля, в вашу систему добавляются некоторые маршруты. Как минимум, создается маршрут, похожий на 10.0.0.0/8 dev tun0. Он направляет трафик в другие системы внутри того же VPN. Для тех, кто хочет направить весь трафик через VPN, также добавляется маршрут по умолчанию, похожий на default via 10.11.12.13 dev tun0.\nЧто происходит, когда 2 маршрута перекрываются таким образом, что IP-адрес назначения совпадает с обоими? Например, адрес 10.0.0.1 находится в обеих подсетях 10.0.0.0/8 и 10.0.0.0/24. Тогда побеждает маршрут с большим префиксом. В данном случае 10.0.0.0/24. Это происходит потому, что это более конкретный маршрут, так как он определяет меньший диапазон адресов, поэтому они являются подмножеством другого, который является более общим.\nЭтим могут воспользоваться вредоносные агенты, имеющие доступ к сети. Они могут настроить свой DHCP-сервер на отправку 2 маршрутов с большим префиксом, чем те, которые настроены вашим VPN. Это заставляет вашу систему использовать больший префикс и не отправлять трафик через виртуальное туннельное устройство, поэтому он остается незашифрованным.\n1 2 3 10.0.0.0/8 dev tun0 \u0026lt;-- route from VPN client 10.0.0.0/9 dev eth0 \u0026lt;-- route from DHCP 10.128.0.0/9 dev eth0 \u0026lt;-- route from DHCP Маршрут по умолчанию также может быть скрыт. Default эквивалентен 0.0.0.0/0, поэтому маршруты для 0.0.0.0/1 и 128.0.0.0/1 переопределят его, так как у них больший префикс. Это означает, что злоумышленник может перенаправить весь трафик.\nПолитики маршрутизации спешит на помощь Мы можем использовать политики маршрутизации для уменьшения влияния атаки TunnelVision и других подобных атак, таких как TunnelCrack. Политики маршрутизации это метод принятия решений о маршрутизации на основе пользовательских политик. В дистрибутивах на базе Linux, вроде Fedora, мы можем увидеть политики с помощью команды ip rule.\nЧтобы предотвратить атаку, мы можем использовать политику маршрутизации, чтобы переместить маршруты VPN в выделенную таблицу маршрутизации с более высоким приоритетом, чем таблица, содержащая маршруты, настроенные через DHCP.\nСделайте это, создав правило политики, которое ищет пользовательскую таблицу маршрутизации, например, таблицу 75, для всех исходящих пакетов. Используйте более низкое значение приоритета, чем в основной таблице, которая равна 32766, чтобы гарантировать, что наши маршруты будут сопоставляться первыми. Обратите внимание, что более низкие значения означают более высокий приоритет.\n1 ip rule add to all table 75 pref 32000 Затем заново создайте те же маршруты, что и у VPN в новой таблице.\n1 2 ip route add 10.0.0.0/8 dev tun0 table 75 ...(etc)... С помощью команды ip rule проверьте, что правило установлено. С помощью ip route list table 75 проверьте, что маршруты действительно были добавлены в таблицу.\nВот и все. Если какой-либо маршрут из нашей пользовательской таблицы применим, он используется. В противном случае основная таблица проверяется как обычно. Поскольку маршруты из DHCP добавляются в основную таблицу по умолчанию, они не могут переопределить маршруты VPN.\nНастройка NetworkManager На этом этапе большинство читателей, возможно, уже заметили, что ручная настройка этого каждый раз будет очень утомительной и подверженной ошибкам. Кроме того, неопытные пользователи могут столкнуться с большими трудностями при определении того, какие из маршрутов VPN, а какие являются остальными маршрутами. Более того, маршруты для несуществующего устройства не могут быть добавлены, поэтому нам нужно дождаться создания устройства VPN (например, tun0). После создания оно обычно сразу же запускается. Это создает риск утечки данных до настройки политики маршрутизации.\nЕсли вы управляете VPN-подключением с помощью NetworkManager, вы можете поручить ему настроить политику маршрутизации. Следующая команда выполнит настройку. Вам нужно запустить ее только один раз, и изменения будут установлены в постоянную конфигурацию (даже после перезагрузки).\n1 2 3 4 5 nmcli connection modify MY_VPN_CONNECTION_NAME \\ ipv4.route-table 75 \\ ipv4.routing-rules \u0026#34;priority 32000 from all table 75\u0026#34; \\ ipv6.route-table 75 \\ ipv6.routing-rules \u0026#34;priority 32000 from all table 75\u0026#34; Совет: найдите имя подключения с помощью команды nmcli connection.\nТеперь деактивируйте и снова активируйте соединение. С этими настройками NetworkManager добавит правила и поместит маршруты в указанную таблицу.\nЭто довольно продвинутые конфигурации, поэтому они обычно не доступны на панели настроек графического интерфейса вашего рабочего стола.\nПримечание: Из-за ошибки в NetworkManager эти настройки частично игнорировались для VPN-подключений. Это исправлено в версии 1.51.6. Новейшие версии Fedora 40 и 41 также содержат исправление. Обновите Fedora и убедитесь, что у вас есть как минимум версия NetworkManager 1.46.6-1 или 1.50.2-1.\nВозможные побочные эффекты Люди используют VPN для безопасного доступа к ресурсам внутри самого VPN. По желанию они используют его для маршрутизации всего трафика через VPN, в основном из соображений конфиденциальности. Такое поведение можно включить с помощью опции «использовать только для ресурсов в этом подключении» (называемой never-default в nmcli).\nКогда VPN используется для маршрутизации всего трафика, описанная конфигурация политика маршрутизации направляет весь трафик через VPN. Даже трафик, направленный в локальную сеть (то есть на 192.168.1.20), который, как следствие, перестанет работать. Captive portals, такие, как те, которые используются для входа в сеть WiFi отелей, также не будут работать. Это происходит потому, что маршрут по умолчанию, такой как default via VPN_ADDRESS, добавляется в новую таблицу с более высоким приоритетом. Если это произойдет, вам может потребоваться отключить эту конфигурацию. Если вы достаточно опытны, вы можете добавить несколько пользовательских маршрутов в таблицу, чтобы исправить это.\nЛюбое другое соединение, которому также необходимо добавлять маршруты, может перестать работать. Вам придется настроить его таблицу маршрутизации и конфигурации правил маршрутизации, чтобы создать правило с более высоким приоритетом.\nДаже с учетом потенциальных недостатков это очень надежная конфигурация, которая должна защитить вас от любой утечки данных, связанной с подобными атаками маршрутизации.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/zashitite-svoj-vpn-ot-atak-tunnelvision-s-pomoshyu-networkmanager/","summary":"\u003cp\u003eМногие пользователи доверяют различным решениям VPN для надежной защиты своей конфиденциальности или доступа к личным\nили корпоративным ресурсам при подключении из ненадежной сети. Это может быть сеть WiFi в аэропорту или кафе. В этой\nстатье описывается, как использовать NetworkManager что бы улучшить эту безопасность.\u003c/p\u003e\n\u003cp\u003eНесколько месяцев назад исследователи раскрыли уязвимость \u003ca href=\"https://www.tunnelvisionbug.com/\"\u003eTunnelVision\u003c/a\u003e с\nидентификатором \u003ca href=\"https://nvd.nist.gov/vuln/detail/cve-2024-3661\"\u003eCVE-2024-3661\u003c/a\u003e. CVE описывает способ перенаправления\nтрафика за пределы зашифрованного канала большого количества VPN-решений. Атака тривиальна для вредоносных агентов,\nимеющих доступ к сети, к которой подключается пользователь. Им даже не нужно эксплуатировать какую-либо неизвестную\nошибку или скрытую уязвимость. Им нужно только перенастроить одну стандартную функцию протокола DHCP.\u003c/p\u003e\n\u003cp\u003eАтака основана на установке через DHCP некоторых маршрутов в вашей системе. Давайте начнем с изучения того, что такое\nDHCP и маршрутизация и как атака их использует. Если вы просто хотите узнать решение, перейдите к\nразделу «\u003ca href=\"/posts/zashitite-svoj-vpn-ot-atak-tunnelvision-s-pomoshyu-networkmanager/#%d0%bf%d0%be%d0%bb%d0%b8%d1%82%d0%b8%d0%ba%d0%b8-%d0%bc%d0%b0%d1%80%d1%88%d1%80%d1%83%d1%82%d0%b8%d0%b7%d0%b0%d1%86%d0%b8%d0%b8-%d1%81%d0%bf%d0%b5%d1%88%d0%b8%d1%82-%d0%bd%d0%b0-%d0%bf%d0%be%d0%bc%d0%be%d1%89%d1%8c\"\u003eПолитики маршрутизации спешит на помощь\u003c/a\u003e». Перейдите к\nразделу «\u003ca href=\"/posts/zashitite-svoj-vpn-ot-atak-tunnelvision-s-pomoshyu-networkmanager/#%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-networkmanager\"\u003eНастройка NetworkManager\u003c/a\u003e», чтобы увидеть, как эффективно смягчить последствия в\nвашей системе при настройке VPN с помощью NetworkManager.\u003c/p\u003e","title":"Защитите свой VPN от атак TunnelVision с помощью NetworkManager"},{"content":"Продолжая наше исследование команд, которые дают представление о внутренней работе вашей системы Fedora Linux, мы обращаем внимание на более простые, такие как lslogins и lsmod. Эти инструменты предоставляют информацию об учетных записях пользователей и модулях ядра соответственно.\nlslogins: просмотр учетных записей пользователей lslogins — это команда, которая извлекает и отображает подробную информацию об учетных записях пользователей в вашей системе. Она извлекает данные из файлов /etc/passwd и /etc/shadow, а также системных журналов, чтобы предоставить вам полный обзор. Это особенно полезно для поиска ошибок входа и составления списка членства в группах.\nОсновы при использовании lslogins Запустите команду без аргументов, чтобы отобразить сводку всех учетных записей пользователей:\n1 lslogins Будет выведен список пользователей, включающий UID (идентификатор пользователя), GID (идентификатор группы), имя пользователя и данные о последнем входе в систему.\nРаспространенные варианты и пример использования -u: Отображение только учетных записей пользователей и root (отфильтровывание системных учетных записей с UID ниже 1000). -G: Отображение информации о существующих группах. -g \u0026lt;group\u0026gt;: Отображение пользователей определенной группы (например, wheel, которая обычно имеет разрешения sudo в системах на основен Red Hat). -e: Отображение в экспортируемом формате вывода ключ-значение, разделенном пробелом. -L: Список последних входов в систему и информации о пароле (пусто, вошел в систему, не вошел в систему). -p: Отображать информацию, связанную с входом по паролю. 1 2 3 4 5 6 7 8 9 $ lslogins -L -p -u UID USER LAST-TTY LAST-HOSTNAME LAST-LOGIN PWD-EMPTY PWD-LOCK PWD-DENY NOLOGIN HUSHED PWD-METHOD 0 root 0 1000 ahaerter tty2 tty2 01:16 0 0 $ lslogins -G -u UID USER GID GROUP SUPP-GIDS SUPP-GROUPS 0 root 0 root 1000 ahaerter 1000 user 971,10,977 docker,wheel lsmod: перечисление модулей ядра Ядро Linux по своей сути монолитно, но оно также обеспечивает возможность загрузки и выгрузки модулей во время выполнения. Модуль ядра обычно понимается как компонент или расширение ядра. Например, драйверы оборудования (например, карты Wi-Fi, звуковые карты и т.д.) обычно реализуются как модули. Команда lsmod обеспечивает хороший обзор всех загруженных в данный момент модулей ядра.\nОсновы при использовании lsmod Просто введите:\n1 lsmod Отобразится таблица с тремя столбцами:\nModule: Имя модуля ядра. Size: Размер памяти (в байтах), занимаемый модулем. Used by: Список зависимых модулей или функций ядра. Например, так:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ lsmod Module Size Used by overlay 241664 0 tun 73728 2 snd_usb_audio 614400 0 snd_usbmidi_lib 57344 1 snd_usb_audio snd_ump 49152 1 snd_usb_audio snd_rawmidi 57344 2 snd_usbmidi_lib,snd_ump hid_jabra 16384 0 uinput 20480 0 rfcomm 102400 16 snd_seq_dummy 12288 0 snd_hrtimer 12288 1 wireguard 122880 0 curve25519_x86_64 36864 1 wireguard libcurve25519_generic 45056 2 curve25519_x86_64,wireguard ip6_udp_tunnel 16384 1 wireguard udp_tunnel 36864 1 wireguard nf_conntrack_netbios_ns 12288 1 nf_conntrack_broadcast 12288 1 nf_conntrack_netbios_ns nft_fib_inet 12288 1 nft_fib_ipv4 12288 1 nft_fib_inet nft_fib_ipv6 12288 1 nft_fib_inet nft_fib 12288 3 nft_fib_ipv6,nft_fib_ipv4,nft_fib_inet [...] Пример использования Хотя у lsmod нет опций, сочетание его с другими командами повышает его полезность:\nВ сочетании с modinfo: получите подробную информацию о конкретном модуле, включая его автора, лицензию и описание. Отладка с помощью dmesg: сопоставьте сообщения журнала ядра с загруженными модулями для диагностики проблем во время загрузки. 1 2 modinfo \u0026lt;module_name\u0026gt; sudo dmesg | grep \u0026lt;module_name\u0026gt; Заключение Команды вроде lslogins и lsmod могут дать вам легкое представление об управлении пользователями и поведении ядра. Попробуйте их сегодня и подумайте о том, чтобы объединить их вывод с другими инструментами, чтобы оценить их возможности.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lslogins-lsmod/","summary":"\u003cp\u003eПродолжая \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lscpu-i-lsusb/\"\u003eнаше\u003c/a\u003e \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-dmidecode-i-lspci/\"\u003eисследование\u003c/a\u003e \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lsof-lsblk/\"\u003eкоманд\u003c/a\u003e,\nкоторые дают представление о внутренней работе вашей системы Fedora Linux, мы обращаем внимание на более простые, такие\nкак \u003ccode\u003elslogins\u003c/code\u003e и \u003ccode\u003elsmod\u003c/code\u003e. Эти инструменты предоставляют информацию об учетных записях пользователей\nи \u003ca href=\"https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C_%D1%8F%D0%B4%D1%80%D0%B0\"\u003eмодулях ядра\u003c/a\u003e\nсоответственно.\u003c/p\u003e","title":"Анализ системы с помощью инструментов командной строки: lslogins и lsmod"},{"content":"Бывает нужно сделать что-то не из своей локации, но под рукой нет доступного Linux, а уж тем более Window сервера. У моего хостера, с не ограниченным доступом в глобальный интернет, тарифы на час часто не доступны. У другого, с минутной тарификацией есть проблемы с доступностью за пределы рунета.\nРешение от h2.nexus У этого хостера есть отдельная страница, с которой можно поключится на 15 минут к серверу. При этом есть возможность выбрать или Linux, или Windows сервер. Собственно вот на нее ссылка: https://h2.nexus/cli или полноэкранный вариант https://cli.h2.nexus. Нужно будет выбрать вариант из Linux (SSH) и Windows (VNC) и пройти рекаптчу.\nНа текущий момент (28 декабря 2024)это сервер:\nXeon 2680v4 (4 cores); 4 GB оперативной памяти; 20 GB SSD диска; 200 Mbps сеть. Linux (SSH) В качество операционной системы используется Ubuntu 22.04 LTS (опять же на текущий момент, 28 декабря 2024).\nСам сервер находится за NAT-ом, но доступ в интернет и возможность установки софта имеется. На примере проверки скорости:\nWindows (VNC) В качестве операционной системы используется Windows Server 2012 R2 (опять же на текущий момент, 28 декабря 2024).\nЕсть поддержка установки софта, по крайней мере Notepad++, при запуске, попросил обновиться и смог это сделать без проблем. Возможно какой-то специфический софт не поставиться или не все настройки можно будет сделать, с Windows давно не имел дела, не знаю что там проверить. Сеть так же NAT, доступ в интернет есть:\nВстраивание на сайт Еще одна интересная особенность в том, что можно получить встроить этот блок на свой сайт:\n1 \u0026lt;iframe src=\u0026#34;https://cli.h2.nexus\u0026#34; width=\u0026#34;100%\u0026#34; height=\u0026#34;100%\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; И выглядеть это будет так (высота здесь задана как 488px):\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/dostup-ssh-na-15-minut/","summary":"\u003cp\u003eБывает нужно сделать что-то не из своей локации, но под рукой нет доступного Linux, а уж тем более Window сервера. У\nмоего хостера, с не ограниченным доступом в глобальный интернет, тарифы на час часто не доступны. У другого, с минутной\nтарификацией есть проблемы с доступностью за пределы рунета.\u003c/p\u003e","title":"Доступ ssh на 15 минут"},{"content":"Сегодня пришло обновление для PhpStorm от JetBrains. Начало показываться уведомление о настройке GPG Agent, и угораздило его тыкнуть. После этого в системе перестало работать любое взаимодействие с GnuPG.\nЧто именно произошло Вот это уведомление, с которого все началось:\nПосле этого в PhpStorm начинает показываться окно с запросом пароля для ключа:\nПри попытке ввести свою парольную фразу мы получаем замечательный результат:\nЗакрадывается мысль \u0026ldquo;что-то не так\u0026rdquo;. Пробую глянуть пароли через pass\n1 2 3 ❯ pass show key/gpg gpg: сбой расшифровки с открытым ключом: Плохой сертификат центра сертификации gpg: сбой расшифровки: Плохой сертификат центра сертификации Продублирую на английском (вдруг кому в выдаче попадется):\n1 2 3 ❯ LANG=en_US pass show key/gpg gpg: public key decryption failed: Bad CA certificate gpg: decryption failed: Bad CA certificate Ну спасибо, и сами не работают, и все другое взаимодействие с GnuPG в системе поломали. Ни пароль получить, ни письмо подписать ничего.\nА что же это за настройка Настройка состоит в том, что создается (у меня по умолчанию его нет, но возможно дописывается в конец) файл .gnupg/gpg-agent.conf с содержанием:\n1 pinentry-program /home/user/.gnupg/pinentry-ide.sh То есть еще и какой-то sh скрипт закидывают, смотрим его содержанием:\n1 2 #!/bin/sh \u0026#34;/home/user/.local/share/JetBrains/Toolbox/apps/phpstorm/jbr/bin/java\u0026#34; -cp \u0026#34;/home/user/.local/share/JetBrains/Toolbox/apps/phpstorm/plugins/vcs-git/lib/git4idea-rt.jar:/home/user/.local/share/JetBrains/Toolbox/apps/phpstorm/lib/externalProcess-rt.jar\u0026#34; git4idea.gpg.PinentryApp Видимо все это для запуска git4idea.gpg.PinentryApp, который к сожалению не работает.\nВозвращаем все как было Возможно в других операционных системах нужны дополнительные действия, но распишу как действовать на Fedora 41. Первым делом сносим эти два новых файла:\n1 ❯ rm .gnupg/{gpg-agent.conf,pinentry-ide.sh} Далее перезапускаем GPG Agent:\n1 ❯ gpgconf --reload gpg-agent После чего GPG в системе начинает работать как положено:\n1 2 ❯ pass -c key/gpg Copied key/gpg to clipboard. Will clear in 45 seconds. ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/perestal-rabotat-gpg-posle-konfiguracii-agenta-cherez-phpstorm/","summary":"\u003cp\u003eСегодня пришло обновление для PhpStorm от JetBrains. Начало показываться уведомление о настройке GPG Agent, и угораздило\nего тыкнуть. После этого в системе перестало работать любое взаимодействие с \u003ca href=\"https://gnupg.org/\"\u003eGnuPG\u003c/a\u003e.\u003c/p\u003e","title":"Перестал работать GPG после конфигурации агента через PhpStorm"},{"content":"Настроим сертификат и полноценный доступ по https с валидным сертификатом к опубликованному в виде сайта бакету Yandex S3. Сертификат будем получить на домен, который находится за пределами Yandex.Cloud через Let\u0026rsquo;s Encrypt.\nЧто именно происходит Если мы откроем настройка сайта для бакета, то увидим такую картину:\nКак вино ссылка будет указана с протоколом http, так как в название бакета использован домен и в нем присутствует разделитель в виде точки (.). Из-за этого открыть сайт без предупреждений браузера не получится:\n1 2 3 4 5 6 7 ❯ curl https://some.website.website.yandexcloud.net/ curl: (60) SSL: no alternative certificate subject name matches target hostname \u0026#39;some.website.website.yandexcloud.net\u0026#39; More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the webpage mentioned above. По этой же причине, если настроить обязательную проверку сертификаты, в Cloudflare сайт так же не будет открываться. Все это связано с тем, что сертификат, который используется для показа бакета в виде сайта имеет только одно доступное для показа имя *.website.yandexcloud.net.\nСертификат Yandex дает возможность выписывать Let\u0026rsquo;s Encrypt сертификаты:\nПосле чего нужно будет выполнить проверку прав. Благо для этого есть возможность указать CNAME запись у своего любимого DNS провайдера, без переноса домена в Yandex.\nПосле чего нужно будет вписать полученный CNAME у своего провайдера и ждать. Процесс совсем не быстрый, в моем случае прошло около 15 минут. Далее сертификат будет обновляться самим Yandex-ом, при условии живой CNAME.\nТеперь этот сертификат можно смело привязать к сайту в S3:\nНеочевидная польза Если после этого открыть настройки сайта, то ссылка так и останется http:\nЭто немного сбивает с толку, видимо это связано с тем, что домен находится вне Yandex-а. Но если попытаться проверить доступность сайта руками. Для этого пригодится параметр --connect-to в curl:\n1 2 ❯ curl --connect-to some.website:443:some.website.yandexcloud.net:443 https://some.website -s | head -c 32 \u0026lt;!doctype html\u0026gt;\u0026lt;html lang=ru dir То есть S3 понимает, что по домену some.website нужно отдавать вебсайт с бакета, у которого есть сертификат на some.website. Просто это нигде не отображается визуально. Теперь в настройках Cloudflare можно включить проверку сертификата источника и проксировать все через https.\nПри этом сертификаты на обоих уровнях теперь будут обновляться автоматически.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/yandex-s-plyus-https-s-vneshnim-domenom/","summary":"\u003cp\u003eНастроим сертификат и полноценный доступ по \u003ccode\u003ehttps\u003c/code\u003e с валидным сертификатом к опубликованному в виде сайта\nбакету \u003ca href=\"https://yandex.cloud/services/storage\"\u003eYandex S3\u003c/a\u003e. Сертификат будем получить на домен, который находится за\nпределами \u003ca href=\"https://yandex.cloud/\"\u003eYandex.Cloud\u003c/a\u003e через \u003ca href=\"https://letsencrypt.org/\"\u003eLet\u0026rsquo;s Encrypt\u003c/a\u003e.\u003c/p\u003e","title":"Yandex S3 плюс HTTPS с внешним доменом"},{"content":"Коротко история такова: после деплоя приложения Plex на сервере TrueNAS Scale бибилиотека сервера перестает быть доступной, можно менять настройки, но \u0026ldquo;подключить\u0026rdquo; ее нельзя. Получается своеобразный локальный WebUI для Plex, для использования других серверов.\nTL;DR: нужно проверить, что бы поле Дополнительные домены было пустым.\nПроблема При первоначальной настройке Plex уже начинают сыпаться ошибки вида \u0026ldquo;не удалось зарегистрировать сервер\u0026rdquo;. После чего сервер с библиотекой в WebGUI начинают отображаться так:\nПри редактировании боковой панели:\nА в административной части сервера так:\nПри этом нажатие на кнопку \u0026ldquo;Выбрать сервер\u0026rdquo; ничего не дает. Если глянуть на вкладку Network в devtools браузера, то можно увидеть, что сервер \u0026ldquo;пятисотит\u0026rdquo;:\nДетали ошибки Если копнуть глубже, зайти в k3s и добраться до логов /config/Library/Application Support/Plex Media Server/Logs/Plex Media Server.log, то можно увидеть следующую картину:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 DEBUG - Request: didn\u0026#39;t get any data from [::ffff:192.168.1.2]:33582: End of file DEBUG - Request: [192.168.2.2:54204 (Allowed Network (Subnet))] GET /:/prefs (8 live) #202 GZIP Token () DEBUG - Request: [192.168.2.2:54208 (Allowed Network (Subnet))] GET /accounts/1 (9 live) #207 GZIP Token () DEBUG - Request: [192.168.2.2:54212 (Allowed Network (Subnet))] GET /myplex/account (9 live) #208 GZIP Token () DEBUG - Completed: [192.168.2.2:54208] 200 GET /accounts/1 (9 live) #207 GZIP 1ms 518 bytes (pipelined: 1) DEBUG - Completed: [192.168.2.2:54212] 200 GET /myplex/account (9 live) #208 GZIP 1ms 2873 bytes (pipelined: 1) DEBUG - Completed: [192.168.2.2:54204] 200 GET /:/prefs (9 live) #202 GZIP 12ms 10739 bytes (pipelined: 1) DEBUG - Request: [192.168.2.2:54204 (Allowed Network (Subnet))] PUT /myplex/refreshReachability (9 live) #20c GZIP Token () DEBUG - Completed: [192.168.2.2:54204] 200 PUT /myplex/refreshReachability (9 live) #20c GZIP 0ms 320 bytes (pipelined: 2) DEBUG - Request: [192.168.2.2:54204 (Allowed Network (Subnet))] POST /myplex/claim?token=xxxxxxxxxxxxxxxxxxxxrXQxxe (9 live) #20d GZIP Token () DEBUG - [Req#20d/HCl#43] HTTP requesting POST https://plex.tv/api/claim/exchange?token=xxxxxxxxxxxxxxxxxxxxrXQxxe WARN - [HttpClient/HCl#43] HTTP error requesting POST https://plex.tv/api/claim/exchange?token=xxxxxxxxxxxxxxxxxxxxrXQxxe (6, Couldn\u0026#39;t resolve host name) (Could not resolve host: plex.tv) DEBUG - [Req#20d] MyPlex: Did token exchange for claim (returnCode: -6) DEBUG - [Req#20d] MyPlex: Got a token poked, let\u0026#39;s act on it. DEBUG - [Req#20d/HCl#45] HTTP requesting GET https://plex.tv/api/v2/user/privacy?X-Plex-Token= WARN - [HttpClient/HCl#45] HTTP error requesting GET https://plex.tv/api/v2/user/privacy?X-Plex-Token=xxxxxxxxxxxxxxxxxxxxe host name) (Could not resolve host: plex.tv) DEBUG - [Req#20d] [Analytics] Using cached data for privacy preferences DEBUG - [Req#20d/HCl#46] HTTP requesting GET https://plex.tv/api/v2/release_channels?X-Plex-Token= WARN - [HttpClient/HCl#46] HTTP error requesting GET https://plex.tv/api/v2/release_channels?X-Plex-Token=xxxxxxxxxxxxxxxxxxxxe host name) (Could not resolve host: plex.tv) DEBUG - [Req#20d] [AutoUpdateRequestHandler] Using cached data for update channels DEBUG - [Req#20d/HCl#47] HTTP requesting GET https://plex.tv/api/v2/features?X-Plex-Token= WARN - [HttpClient/HCl#47] HTTP error requesting GET https://plex.tv/api/v2/features?X-Plex-Token=xxxxxxxxxxxxxxxxxxxxe host name) (Could not resolve host: plex.tv) WARN - [Req#20d] FeatureManager: Couldn\u0026#39;t get features. Trying again soon. DEBUG - Completed: [192.168.2.2:54204] 500 POST /myplex/claim?token=xxxxxxxxxxxxxxxxxxxxrXQxxe (9 live) #20d GZIP 9ms 530 bytes (pipelined: 3) DEBUG - [Req#20d/MediaProviderManager] we had 0 cloud providers online, we now have 0 DEBUG - Request: didn\u0026#39;t get any data from [::ffff:192.168.1.2]:33474: End of file 192.168.1.2 - ip клиента (браузера); 192.168.2.2 - ip пода (pod) в котором крутиться Plex. Видно, что в контейнере есть проблема, а вот полноценного DNS нету. Для тестов пробовал запускать apt update, с тем же результатом:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # apt update Err:1 http://archive.ubuntu.com/ubuntu focal InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; Err:2 http://security.ubuntu.com/ubuntu focal-security InRelease Temporary failure resolving \u0026#39;security.ubuntu.com\u0026#39; Err:3 http://archive.ubuntu.com/ubuntu focal-updates InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; Err:4 http://archive.ubuntu.com/ubuntu focal-backports InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; Reading package lists... Done Building dependency tree Reading state information... Done All packages are up to date. W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/focal/InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/focal-updates/InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/focal-backports/InRelease Temporary failure resolving \u0026#39;archive.ubuntu.com\u0026#39; W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/focal-security/InRelease Temporary failure resolving \u0026#39;security.ubuntu.com\u0026#39; W: Some index files failed to download. They have been ignored, or old ones used instead. При этом файл resolv.conf выглядит так:\n1 2 3 4 # cat /etc/resolv.conf search ix-plex.svc.cluster.local svc.cluster.local cluster.local some.domain.tld nameserver 172.17.0.10 options ndots:5 Решение Этот самый домен, в конце списка search - это дополнительный домен TrueNAS, задается в настройках сети (Сеть =\u0026gt; Общие настройки):\nПо какому-то странному (или может не очень, я с кубером в любом проявление не сильно знаком) стечению обстоятельств, именно заполненное поле Дополнительные домены приводит к такому поведению. Как только это поле очистить и перезапустить приложение Plex - все начинает работать как нужно (в поде появился DNS и запросы к plex.tv пошли).\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/problema-s-rabotosposobnostyu-servera-dannyh-plex-na-truenas/","summary":"\u003cp\u003eКоротко история такова: после деплоя приложения \u003ca href=\"https://www.plex.tv/\"\u003ePlex\u003c/a\u003e на\nсервере \u003ca href=\"https://www.truenas.com/truenas-scale/\"\u003eTrueNAS Scale\u003c/a\u003e бибилиотека сервера перестает быть доступной, можно\nменять настройки, но \u0026ldquo;подключить\u0026rdquo; ее нельзя. Получается своеобразный локальный WebUI для Plex, для использования других\nсерверов.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/posts/problema-s-rabotosposobnostyu-servera-dannyh-plex-na-truenas/#%d1%80%d0%b5%d1%88%d0%b5%d0%bd%d0%b8%d0%b5\"\u003eTL;DR\u003c/a\u003e: нужно проверить, что бы поле \u003ccode\u003eДополнительные домены\u003c/code\u003e было пустым.\u003c/p\u003e","title":"Проблема с работоспособностью сервера данных Plex на TrueNAS"},{"content":"Сейчас для домашней сети есть запасной канал выхода в интернет - это usb Yota модем. Модем подключается к ноде Proxmox, на который запущена виртуальная машина с OPNsense. В Proxmox это выглядит как отдельный bridge, но он привязан к ноде, в которую воткнут модем.\nЗачем Для поддержки удобной миграции машин (в идеале автоматически, в режиме HA) нужно отказаться от физического подключения модема к ноде Proxmox и дать возможность работать сразу по сети, доступной на всех трех нодах.\nСамое простое решение Самым простым решение, как мне кажется, является покупка 4g модема с сетевым портом или роутера с лотком для SIM-карты. Но для этого нужно искать, выбирать и платить деньги. У меня же уже есть модем Yota, есть Orange Pi Zero2 c usb и ethernet портом, нужно только собраться и настроить их для работы вместе.\nСетевой план Так как порт ethernet только одни, это будет своеобразная вариация на тему Router-on-a-Stick. Максимальные скорости для Yota были намерены плюс-минус такими:\nА значит имеющегося gigabit-ного порта хватит с запасом. Можем в момент подключения будет создавать виртуальный ethernet адаптер в системе. Этот вирутальный адаптер раздает ip адреса по DHCP. Что бы не пересекаться с DHCP от ДОМ.РУ или DHCP локальной сети, нужно закинуть виртуальный интерфейс в виртуальную сеть (VLAN). Инфраструктура в виде управляемых свитчей уже есть, и \u0026ldquo;донести\u0026rdquo; интернет до кластера внутри VLAN не представляется чем-то сложным. Но и доступ из локальной сети к Zero2 нужно оставить. Итого план:\nсоздать bridge; создать vlan подключение на Zero2; добавить в bridge подключение от модема и подключение с vlan. Настройка На Zero2 установлен Armbian 24.8.4, и есть NetworkManager (nmcli), именно через него и будет производиться настройка. Для начала создаем bridge:\n1 nmcli con add type bridge ifname yotabr con-name yotabr connection.autoconnect yes type bridge - установка типа подключения как bridge; con-name yotabr - название подключения; ifname yotabr - название интерфейса для привязки соединения. Далее добавляем VLAN подключение сразу в новый bridge yotabr:\n1 nmcli con add type vlan con-name end0.10 dev end0 id 10 master yotabr connection.autoconnect yes type vlan - установить тип подключения vlan; con-name end0.10 - название подключения, обычно состоит из устройства и номера vlan разделенных точкой; dev end0 - основное физическое устройство, на которое vlan будет находиться; id 10 - номер vlan (vlan tag); master yotabr - указывает, к какому bridge интерфейсу подключиться это соединение. Далее нужно добавить второй интерфейс в bridge, а именно модифицировать интерфейс созданный модемом ( enxac50431aeefd):\n1 nmcli con modify enxac50431aeefd master yotabr master yotabr - указывает, к какому bridge интерфейсу подключиться это соединение. После этого нужно перезагрузить все соединения:\n1 nmcli con reload Вывод списка соединений:\n1 2 3 4 5 6 7 zero2:~:# nmcli con NAME UUID TYPE DEVICE end0 64e76368-2378-3d3a-a0f0-fe3c38fade98 ethernet end0 yotabr 036256de-8f8e-4af1-a5e3-e0f1daef8ae9 bridge yotabr end0.10 53ba1a30-1e61-42bd-811a-5bc8efed44ad vlan end0.10 enxac50431aeefd 0cc8a663-882d-3387-b788-dedba9a11749 ethernet enxac50431aeefd lo 6d969597-5e5d-4f21-9cc1-60cddbd8e1d0 loopback lo Итог Теперь на свитч попадают 2 сети от Zero2:\nvlan 1 - общая локальная сеть; vlan 10 - сеть с доступов в интернет Yota. Благодаря этому сам Zero2 остается доступным из локалки, а интернет \u0026ldquo;доставляется\u0026rdquo; до OPNsense и не виден из общей сети. На уровне подключения виртуальной машины в Proxmox теперь нужно только выставить нужный тег:\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/modem-yota-orange-pi-zero-razdaem-internet-v-lokalnuyu-set/","summary":"\u003cp\u003eСейчас для домашней сети есть запасной канал выхода в интернет -\nэто \u003ca href=\"https://www.yota.ru/store/modem-yota\"\u003eusb Yota модем\u003c/a\u003e. Модем подключается к ноде Proxmox, на который запущена\nвиртуальная машина с \u003ca href=\"https://opnsense.org/\"\u003eOPNsense\u003c/a\u003e. В Proxmox это выглядит как отдельный bridge, но он привязан к\nноде, в которую воткнут модем.\u003c/p\u003e","title":"Модем Yota и Orange Pi Zero2 - раздаем интернет в локальную сеть"},{"content":"Рассмотрим как собрать логи всех запросов который выполняются в MySQL. С помощью этого лога можно не только рассчитать количество запросов, но и проверить как изменилось взаимодействие с БД со стороны программы. Визуально увидеть повторяющиеся по смыслу последовательности запросов или даже провести анализ и explain для особо подозрительных.\nВарианты логирования и хранения запросов Для начала нужно определиться какие запросы нам нужны. Есть два варианта, запросов которые будут записываться сервером MySQL:\nslow_query_log - это запросы, которые выполняются дольше определенного порогового значения, обычно этот лог включается на продолжительное время или вообще работает на постоянной основе; general_log - это все запросы, которые поступают на сервер. Так как приходится записывать все запросы, а сервер может быть очень нагружен ( сотни, а то и более rps), то такой лог лучше включать локально, в тестовых стендах или прогонах в CI. Оба лога включаются ТОЛЬКО на глобальном уровне (SET GLOBAL ... или файл конфигурации). Так же ТОЛЬКО на глобальном уровне определяется место хранения лога ( log_output: FILE или TABLE). Можно писать лог сразу в файл и таблицу, если задать значение TABLE,FILE.\nЛогирование медленных запросов Это лог, который сохраняет \u0026ldquo;слишком долго\u0026rdquo; выполняющиеся запросы. Слишком долго можно определить через переменную long_query_time. При этом в отличие от самого лога, эту переменную можно изменить для сессии.\nЕсли таблицы нет, то ее можно создать. Для этого в MySQL уже есть подготовленная команда, в ней содержится описание схемы таблицы:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 mysql\u0026gt; SHOW CREATE TABLE mysql.slow_log\\G *************************** 1. row *************************** Table: slow_log Create Table : CREATE TABLE `slow_log` ( `start_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, `query_time` time(6) NOT NULL, `lock_time` time(6) NOT NULL, `rows_sent` int NOT NULL, `rows_examined` int NOT NULL, `db` varchar(512) NOT NULL, `last_insert_id` int NOT NULL, `insert_id` int NOT NULL, `server_id` int unsigned NOT NULL, `sql_text` mediumblob NOT NULL, `thread_id` bigint unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8mb3 COMMENT=\u0026#39;Slow log\u0026#39; По умолчанию таблица создается с ENGINE=CSV, что позволяет хранить данные на диске в виде файла:\n1 2 bash-5.1# ls -al /var/lib/mysql/mysql/slow_log.CSV -rw-r-----. 1 mysql mysql 0 Sep 27 20:19 /var/lib/mysql/mysql/slow_log.CSV Посмотреть текущие настройки логирования медленных запросов можно так:\n1 2 3 4 5 6 7 8 9 mysql\u0026gt; SHOW VARIABLES WHERE Variable_name in (\u0026#39;log_output\u0026#39;, \u0026#39;slow_query_log\u0026#39;, \u0026#39;slow_query_log_file\u0026#39;, \u0026#39;long_query_time\u0026#39;); +---------------------+--------------------------------------+ | Variable_name | Value | +---------------------+--------------------------------------+ | log_output | FILE | | long_query_time | 10.000000 | | slow_query_log | OFF | | slow_query_log_file | /var/lib/mysql/f07f97f6ef28-slow.log | +---------------------+--------------------------------------+ Полный лог запросов Это лог со ВСЕМИ запросами, которые приходят в MySQL. Тут будут так же запросы на подключение, изменения настроек и прочего. Для хранения потребуется отличаня от slow_log схема таблицы для хранения, ее можно получить так:\n1 2 3 4 5 6 7 8 9 10 11 mysql\u0026gt; SHOW CREATE TABLE mysql.general_log\\G *************************** 1. row *************************** Table: general_log Create Table : CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, `thread_id` bigint unsigned NOT NULL, `server_id` int unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumblob NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8mb3 COMMENT=\u0026#39;General log\u0026#39; Так же как и лог медленных запросов, по умолчанию используется CSV движок.\n1 2 bash-5.1# ls -al /var/lib/mysql/mysql/general_log.CSV -rw-r-----. 1 mysql mysql 0 Sep 27 20:19 /var/lib/mysql/mysql/general_log.CSV Посмотреть текущие настройки полного логирования можно так:\n1 2 3 4 5 6 7 8 mysql\u0026gt; SHOW VARIABLES WHERE Variable_name in (\u0026#39;log_output\u0026#39;, \u0026#39;general_log\u0026#39;, \u0026#39;general_log_file\u0026#39;); +------------------+---------------------------------+ | Variable_name | Value | +------------------+---------------------------------+ | general_log | OFF | | general_log_file | /var/lib/mysql/f07f97f6ef28.log | | log_output | FILE | +------------------+---------------------------------+ Примеры использования Для тестов будем использовать MySQL 8.4 в виде docker контейнера:\n1 docker run --rm --name mysql-test -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:8.4 Подключаться мы будем из другого контейнера, что бы быть в ситуации, когда наше приложение не находится в одной системе с базой данных.\n1 docker run -it --rm --net=container:mysql-test mysql:8.4 mysql -h 127.0.0.1 -uroot Первая важная настройка, включаем хранение логов в таблице. Это самый просто вариант получить логи, если у нас нет доступа к файловой системе удаленного сервера MySQL. Так же это позволи производить манипцуляции с логами на привычном ( если уж мы полезли) языке SQL.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 mysql\u0026gt; SET GLOBAL log_output = \u0026#39;TABLE\u0026#39;, general_log = \u0026#39;ON\u0026#39;, slow_query_log = \u0026#39;ON\u0026#39;; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; SHOW VARIABLES WHERE Variable_name in (\u0026#39;log_output\u0026#39;, \u0026#39;general_log\u0026#39;, \u0026#39;general_log_file\u0026#39;, \u0026#39;slow_query_log\u0026#39;, \u0026#39;slow_query_log_file\u0026#39;, \u0026#39;long_query_time\u0026#39;); +---------------------+--------------------------------------+ | Variable_name | Value | +---------------------+--------------------------------------+ | general_log | ON | | general_log_file | /var/lib/mysql/f07f97f6ef28.log | | log_output | TABLE | | long_query_time | 10.000000 | | slow_query_log | ON | | slow_query_log_file | /var/lib/mysql/f07f97f6ef28-slow.log | +---------------------+--------------------------------------+ Теперь попробуем выполнить длинный (более 10 секунд) запрос:\n1 SELECT SLEEP(12); После этого подключится вторым клиентом (допустим контейнером, который выполняет тесты приложения) и попробуем получить списко медленых запросов:\n1 2 3 4 5 6 mysql\u0026gt; SELECT query_time, sql_text FROM mysql.slow_log; +-----------------+------------------------------------+ | query_time | sql_text | +-----------------+------------------------------------+ | 00:00:12.000715 | 0x53454C45435420534C45455028313229 | +-----------------+------------------------------------+ Действительно, запись попала в лог, но sql текст совершенно не читается. Это связано с тем, что таблица была создана с полем 'sql_text' mediumblob NOT NULL. Придется добавить дополнительную колонку с конвертацией:\n1 2 3 4 5 6 mysql\u0026gt; SELECT query_time, CONVERT(sql_text USING utf8) as \u0026#39;sql\u0026#39; FROM mysql.slow_log; +-----------------+------------------+ | query_time | sql | +-----------------+------------------+ | 00:00:12.000715 | SELECT SLEEP(12) | +-----------------+------------------+ А вот что попало в general_log:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 mysql\u0026gt; SELECT command_type, CONVERT(argument USING utf8) AS \u0026#39;sql\u0026#39; FROM mysql.general_log LIMIT 9; +--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+ | command_type | sql | +--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+ | Query | SHOW VARIABLES WHERE Variable_name in (\u0026#39;log_output\u0026#39;, \u0026#39;general_log\u0026#39;, \u0026#39;general_log_file\u0026#39;, \u0026#39;slow_query_log\u0026#39;, \u0026#39;slow_query_log_file\u0026#39;, \u0026#39;long_query_time\u0026#39;) | | Query | SELECT SLEEP(12) | | Connect | root@127.0.0.1 on using SSL/TLS | | Query | select @@version_comment limit 1 | | Query | SELECT * FROM mysql.slow_log | | Query | SELECT * FROM mysql.slow_log | | Connect | root@172.17.0.1 on using SSL/TLS | | Connect | Access denied for user \u0026#39;root\u0026#39;@\u0026#39;172.17.0.1\u0026#39; (using password: YES) | | Query | SELECT *, convert(sql_text using utf8) FROM mysql.slow_log | +--------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+ А вот тут мы видим не только все запросы, которые выполнялись, но и такие события как Connect, в которых видно кто и когда подключался.\nИтог Это неплохой механизм для сбора статистики по запросам, или получения последних несколько запросов, после которыйх упал тест в CI. Так же можно поменять движок хранения для таблиц. Но нужно понимать, что как минимум лог всех запросов сильно ударит по производительности. Мне эта техника помогла разобраться как изменилось взаимодействие приложения с БД, что вызвало рост запросов.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/mysql-logirovanie-zaprosov-vo-vnutrennee-hranilishe/","summary":"\u003cp\u003eРассмотрим как собрать логи всех запросов который выполняются в MySQL. С помощью этого лога можно не только рассчитать\nколичество запросов, но и проверить как изменилось взаимодействие с БД со стороны программы. Визуально увидеть\nповторяющиеся по смыслу последовательности запросов или даже провести анализ и \u003ccode\u003eexplain\u003c/code\u003e для особо подозрительных.\u003c/p\u003e","title":"MySQL логирование запросов во внутреннее хранилище"},{"content":"В нашей продолжающейся серии статей о системе Linux мы рассмотрим основные утилиты командной строки, которые предоставляют информацию об оборудовании и состоянии системы. После наших предыдущих обсуждений lscpu, lsusb, dmidecode и lspci мы теперь обратим внимание на lsof и lsblk. Эти инструменты особенно полезны для поиска открытых файлов, активных сетевых подключений и смонтированных блочных устройств в вашей системе Fedora Linux.\nПоиск открытых файлов с помощью lsof lsof (list open files) - мощный инструмент командной строки. Поскольку почти все в Linux рассматривается как файл, lsof обеспечивает детальное представление о многих частях вашей системы. Он перечисляет какие файлы используются, какие процессы к ним обращаются и даже какие сетевые порты открыты (см., например, Wikipedia о сетевых сокетах для получения дополнительной информации).\nПример использования Для начала просто выполните команду lsof, что бы получить список открытых в системе файлов:\n1 $ sudo lsof sudo использовался для расширенных привилегий. Это нужно, что бы получить информацию о файла, которые не открывались процессами вашего пользователя. Вывод команды содержит множество информации, которая может ошеломить. Мы собираемся сузить вывод до специфической информации о некоторых распространенных вариантах использования в следующих примерах.\nПример 1: поиск открытых пользователем или процессом файлов lsof может быть очень полезен, что бы определить какие файлы открыты конкретным пользователем или процессом.\nВывести список файлов открытых конкретным пользователем можно так:\n1 $ sudo lsof -u \u0026lt;username\u0026gt; Такая команда вернет список открытых файлов занятых указанным пользователем. Например:\n1 $ sudo lsof -u johndoe Вы увидите такую информацию как: идентификатор процесса (PID), файловый дескриптор, тип файла и его путь.\nДля фильтрации вывода по процессу нужно передать флаг -p:\n1 $ lsof -p \u0026lt;PID\u0026gt; Это особенно полезно для устранения неполадок, связанных с определенными процессами или когда вам нужно проверить, какие файлы служба держит открытыми. Используйте sudo, если процесс не принадлежит вашему пользователю.\nПример вывода:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ lsof -p 873648 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 873648 user cwd DIR 0,39 8666 257 /home/user bash 873648 user rtd DIR 0,35 158 256 / bash 873648 user txt REG 0,35 1443376 12841259 /usr/bin/bash bash 873648 user mem REG 0,33 12841259 /usr/bin/bash (path dev=0,35) bash 873648 user mem REG 0,33 14055145 /usr/lib/locale/locale-archive (path dev=0,35) bash 873648 user mem REG 0,33 14055914 /usr/lib64/libc.so.6 (path dev=0,35) bash 873648 user mem REG 0,33 13309071 /usr/lib64/libtinfo.so.6.4 (path dev=0,35) bash 873648 user mem REG 0,33 14059926 /usr/lib64/gconv/gconv-modules.cache (path dev=0,35) bash 873648 user mem REG 0,33 14055911 /usr/lib64/ld-linux-x86-64.so.2 (path dev=0,35) bash 873648 user 0u CHR 136,3 0t0 6 /dev/pts/3 bash 873648 user 1u CHR 136,3 0t0 6 /dev/pts/3 bash 873648 user 2u CHR 136,3 0t0 6 /dev/pts/3 bash 873648 user 255u CHR 136,3 0t0 6 /dev/pts/3 Пример 2: идентификация открытых сетевых подключений Благодаря возможности показывать список сетевых подключений, lsof так же становится удобным инструментом для диагностики сетевых проблем, поскольку он доступен обычно даже в защищенных и минимальных системах.\nTo display all open network connections (TCP/UDP sockets), run: Что бы показать все открытые сетевые соединения ( TCP/UDP сокеты), выполните:\n1 $ sudo lsof -i Команда выведет список активных интернет соединений вместе с используемым для них протоколом, портом и информацией о процессе.\nВы можете отфильтровать вывод для специфического протокола (например, TCP или UDP), включить или исключить IPv4 и IPv6, а так же комбинировать несколько значений (раздел примеров man lsof предоставляет много полезной информации, включая отрицание):\n1 2 3 4 5 $ sudo lsof -i tcp $ sudo lsof -i udp $ sudo lsof -i 4tcp $ sudo lsof -i 6tcp $ sudo lsof -i 4tcp@example.com Для подключений связанных с определенным портом:\n1 $ sudo lsof -i :\u0026lt;port_number\u0026gt; Например, вывести список соединений к порту 22 (SSH):\n1 2 3 4 $ sudo lsof -i :22 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 904379 root 3u IPv4 5622530 0t0 TCP *:ssh (LISTEN) sshd 904379 root 4u IPv6 5622532 0t0 TCP *:ssh (LISTEN) Эта информация может иметь решающее значение для выявления несанкционированных подключений или простого мониторинга сетевой активности в системе с целью отладки.\nИсследование блочных устройств с помощью lsblk Другой полезный инструмент - lsblk, который показывает информацию о все доступных в вашей системе блочных устройствах. Блочные устройства включают в себя жесткие диски (HDD), SSD и USB-накопители. Эта команда обеспечивает древовидное представление, что помогает вам понять взаимосвязи между разделами, устройствами и их точками монтирования.\nПример использования Запуск lsblk без каких-либо параметров предоставляет четкую иерархическую структуру блочных устройств:\n1 $ lsblk Здесь отображаются все блочные устройства в древовидной структуре включая их размер, тип (диск или раздел) и точку монтирования (если применимо).\nДля более глубокого изучения файловых систем на ваших блочных устройствах используйте флаг -f:\n1 $ lsblk -f При этом будут отображены не только блочные устройства, но и сведения о файловых системах на каждом разделе, включая тип (например, ext4, vfat, swap), UUID и текущие точки монтирования.\nЕсли вам нужно меньше информации о самих устройствах (без отображения разделов или точек монтирования), будет полезен параметр -d:\n1 $ lsblk -d Также есть опция -J или –json. Если используется, команда выводит информацию в формате JSON. Это обеспечивает структурированное представление, которое особенно полезно для сценариев и автоматизации.\nПримеры вывода с моего ноутбука (некоторая длинная информация, например UUID, удалена для удобства чтения):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 $ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 1 0B 0 disk sdb 8:16 1 0B 0 disk sdc 8:32 1 0B 0 disk zram0 252:0 0 8G 0 disk [SWAP] nvme0n1 259:0 0 931,5G 0 disk ├─nvme0n1p1 259:1 0 600M 0 part /boot/efi ├─nvme0n1p2 259:2 0 1G 0 part /boot └─nvme0n1p3 259:3 0 929,9G 0 part └─luks-84257c20[...] 253:0 0 929,9G 0 crypt /home $ lsblk -d NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 1 0B 0 disk sdb 8:16 1 0B 0 disk sdc 8:32 1 0B 0 disk zram0 252:0 0 8G 0 disk [SWAP] nvme0n1 259:0 0 931,5G 0 disk $ lsblk -f NAME FSTYPE [...]LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS sda sdb sdc zram0 [SWAP] nvme0n1 ├─nvme0n1p1 vfat 4C5B-4355 579,7M 3% /boot/efi ├─nvme0n1p2 ext4 30eff827[...] 605M 31% /boot └─nvme0n1p3 crypto_LUKS 84257c20[...] └─luks-84257[...] btrfs fe[...] 666f9d6f[...] 303,1G 67% /home / $ lsblk -f -J { \u0026#34;blockdevices\u0026#34;: [ [...],{ \u0026#34;name\u0026#34;: \u0026#34;nvme0n1\u0026#34;, \u0026#34;fstype\u0026#34;: null, \u0026#34;fsver\u0026#34;: null, \u0026#34;label\u0026#34;: null, \u0026#34;uuid\u0026#34;: null, \u0026#34;fsavail\u0026#34;: null, \u0026#34;fsuse%\u0026#34;: null, \u0026#34;mountpoints\u0026#34;: [ null ], \u0026#34;children\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;nvme0n1p1\u0026#34;, \u0026#34;fstype\u0026#34;: \u0026#34;vfat\u0026#34;, \u0026#34;fsver\u0026#34;: \u0026#34;FAT32\u0026#34;, \u0026#34;label\u0026#34;: null, \u0026#34;uuid\u0026#34;: \u0026#34;4C5B-4355\u0026#34;, \u0026#34;fsavail\u0026#34;: \u0026#34;579,7M\u0026#34;, \u0026#34;fsuse%\u0026#34;: \u0026#34;3%\u0026#34;, \u0026#34;mountpoints\u0026#34;: [ \u0026#34;/boot/efi\u0026#34; ] },{ \u0026#34;name\u0026#34;: \u0026#34;nvme0n1p2\u0026#34;, \u0026#34;fstype\u0026#34;: \u0026#34;ext4\u0026#34;, \u0026#34;fsver\u0026#34;: \u0026#34;1.0\u0026#34;, \u0026#34;label\u0026#34;: null, \u0026#34;uuid\u0026#34;: \u0026#34;30eff827-[...]\u0026#34;, \u0026#34;fsavail\u0026#34;: \u0026#34;605M\u0026#34;, \u0026#34;fsuse%\u0026#34;: \u0026#34;31%\u0026#34;, \u0026#34;mountpoints\u0026#34;: [ \u0026#34;/boot\u0026#34; ] },{ \u0026#34;name\u0026#34;: \u0026#34;nvme0n1p3\u0026#34;, \u0026#34;fstype\u0026#34;: \u0026#34;crypto_LUKS\u0026#34;, \u0026#34;fsver\u0026#34;: \u0026#34;2\u0026#34;, \u0026#34;label\u0026#34;: null, \u0026#34;uuid\u0026#34;: \u0026#34;84257c20-[...]\u0026#34;, \u0026#34;fsavail\u0026#34;: null, \u0026#34;fsuse%\u0026#34;: null, \u0026#34;mountpoints\u0026#34;: [ null ], \u0026#34;children\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;luks-[...]\u0026#34;, \u0026#34;fstype\u0026#34;: \u0026#34;btrfs\u0026#34;, \u0026#34;fsver\u0026#34;: null, \u0026#34;label\u0026#34;: \u0026#34;fedora_localhost-live\u0026#34;, \u0026#34;uuid\u0026#34;: \u0026#34;666f9d6f-[...]\u0026#34;, \u0026#34;fsavail\u0026#34;: \u0026#34;303,1G\u0026#34;, \u0026#34;fsuse%\u0026#34;: \u0026#34;67%\u0026#34;, \u0026#34;mountpoints\u0026#34;: [ \u0026#34;/home\u0026#34;, \u0026#34;/\u0026#34; ] } ] } ] } ] } Заключение Команды lsof и lsblk предоставляют информацию об использовании файлов, сетевой активности и структурах блочных устройств. Независимо от того, отслеживаете ли вы открытые дескрипторы файлов, диагностируете ли сетевые соединения, просматриваете устройства хранения данных, устраняете ли вы неполадки, оптимизируете или просто любопытствуете эти инструменты предоставляют ценные данные, которые помогут вам лучше понять и управлять вашей средой Fedora Linux. Увидимся в следующий раз, когда мы рассмотрим более полезные инструменты командной строки для просмотра информации и как их использовать.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lsof-lsblk/","summary":"\u003cp\u003eВ нашей \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-dmidecode-i-lspci/\"\u003eпродолжающейся\u003c/a\u003e серии статей\nо системе Linux мы рассмотрим основные утилиты командной строки, которые предоставляют информацию об оборудовании и\nсостоянии системы. После наших предыдущих обсуждений \u003ccode\u003elscpu\u003c/code\u003e, \u003ccode\u003elsusb\u003c/code\u003e, \u003ccode\u003edmidecode\u003c/code\u003e и \u003ccode\u003elspci\u003c/code\u003e мы теперь обратим внимание\nна \u003ccode\u003elsof\u003c/code\u003e и \u003ccode\u003elsblk\u003c/code\u003e. Эти инструменты особенно полезны для поиска открытых файлов, активных сетевых подключений и\nсмонтированных блочных устройств в вашей системе Fedora Linux.\u003c/p\u003e","title":"Анализ системы с помощью инструментов командной строки: lsof и lsblk"},{"content":"Так как последнее время кое-кто начал совершать акты вандализма на сетях моего оператора связи, пришлось озаботиться защитой канала связи с контентными серверами всем известного сервиса хостинга видео - YouTube. В качестве провайдера у меня используется Дом.ру.\nСписок доменов Возможно список будет дополняться, но на текущий момент (09.08.2024) использую этот:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 googleapis.com googlevideo.com i.ytimg.com i1.ytimg.com i2.ytimg.com i3.ytimg.com i4.ytimg.com i5.ytimg.com i6.ytimg.com i7.ytimg.com i8.ytimg.com i9.ytimg.com yt3.ggpht.com yt3.googleusercontent.com yt4.ggpht.com yt4.googleusercontent.com gvt1.com gstatic.com youtubei.googleapis.com youtube-ui.l.google.com ytimg.l.google.com ytstatic.l.google.com Как видно это только *.googlevideo.com домен, то есть тот самый, с которого выкачиваются видео. Далее, если что-то будет меняться, будут добавлять домены тут.\nUPD 11.08.2024: добавил еще домены (youtube.googleapis.com, youtubeembeddedplayer.googleapis.com, youtubei.googleapis.com), так как были сильные зависания на мобильном телефоне.\nUPD 16.08.2024: сократил список доменов, с таким списком вроде работает сносно, без переключения на английский YouTube.\nНастройка OPNsense Так как домены могут со временем поменять свои конечные адреса (это одна из их заложенных из коробки функций), то правило нужно завести тип Хотст (-ы) (Hosts):\nТеперь нужно лишь прописать правило для lan (или других сетей) что бы трафик отправлялся туда, где чужие руки его не будут трогать.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/domennye-adresa-youtube-s-kontentom-dlya-marshrutizacii-v-opnsense/","summary":"\u003cp\u003eТак как последнее время \u003ca href=\"https://rkn.gov.ru/\"\u003eкое-кто\u003c/a\u003e начал\nсовершать \u003ca href=\"https://habr.com/ru/news/834792/\"\u003eакты вандализма на сетях моего оператора связи\u003c/a\u003e, пришлось озаботиться\nзащитой канала связи с контентными серверами всем известного сервиса хостинга\nвидео - \u003ca href=\"https://www.youtube.com/\"\u003eYouTube\u003c/a\u003e. В качестве провайдера у меня используется \u003ca href=\"https://dom.ru/\"\u003eДом.ру\u003c/a\u003e.\u003c/p\u003e","title":"Доменные адреса YouTube с контентом для маршрутизации в OPNsense"},{"content":"В нашей продолжающейся серии статей о системе Fedora Linux мы рассмотрим основные утилиты командной строки, которые предоставляют информацию об оборудовании и состоянии системы. После нашего предыдущего обсуждения lscpu и lsusb мы теперь обратим внимание на dmidecode и lspci.\ndmidecode - Расшифровка таблицы DMI dmidecode — это утилита командной строки для получения подробной информации об оборудовании системы. Она считывает таблицу DMI (Desktop Management Interface), содержащую данные, предоставленные прошивкой системы. Эти данные включают сведения о BIOS системы, процессоре, памяти и других аппаратных компонентах. Используя dmidecode, вы можете получить представление о конфигурации оборудования без необходимости присутствовать на месте или открывать корпус системы.\nПример использования Для начала давайте выполним базовую команду dmidecode, чтобы получить обзор таблицы DMI системы:\n1 $ sudo dmidecode Эта команда выводит полный список записей таблицы DMI, который может оказаться огромным.\nЧтобы сузить вывод до конкретной информации, можно использовать различные параметры, в частности, определяя тип с помощью -t число:\n1 $ sudo dmidecode -t number где число - целое число. Ниже приведен список доступных чисел из man dmidecode:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 [... output omitted for readability ...] The SMBIOS specification defines the following DMI types: Type Information ──────────────────────────────────────────── 0 BIOS 1 System 2 Baseboard 3 Chassis 4 Processor 5 Memory Controller 6 Memory Module 7 Cache 8 Port Connector 9 System Slots 10 On Board Devices 11 OEM Strings 12 System Configuration Options 13 BIOS Language 14 Group Associations 15 System Event Log 16 Physical Memory Array 17 Memory Device 18 32-bit Memory Error 19 Memory Array Mapped Address 20 Memory Device Mapped Address 21 Built-in Pointing Device 22 Portable Battery 23 System Reset 24 Hardware Security 25 System Power Controls 26 Voltage Probe 27 Cooling Device 28 Temperature Probe 29 Electrical Current Probe 30 Out-of-band Remote Access 31 Boot Integrity Services 32 System Boot 33 64-bit Memory Error 34 Management Device 35 Management Device Component 36 Management Device Threshold Data 37 Memory Channel 38 IPMI Device 39 Power Supply 40 Additional Information 41 Onboard Devices Extended Information 42 Management Controller Host Interface Additionally, type 126 is used for disabled entries and type 127 is an end-of-table marker. Types 128 to 255 are for OEM-specific data. dmidecode will display these entries by default, but it can only decode them when the vendors have contributed documentation or code for them. [... further output omitted for readability ...] Пример 1: Получение информации о BIOS Чтобы получить подробную информацию о BIOS, используйте опцию -t, за которой укажите номер типа информации BIOS ( введите 0):\n1 $ sudo dmidecode -t 0 Эта команда выводит такую информацию, как версия BIOS, дата его выпуска и поставщик BIOS. Пример вывода (здесь используется ThinkPad T480S):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 $ sudo dmidecode -t 0 # dmidecode 3.6 Getting SMBIOS data from sysfs. SMBIOS 3.0.0 present. Handle 0x000B, DMI type 0, 24 bytes BIOS Information Vendor: LENOVO Version: N22ET80W (1.57 ) Release Date: 02/27/2024 Address: 0xE0000 Runtime Size: 128 kB ROM Size: 16 MB Characteristics: PCI is supported PNP is supported BIOS is upgradeable BIOS shadowing is allowed Boot from CD is supported Selectable boot is supported EDD is supported 3.5\u0026#34;/720 kB floppy services are supported (int 13h) Print screen service is supported (int 5h) 8042 keyboard services are supported (int 9h) Serial services are supported (int 14h) Printer services are supported (int 17h) CGA/mono video services are supported (int 10h) ACPI is supported USB legacy is supported BIOS boot specification is supported Targeted content distribution is supported UEFI is supported BIOS Revision: 1.57 Firmware Revision: 1.23 Пример 2: Получение информации о материнской плате и памяти Для получения конкретных сведений о материнской плате вы можете запросить тип 2 и тип 17 для информации о памяти. Будут предоставлены сведения о модулях памяти, включая размер (объем), скорость и производителя. Эта информация особенно полезна при обновлении или устранении неполадок системной памяти или если вам нужно купить дополнительную совместимую оперативную память для сервера, которые были собраны не вами.\nРеальный пример небольшого лабораторного сервера с четырьмя планками памяти:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 $ sudo dmidecode -t 2,17 [... output omitted for readability ...] Manufacturer: Supermicro Product Name: X11SPL-F Version: 1.02 [... output omitted for readability ...] Handle 0x0029, DMI type 17, 84 bytes Memory Device Array Handle: 0x0025 Error Information Handle: Not Provided Total Width: 72 bits Data Width: 64 bits Size: 64 GB Form Factor: DIMM Set: None Locator: DIMMB1 Bank Locator: P0_Node0_Channel1_Dimm0 Type: DDR4 Type Detail: Synchronous Registered (Buffered) Speed: 2933 MT/s Manufacturer: Samsung Serial Number: 167D51E1 Asset Tag: DIMMB1_AssetTag (date:22/38) Part Number: M393A8G40MB2-CVF Rank: 2 Configured Memory Speed: 2400 MT/s Minimum Voltage: 1.2 V Maximum Voltage: 1.2 V Configured Voltage: 1.2 V Memory Technology: DRAM Memory Operating Mode Capability: Volatile memory Firmware Version: 0000 Module Manufacturer ID: Bank 1, Hex 0xCE Module Product ID: Unknown Memory Subsystem Controller Manufacturer ID: Unknown Memory Subsystem Controller Product ID: Unknown Non-Volatile Size: None Volatile Size: 64 GB Cache Size: None Logical Size: None $ sudo dmidecode -t 17 | grep -E \u0026#34;(Manufacturer|Part Number):\u0026#34; Manufacturer: Samsung Part Number: M393A8G40MB2-CVF Manufacturer: Samsung Part Number: M393A8G40MB2-CVF Manufacturer: Samsung Part Number: M393A8G40MB2-CVF Manufacturer: Samsung Part Number: M393A8G40MB2-CVF Используя эту простую команду, легко определить, какое оборудование используется. Эта информация особенно полезна, когда необходимо обновить или заменить оборудование.\nlspci - Список устройств PCI Команда lspci используется для вывода списка всех устройств PCI в системе. Устройства PCI (Peripheral Component Interconnect) включают в себя сетевые карты, видео карты, контроллеры USB и многое другое. Эта команда обеспечивает снимок списка устройств, подключенных к шине PCI системы, предлагая подробный обзор их конфигурации и состояния.\nlspci не требует расширенных привилегий, для определения полезной информации достаточно обычного пользователя.\nПример использования Простое выполнение команды lspci выводит список всех устройств PCI:\n1 $ lspci Для получения более подробной информации о конкретном устройстве вы можете использовать опцию -v (подробный вывод):\n1 $ lspci -v Пример 1: Поиск информации о видеокарте Чтобы найти подробную информацию о видеокарте, вы можете отфильтровать вывод lspci с помощью grep. Пример вывода ( здесь используется ThinkPad T480S):\n1 2 $ lspci | grep -i vga 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) Пример 2: Проверьте, какой драйвер ядра используется вашим оборудованием Чтобы узнать, какой драйвер ядра используется каждым устройством, можно использовать опцию -k. Это выводит список драйверов, используемых для устройства PCI, что может быть полезно для устранения неполадок, связанных с драйверами, особенно благодаря возможности поиска проблем в сети с использованием имени драйвера и модели вашего оборудования.\nПример вывода (здесь используется ThinkPad T480S):\n1 2 3 4 5 6 7 8 9 10 11 12 13 $ lspci -k [... output omitted for readability ...] 00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (rev 07) Subsystem: Lenovo Device 2258 Kernel driver in use: i915 Kernel modules: i915 [... output omitted for readability ...] 00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (4) I219-V (rev 21) Subsystem: Lenovo Device 2258 Kernel driver in use: e1000e Kernel modules: e1000e Как вы можете видеть, видеокарта использует драйвер drm/i915 Intel GFX Driver, а сетевая карта - e1000e.\nЗаключение Команды dmidecode и lspci — это мощные инструменты для извлечения подробной информации об оборудовании из системы Linux. Несмотря на свою простоту, обе команды предоставляют сведения о конфигурации и состоянии системы. Независимо от того, устраняете ли вы неполадки, оптимизируете или просто любопытствуете, эти инструменты предоставляют ценные данные, которые помогут вам лучше понять и управлять средой Linux. Увидимся в следующий раз, когда мы рассмотрим более полезные инструменты командной строки для листинга и информации и как их использовать.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-dmidecode-i-lspci/","summary":"\u003cp\u003eВ нашей \u003ca href=\"/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lscpu-i-lsusb/\"\u003eпродолжающейся\u003c/a\u003e серии статей о\nсистеме Fedora Linux мы рассмотрим основные утилиты командной строки, которые предоставляют информацию об оборудовании и\nсостоянии системы. После нашего предыдущего обсуждения \u003ccode\u003elscpu\u003c/code\u003e и \u003ccode\u003elsusb\u003c/code\u003e мы теперь обратим внимание на \u003ccode\u003edmidecode\u003c/code\u003e\nи \u003ccode\u003elspci\u003c/code\u003e.\u003c/p\u003e","title":"Анализ системы с помощью инструментов командной строки: dmidecode и lspci"},{"content":"На сайте fedoramagazine.org вышла серия статей о программах для получения информации о своей системе. Возможно кто-то узнает для себя что-то новое или найдет интересные флаги и/или возможности, которыми еще не пользовался. Первая статья будет об двух инструментах:\nlscpu - информация о центральном процессоре; lsusb - информация о подключенных USB устройствах. Введение Fedora (и другие распространенные системы Linux) предлагает вам набор инструментов для управления, мониторинга и понимания системы. Среди этих инструментов есть ряд команд, которые начинаются с ls (что означает «список»).\nОни обеспечивают легкое понимание различных аспектов аппаратного обеспечения и ресурсов системы. В этой серии статей вы найдете введение и обзор многих из них, начиная с самых простых. В этом посте будут рассмотрены lscpu и lsusb.\nlscpu - отображение информации о процессоре Команда lscpu собирает и отображает информацию об архитектуре центрального процессора. Она предоставляется пакетом util-linux. Команда собирает информацию о процессоре из нескольких источников, таких как /proc/cpuinfo и библиотек, зависящих от архитектуры (например, librtas на PowerPC):\n1 $ lscpu Эта команда выводит такую информацию как: количество процессоров, количество потоков на ядро, количество ядер на сокет, а также семейство и модель процессора.\nПо запросу она может выводить подробную информацию о процессоре в формате JSON. Это обеспечивает структурированное представление, которое особенно полезно для сценариев и автоматизации:\n1 $ lscpu --extended --json Расширенный пример использования Благодаря машиночитаемому выводу JSON вы можете извлекать информацию с помощью jq (мощного инструмента командной строки, который позволяет пользователям эффективно анализировать, фильтровать и манипулировать данными JSON и заслуживает отдельной статьи). Например, следующая команда извлечет текущие значения MHz для каждого ядра центрального процессора:\n1 2 3 4 export LANG=en_US.UTF-8 export LC_ALL=\u0026#34;en_US.UTF-8\u0026#34; lscpu --json --extended \\ | jq \u0026#39;.cpus[] | {cpu: .cpu, mhz: .mhz}\u0026#39; Давайте посмотрим на отдельные части команды:\nexport LANG=en_US.UTF-8 и export LC_ALL=\u0026quot;en_US.UTF-8\u0026quot; гарантируют, что в выводе не используются локализованные числа. Например, для немецкого или русского языка используется запятая вместо точек, в качестве разделителя целой и десятичной части чисел. Это приведет к нарушению формата JSON и ошибкам ввода программы jq (jq: parse error: Objects must consist of key:value pairs at line 10, column 29). lscpu --json --extended генерирует подробную информацию о процессоре в формате JSON. jq '.cpus[] | будет перебирать каждую запись из массива данных о центральном процессоре. {cpu: .cpu, mhz: .mhz}' создает новый объект JSON для каждого ядра процессора, отображающий порядковый номер ядра (cpu) и его текущую частоту в MHz (mhz). Пример вывода для ноутбука, работающего в режиме производительности:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ lscpu --json --extended \\ | jq \u0026#39;.cpus[] | {cpu: .cpu, mhz: .mhz}\u0026#39; { \u0026#34;cpu\u0026#34;: 0, \u0026#34;mhz\u0026#34;: 3700.0171 } { \u0026#34;cpu\u0026#34;: 1, \u0026#34;mhz\u0026#34;: 3700.2241 } { \u0026#34;cpu\u0026#34;: 2, \u0026#34;mhz\u0026#34;: 3700.1121 } { \u0026#34;cpu\u0026#34;: 3, \u0026#34;mhz\u0026#34;: 3884.2539 } А теперь в режиме энергосбережения:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ lscpu --json --extended \\ | jq \u0026#39;.cpus[] | {cpu: .cpu, mhz: .mhz}\u0026#39; { \u0026#34;cpu\u0026#34;: 0, \u0026#34;mhz\u0026#34;: 1200.0580 } { \u0026#34;cpu\u0026#34;: 1, \u0026#34;mhz\u0026#34;: 1200.0070 } { \u0026#34;cpu\u0026#34;: 2, \u0026#34;mhz\u0026#34;: 1200.5450 } { \u0026#34;cpu\u0026#34;: 3, \u0026#34;mhz\u0026#34;: 1200.0010 } lsusb - отображение информации об USB-устройствах Команда lsusb отображает подробную информацию о шинах USB в системе и подключенных к ним устройствах. Она предоставляется пакетом usbutils и помогает пользователям и системным администраторам легко просматривать конфигурацию и устройства, подключенные к их USB-интерфейсам:\n1 $ lsusb В результате выводится список всех шин USB, подключенных к ним устройств, а также краткая информация о каждом устройстве, например идентификатор и производитель. Это особенно полезно для быстрой проверки того, какие устройства подключены к системе. Так же полезно, если нужен идентификатор USB-устройства для правил udev или чего-то подобного.\nПример использования и вывод программы Для тех, кому нужна более подробная информация о USB-устройствах, lsusb позволяет вывести более подробную информацию:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 $ lsusb | grep Fibocom Bus 001 Device 013: ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem $ sudo lsusb -d 2cb7:0210 -v Bus 001 Device 013: ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 [unknown] bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x2cb7 Fibocom idProduct 0x0210 L830-EB-00 LTE WWAN Modem bcdDevice 3.33 iManufacturer 1 FIBOCOM iProduct 2 L830-EB-00 iSerial 3 004999010640000 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x00a1 bNumInterfaces 4 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xe0 Self Powered Remote Wakeup MaxPower 100mA [ more output omitted for readability ] Использование опций -v и -t укажет lsusb выгрузить иерархию физических USB-устройств в виде дерева, включая идентификаторы. Ниже показано подробное дерево всех USB-устройств (здесь используется ThinkPad T480S), их типы, скорости и классы устройств. Это особенно полезно для устранения неполадок с USB-устройствами:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 $ lsusb -t -v /: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/12p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub |__ Port 001: Dev 002, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M ID 046d:c069 Logitech, Inc. M-U0007 [Corded Mouse M500] |__ Port 002: Dev 003, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M ID 046a:c098 CHERRY |__ Port 002: Dev 003, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M ID 046a:c098 CHERRY |__ Port 003: Dev 004, If 0, Class=Chip/SmartCard, Driver=usbfs, 12M ID 058f:9540 Alcor Micro Corp. AU9540 Smartcard Reader |__ Port 005: Dev 005, If 0, Class=Video, Driver=uvcvideo, 480M ID 5986:2123 Bison Electronics Inc. |__ Port 005: Dev 005, If 1, Class=Video, Driver=uvcvideo, 480M ID 5986:2123 Bison Electronics Inc. |__ Port 006: Dev 013, If 0, Class=Communications, Driver=cdc_mbim, 480M ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem |__ Port 006: Dev 013, If 1, Class=CDC Data, Driver=cdc_mbim, 480M ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem |__ Port 006: Dev 013, If 2, Class=Communications, Driver=cdc_acm, 480M ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem |__ Port 006: Dev 013, If 3, Class=CDC Data, Driver=cdc_acm, 480M ID 2cb7:0210 Fibocom L830-EB-00 LTE WWAN Modem |__ Port 007: Dev 007, If 0, Class=Wireless, Driver=btusb, 12M ID 8087:0a2b Intel Corp. Bluetooth wireless interface |__ Port 007: Dev 007, If 1, Class=Wireless, Driver=btusb, 12M ID 8087:0a2b Intel Corp. Bluetooth wireless interface |__ Port 008: Dev 008, If 0, Class=Video, Driver=uvcvideo, 480M ID 5986:2115 Bison Electronics Inc. |__ Port 008: Dev 008, If 1, Class=Video, Driver=uvcvideo, 480M ID 5986:2115 Bison Electronics Inc. |__ Port 009: Dev 009, If 0, Class=Vendor Specific Class, Driver=[none], 12M ID 06cb:009a Synaptics, Inc. Metallica MIS Touch Fingerprint Reader /: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/6p, 5000M ID 1d6b:0003 Linux Foundation 3.0 root hub |__ Port 003: Dev 002, If 0, Class=Mass Storage, Driver=usb-storage, 5000M ID 0bda:0316 Realtek Semiconductor Corp. Card Reader /: Bus 003.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/2p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub |__ Port 001: Dev 002, If 0, Class=Hub, Driver=hub/5p, 480M ID 0451:8442 Texas Instruments, Inc. |__ Port 001: Dev 003, If 0, Class=Hub, Driver=hub/7p, 480M ID 0424:2137 Microchip Technology, Inc. (formerly SMSC) |__ Port 003: Dev 005, If 0, Class=Hub, Driver=hub/3p, 480M ID 0bda:5411 Realtek Semiconductor Corp. RTS5411 Hub |__ Port 001: Dev 006, If 0, Class=Vendor Specific Class, Driver=r8152, 480M ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter |__ Port 004: Dev 004, If 0, Class=Human Interface Device, Driver=usbhid, 480M ID 0451:82ff Texas Instruments, Inc. /: Bus 004.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/2p, 10000M ID 1d6b:0003 Linux Foundation 3.0 root hub Заключение Несмотря на свою простоту, обе команды дают представление о конфигурации и состоянии системы. Независимо от того, занимаетесь ли вы устранением неполадок, оптимизацией или просто проявляете любопытство, эти инструменты предоставляют ценные данные, которые помогут вам лучше понимать и управлять вашим окружением Linux. Увидимся в следующий раз, когда мы рассмотрим более полезные инструменты командной строки для получения списков и информации, а также способы их использования.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/analiz-sistemy-s-pomoshyu-instrumentov-komandnoj-stroki-lscpu-i-lsusb/","summary":"\u003cp\u003eНа сайте \u003ca href=\"https://fedoramagazine.org/\"\u003efedoramagazine.org\u003c/a\u003e вышла серия статей о программах для получения информации о\nсвоей системе. Возможно кто-то узнает для себя что-то новое или найдет интересные флаги и/или возможности, которыми еще\nне пользовался. Первая статья будет об двух инструментах:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003elscpu\u003c/code\u003e - информация о центральном процессоре;\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elsusb\u003c/code\u003e - информация о подключенных USB устройствах.\u003c/li\u003e\n\u003c/ul\u003e","title":"Анализ системы с помощью инструментов командной строки: lscpu и lsusb"},{"content":"Нужно настроить резервное копирование для OpenWrt роутера. Расскажу как это сделано у меня на момент написания этой заметки.\nТочка доступа Есть китайский роутер (китайский он аж до формы вилки) Xiaomi Redmi AX6. С недавних пор на него стала доступна официальная прошивка OpenWrt 23.05. До этого была не официальная бета прошивка, бекап для которой еще более актуален.\nСкрипт для OpenWrt Основной скрипт простой:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #!/usr/bin/env zsh BACKUP_PATH=\u0026#34;$HOME/backup-sync\u0026#34; backup_ax6 () { HOST=\u0026#34;ax6.home\u0026#34; SSH_CONNECT=\u0026#34;root@$HOST\u0026#34; FILE=\u0026#34;/tmp/backup-$HOST-$(date +%F_%H-%M-%S).tar.gz\u0026#34; ssh $SSH_CONNECT sysupgrade -b \u0026#34;$FILE\u0026#34; scp -O $SSH_CONNECT:$FILE $BACKUP_PATH ssh $SSH_CONNECT rm $FILE echo \u0026#34;ax6 backup finish\u0026#34; } backup_ax6 clear_old () { find $BACKUP_PATH -maxdepth 1 -type f -mtime +30 -delete echo \u0026#34;old backup clean finish\u0026#34; } clear_old Тут мы указываем:\nBACKUP_PATH - путь, куда складывать бекап (на текущей машине это syncthing папка); HOST - домен (или адрес) точки доступа. Имя файла с бекапом будет состоять из названия хоста и даты-времени создания бекапа. Так же есть маленький вспомогательный скрипт, который удаляет все бекапы старше 30 дней. Теперь запустим этот скрипт можно сделать бекап, скачать его, удалить старые бекапы.\nАвтоматизация запуска На текущий момент этот процесс происходит с рабочей станции. Тут есть несколько причин почему, но основные две:\nвключаю я рабочую станцию чаще, чем меняю настройки точки доступа (обычно с нее и меняю); на момент настройки бекапа не было ничего кроме точки доступа и рабочей станции. Из-за этих особенностей пришлось дописать еще небольшую обвязку поверх systemd и его таймеров. Сервис, который запускает бекап, выглядит так:\n1 2 3 4 5 6 7 8 9 10 [Unit] Description=Backup ax6.home config Wants=ax6-backup.timer [Service] Type=oneshot ExecStart=/usr/bin/zsh /home/user/.local/bin/ax6-backup [Install] WantedBy=default.target Тут указано, что это \u0026ldquo;одноразовый\u0026rdquo; скрипт и путь к его запуску. Так же для его запуска имеется таймер:\n1 2 3 4 5 6 7 8 9 10 11 [Unit] Description=Backup ax6.home config timer Requires=ax6-backup.timer [Timer] Unit=iot-backup.service OnBootSec=15min OnUnitActiveSec=1d [Install] WantedBy=timers.target В таймер срабатывает через 15 минут после загрузки системы и далее раз в сутки в это время, если компьютер не выключался.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/bekap-konfiguracii-openwrt-routera/","summary":"\u003cp\u003eНужно настроить резервное копирование для \u003ca href=\"https://openwrt.org/start\"\u003eOpenWrt\u003c/a\u003e роутера. Расскажу как это сделано у меня\nна момент написания этой заметки.\u003c/p\u003e","title":"Бекап конфигурации OpenWrt роутера"},{"content":"Есть скрипт на php, и он куда-то стучится. И вот появляется \u0026ldquo;плавающий баг\u0026rdquo;, когда скрипт иногда зависает и больше ничего не происходит. Нет ни нагрузки на процессор, ни на диск, но время выполнения уже \u0026ldquo;давно пройдено\u0026rdquo;.\nПолучаем PID Для начала было бы хорошо глянуть \u0026ldquo;в процесс\u0026rdquo; скрипта, что бы увидеть что там происходит. Для этого через удобный инструмент нужно получить PID. На примере ps:\n1 2 3 4 ps -A -o pid,user,time,cmd | grep \u0026#34;crm\\|PID\u0026#34; PID USER TIME CMD 144275 www-data 00:01:54 /usr/local/bin/php -f /path/to/crm/script.php 148340 www-data 00:00:00 grep crm\\|PID Первый столбец это и есть нужный нам PID: 144275.\nAlpine linux Так получается, что для Alpine linux ps это всего лишь ссылка на busybox:\n1 2 $ ls -al $(which ps) lrwxrwxrwx 1 root root 12 Jun 27 22:42 /bin/ps -\u0026gt; /bin/busybox Установить полноценный ps можно так:\n1 # apk add procps Если нет возможности установить его, то модифицируем команду с поиском (а точнее упростим по сравнению с ps):\n1 2 3 4 $ ps | grep \u0026#34;crm\\|PID\u0026#34; PID USER TIME COMMAND 144275 www-data 1:54 /usr/local/bin/php -f /path/to/crm/script.php 148291 www-data 0:00 grep crm\\|PID Как видим PID тут точно такой же как и при использовании полноценного ps-а: 144275.\nПроцесс выполнения скрипта Что бы понять действительно ли он завис, или просто выполняет долгую операцию достаточно выполнить strace по полученному ранее PID-у:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # strace -p 144275 strace: Process 144275 attached restart_syscall(\u0026lt;... resuming interrupted read ...\u0026gt;) = 0 rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout) rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000) = 0 (Timeout) rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, 8) = 0 rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f1b668ceea8}, NULL, 8) = 0 poll([{fd=11, events=POLLIN}, {fd=9, events=POLLIN}], 2, 1000^Cstrace: Process 144275 detached \u0026lt;detached ...\u0026gt; Судя по зацикленному выводу poll, rt_sigaction, poll, rt_sigaction, rt_sigaction, poll, ... скрипт повис. Повис он на попытке выполнить poll каких-то данных. В вызове мы видим и идентификатор файлового дескриптора (fd), из которого и происходит попытка получения данных.\nИщем файловый дескриптор Теперь у нас есть PID (144275) и номер файлового дескриптора (11). Узнать что же за ним скрывается можно через lsof с помощью такой команды:\n1 2 3 $ lsof -a -d 11 -p 144275 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME php 144275 www-data 11u IPv4 718693431 0t0 TCP eec4ba68ed28:33780-\u0026gt;84.252.130.113:https (ESTABLISHED) Тут мы видим, что это TCP соединение со сторонним сервером. По ip адресу можно более менее понять куда происходит соединение и копать в эту сторону.\nAlpine linux Как и с ps, lsof тут по умолчанию ведет на busybox:\n1 2 $ ls -al $(which lsof) lrwxrwxrwx 1 root root 12 Jun 27 22:23 /usr/bin/lsof -\u0026gt; /bin/busybox Установить полноценный lsof можно так:\n1 # apk add lsof Если по какой-то причине установить не представляется возможным, то придется немного \u0026ldquo;модифицировать\u0026rdquo; запрос lsof. Сам он не отфильтрует результат (да и значения будут \u0026ldquo;сырыми\u0026rdquo;):\n1 2 3 $ lsof -a -d 11 -p 144275 | grep 11 144275\t/usr/local/bin/php\t8\t/tmp/13d4d7db110bc477e837391bb31997cb 144275\t/usr/local/bin/php\t11\tsocket:[718693431] Тут номер дескриптора идет в третьем столбце. Значит нас интересует socket:[718693431], в обычном выводе это столбец DEVICE. Тут же придется поискать устройство \u0026ldquo;руками\u0026rdquo;. Ищем в /proc/net/tcp (благо мы уже знаем, что это tcp соединение):\n1 2 3 $ grep \u0026#34;718693431\\|inode\u0026#34; /proc/net/tcp sl local_address rem_address st tx_queue rx_queue tr tm-\u0026gt;when retrnsmt uid timeout inode 17: 0453E30A:83F4 7182FC54:01BB 01 00000000:00000000 00:00000000 00000000 101 0 718693431 1 0000000000000000 20 4 10 10 -1 Интересный момент, но адреса и порты тут записаны в шестнадцатеричном формате. Сам ip адрес отображен задом-на-перед. То есть декодируем:\nАдреса tcp соединения local_address rem_address Начальное значение 0453E30A:83F4 7182FC54:01BB Добавляем точки 04.53.E3.0A:83F4 71.82.FC.54:01BB Переводим в десятичную систему счисления 4.83.227.10:33780 113.130.252.84:443 Отображаем ip адрес 10.227.83.4:33780 84.252.130.113:443 Получается, повисшее соединение: 10.227.83.4:33780-\u0026gt;84.252.130.113:443, что соответствует выводу полноценного lsof.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/poisk-zavisshego-zaprosa-na-vneshnij-adres-iz-php-skripta/","summary":"\u003cp\u003eЕсть скрипт на php, и он куда-то стучится. И вот появляется \u0026ldquo;плавающий баг\u0026rdquo;, когда скрипт иногда зависает и больше\nничего не происходит. Нет ни нагрузки на процессор, ни на диск, но время выполнения уже \u0026ldquo;давно пройдено\u0026rdquo;.\u003c/p\u003e","title":"Поиск зависшего запроса на внешний адрес из php скрипта"},{"content":"На очень большом и тяжелом сайте столкнулся с просадкой производительности, при запуске какой-либо рекламной компании. Вроде бы это нормальная история, но в один прекрасный день случилось что-то странное. Не с того ни с сего сайт на Bitrix стал ощутимо тормозить. Что ж, капнем.\nНачало истории Как оказалось по мониторингу начала захлебываться БД. При этом количество запросов, идущих к сайту было в статистической норме для этого времени дня. Покопавшись в запросах увидел что запрашиваются один и те же страницы, но с добавлением странных utm меток. Тут тоже вопросов бы не возникло, мало ли откуда идет трафик, мало ли кто решил отследить свою компанию.\nПроблема заключалась в том, что эти несколько страниц являются очень тяжелыми (сотни запросов в MySQL), но в то же время совершенно не зависела от GET параметров запроса, в расчет брался только путь страницы. Эта проблема должна была решиться тем, что после первого захода страница (а точнее компоненты на ней) кешировались. После этого количество запросов падало менее десяти (при этом первые два были стандартной настройкой соединения Bitrix).\nПроверка Что бы абстрагироваться от конкретного сайта подниму стенд с битриксом. Для тестов установлю стандартный интернет магазин. Он конечно небольшой (не более 20к товаров с сотнями свойств и разделов) и пользоваться им будут я один (и он не будет постоянно находиться под около 300rps только php запросов). Но зато можно в любой момент поднять \u0026ldquo;такой же\u0026rdquo; и посмотреть \u0026ldquo;своими руками\u0026rdquo;.\nИтак, включим статистику SQL через параметр show_sql_stat=Y в url (для этого нужно авторизоваться и получить доступ к функционалу).\nСписок товаров Запрашиваем первый раз страницу: 246 запроса и 0.1902 времени генерации.\nИ тут же повторяем запрос, что бы поглядеть на кешированную версию: 32 запроса и 0.0781 секунды генерации. Страница реально почти полностью получена из кеша и база может отдохнуть.\nНо что если добавить какой-либо get параметр. А тут мы получаем очень странные значения: 133 запроса и 0.1433 секунды. Это почти в 4 раза больше, чем на странице с кешем.\nВидимо не все компоненты на странице сбросили кеш (или промахнулись).\nДетальная карточка товара Тут ничего удивительного, нет кеша: 282 запроса и 0.3329 секунды на страницу. После этого запросим страницу еще раз, теперь то она должна отдаваться из кеша.\nКак ни странно, но да, из кеша: 30 запросов и 0.0765 секунды на генерацию страницы.\nНо если мы опять добавим какой-то get параметр (например asd=asd), то наша страница опять будет частично \u0026ldquo;собрана с нуля\u0026rdquo;. Мы получаем 185 запросов и 0.1722 секунды на генерацию страницы.\nВ этом случае такая же история, какие-то компоненты просто промахнулись мимо кеша и выполнились заново.\nОбъяснение Кеш компонента в Bitrix ВСЕГДА полагается на свой уникальный идентификатор (md5 строка). Именно так компонент понимает и сравнивает вызовы себя. Сам этот расчет происходит еще в старой реализации \\CBitrixComponent::getCacheID. Тут собирается длинная строка, в которую добавляются все зависимости кеша. Внутри кеш будет зависеть от:\nидентификатора текущего сайта; идентификатора текущего языка; идентификатора текущего шаблона; названия компонента; шаблона компонента; списка параметров компонента (учитываются и значения и порядок этих параметров); смещения часового пояса; дополнительных параметров компонента; порядкового номера вызова компонента с данным именем. По сути ни про какие get параметры тут не упоминается. Разве что разработчики запихнули их в виде дополнительных параметров кеша ($additionalCacheID). Но оказывается нет, если начать разбирать строку кеши для компонентов, то станет видно, что при изменении набора и значений get параметров - уникальная строка кеширования тоже меняется.\nРаскопав все хитросплетения формирования строки кеша на примере этих двух страниц, был найден инициатор этого праздника жизни \\Bitrix\\Iblock\\Component\\Base. Оказывается в нем, есть маленький кусочек кода, который и дробит кеш компонента под все возможные get параметры. Это переопределенная функция \\Bitrix\\Iblock\\Component\\Base::onPrepareComponentParams, а точнее ее часть:\n1 2 3 4 5 6 7 8 9 10 public function onPrepareComponentParams($params) { /** ... */ if (!isset($params[\u0026#39;CURRENT_BASE_PAGE\u0026#39;])) { $uri = new Main\\Web\\Uri($this-\u0026gt;request-\u0026gt;getRequestUri()); $uri-\u0026gt;deleteParams(Main\\HttpRequest::getSystemParameters()); $params[\u0026#39;CURRENT_BASE_PAGE\u0026#39;] = $uri-\u0026gt;getUri(); } /** ... */ } То есть, если мы не задаем параметр компонента CURRENT_BASE_PAGE, то компонент сам берет полный текущий url включая все get параметры, кроме \u0026ldquo;системных\u0026rdquo; и зашитых внутри \\Bitrix\\Main\\HttpRequest::getSystemParameters:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static function getSystemParameters() { static $params = array( \u0026#34;login\u0026#34;, \u0026#34;login_form\u0026#34;, \u0026#34;logout\u0026#34;, \u0026#34;register\u0026#34;, \u0026#34;forgot_password\u0026#34;, \u0026#34;change_password\u0026#34;, \u0026#34;confirm_registration\u0026#34;, \u0026#34;confirm_code\u0026#34;, \u0026#34;confirm_user_id\u0026#34;, \u0026#34;bitrix_include_areas\u0026#34;, \u0026#34;clear_cache\u0026#34;, \u0026#34;show_page_exec_time\u0026#34;, \u0026#34;show_include_exec_time\u0026#34;, \u0026#34;show_sql_stat\u0026#34;, \u0026#34;show_cache_stat\u0026#34;, \u0026#34;show_link_stat\u0026#34;, \u0026#34;sessid\u0026#34;, ); return $params; } Цепочка получается такая:\nпараметр CURRENT_BASE_PAGE не указан (а его и нет в настройках компонента или его упоминания на сайте); параметр заполняется полным урлом со всеми гет параметрам, которые окажутся в url; параметр CURRENT_BASE_PAGE учитывается при расчете использования уже готового кеша. Особенность параметра Так же этот параметр имеет особенность и очень важную. Если в компоненте не установить параметру PAGER_BASE_LINK_ENABLE значение Y, то именно строка из CURRENT_BASE_PAGE будет использована как основной путь для построения url пагинации. То есть получается, что в CURRENT_BASE_PAGE нельзя задать просто задать свое значение. Ведь если указать просто строку, скажем Y, то именно от нее будет строиться пагинация, на сайте появятся не рабочие ссылки вида Y?PAGEN_1=2. Если же туда вписать путь страницы без get параметров, то и в ссылки пагинации они тоже пропадут. То есть следует всегда иметь в виду, при ручном указании CURRENT_BASE_PAGE обязательно еще включить PAGER_BASE_LINK_ENABLE.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/osobennosti-keshirovaniya-komponentov-catalog-v-bitrix/","summary":"\u003cp\u003eНа очень большом и тяжелом сайте столкнулся с просадкой производительности, при запуске какой-либо рекламной компании.\nВроде бы это нормальная история, но в один прекрасный день случилось что-то странное. Не с того ни с сего сайт на\n\u003ca href=\"https://www.1c-bitrix.ru/\"\u003eBitrix\u003c/a\u003e стал ощутимо тормозить. Что ж, капнем.\u003c/p\u003e","title":"Особенности кеширования компонентов catalog в Bitrix"},{"content":"Пример маленького конфиг файла для Nginx что бы не прописывать и не закидывать отдельные robots.txt файлы Запрещающие индексацию.\nПример такого файла (запрет индексации всего контента):\n1 2 User-agent: * Disallow: / Переводя его в виде конфига для Nginx получаем файл noindex.conf:\n1 2 3 4 5 6 7 location = /robots.txt { try_files $uri @noindex; } location @noindex { return 200 \u0026#34;User-agent: *\\nDisallow: /\\n\u0026#34;; } Теперь это файл можно закидывать в блок server и если среди статики, которую обрабатывает сервер нет robots.txt файла, то будет отдан запрещающий индексацию файл.\nЕсли Nginx выступает только как реверс-прокси, то локальных файлов не будет ни у какого домена и этот конфиг превратиться в более простой:\n1 2 3 location = /robots.txt { return 200 \u0026#34;User-agent: *\\nDisallow: /\\n\u0026#34;; } Опять же, можно держать два конфига. Тогда в зависимости от сайта использовать полный запрет или запрет до появления осмысленного robots.txt.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/nginx-i-robotstxt-dlya-privatnyh-domenov/","summary":"\u003cp\u003eПример маленького конфиг файла для \u003ca href=\"https://nginx.org/\"\u003eNginx\u003c/a\u003e что бы не прописывать и не закидывать\nотдельные \u003ca href=\"https://www.robotstxt.org/robotstxt.html\"\u003erobots.txt файлы\u003c/a\u003e Запрещающие индексацию.\u003c/p\u003e","title":"Nginx и robots.txt для приватных доменов"},{"content":"Столкнулся с несколькими неприятными моментами, после подключения usb модема Yota (на прошивке D07C_Yota_V046). Судя по информации из интернета можем полностью именуется YOTA 4G LTE WLTUBQ-108. Так как в бумажечке с модемом этого нет, то напишу на память. Вдруг кому-то это тоже пригодится. Сам адрес с настройками указан, но не прописана одна из важнейших функций.\nНеприятные моменты Основных моментов, с которыми я столкнулся два (два с половиной):\nне работал WireGuard через модем; не работал DNS через модем; 10.0.0.1/8 выдает модем по DHCP (это та самая половину проблемы). Первая проблема вполне объяснима, так как я взял модем как резервный источник интернета (Multi-WAN Failover Opnsense). Получается, что в ситуации с падением основного канала нужно все равно идти в админку Opnsense и выключать принудительную отправку трафика в vpn.\nВторая проблема связана с тем, что локальный pihole настроен на вполне определенные DNS сервера. В момент отключения основного канала просто не может достучаться никуда. Все запросы на DNS просто срезаются на уровне модема. Можно конечно перенастроить (сделать перехват DNS и перенаправление в модем), но это не очень удобно. Да и пользоваться DNS Yota мне не хочется.\nТретий пункт, не то что бы прям минус, все равно эта подсеть раздается только на интерфейс Opnsense и дальше не идет. Так же Opnsense довольно-таки умен, что бы локальные подсети не направлять в модем. Но очень хотелось бы взять другой диапазон.\nДополнительные настройки Кроме основной страницы статуса модема:\nЕсть страницы дополнительных настроек по пути http://status.yota.ru/advanced. Для доступа нужно воспользоваться DNS сервером модема, либо посмотреть gateway для интерфейса с модемом и перейти просто по ip. У меня по умолчанию это был адрес http://10.0.0.1/advanced. Так же можно настроить доступ на нее прямо из сети Opnsense, для этого понадобиться добавить правило для сети, которая должны иметь доступ:\nДа, все в лан сети, по сути стоит иметь отдельную сеть \u0026ldquo;to rule them all\u0026rdquo;, но пока так.\nПосле перехода откроется страница:\nDHCP Тут можно выбрать ip модема, который будет влиять на диапазон DHCP (указываю адрес шлюза и первый по порядку адрес DHCP):\nНастройка Шлюз Адрес клиента 10.0.0.0 10.0.0.1/8 10.0.0.100/8 172.16.0.0 172.16.0.1/16 172.16.0.100/16 192.168.0.0 192.168.0.1/24 192.168.0.100/24 DMZ Основная нужная настройка это DMZ. Его пришлось выключить. Видимо в модеме DMZ направлен строго на 172.16.0.100/16 ( для это сети), но интерфейс в Opnsense получается адрес то 172.16.0.104/16, то 172.16.0.102/16 (не заметил закономерности). В таких условиях оказалось проще всего просто выключить DMZ и настроить как полагается фаервол.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/dopolnitelnye-nastrojki-modema-yota/","summary":"\u003cp\u003eСтолкнулся с несколькими неприятными моментами, после подключения usb модема Yota (на прошивке \u003ccode\u003eD07C_Yota_V046\u003c/code\u003e). Судя\nпо информации из интернета можем полностью именуется \u003cstrong\u003eYOTA 4G LTE WLTUBQ-108\u003c/strong\u003e. Так как в бумажечке с модемом этого\nнет, то напишу на память. Вдруг кому-то это тоже пригодится. Сам адрес с настройками указан, но не прописана одна из\nважнейших функций.\u003c/p\u003e","title":"Дополнительные настройки модема Yota 4G LTE WLTUBQ-108"},{"content":"Сегодня для многих день начался с проблем загрузки образов из регистра docker hub. Причина написана на странице, которую отдает регистр при попытке открыть его или запросить какой-либо образ.\nЧто случилось Докер хаб больше не обслуживает клиентов из России, судя по всему разбор \u0026ldquo;откуда пришел клиент\u0026rdquo; идет по GeoIP. При попытке открыть главную страницу получаем такое сообщение:\nТот же самый ответ показывается при попытке стянуть како-нибудь образ через консольную утилиту:\n1 2 3 ❯ docker pull docker Using default tag: latest Error response from daemon: error parsing HTTP 403 response body: invalid character \u0026#39;\u0026lt;\u0026#39; looking for beginning of value: \u0026#34;\u0026lt;html\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;403 Forbidden\u0026lt;/h1\u0026gt;\\nSince Docker is a US company, we must comply with US export control regulations. In an effort to comply with these, we now block all IP addresses that are located in Cuba, Iran, North Korea, Republic of Crimea, Sudan, and Syria. If you are not in one of these cities, countries, or regions and are blocked, please reach out to https://hub.docker.com/support/contact/\\n\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\\n\u0026#34; Решение через VPN Так как дома у меня есть несколько виртуальных серверов, которым, как и основному компьютеру, нужен доступ к хабу (не считая еще и ноутбука), то проще завернуть все запросы к хабу в VPN с выходным ip страны не из списка. Есть два основных домена которые нужно проверить registry-1.docker.io для получения и загрузки образов. Так же hub.docker.com для работы интерфейсом регистра. Каждый из них сейчас ссылается на три адреса внутри облака amazon.\nРешение через mirror Так же можно получить доступ через зеркало хаба. Для этого нужно указать его в настройках докера. Файл настроек лежит по следующим путям (взято из документации):\nОперационная система и тип Путь до файла настроек Linux (основной) /etc/docker/daemon.json Linux (пользовательский) ~/.config/docker/daemon.json Windows C:\\ProgramData\\docker\\config\\daemon.json Указывая тут путь ~/.config подразумевается, что внутри $XDG_CONFIG_HOME лежит именно такой путь. К корневому объекту нужно добавить свойство registry-mirrors, значение для которого будет массив ссылок на зеркала. Например:\n1 2 3 4 5 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://mirror.gcr.io\u0026#34; ] } Если же будет использоваться регистр без https, то обязательно нужно будет указать параметр insecure-registries, в котором перечислить регистры с доступом без https:\n1 2 3 4 5 6 7 8 9 { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://mirror.gcr.io\u0026#34;, \u0026#34;http://registy.lab.home:5006\u0026#34; ], \u0026#34;insecure-registries\u0026#34;: [ \u0026#34;registy.lab.home:5006\u0026#34; ] } Так же после всех этих изменений нужно либо перезапустить docker, либо (что лучше) выполнить reload конфигурации.\nПараметр registry-mirrors на работает У меня после указания такого параметра и перезапуска зеркала не заработали. При поиске проблем обнаружил две причины:\nочень старый клиент, нужно обязательно указывать параметр запуска /usr/bin/dockerd --registry-mirror https://mirror.gcr.io; до установки параметров была выполнена авторизация, и теперь нужно разлогиниться из всех регистров (docker logout hub.docker.com) и заново авторизоваться. У меня случилась именно вторая проблема, видимо это способ не позволить \u0026ldquo;утечь\u0026rdquo; токену (или паролю?) в сторону зеркала.\nСписок зеркал (актуален на момент публикации) Url зеркала Дополнительная информация https://mirror.gcr.io Зеркало от Google Cloud https://quay.io Зеркало от Red Hat https://dockerhub.timeweb.cloud Зеркало от Timeweb Cloud https://daocloud.io Какое-то зеркало из Китая https://c.163.com Какое-то зеркало из Китая https://registry.docker-cn.com Какое-то зеркало из Китая Важный совет по зеркалам! Так как зеркало ВСЕГДА контролируется третьими лицами (даже если это известная компания), нужно ВСЕГДА сравнивать sha256 слепки образов на hub.docker.com и тех, что скачаны от стороннего регистра.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/nastrojka-zerkal-dlya-docker-hub/","summary":"\u003cp\u003eСегодня для многих день начался с проблем загрузки образов из регистра \u003ca href=\"https://hub.docker.com/\"\u003edocker hub\u003c/a\u003e. Причина\nнаписана на странице, которую отдает регистр при попытке открыть его или запросить какой-либо образ.\u003c/p\u003e","title":"Настройка зеркал для docker hub"},{"content":"В ожидание технических работ у моего провайдера (а обещают их с переменным успехом с 9 утра до 19 вечера) было решено сделать домашний интернет более устойчивым к подобным событиям. Работы будут в будний день, в промежутке времени интернет может отваливать на неопределенное время несколько раз.\nИсходные данные Есть граничный маршрутизатор (по совместительству фаервол), и ему доступны два интерфейса с двумя провайдерами. На одном дом.ру, на втором Yota. Оба провайдера выдают интернет по DHTP, оба оказываются в Opnsense уже в виде 10-гигабитных виртуальных интерфейсов. То есть внешней настройкой и доступом портов внутри Opnsense тут заниматься не будем.\nПочему именно Yota, тут несколько причин, но в основном из-за отсутствия всяких дополнительных сервисов как таковых и возможность включить такой тариф:\nДа, это позволяет не платить за интернет, пока он не понадобится. И в моменте заплатить за сутки или двое.\nОпишу тут только настройку DHCP на интерфейсах (на примере Yota, Интерфейсы -\u0026gt; Назначения портов):\nНа странице Интерфейсы проверяем что бы наш первый и второй интернет интерфейсы были назначены в качестве устройств. В устройстве нужно включить DHCP для протокола IPv4 (да, ни первый ни второй провайдер не дает IPv6 здесь и сейчас).\nТеперь в статусах интерфейсов можно глянуть, что бы каждому из них выдался внутренний IP адрес от провайдера и двигаться дальше.\nНастройка Gateway (сетевых шлюзов) Теперь настраивается несколько gateway (сетевых шлюзов) для каждого из подключений (Система -\u0026gt; Шлюзы -\u0026gt; Конфигурация).\nТут основные моменты такие:\nне ставить галочку \u0026ldquo;Основной шлюз\u0026rdquo;, так как у нас будет группы шлюзов; не ставить галочку \u0026ldquo;Отключите Мониторинг шлюзов\u0026rdquo;, иначе будет не понятно когда шлюз \u0026ldquo;упал\u0026rdquo;; указать ip для мониторинга из интернета, по умолчанию для мониторинга будет использован адрес из DHCP, и то что он доступен не всегда означает, что доступен интернет; В расширенном режиме можно \u0026ldquo;тонко\u0026rdquo; настроить параметры сигнализации падения шлюза:\nИз двух (а может быть и более) gateway-ев собираем группу (Система -\u0026gt; Шлюзы -\u0026gt; Группы):\nТут нужно выставить правильные тиры для подключений. Чем первее тир, тем приоритетнее использование gateway. У меня на скриншоте сначала используется WAN_DHCP (дом.ру), потом YOTA_DHCP (Yota). Остальные gateway-и не используются. Так же стоит обратить внимание на параметр \u0026ldquo;Уровень срабатывания\u0026rdquo;. Настройки задержек и потерь пакетов устанавливаются для каждого gateway отдельно (в блоке дополнительные настройки).\nТеперь нужно поправить конфиг для доступа в интернет из lan (и остальные правила фаервола). Если в правилах не настроен какой-то конкретный gateway, то будет использоваться по умолчанию. Значит настраивать отдельно ничего не нужно. Так же стоит проверить все правила, которые полагаются на интерфейс, ведь при переключении gateway сменится интерфейс. У меня это правило, которое не позволяет трафику для VPN выходить в интерфейс wan, пришлось добавить в правило интерфейс yota.\nSticky Connections Это может быть важным, так как часть сайтов \u0026ldquo;не любят\u0026rdquo;, когда у пользователя скачет ip адрес. Поэтому в настройках системы (Межсетевой экран -\u0026gt; Настройки -\u0026gt; Дополнительно) стоит включить Фиксированные соединения. Так при пограничном случае, когда gateway-и первого тира то и дело уходят в офлайн (отключаются, теряют пакеты или прыгает пинг, в зависимости от настроек переключения) соединения будут \u0026ldquo;прилипать\u0026rdquo; ко второму тиру. Если в какой-то системе использует отслеживание ip адреса для пользователей, то не придется при каждом переключении заново логиниться.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/rezervirovanie-kanala-na-opnsense-multi-wan/","summary":"\u003cp\u003eВ ожидание технических работ у моего провайдера (а обещают их с переменным успехом с 9 утра до 19 вечера) было решено\nсделать домашний интернет более устойчивым к подобным событиям. Работы будут в будний день, в промежутке времени\nинтернет может отваливать на неопределенное время несколько раз.\u003c/p\u003e","title":"Резервирование канала на Opnsense multi WAN"},{"content":"Появилась необходимость создать запасной канал связи в интернет. Тех работы у оператора длинной в 12 часов совпадают с рабочим временем. Вариантов для частного дома (на территории СНТ) не так что бы много. Самым простым выглядит usb \u0026ldquo;свисток\u0026rdquo; от мобильного оператора. Выбор мобильного оператора остается за рамками. Нужно получить доступ в интернет в момент \u0026ldquo;отвала\u0026rdquo; основного провайдера.\nИсходные данные Итого имеет такую ситуацию:\nProxmox 8.2.2; сервер (внутри кластера, как основа виртуализации); Opnsense 24.1.7; (как фаервол и граничный маршрутизатор); модем Yota. (как источник интернета \u0026ldquo;по воздуху\u0026rdquo;). Выглядит все это так: есть кластер из нескольких (сейчас двух) сервером Proxmox. Внутри него крутиться виртуальная машина с Opnsense, которая играет роль граничного маршрутизатора и фаервола. Модем, активированные и настроенный на стационарном компьютере (Fedora 40).\nПопытка один Изначальная идея была такова: подключить модем в USB порт сервера и пробросить USB в виртуальную машину. Там уже попробовать \u0026ldquo;решить драйверную проблему\u0026rdquo;, если она будет и поднимать сеть силами Opnsense.\nТут же выяснилась, что первый раз (после подключения в USB) домем попадает в Opnsense и там начинается \u0026ldquo;вакханалия\u0026rdquo; с ошибками. Но далее, Proxmox (видимо именно сам Debian) сам определяет, что в сервер воткнут модем и начинает пробрасывать только USB \u0026ldquo;хаб\u0026rdquo;. Толку от которого внутри Opnsense уже нет.\nИтак, по порядку. Создаем USB mapping device:\nПробрасываем его в Opnsense виртуальную машину:\n1 2 3 4 # usbconfig ugen0.1: \u0026lt;Intel UHCI root HUB\u0026gt; at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=SAVE (0mA) ugen0.2: \u0026lt;QEMU QEMU USB Tablet\u0026gt; at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA) ugen1.1: \u0026lt;(0x1b36) XHCI root HUB\u0026gt; at usbus1, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA) Вот такой список устройств видится изнутри Opnsense:\nПочему же так Если выбраться \u0026ldquo;на уровень выше\u0026rdquo;, в Proxmox, то можно заметать, что в нем появилось несколько новых устройств. Это виртуальный CD-rom (/dev/sr0), и сетевое устройство (/dev/eth0). Выглядит как буд-то бы Proxmox \u0026ldquo;успел захватить\u0026rdquo; часть устройств, которыми представляется флешка. При этом начинаются сыпаться горы ошибок:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 blk_print_req_error: 400 callbacks suppressed I/O error, dev sr0, sector 10096 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0 I/O error, dev sr0, sector 10096 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 buffer_io_error: 382 callbacks suppressed Buffer I/O error on dev sr0, logical block 2524, async page read I/O error, dev sr0, sector 10100 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 2525, async page read I/O error, dev sr0, sector 0 op 0x0:(READ) flags 0x80700 phys_seg 1 prio class 0 I/O error, dev sr0, sector 0 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 0, async page read I/O error, dev sr0, sector 4 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 1, async page read I/O error, dev sr0, sector 0 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 0, async page read I/O error, dev sr0, sector 4 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 1, async page read I/O error, dev sr0, sector 0 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 0, async page read I/O error, dev sr0, sector 4 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 Buffer I/O error on dev sr0, logical block 1, async page read Buffer I/O error on dev sr0, logical block 0, async page read Buffer I/O error on dev sr0, logical block 1, async page read Решение Решением этого служим простая команда:\n1 # eject /dev/sr0 После ее выполнения поток ошибок прекращается. Пропадает устройство /dev/sr0 и ошибки в выводе. Но это не решает основную проблему: usb-lan не появляется в Opnsense.\nПопытка номер два Так как сам сетевой интерфейс прокинуть в Opnsense не получается, можно создать бридж на сетевой интерфейс в Proxmox. Потом добавить этот бридж в качестве порта в Opnsense и использовать как DHCP шлюз. Это вариант оказывается проще, так как сетевой интерфейс появляется в Proxmox, и для Opnsense всегда существует виртуальный бридж.\nПроблемы с sr0 Так как после подключения (и во время загрузки) происходят постоянные ошибки чтения, весь сервер тормозит. Решить эту проблему сходу не получается, пока-что приходится обходиться простой командой:\n1 eject /dev/sr0 На рабочей станции таких \u0026ldquo;приколов\u0026rdquo; нет, но найти отличия сходу так же не получилось. Выпиливать драйвер для cd-rom считаю слишком радикальной идеей. Правила дял udev тоже не очень помогли.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/druzhim-proxmox-i-yota-modem/","summary":"\u003cp\u003eПоявилась необходимость создать запасной канал связи в интернет. Тех работы у оператора длинной в 12 часов совпадают с\nрабочим временем. Вариантов для частного дома (на территории СНТ) не так что бы много. Самым простым выглядит usb\n\u0026ldquo;свисток\u0026rdquo; от мобильного оператора. Выбор мобильного оператора остается за рамками. Нужно получить доступ в интернет в\nмомент \u0026ldquo;отвала\u0026rdquo; основного провайдера.\u003c/p\u003e","title":"Дружим Proxmox и Yota модем"},{"content":"Теперь, после проверки, связки Cloudflare и Yandex.CLoud S3, можно перенести этот сайт на Yandex. Придется переделать Gitlab CI скрипты, но по большому счету, переделка будет связана с условиями запуска этапов. Здесь же речь пойдет об загрузке файлов в S3 хранилище.\nВарианты решений В документации Yandex.Cloud S3 есть несколько вариантов загрузки:\nконсольные клиенты; файловые браузеры; SDK; FUSE; FTP. Из всего этого нас интересуют именно консольные клиенты, так как именно их можно без проблем использовать в .gitlab-ci.yml. По остальным вариантам сказать особо нечего, файловые браузеры не подходят для использования в скриптах, SDK предполагают интеграцию в приложения, FUSE (лично мое мнение) это overkill для просто загрузки файлов, а FTP это Docker образ с FTP сервером и какой-то магической загрузкой внутри.\nТак как для универсальности нужно выбрать консольный клиент и для выбора в документации есть два:\nAWS CLI; S3cmd. Лично мой выбор оказался AWS CLI, так как для этого инструмента в Gitlab CI уже есть готовый и настроенный Docker образ registry.gitlab.com/gitlab-org/cloud-deploy/aws-base. В нем нужно будет указать переменные окружения и endpoint-url.\nЧерез образ от Gitlab Вот так в файле .gitlab-ci.yml записывается задача:\n1 2 3 4 5 6 7 8 9 10 11 12 13 upload-prod: stage: upload image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest needs: - job: build-prod artifacts: true script: - aws s3 sync ./public/ \u0026#34;$AWS_S3_BUCKET_NAME\u0026#34; --delete --endpoint-url \u0026#34;https://storage.yandexcloud.net\u0026#34; rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH environment: name: production action: prepare Как видно тут \u0026ndash;endpoint-url приходится указывать полностью \u0026ldquo;руками\u0026rdquo;. В современной версии AWS CLI для каждого сервиса можно указать свой endpoint-url через переменные окружения. Проблема в том, что образ от Gitlab давненько не обновляется. Минимальная версия для работы с переменной окружения AWS_ENDPOINT_URL_S3 2.13.0:\n2.13.0\nfeature:configuration: Configure the endpoint URL in the shared configuration file or via an environment variable for a specific AWS service or all AWS services.\nВ образе от Gitlab версия 2.9.8 (на момент написания):\n1 2 $ aws --version aws-cli/2.9.8 Python/3.9.11 Linux/5.15.154+ exe/x86_64.ubuntu.20 prompt/off Через образ от AWS Для работы через образ AWS нужно будет немного изменить .gitlab-ci.yml:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 upload-prod: stage: upload image: name: amazon/aws-cli:latest entrypoint: [ \u0026#34;\u0026#34; ] needs: - job: build-prod artifacts: true script: - aws s3 sync ./public/ \u0026#34;$AWS_S3_BUCKET_NAME\u0026#34; --delete rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH environment: name: production action: prepare Здесь entrypoint-url задается через переменную CI AWS_ENDPOINT_URL_S3. Стоит обратить внимание на запись блока image. Она имеет такой вид из-за особенностей образа от AWS.\nДополнительные действия Для работы нужно будет создать переменные CI:\nAWS_ACCESS_KEY_ID; AWS_SECRET_ACCESS_KEY; AWS_DEFAULT_REGION; AWS_ENDPOINT_URL_S3. Так же нужно настроить сервисный аккаунт, дать ему права доступа к бакету и настроить ключи по мануалу. Это описано в документации Yandex.Cloud для S3.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/zagruzka-hugo-sajta-v-yandexcloud-s3/","summary":"\u003cp\u003eТеперь, после проверки, связки \u003ca href=\"https://www.cloudflare.com/\"\u003eCloudflare\u003c/a\u003e\nи \u003ca href=\"https://yandex.cloud/ru/services/storage\"\u003eYandex.CLoud S3\u003c/a\u003e, можно перенести этот сайт на Yandex. Придется переделать\nGitlab CI скрипты, но по большому счету, переделка будет связана с условиями запуска этапов. Здесь же речь пойдет об\nзагрузке файлов в S3 хранилище.\u003c/p\u003e","title":"Загрузка Hugo сайта в Yandex.Cloud S3"},{"content":"Хочется зафиксировать для себя все шаги и настройки, которые нужно будет сделать, что бы получить работоспособный вариант статического сайта на основе Yandex.Cloud S3, который по сути является аналогом AWS S3.\nНастройки на стороне Yandex.Cloud S3 Тут буду действовать через web интерфейс. Создаем бакет.\nТут главное 2 момента:\nназвание бакета должно совпадать с доменом, который будет ссылаться на этот бакет через Cloudflare; доступ для чтения объектов должен быть публичным. Если задать имя бакета произвольным, то для доступа к нему нужно будет указывать заголовок host. Через настройки сайта нельзя указать произвольные домены, вся работа с бакетом будет происходить через сгенерированный яндексом домен. Получить доступ к нему без замены заголовка host можно учитывая подстановку: {bucket-name}.website.yandexcloud.net это домен {bucket-name} в Cloudflare. Например:\nBucket Домен в Cloudflare Домен в Yandex.Cloud domain.tld domain.tld domain.tld.website.yandexcloud.net www.domain.tld www.domain.tld www.domain.tld.website.yandexcloud.net some-name some-name some-name.website.yandexcloud.net Как видно из таблицы без дополнительных подмен заголовокв в Cloudflare в третьем варианте получить доступ к сайту не получится. Возможно конечно поменять заголовок host по инструкции. Но нужно учитывать что эта возможность работает ТОЛЬКО в Enterprise версии:\nCustomers can rewrite Host headers using different Cloudflare rules. This feature is currently available for domains on the Enterprise plan.\nЕсли же хочется получить все с минимальными (а лучше вообще без 🤑) затратами, то стоит это учитывать. После этого на странице настроек \u0026ldquo;Веб-сайт\u0026rdquo; нужно указать параметры и сохранить настройку:\nВ настройках нет ничего сложного:\nГлавная страница - путь до главной страницы сайта; Страница ошибки - путь до страницы, которая будет отдаваться на все 4xx ошибки; Ссылка - домен, который нужно будет указать в настройках проксирования Cloudflare. Остальные параметры не принципиальны и их можно не трогать.\nНастройки на стороне Cloudflare На стороне CLoudflare тоже все буду делать все через web интерфейс. Тут все еще проще, нужно только добавить CNAME запись к основному домену (или поддомену, в зависимости от имени бакета, если нет желания менять настройки заголовка host).\nПри этом, если CNAME создается для корневого домена (знак @ в поле имени), то Cloudflare покажет предупреждение, что для домена включается CNAME flattening.\nПроверка работы Практически сразу можно будет проверить, что все работает как нужно, в том числе кеши:\nВ том числе картинки (попадающие в кеш самого Cloudflare):\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/yandexcloud-free-tier-i-cloudflare-kak-hosting-staticheskogo-sajta/","summary":"\u003cp\u003eХочется зафиксировать для себя все шаги и настройки, которые нужно будет сделать, что бы получить работоспособный\nвариант статического сайта на основе \u003ca href=\"https://yandex.cloud/\"\u003eYandex.Cloud\u003c/a\u003e \u003ca href=\"https://yandex.cloud/services/storage\"\u003eS3\u003c/a\u003e,\nкоторый по сути является аналогом \u003ca href=\"https://aws.amazon.com/s3/\"\u003eAWS S3\u003c/a\u003e.\u003c/p\u003e","title":"Yandex.Cloud free tier и Cloudflare - как хостинг статического сайта"},{"content":"Не все провайдеры и/или хостеры до сих пор предоставляют ipv6 адреса для своих клиентов. И что бы получить доступ в ipv6 сеть, приходится пользоваться 6to4 туннелями. На примере tunnelbroker от Hurricane Electric проведем настройку для NetworkManager.\nЗачем В основном для связанности vps-ки с ipv6 миром. Можно жить и без него, но постепенно появляются хостеры, которые наоборот дают ipv6, но ipv4 предоставляют только за отдельную плату. Есть конечно же одно замечание: брокер принимает на себя весь трафик идущий через \u0026ldquo;наш\u0026rdquo; ipv6 адрес. Для кого-то это может иметь значение (пинги, приватность и т.д.).\nНастройки на сайте брокера есть под разные оси и системы управления сетью, но под NetworkManager не нашлось. Возможно я плохо искал или уже появились. Тем не менее настройка простая, и для себя ее запишу на всякий случай в виде команды и файла конфигурации.\nСоздаем туннель Нужно зарегестрироваться на https://tunnelbroker.net/ и перейти на страницу создания нового туннеля. На ней вписать белый ipv4 адрес компьютера и выбрать ближайшую к нему точку (это благоприятно скажется на пинге, в идеале 😁, но не точно).\nПосле этого нажать кнопку \u0026ldquo;Create Tunnel\u0026rdquo; и ждать. В итоге откроется страницы с данными для туннеля.\nС этой страницы понадобятся:\nServer IPv4 Address (сохраним в переменную SERVER_IPV4); Server IPv6 Address (сохраним в переменную SERVER_IPV6, без маски подсети, можно вообще сократить до ::xx.xx.xx.xx где xx.xx.xx.xx это белый ipv4 адрес клиента); Client IPv6 Address (сохраним в переменную CLIENT_IPV6). Команда Если кратко, то команда имеет такой вид:\n1 2 3 4 nmcli connection add type ip-tunnel con-name 6to4 ifname 6to4 mode sit \\ remote ${SERVER_IPV4} -- ipv4.method disabled ipv6.method manual \\ ipv6.address ${CLIENT_IPV6} ipv6.gateway ${SERVER_IPV6} \\ ip-tunnel.ttl 64 В команде есть 3 переменные, по названию они соответствуют тому, что есть на странице настроек. После этого сразу появится доступ к ipv6 адресам.\nФайл с конфигурацией NetworkManager Если писать команду не очень хочется, то можно создать файл конфигурации руками. Внутри будет что-то вроде:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [connection] id=6to4 uuid=eaf9bef3-cfae-4965-ba80-139b0d158f1a type=ip-tunnel interface-name=6to4 [ip-tunnel] mode=3 remote=xxx.xx.xx.xx ttl=64 [ipv4] method=disabled [ipv6] addr-gen-mode=default address1=2001:xxx:xxxx:xxx::2/64,::yy.yyy.yy.yy method=manual [proxy] Где:\nxxx.xx.xx.xx - адрес брокера в сети ipv4; 2001:xxx:xxxx:xxx::2/64 - адрес клиента в сети ipv6; ::yy.yyy.yy.yy - специальная запись ipv4 клиента. ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/nastrojka-tunnelbroker-ot-hurricane-electric-v-fedora/","summary":"\u003cp\u003eНе все провайдеры и/или хостеры до сих пор предоставляют \u003ca href=\"https://ru.wikipedia.org/wiki/IPv6\"\u003eipv6\u003c/a\u003e адреса для своих\nклиентов. И что бы получить доступ в \u003ca href=\"https://ru.wikipedia.org/wiki/IPv6\"\u003eipv6\u003c/a\u003e сеть, приходится\nпользоваться \u003ca href=\"https://ru.wikipedia.org/wiki/6to4\"\u003e6to4\u003c/a\u003e туннелями. На примере \u003ca href=\"https://tunnelbroker.net/\"\u003etunnelbroker\u003c/a\u003e\nот \u003ca href=\"https://he.net/\"\u003eHurricane Electric\u003c/a\u003e проведем настройку\nдля \u003ca href=\"https://gitlab.freedesktop.org/NetworkManager/NetworkManager\"\u003eNetworkManager\u003c/a\u003e.\u003c/p\u003e","title":"Настройка tunnelbroker от Hurricane Electric в Fedora"},{"content":"Решил я объединить новый мини компьютер с уже живущим у меня n305. Но пошел \u0026ldquo;не с того конца\u0026rdquo;. Создал кластер на новом компьютере и попробовал добавить старый в него. Но не тут то было, ведь на старом компьютере уже есть пачка виртуалок, в том числе с пробросом pcie устройств. В общем убивать их нет никакого желания, даже с бекапами в наличие.\nНужно будет поковыряться с Proxmox Cluster File System (pmxcfs), удалить часть файликов.\nОстанавливаем Proxmox Cluster File System:\n1 systemctl stop pve-cluster corosync Запускаем Proxmox Cluster File System в режиме локальной ноды:\n1 pmxcfs -l Очищаем директорию с настройками кластера:\n1 rm /etc/corosync/* Удаляем настройку кластера из папки настроек Proxmox:\n1 rm /etc/pve/corosync.conf Завершаем ранее запущенный Proxmox Cluster File System (то что мы запустили руками в режиме локальной ноды):\n1 killall pmxcfs Снова запускаем Proxmox Cluster File System:\n1 systemctl start pve-cluster Готово, нода больше не в кластере. Ее можно добавить в другой кластер или создать новый кластер на ней.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/sbros-nastroek-klastera-proxmox-dlya-nody/","summary":"\u003cp\u003eРешил я объединить новый мини компьютер с уже живущим у меня \u003ca href=\"/posts/topton-i3-n305/\"\u003en305\u003c/a\u003e. Но пошел \u0026ldquo;не с того конца\u0026rdquo;.\nСоздал кластер на новом компьютере и попробовал добавить старый в него. Но не тут то было, ведь на старом компьютере уже\nесть пачка виртуалок, в том числе с пробросом pcie устройств. В общем убивать их нет никакого желания, даже с бекапами в\nналичие.\u003c/p\u003e\n\u003cp\u003eНужно будет поковыряться\nс \u003ca href=\"https://pve.proxmox.com/wiki/Proxmox_Cluster_File_System_%28pmxcfs%29\"\u003eProxmox Cluster File System\u003c/a\u003e (\u003ccode\u003epmxcfs\u003c/code\u003e), удалить\nчасть файликов.\u003c/p\u003e","title":"Сброс настроек кластера Proxmox для ноды"},{"content":"Запускам на базе SBC с установленным Armbian узел Yggdrasil.\nЧто такое Yggdrasil и зачем вся эта затея. Yggdrasil - это сетевой протокол и его реализация mesh-сети, работающей в оверлейном режиме. Внутри сети сейчас есть набор сервисов доступных уже сейчас. Каждый проверит и решит сам, есть ли там что-то интересное. На текущий момент мне приглянулись несколько из них.\nГлавный плюс и надежда в этой сети на доступность. Так как она mesh, то может перестраиваться при \u0026ldquo;выпадении\u0026rdquo; некоторых узлов и/или маршрутов. То есть шанс, что я смогу в ней достучаться до своего VPS если в очередной раз рухнул трафик в районе Москвы больше, чем через родного провайдера. Да и это дает возможность настроить доступ к внутренним ресурсам домашней сети (двойной NAT от провайдера).\nДа и к тому же недавно PiHole переехал в контейнер на мини-сервер, освободив плату Orange Pi Zero 2. Вот под нее и задачка.\nУстановка Так как готового пакета для установки в Armbian нет, придется идти за бинарниками на github. Скачиваем оттуда версию под свою архитектуру и систему. У меня это arm64 и Debian. Далее эта команда будет использоваться в скрипте обновления.\n1 curl -Ls -o /tmp/yggdrasil.deb https://github.com/yggdrasil-network/yggdrasil-go/releases/download/v0.5.5/yggdrasil-0.5.5-arm64.deb После этого запускаем установку:\n1 apt install /tmp/yggdrasil.deb Настройки Настройки хранятся внутри /etc/yggdrasil/yggdrasil.conf (именно для такого пакета, иногда настройки можно найти в файле /etc/yggdrasil.conf). Если по каким-то причинам файла нет, то нужно сгенерировать новый файл:\n1 2 3 /usr/bin/yggdrasil -genconf \u0026gt; /etc/yggdrasil/yggdrasil.conf chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf chmod 640 /etc/yggdrasil/yggdrasil.conf Пройдемся по самым важным настройкам.\nPeers В настройке указываются адреса узлов yggdrasil. Тут можно указать любой узел из списка. Так как по умолчанию авто обнаружение других узлов работает через multicast, то при отсутствии таковых в локальной сети подключится к yggdrasil не получится. Параметры не отключает авто обнаружение, но позволяет узлу оставаться на связи через узлы доступные по белым ip адресам.\nНапример, в моей сети этот список заполнен только у серверов (локальных или VPS), клиентам же пока достаточно обнаружить в сети сервер и через него попасть в сеть.\nMulticastInterfaces А тут можно настроить какие интерфейсы системы можно будет использовать для multicast обнаружения.\nНапример, на своих VPS серверах я этот параметр оставляю пустым. Серверы имеют белый ip (если захочу выдать интервейс подключения в мир) и находятся в сети хостера, в которую бы не хотелось лишний раз \u0026ldquo;спамить\u0026rdquo;.\nAllowedPublicKeys Тут можно указать список публичных ключей yggdrasil нод, которым будет разрешено обращаться к серверу с интерфейса yggdrasil. По сути, после внесения хотя бы одной записи этот параметр полностью отсекает видимость сервера в сети yggdrasil для остальных пользователей.\nНапример, Zero 2 скрыт ото всех, кроме рабочей станции и ноутбука. На VPS-ке же интерфейс добавлен в ограниченную зону фаервола, так как некоторые вещи в будущем будут выставлены именно в yggdrasil сеть.\nIfName Чисто косметический параметр, но лучше его задать. Проще будет искать в списке интерфейсов нужных далее.\nНапример, у меня это везде yggd, так как tun0 ни о чем не говорит своим названием.\nОбновление Так как мы ставим пакет не из репозитория, то задачей обновления должны озаботиться сам. Данные по релизам можно получить от публичного API Github. Понадобится нам jq и curl. В самом простом варианте (без проверок версий и прочего):\n1 2 3 4 5 6 7 8 9 yggdrasil-update() { local ARCH=\u0026#34;-arm64.deb\u0026#34; local JQ_FILTER=\u0026#34;[ .[] | select( .draft == false and .prerelease == false )][0].assets | map( select( .name | endswith(\\\u0026#34;$ARCH\\\u0026#34;)) )[0].browser_download_url\u0026#34; echo \u0026#34;Get releases...\u0026#34; local DOWNLOAD_LINK=$(curl https://api.github.com/repos/yggdrasil-network/yggdrasil-go/releases -s | jq -r \u0026#34;$JQ_FILTER\u0026#34;) echo \u0026#34;Download from link: $DOWNLOAD_LINK\u0026#34; curl -Ls -o /tmp/yggdrasil.deb $DOWNLOAD_LINK apt install /tmp/yggdrasil.deb } Тут бы еще добавить проверку пакетного менеджера с выбором .rpm или .deb пакета. Получить архитектуру с переводом ее в формат записи разработчиков. Но пока так.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/yggdrasil-ustanovka-nastrojka-i-obnovlenie/","summary":"\u003cp\u003eЗапускам на\nбазе \u003ca href=\"https://ru.wikipedia.org/wiki/%D0%9E%D0%B4%D0%BD%D0%BE%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80\"\u003eSBC\u003c/a\u003e\nс установленным \u003ca href=\"https://www.armbian.com/\"\u003eArmbian\u003c/a\u003e узел \u003ca href=\"https://yggdrasil-network.github.io/about.html\"\u003eYggdrasil\u003c/a\u003e.\u003c/p\u003e","title":"Yggdrasil: установка, настройка и обновление"},{"content":"В gitlab есть функционал об отправке сообщений в telegram. Но на примере этого сайта есть свои ограничения.\nЧто не так Добавляя статьи и тестируя их через тестовые домены (окружения) в Gitlab CI (подробнее тут) понадобилась возможность получать уведомление, когда площадка готова для проверки. Процесс сборки, заливки и установки домена не мгновенный, а сидеть и отслеживать \u0026ldquo;когда же оно уже всё\u0026rdquo; - не очень продуктивно. Поэтому начал искать способ получения уведомлений при завершении работы пайплайна. В gitlab есть встроенная интеграция с telegram. И она даже умеет реагировать на изменение статуса пайплайнов. Проблема в сообщение, которое приходит из gitlab:\n1 repo_name: Pipeline #0000000000 of branch main by Anon Anonovich (username) has passed in 00:44 Где:\nrepo_name - ссылка на репозиторий; #0000000000 - ссылка на пайплайн; main - ссылка на ветку; Anon Anonovich (username) - имя пользователя на сайте и его ник в скобках. Как видно из сообщения - нет ссылки на окружение, что бы можно было сразу перейти и посмотреть результат в браузере. Для ветки main это не большая проблема, так как это основной домен сайта. Но домены для тестовых площадок создаются с рандомным кодом, и что бы на него попасть всеравно приходится идти в репозиторий. Из-за этого было решено оставить такие уведомления только для сбойных пайплайнов и включить дополнительный шаг в свой .gitlab-ci.yml.\nТеория Для отправки сообщения в telegram ботом нужно воспользоваться методом sendMessage. Здесь есть три важных параметра:\nchat_id - идентификатор, куда будет отправлено сообщение (в моем случае идентификатор канала); text - собственно само сообщение; parse_mode - единственный не обязательный параметр, но лучше его задать самому, что бы можно было применить форматирование для сообщения. Так же в ссылке https://api.telegram.org/bot\u0026lt;token\u0026gt;/sendMessage, нужно будет указать token бота.\nДополнительный этап в .gitlab-ci.yml Теперь можно дополнить .gitlab-ci.yml (install-curl-jq-jo уже был описан в предыдущей статье):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 dev-link: stage: notify needs: - dev-domain variables: TEXT: |- Ветка: \u0026lt;a href=\u0026#34;https://gitlab.com/***/***/-/tree/${CI_COMMIT_BRANCH}\u0026#34;\u0026gt;${CI_COMMIT_BRANCH}\u0026lt;/a\u0026gt; Ссылка: \u0026lt;a href=\u0026#34;https://${CI_ENVIRONMENT_SLUG}.${SITE_DOMAIN}\u0026#34;\u0026gt;https://${CI_ENVIRONMENT_SLUG}.${SITE_DOMAIN}\u0026lt;/a\u0026gt; before_script: - *install-curl-jq-jo script: - DATA=$( jo chat_id=${TG_NOTIFY_CHAT_ID} parse_mode=HTML text=\u0026#34;${TEXT}\u0026#34; ) - curl -s -X POST \u0026#34;https://api.telegram.org/bot${TG_NOTIFY_TOKEN}/sendMessage\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | jq environment: name: staging/$CI_COMMIT_REF_SLUG action: access rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH Теперь после удачной сборки и добавления временного поддомена в cloudflare прите сообщение в telegram со ссылкой на ветку и ссылкой на тестовый домен.\nЖелать отдельное сообщение для продакшн окружения не стал, так как там ссылка всегда одна и та же. Но если понадобится выводить дополнительную информацию, то добавить не составит труда. Главное перепроверить что указано в параметрах needs и rules.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/gitlab-ci-environment-link-to-telegram/","summary":"\u003cp\u003eВ \u003ca href=\"https://gitlab.com/\"\u003egitlab\u003c/a\u003e есть функционал об отправке сообщений в telegram. Но на примере этого сайта есть свои\nограничения.\u003c/p\u003e","title":"Отправка ссылки GitLab CI environment в telegram"},{"content":"Соберем свой локальный docker контейнер для написания заметок сюда, на этот сайт.\nПредыстория С некоторых пор hugo тема PaperMod, которая используется здесь, обновилась. Поднялись требования по минимальной версии hugo. Но так уж вышло, что доступная из репозитория версия hugo оказалась ниже.\n1 2 ❯ dnf list installed | grep hugo hugo.x86_64 0.111.3-4.fc39 @fedora При попытке запустить дев сервер получаю сообщение:\n1 2 3 4 5 ❯ hugo serve WARN 2024/01/27 19:50:57 Module \u0026#34;PaperMod\u0026#34; is not compatible with this Hugo version; run \u0026#34;hugo mod graph\u0026#34; for more information. Start building sites … hugo v0.111.3+extended linux/amd64 BuildDate=2023-07-26T00:00:00+00:00 VendorInfo=Fedora:0.111.3-4.fc39 ERROR 2024/01/27 19:50:57 =\u0026gt; hugo v0.112.4 or greater is required for hugo-PaperMod to build Повезло что в alpine версия посвежее и подойдет. Благодаря этому не отвалилась сборка сайта в GitLab CI.\nВарианты решения Вариантов решения на самом деле несколько:\nпросто скачать скомпилированный бинарник hugo из github-а и положить в проект; настроить сборку свежей версии локально в системе; взять готовый образ docker с hugo; собрать свой образ docker. Просто скачать Способ максимально простой, можно даже добавить этот бинарник в .gitignore, что бы не \u0026ldquo;утяжелять\u0026rdquo; репозиторий. Но тут есть минус с обновлением, нужно либо писать скрипт, который будет уметь выкачивать этот бинарник (желательно учитывая версию hugo). Если со скриптом возиться не хочется, можно руками скачивать временами. Но этот процесс не интересен и слишком \u0026ldquo;ручной\u0026rdquo;.\nКомпилировать локально Так как у меня уже есть локально установленный golang пакет, то можно настроить сборку локально. Положить потом этот бинарник в $PATH. Но процесс обновления тоже будет не \u0026ldquo;однокнопочным\u0026rdquo;. Тоже понадобиться скрипт, что бы определять последнюю стабильную версию, выкачивать/переключаться на ее коммит, собирать и раскладывать скомпилированные бинарники. При этом нет никакой гарантии, что со временем процесс сборки не поменяется, или не добавятся доп зависимости от сторонних пакетов.\nВзять готовый docker образ Тут все больше опирается в то, что образ это собирается \u0026ldquo;кем-то\u0026rdquo;. В общем вопрос доверия. Ну и нет никакой уверенности, что человек как-нибудь не передаст проект в \u0026ldquo;слишком хорошие руки\u0026rdquo; или просто устанет.\nСобрать свой локальный docker образ Так как для hugo нет официального образа на hub.docker.io, то придется идти тем же путем, что и в статье про GitLab CI. Это даст дополнительный плюс, перед первыми тремя вариантами: не нужно отслеживать соответствие версий пакета hugo локально и в CI. То есть получится сделать унификацию окружения, ведь и там и там одинаковый базовый образ и одинаковый репозиторий пакетов.\nDockerfile Так как нам нужно унифицироваться с окружением CI, нужен только \u0026ldquo;голый\u0026rdquo; hugo, то файл получается максимально простым:\n1 2 3 4 5 6 7 8 9 10 11 FROM alpine:3 VOLUME /data WORKDIR /data EXPOSE 1313 RUN apk add --update --no-cache hugo ENTRYPOINT [\u0026#34;hugo\u0026#34;] CMD [\u0026#34;server\u0026#34;, \u0026#34;-D\u0026#34;, \u0026#34;--disableFastRender\u0026#34;, \u0026#34;--navigateToChanged\u0026#34;, \u0026#34;--noBuildLock\u0026#34;, \u0026#34;--bind=0.0.0.0\u0026#34;] Пойдем по порядку, и первым делом устанавливаем VOLUME, WORKDIR и EXPOSE. Эти параметры не будут меняться в обозримом будущем и стоит их поместить ближе к началу. В ENTRYPOINT указываем hugo, именно в квадратных скобках, что бы запускать его в режиме exec(почитать в документации).\nВообще как работают ENTRYPOINT и CMD сейчас останется за рамками этой заметки. Набор флагов выбран такой:\nserver - это в общем-то http сервер для разработки, он будет работать на порту 1313 (его то и указали в EXPOSE); -D - для показа черновиков; --disableFastRender - полная перерисовка сайта при изменениях; --navigateToChanged - переход на страницу, где было изменение; --noBuildLock - не создавать файл .hugo_build.lock, так как все будет работать в памяти, а файловая структура будет монтироваться в режиме только для чтения; --bind=0.0.0.0 - внутри docker-а без этой настройки доступа к серверу по http не будет. Полный список параметров посмотреть тут.\nЗапуск Теперь для запуска сервера нужно лишь выполнить команду:\n1 docker build -t spwsmr.website:hugo . \u0026amp;\u0026amp; docker run -p 1313:1313 -v $(pwd):/data:ro --name spwsmr.website.hugo spwsmr.website:hugo После этого открыть ссылку http://localhost:1313/ в браузере.\nPhpStorm Для PhpStorm эту команду можно свести к нажатию одной кнопки. Для этого создать новую конфигурацию запуска: ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/hugo-docker-image/","summary":"\u003cp\u003eСоберем свой локальный docker контейнер для написания заметок сюда, на этот сайт.\u003c/p\u003e","title":"Локальный docker контейнер с hugo"},{"content":"Перевод статьи написанной Íñigo Huguet для сайта Fedora Magazine. Тут будет общее описание и принципы работы D-Bus.\nЧто такое D-Bus D-Bus служит различным целям, правленным на облегчение взаимодействия различных процессов в системе. В этой статье будет описан D-Bus и то, как он выполняет эту функцию.\nИз определения создателей D-Bus:\nD-Bus - это система шины сообщений, простой способ взаимодействия приложений друг с другом. Помимо межпроцессного взаимодействия, D-Bus помогает координировать жизненный цикл процесса; это позволяет просто и надежно писать код приложения или демона с «одним экземпляром», а также запускать приложения и демоны по требованию, когда их услуги необходимы.\nD-Bus - это, главным образом, протокол межпроцессного взаимодействия (IPC). Его главное преимущество заключается в том, что приложениям не нужно установить индивидуальную связь со всеми остальными процессами, с которыми им необходимо общаться. Вместо этого D-Bus предлагает виртуальную шину, к которой подключаются все приложения и отправляют сообщения.\nПроцессы без D-Bus Processes with D-Bus Достижение желаемого приложения Мы говорим о системе D-Bus, но в системе может быть более одной шины. На самом деле, обычно их как минимум 2:\nСистемная шина: приложения, управляющие общесистемными ресурсами, подключенными к ней. Одна сеансовая шина на каждого вошедшего в систему пользователя: настольные приложения обычно используют эту шину. Чтобы отправлять сообщения D-Bus в желаемый пункт назначения по шине, необходим способ идентификации каждого из приложений, подключенных к шине. По этой причине каждое приложение получает по крайней мере одно имя шины, которое другие могут указать в качестве места назначения своих сообщений. Существует два типа имен:\nУникальные имена соединений: Каждое приложение, подключающееся к шине, автоматически получает его. Это уникальный идентификатор, который никогда не будет повторно использоваться для другого приложения. Эти имена начинаются с двоеточия (:) и обычно выглядят примерно так: :1.94 (символы после двоеточия не имеют особого значения, но уникальны). Известные имена: Хотя уникальные имена соединений назначаются динамически, приложения должны быть легко обнаружены и должны иметь фиксированное имя, которое хорошо известно. Они выглядят как перевернутые доменные имена, похожие на org.freedesktop.NetworkManager. Вы можете увидеть, какие приложения подключены к двум основным шинам, используя команды busctl ниже. Мы обнаружим, что в настоящее время многие основные компоненты системы используют D-Bus. В следующих примерах опустите параметр –acquired, чтобы увидеть уникальные имена соединений.\n1 2 3 4 $ busctl list --system --acquired ... $ busctl list --user --acquired ... По аналогии с IP-сетями уникальные имена соединений подобны динамическим IP-адресам, а общеизвестные имена подобны именам хостов или доменным именам.\nНапример, NetworkManager - это демон, который управляет сетевой конфигурацией системы. Клиенты, такие как центр управления GNOME или инструменты командной строки nmcli и nmstate, подключаются к системной шине и отправляют сообщения D-Bus на широко известное имя org.freedesktop.NetworkManager, чтобы получить информацию или запросить изменения.\nПримечание: в спецификации D-Bus используется термин \u0026ldquo;имя шины\u0026rdquo;, так что это как-то официально. Это может сбивать с толку, поскольку люди склонны думать, что это относится к разным шинам, а не к приложениям, подключенным к одной шине. Во многих статьях и инструментах используется термин \u0026ldquo;сервис\u0026rdquo; вместо \u0026ldquo;имя шины\u0026rdquo;, потому что он более интуитивен, но в спецификации он имеет другое значение, поэтому не рекомендуется. В этой статье я использую термин \u0026ldquo;пункт назначения\u0026rdquo; везде, где это возможно. В любом случае помните, что \u0026ldquo;имя шины\u0026rdquo; относится к \u0026ldquo;имени В шине\u0026rdquo;, а не к \u0026ldquo;имени шины\u0026rdquo;.\nОбъекты D-Bus Приложения D-Bus могут предоставлять некоторые из своих ресурсов и действий в том, что мы могли бы назвать API D-Bus.\nОткрытые ресурсы напоминают основные концепции объектно-ориентированных языков программирования, поэтому их называют объектами D-Bus. В частности, каждое приложение может предоставлять неопределенное количество объектов со свойствами и методами. Отдельные объекты D-Bus идентифицируются по своим путям D-Bus, которые очень похожи на пути Unix (т. е. /path/to/the/object).\nВы можете проверить, какие объекты предоставляет определенное приложение, с помощью команды busctl. Например:\n1 $ busctl tree org.freedesktop.NetworkManager Приложения могут интуитивно выражать иерархию между объектами благодаря этому идентификатору, напоминающему путь. Легко понять, что /Background/Color - это объект, который иерархически принадлежит объекту /Background. Спецификация не требует соблюдения этой иерархической структуры, но почти все приложения это делают.\nПримечание. Во многих приложениях пути к объектам обычно начинаются с обратного домена автора (т. е. /org/freedesktop/NetworkManager/*). Это позволяет избежать конфликтов имен с другими объектами из разных библиотек или источников. Однако это не является обязательным и не все приложения это делают.\nИнтерфейсы, методы и свойства D-Bus Каждый объект D-Bus реализует один или несколько интерфейсов D-Bus, и каждый интерфейс определяет некоторые свойства и методы. Свойства подобны переменным, а методы - функциям. Имена интерфейсов D-Bus выглядят так: org.example.AppName.InterfaceName.\nЕсли вы уже знаете какой-либо язык программирования, использующий концепцию интерфейса, например Java, он будет вам знаком. Если нет, просто помните, что интерфейсы подобны типам объектов, и каждый объект может иметь более одного типа одновременно. Но имейте в виду, что, в отличие от того, что происходит во многих из этих языков, объекты D-Bus никогда не определяют прямых членов, а только интерфейсы.\nПример:\nПриложение сервера печати определяет следующие интерфейсы: Принтер: определяет метод PrintDocument и свойство InkLevel. Сканер: определяет метод Сканировать документ. Объект /Devices/1 представляет собой обычный принтер, поэтому он реализует только интерфейс Принтер. Объект /Devices/2 представляет принтер со сканером, поэтому реализует оба интерфейса. Интроспекция Интроспекция позволяет вам получить список интерфейсов с их методами и свойствами, которые реализует объект D-Bus. Например, команда busctl используется следующим образом:\n1 $ busctl introspect org.freedesktop.NetworkManager /org/freedesktop/NetworkManager Методы и свойства Теперь давайте посмотрим, как вызывать методы D-Bus и читать свойства из терминала на двух примерах:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Method call busctl call \\ --system \\ org.freedesktop.NetworkManager # app name \\ /org/freedesktop/NetworkManager/Settings # object \\ org.freedesktop.NetworkManager.Settings # interface \\ ListConnections # method # Property read busctl get-property \\ --system \\ org.freedesktop.NetworkManager # app name \\ /org/freedesktop/NetworkManager/Devices/2 # object \\ org.freedesktop.NetworkManager.Device.Wired # interface \\ PermHwAddress # property Чтобы читать или записывать свойства, нам фактически нужно вызывать методы стандартного интерфейса org.freedesktop.DBus.Properties, определенного спецификациями D-Bus. Busctl, однако, делает это скрытно с помощью команды get/set-property, чтобы немного абстрагироваться от этого. Совет по упражнению: прочитайте свойство с помощью вызова busctl (подсказка: вам нужно указать сигнатуру аргументов, то есть ss).\nПримечание: при вызове методов D-Bus указывать интерфейс не обязательно, но если два интерфейса определяют одно и то же имя метода, результат не определен, поэтому его следует всегда указывать. По этой причине инструмент busctl делает это обязательным.\nСистема типов D-Bus D-Bus использует строгую систему типов, поэтому все свойства, аргументы и возвращаемые значения должны иметь четко определенный тип.\nСуществует несколько основных типов, таких как BYTE, BOOLEAN, INT32, UINT32, DOUBLE, STRING или OBJECT_PATH. Эти базовые типы можно сгруппировать в три различных типа контейнеров: STRUCT, ARRAY и DICT_ENTRY (словари). Контейнеры могут быть вложены в другие контейнеры того же или другого типа.\nСуществует также тип VARIANT, который допускает динамическую типизацию.\nКаждый тип можно идентифицировать по сигнатуре. Сигнатуры базовых типов представляют собой одиночные символы, такие как i, u, s и т. д. Сигнатуры составных типов представляют собой строки типа (iibs), ai или a{s(ii)}. Полное описание всех типов и сигнатур выходит за рамки этой статьи, но в зависимости от используемого вами языка и/или библиотеки D-Bus вам потребуются по крайней мере некоторые знания о том, как указать тип значений, передаваемых или получаемых. Дополнительную информацию см. в спецификации D-Bus.\nСобираем все вместе (пример на Python) Теперь, когда мы знаем основные понятия D-Bus, мы готовы отправлять сообщения D-Bus. Давайте посмотрим полный пример Python.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #!/usr/bin/env python3 # Import the \u0026#39;dbus\u0026#39; module from dbus-python package. # The package can be installed with `pip install dbus-python`. # Documenation: https://dbus.freedesktop.org/doc/dbus-python/ import dbus # We need the NetworkManager\u0026#39;s D-Bus API documentation to # know what objects and methods we are interested in: # https://networkmanager.dev/docs/api/latest/spec.html # We\u0026#39;ll connect to system bus bus = dbus.SystemBus() # We\u0026#39;ll send our messages to NetworkManager NM_WELL_KNOWN_NAME = \u0026#34;org.freedesktop.NetworkManager\u0026#34; # Call to the following method: # - object: /org/freedesktop/NetworkManager # - interface: org.freedesktop.NetworkManager # - method: GetDeviceByIpIface # - input argument: \u0026#34;eth0\u0026#34; (type: STRING) # - return value: the device\u0026#39;s path (type: OBJECT_PATH) # # Get the path to the object that represents the device with # the interface name \u0026#34;eth0\u0026#34;. nm_dbus_obj = bus.get_object( NM_WELL_KNOWN_NAME, \u0026#34;/org/freedesktop/NetworkManager\u0026#34; ) nm_dbus_iface = dbus.Interface( nm_dbus_obj, \u0026#34;org.freedesktop.NetworkManager\u0026#34; ) try: device_dbus_path = nm_dbus_iface.GetDeviceByIpIface(\u0026#34;eth0\u0026#34;) except dbus.exceptions.DBusException as e: print(\u0026#34;D-Bus error: \u0026#34; + str(e)) quit() print(\u0026#34;D-Bus path to eth0 device: \u0026#34; + str(device_dbus_path)) # Call to the following method: # - object: the device that we obtained in the previous step # - interface: org.freedesktop.NetworkManager.Device # - method: Disconnect # # Request to the NM daemon to disconnect the device # Note: NM will return an error if it was already disconnected device_dbus_obj = bus.get_object( NM_WELL_KNOWN_NAME, device_dbus_path ) device_dbus_iface = dbus.Interface( device_dbus_obj, \u0026#34;org.freedesktop.NetworkManager.Device\u0026#34; ) try: device_dbus_iface.Disconnect() except dbus.exceptions.DBusException as e: print(\u0026#34;D-Bus error: \u0026#34; + str(e)) quit() print(\u0026#34;Device disconnected\u0026#34;) Обратите внимание, что нам не нужно было указывать тип аргумента метода. Это связано с тем, что dbus-python делает все возможное, чтобы преобразовать значения Python в эквивалентные значения D-Bus (т. е. str в STRING, list в ARRAY, dict в DICT_ENTRY и т. д.). Однако, поскольку D-Bus имеет строгую систему типов, вам нужно будет указать тип в случае неоднозначности. Например, для целочисленных типов вам часто потребуется использовать dbus.UInt16(значение), dbus.Int64(значение) и т. д.\nДополнительные возможности D-Bus Еще одной важной и широко используемой функцией D-Bus являются сигнальные сообщения. Приложения могут подписываться на сигналы других приложений, указывая, какие из них им интересны. Приложение-продюсер отправляет сигнальные сообщения при возникновении определенных событий, а подписчик получает их асинхронно. Это позволяет избежать необходимости постоянного опроса.\nВ D-Bus есть много других полезных функций, таких как активация служб, аутентификация, самоанализ и т. д., которые выходят далеко за рамки этой статьи.\nПолезные инструменты Для командной строки busctl - наиболее интуитивно понятный и полный инструмент с такими замечательными функциями, как мониторинг или захват трафика D-Bus в виде файла pcap. Однако типы значений должны быть указаны как сигнатуры D-Bus, что сложно, если вы их не очень хорошо знаете. В этом случае проще использовать dbus-send из пакета dbus-tools или qdbus из Qt.\nКогда вы начинаете экспериментировать с D-Bus, изучение иерархии объектов различных приложений становится намного проще с помощью инструмента с графическим интерфейсом, такого как Qt DBUS Viewer (QDbusViewer).\nРекомендованные статьи https://www.freedesktop.org/wiki/Software/dbus/ https://dbus.freedesktop.org/doc/dbus-tutorial.html https://dbus.freedesktop.org/doc/dbus-specification.html https://develop.kde.org/docs/features/d-bus/introduction_to_dbus/ https://en.wikipedia.org/wiki/D-Bus https://networkmanager.dev/docs/api/latest/spec.html https://dbus.freedesktop.org/doc/dbus-python/index.html ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/d-bus-overview/","summary":"\u003cp\u003eПеревод статьи написанной \u003ca href=\"https://fedoramagazine.org/author/ihuguet/\"\u003eÍñigo Huguet\u003c/a\u003e для\nсайта \u003ca href=\"https://fedoramagazine.org/\"\u003eFedora Magazine\u003c/a\u003e. Тут будет общее описание и принципы работы D-Bus.\u003c/p\u003e","title":"Обзор D-Bus"},{"content":"Может так случится, что появляется необходимость завести свой tcp сервис на 443 порту. Но что же делать, если там уже висит сайт, который доступен через nginx reverse-proxy. Это можно сделать с помощью конфига в nginx.\nНеобходимые условия Для работы этого хака нужно, что бы nginx был скомпилирован с модулем ngx_stream_ssl_preread_module. Проверить это можно такой командой:\n1 nginx -V 2\u0026gt;\u0026amp;1 | grep \u0026#39;with-stream_ssl_preread_module\u0026#39; Если в выводе что-то есть, значит grep нашел модель. Если модуля нет, то нужно сделать его \u0026ldquo;есть\u0026rdquo;. Это выходит за рамки заметки.\nПример с ssh сервисом Почему ssh? Он есть и нужен почти всегда, а некоторые фаерволы блокируют все запросы мимо 80/443 портов, во избежание. Особенно это относится к публичным WiFi сетям и тому подобным.\nИтак, добавляем stream, который будет слушать наш 443 порт и распределять подключения. При этому upstream-ы могут быть как на внутреннем ip, так и на публичном или каком-то удаленном адресе. Например, для ssh часто меняют порт, но с этим методом можно будет вообще убрать общедоступный порт из сети и привязать демон sshd на 127.0.0.1.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 stream { upstream ssh { server 127.0.0.1:22; } upstream web { server 127.0.0.1:443; } map $ssl_preread_protocol $upstream { default ssh; \u0026#34;TLSv1\u0026#34; web; \u0026#34;TLSv1.1\u0026#34; web; \u0026#34;TLSv1.2\u0026#34; web; \u0026#34;TLSv1.3\u0026#34; web; } # SSH and SSL on the same port server { listen 443; proxy_pass $upstream; ssl_preread on; } } Теперь подключится к ssh можно будет так:\n1 ssh server.ip.addr -p 443 При обычном https запросе (скажем от браузера) - будет открыт сайт, который досутпен на апстреме web, что не помешает сайту быть доступным для пользователей и краулеров.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/nginx-i-ne-ssltls-servis-na-443-portu/","summary":"\u003cp\u003eМожет так случится, что появляется необходимость завести свой tcp сервис на 443 порту. Но что же делать, если там уже\nвисит сайт, который доступен через nginx reverse-proxy. Это можно сделать с помощью конфига в nginx.\u003c/p\u003e","title":"Nginx и не SSL/TLS сервис на 443 порту"},{"content":"После обновления на Fedora 39 столкнулся с проблемой, что перестала запускаться сборка Docker из рабочего проекта. А так как сегодня понедельник, то нужно срочно (малой кровью) решить все, не откатываясь на предыдущую версию дистрибутива.\nОшибка Ошибки менялись в зависимости \u0026ldquo;холодный\u0026rdquo; это запуск, или уже созданных контейнеров, или ручной запуск (рабочий проект использует docker-compose файл). Но вот несколько вариантов что \u0026ldquo;выпали\u0026rdquo; мне:\n1 Cannot start service ***: failed to create task for container: failed to create shim task: ttrpc: cannot marshal unknown type: *task.CreateTaskRequest: unknown 1 Error response from daemon: failed to create task for container: failed to start shim: mkdir /var/run/docker/containerd/daemon/io.containerd.runtime.v2.task/moby/*****************: file exists: unknown Даже запуск docker run hellow-world выдает такую же ошибку.\nОказалось что это известная проблема релиза. Но о ней почему-то не было указано в сводке проблем при обновлении. Так же под это дело уже есть оформленный баг.\nРешение Все дело в пакете containerd-1.6.23-1.fc39.x86_64. Именно манипуляции с ним решают проблему:\ndowngrade пакета до containerd-1.6.19-2.fc39.x86_64; использование пакеты из copr-репозитория с пакетом containerd-1.7.0-5.fc39.x86_64. Метод с откатыванием пакета я не проверял, так как не очень понял где \u0026ldquo;добыть\u0026rdquo; предыдущую версию, собранную именно под релиз f39. В актуальном репозитории сейчас есть только такие версии:\n1 2 3 4 ❯ dnf --showduplicates list --enablerepo=updates-testing containerd containerd.x86_64 1.6.23-1.fc39 fedora containerd.x86_64 1.6.23-1.fc39 updates-testing Поэтому пошел путем номер два. Добавил репозиторий в систему:\n1 sudo dnf copr enable sergiomb/docker2 И обновил пакет:\n1 sudo dnf upgrade containerd После этого все заработало:\n1 2 3 4 ❯ docker run --rm hello-world | head -3 Hello from Docker! This message shows that your installation appears to be working correctly. Далее когда решение \u0026ldquo;доедет\u0026rdquo; до основного репозитория, нужно будет только отключить copr.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/fedora-39-problema-s-zapuskom-docker-posle-obnovleniya/","summary":"\u003cp\u003eПосле обновления на \u003ca href=\"https://fedoramagazine.org/announcing-fedora-linux-39/\"\u003eFedora 39\u003c/a\u003e столкнулся с проблемой, что\nперестала запускаться сборка Docker из рабочего проекта. А так как сегодня понедельник, то нужно срочно (малой кровью)\nрешить все, не откатываясь на предыдущую версию дистрибутива.\u003c/p\u003e","title":"Fedora 39: проблема с запуском docker после обновления"},{"content":"Вот такое приключилось сегодня: запустил обновляться OPNsense. Процесс шел относительно долго ( дольше чем обычно). В результате после загрузки роутера одно из двух ядер CPU уперлось в 100% утилизации. Нужно найти и \u0026ldquo;остановить\u0026rdquo;.\nUPD 28.11.2023: Добавил решение через web-gui.\nПоиск проблемы Для начала глянем в top-е кто же выедает CPU. В результате был найден python3.9. Но это мало о чем может сказать, ведь это интерпретатор и под ним может быть запущен любой скрипт. Тогда подключаем ps (мы уже знаем кто \u0026quot; приблизительно нам нужен, поэтому grep-аем по подстроке python):\n1 ps awwux | grep python Получает результат что-то вроде этого (не мой):\n1 2 3 4 5 6 7 root 21837 100.0 0.7 43104 30936 - Rs 16:45 25:49.91 /usr/local/bin/python3 /usr/local/opnsense/scripts/netflow/flowd_aggregate.py (python3.9) root 273 0.1 0.9 75060 37460 - S 16:44 0:28.82 /usr/local/bin/python3 /usr/local/opnsense/service/configd.py console (python3.9) root 268 0.0 0.5 36256 22692 - Is 16:43 0:03.69 /usr/local/bin/python3 /usr/local/opnsense/service/configd.py (python3.9) root 21359 0.0 0.4 25764 15512 - Ss 16:45 0:05.88 /usr/local/bin/python3 /usr/local/opnsense/scripts/dhcp/unbound_watcher.py --domain home.addars (python3.9) root 78987 0.0 0.3 23736 12468 - S 16:45 0:01.42 /usr/local/bin/python3 /usr/local/sbin/configctl -e -t 0.5 system event config_changed (python3.9) root 92022 0.0 0.3 23972 12580 - S 16:45 0:01.37 /usr/local/bin/python3 /usr/local/opnsense/scripts/syslog/lockout_handler (python3.9) root 30635 0.0 0.1 12748 2364 0 S+ 17:11 0:00.01 grep python Как видно, нам нужен некто flowd_aggregate.py. Исходя из названия, это какой-то скрипт, который используется вместе с netflow.\nРешение На форуме OPNsense, есть предложения по выключению IPv6. К сожалению это мне не помогло (хотя может быть в каких-то случаях и будет работать). Судя по тому, что в скрипте есть слово aggregate скрипт что-то считает. А так как netflow - это штука для сбора трафика, то есть вероятность, что очистка данных поможет. Именно это шаг решил проблему в моем случае. Нужно выполнить:\n1 configctl netflow flush После чего, чуть призадумавшись, OPNsense ответит в консоль кратное OK. Нагрузка сразу упала.\nЧерез web-gui Еще один вариант для тех, у кого не настроен ssh, а ctrl+v в web-консоли proxmox не хочет работать, через web-gui. Для этого нужно зайти по адресу /reporting_settings.php и нажать кнопку Repair Netflow Data. Буквально через пару десятков секунд (в предупреждении говорится что все зависит от размера логов и мощности cpu) все заработает как нужно.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/opnsense-posle-obnovleniya-polnaya-utilizaciya-odnogo-yadra-cpu/","summary":"\u003cp\u003eВот такое приключилось сегодня: запустил обновляться \u003ca href=\"https://opnsense.org/\"\u003eOPNsense\u003c/a\u003e. Процесс шел относительно долго (\nдольше чем обычно). В результате после загрузки роутера одно из двух ядер CPU уперлось в 100% утилизации. Нужно найти\nи \u0026ldquo;остановить\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUPD 28.11.2023\u003c/strong\u003e: Добавил решение \u003ca href=\"/posts/opnsense-posle-obnovleniya-polnaya-utilizaciya-odnogo-yadra-cpu/#%d1%87%d0%b5%d1%80%d0%b5%d0%b7-web-gui\"\u003eчерез web-gui\u003c/a\u003e.\u003c/p\u003e","title":"OPNsense: после обновления полная утилизация одного ядра CPU"},{"content":"Есть у нас сайт, движком которому выступает Hugo. Написав что-то новое или поменяв структуру хочется как-то более менее просто \u0026ldquo;посмотреть\u0026rdquo; результат не только локально, но и выложить на временный домен.\nTL;DR: полный файл будет в конце.\nДля чего Для чего выкладывать на временный домен? Для этого может быть несколько причин, например:\nглянуть с \u0026ldquo;живых\u0026rdquo; устройств; натравить инструмент проверки оптимизации (тот же PageSpeed Insights); показать кому-то далекому от компьютера результат. Так же хочется не подчищать домены самому за собой, научится деплоить релизную версию максимально просто. Итого пожелания такие:\nавтоматическое создание временных поддоменов; автоматическое удаление таких поддоменов (по таймауту или при удалении ветки); автоматический выкладывание сайта на основной домен синхронизированный с веткой master. Стек технологий Будет использоваться следующий технологии для решения проблемы:\nHugo, так как сайт уже на Hugo и без него никуда; GitLab CI, так как код хранится именно на этой площадке (есть резервные синхронизации, но это резерв, а не основной репозиторий); nginx, так как исторически сложилось, что на хосте именно этот сервер обслуживает статику; Cloudflare, так как это довольно-таки удобный DNS провайдер с функциями DDoS защиты, CDN и дашбордом (не хочется прикручивать аналитику/метрику на сайт). Файл .gitlab-ci.yml Файл будем разбирать по частям, а блоки по умолчанию пропускать. Тут только описание нужных параметров.\nВсе будет собираться внутри alpine. Почему так? Потому что официального образа Hugo в DockerHub нет, и мы будем использовать готовый пакет для Alpine Linux. Тем более, что нам так же понадобятся и другие программы, доступные в репозитории пакетов. Посему начнем .gitlab-ci.yml так:\n1 2 default: image: alpine:3 Основные этапы Итого основные этапы (stages) таковы:\nbuild - сборка статического сайта; upload - загрузка сайта на хост с nginx; stage - стейджинг сайта (добавление домена); cleanup - окончание стейджинга (удаление домена, удаление файлов сайта); release - сброс кеша Cloudflare после загрузки финальной версии. Вспомогательные функции В обработчиках встречаются одинаковые места/блоки, которые будут вынесены в общие функции.\nsetup-ssh Функция по добавлению и настройке ssh клиента, который нужен для загрузки и очистки файлов на хосте с nginx.\n1 2 3 4 5 6 .setup-ssh: \u0026amp;setup-ssh - apk add --update openssh-client - eval $(ssh-agent -s) - chmod 400 \u0026#34;$SERVER_AUTH_KEY\u0026#34; \u0026amp;\u0026amp; ssh-add \u0026#34;$SERVER_AUTH_KEY\u0026#34; - mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh - cp \u0026#34;$SERVER_HOST_KEY\u0026#34; ~/.ssh/known_hosts \u0026amp;\u0026amp; chmod 644 ~/.ssh/known_hosts install-curl-jq-jo Функция для установки зависимостей используемых при общении с Cloudflare API:\n1 2 .install-curl-jq-jo: \u0026amp;install-curl-jq-jo - apk add --update curl jq jo Тут все стандартно: curl - как клиент и jq/jo - для разбора и сборки json.\nget-cf-hostnames Функция по поиску стейджевого поддомена в списке доступных в Cloudflare и записи данных в переменную, которая будет в дальнейшем сигнализировать нужно ли создавать/удалять поддомен или можно только сбросить кеш.\n1 2 3 .get-cf-hostnames: \u0026amp;get-cf-hostnames - curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records?type=CNAME\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; | tee hostnames.json - CF_HOSTNAME_ID=$(jq -r .\u0026#34;result[] | select(.name==\\\u0026#34;$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN\\\u0026#34;) | .id\u0026#34; hostnames.json) Окружения Часть этапов будет доступна либо в одном, либо в другом окружении. Это нужно, что бы была возможность разделить некоторые настройки, которые влияют на сборку или отображение сайта.\nУсловия запуска задач Для каждой задачи в GitLab (job), будем указывать rules блок. Блок ниже будет ограничивать работу задачи только продакшен окружением (master или main, в зависимости от настроек репозитория):\n1 2 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH Для задач стейджинга указан такой блок:\n1 2 3 4 rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH Сборка сайта Это первый этап для каждого из окружений. Сборка немного отличается, и для продакшен окружения сборка будет выполнена так:\n1 2 3 script: - apk add --update hugo - hugo -e production -b \u0026#34;https://$SITE_DOMAIN/\u0026#34; Тут мы указываем, что сборка должна пройти в production окружении и устанавливаем основной домен сайта в качестве базового URL. Для стейджинга используются частично другие настройки:\n1 2 3 script: - apk add --update hugo - hugo -D -b \u0026#34;https://$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN/\u0026#34; Тут сборка происходит вместе с шаблонами (draft) статей. Так же указан стейджевый домен конкретной ветки. Это важно, так как у нас одновременно может быть несколько стейдж окружений в рамках одного репозитория.\nТак же указываем срок хранения артефактов (они будут переданы в следующие этапы):\n1 2 3 4 artifacts: paths: - public/ expire_in: 1 days Срок хранения будет один день, что бы если следующий этап (загрузка) \u0026ldquo;свалится\u0026rdquo;, то у нас были сутки на правки без новой сборки всего сайта. Это может быть удобным для бесплатного аккаунта GitLab (есть лимиты на время работы CI), либо если сборка занимает продолжительное время.\nЗагрузка на хост nginx Загрузка на хост будет одинакова для всех окружений:\n1 2 3 script: - apk add --update rsync - rsync -at --delete --progress ./public/. \u0026#34;${SERVER_URI}:/var/www/html/${SITE_DOMAIN}/${CI_COMMIT_REF_SLUG}\u0026#34; Тут для каждого окружения будет создана своя папка. При этом при обновлении окружения папка будет полностью обновлена с тем же путем в рамках файловой системы сервера.\nОбновление продакшен окружения После загрузки продакшен окружения нужно только сбросить кеш Cloudflare, что делается простым скриптом:\n1 2 3 4 script: - DATA=$( jo purge_everything=\u0026#34;true\u0026#34; ) - curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json - jq -e \u0026#39;.success?\u0026#39; result.json Тут собирается специальный json, который говорит что нужно сбросить весь кеш. Этот json отправляется в Cloudflare API и проверяется \u0026ldquo;удачность\u0026rdquo; выполнения запроса.\nЗапуск/обновление стейджевого окружения Работу со стейджевым окружением нужно разделить на два этапа: запуск и остановку.\nЗапуск стейджевого окружения Для запуска стейджевого окружения используется такой скрипт:\n1 2 3 4 5 6 7 8 9 10 11 12 script: - ssh \u0026#34;${SERVER_URI}\u0026#34; ln -sf \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_COMMIT_REF_SLUG}\u0026#34; \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_ENVIRONMENT_SLUG}\u0026#34; - \u0026gt; if [ -z \u0026#34;${CF_HOSTNAME_ID}\u0026#34; ]; then DATA=$( jo type=\u0026#34;CNAME\u0026#34; name=\u0026#34;${CI_ENVIRONMENT_SLUG}.${SITE_DOMAIN}\u0026#34; content=\u0026#34;${SITE_DOMAIN}\u0026#34; ttl=\u0026#34;1\u0026#34; proxied=\u0026#34;true\u0026#34; ) curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json jq -e \u0026#39;.success?\u0026#39; result.json else DATA=$( jo purge_everything=\u0026#34;true\u0026#34; ) curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json fi - jq -e \u0026#39;.success?\u0026#39; result.json Тут мы указываем ссылку для того, что бы nginx начал обработку стейджевого домена. Так же в зависимости от того есть ли уже стейджевый домен в Cloudflare мы его либо создадим, либо сбросим для него кеш.\nТак же важно указать в параметрах окружения к этой задаче следующее:\n1 2 3 4 5 6 environment: name: staging/$CI_COMMIT_REF_SLUG url: https://$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN auto_stop_in: 30 minutes on_stop: teardown action: start Главный параметры тут:\nauto_stop_in - автоматическое отключение стейджевого сайта через период времени; on_stop - название задачи, для остановки окружения (задачу будет запущена при удалении ветки, таймаута жизни окружения и т.д.); action - указывает что оружение запускается этой задачей, что бы для него появилась ссылка в интерфейсе GitLab и начался отсчет его времени жизни. Готовый файл Так выглядит готовый файл на момент написания статьи:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 default: image: alpine:3 .setup-ssh: \u0026amp;setup-ssh - apk add --update openssh-client - eval $(ssh-agent -s) - chmod 400 \u0026#34;$SERVER_AUTH_KEY\u0026#34; \u0026amp;\u0026amp; ssh-add \u0026#34;$SERVER_AUTH_KEY\u0026#34; - mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh - cp \u0026#34;$SERVER_HOST_KEY\u0026#34; ~/.ssh/known_hosts \u0026amp;\u0026amp; chmod 644 ~/.ssh/known_hosts .install-curl-jq-jo: \u0026amp;install-curl-jq-jo - apk add --update curl jq jo .get-cf-hostnames: \u0026amp;get-cf-hostnames - curl -s -X GET \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records?type=CNAME\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; | tee hostnames.json - CF_HOSTNAME_ID=$(jq -r .\u0026#34;result[] | select(.name==\\\u0026#34;$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN\\\u0026#34;) | .id\u0026#34; hostnames.json) stages: - build - upload - stage - cleanup - release build-dev: stage: build variables: GIT_SUBMODULE_STRATEGY: recursive script: - apk add --update hugo - hugo -D -b \u0026#34;https://$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN/\u0026#34; artifacts: paths: - public/ expire_in: 1 days environment: name: staging/$CI_COMMIT_REF_SLUG url: https://$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN action: prepare rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH build-production: stage: build variables: GIT_SUBMODULE_STRATEGY: recursive script: - apk add --update hugo - hugo -e production -b \u0026#34;https://$SITE_DOMAIN/\u0026#34; artifacts: paths: - public/ expire_in: 1 days environment: name: production action: prepare rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH upload-site: stage: upload needs: - job: build-dev optional: true - job: build-production optional: true before_script: - *setup-ssh script: - apk add --update rsync - rsync -at --delete --progress ./public/. \u0026#34;${SERVER_URI}:/var/www/html/${SITE_DOMAIN}/${CI_COMMIT_REF_SLUG}\u0026#34; setup-cf: stage: stage needs: - upload-site before_script: - *install-curl-jq-jo - *setup-ssh - *get-cf-hostnames script: - ssh \u0026#34;${SERVER_URI}\u0026#34; ln -sf \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_COMMIT_REF_SLUG}\u0026#34; \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_ENVIRONMENT_SLUG}\u0026#34; - \u0026gt; if [ -z \u0026#34;${CF_HOSTNAME_ID}\u0026#34; ]; then DATA=$( jo type=\u0026#34;CNAME\u0026#34; name=\u0026#34;${CI_ENVIRONMENT_SLUG}.${SITE_DOMAIN}\u0026#34; content=\u0026#34;${SITE_DOMAIN}\u0026#34; ttl=\u0026#34;1\u0026#34; proxied=\u0026#34;true\u0026#34; ) curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json jq -e \u0026#39;.success?\u0026#39; result.json else DATA=$( jo purge_everything=\u0026#34;true\u0026#34; ) curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json fi - jq -e \u0026#39;.success?\u0026#39; result.json environment: name: staging/$CI_COMMIT_REF_SLUG url: https://$CI_ENVIRONMENT_SLUG.$SITE_DOMAIN auto_stop_in: 30 minutes on_stop: teardown action: start rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH teardown: stage: cleanup before_script: - *install-curl-jq-jo - *setup-ssh - *get-cf-hostnames script: - \u0026gt; if [ -z \u0026#34;${CF_HOSTNAME_ID}\u0026#34; ]; then curl -s -X DELETE \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/dns_records/${CF_HOSTNAME_ID}\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; | tee result.json jq -e \u0026#39;.success?\u0026#39; result.json fi - ssh \u0026#34;${SERVER_URI}\u0026#34; unlink \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_ENVIRONMENT_SLUG}\u0026#34; - ssh \u0026#34;${SERVER_URI}\u0026#34; rm -r \u0026#34;/var/www/html/${SITE_DOMAIN}/${CI_COMMIT_REF_SLUG}\u0026#34; when: manual variables: GIT_STRATEGY: none environment: name: staging/$CI_COMMIT_REF_SLUG action: stop rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: never - if: $CI_COMMIT_BRANCH deploy: stage: release needs: - upload-site before_script: - *install-curl-jq-jo script: - DATA=$( jo purge_everything=\u0026#34;true\u0026#34; ) - curl -s -X POST \u0026#34;https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache\u0026#34; -H \u0026#34;Authorization:Bearer ${CLOUDFLARE_TOKEN}\u0026#34; -H \u0026#34;Content-Type:application/json\u0026#34; --data \u0026#34;${DATA}\u0026#34; | tee result.json - jq -e \u0026#39;.success?\u0026#39; result.json only: - main environment: production Такой файл позволяет легко, в автоматическом режиме запускать временные поддомены по веткам и выкатывать свежую версию по пушу в master ветку или слиянию мерж-реквеста в нее.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/gitlab-ci-hugo-na-primer-etogo-sajta/","summary":"\u003cp\u003eЕсть у нас сайт, движком которому выступает \u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e. Написав что-то новое или поменяв структуру\nхочется как-то более менее просто \u0026ldquo;посмотреть\u0026rdquo; результат не только локально, но и выложить на временный домен.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTL;DR\u003c/strong\u003e: полный файл будет \u003ca href=\"/posts/gitlab-ci-hugo-na-primer-etogo-sajta/#%d0%b3%d0%be%d1%82%d0%be%d0%b2%d1%8b%d0%b9-%d1%84%d0%b0%d0%b9%d0%bb\"\u003eв конце\u003c/a\u003e.\u003c/p\u003e","title":"Gitlab CI + hugo на пример этого сайта"},{"content":"По умолчанию, при монтировании smb/cifs удаленной папки файлы внутри нее показываются, как файлы пользователя и группы root. Но частенько бывает так, что у нас есть пользователь под которым мы логинимся на smb/cifs сервере и локальный пользователь не root.\nМинимальный набор параметров В самом простом виде команда для монтирования выглядит так:\n1 mount -t cifs -o username=user_name,password=user_password //server_name/share_name /mnt/share_name При этом в локально папке файлы и папки будут отображаться так:\n1 2 3 4 5 ❯ ls -l /mnt/share_name итого 0 drwxr-xr-x. 2 root root 0 окт 12 14:00 some drwxr-xr-x. 2 root root 0 окт 4 16:12 other drwxr-xr-x. 2 root root 0 окт 16 22:52 folder Да, на сервер все изменения будут прилетать именно от имени пользователя, указанного в параметрах монтирования, но локально с этими данными можно будет работать только от root-а.\nМонтируем для локального пользователя Для использования локальным пользователем хочется, что бы все в примонтированной папке было с нашим uid/gid. Для этого есть специальные параметры монтирования (они довольно интуитивны по названию, поэтому догадать какой для чего не сложно):\nuid - файлы отображаются как принадлежащие пользователю с указанным цифровым идентификатором; gid - файлы отображаются как принадлежащие группе с указанным цифровым идентификатором. Итого дополним нашу команду:\n1 mount -t cifs -o username=user_name,password=user_password,uid=$(id -u),gid=$(id -g) //server_name/share_name /mnt/share_name Тогда папка уже будет выглядеть так:\n1 2 3 4 5 ❯ ls -l итого 0 drwxr-xr-x. 2 user group 0 окт 12 14:00 some drwxr-xr-x. 2 user group 0 окт 4 16:12 other drwxr-xr-x. 2 user group 0 окт 16 22:52 folder ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/mapping-polzovatelej-pri-montirovanii-smb/","summary":"\u003cp\u003eПо умолчанию, при монтировании \u003ccode\u003esmb\u003c/code\u003e/\u003ccode\u003ecifs\u003c/code\u003e удаленной папки файлы внутри нее показываются, как файлы пользователя и\nгруппы \u003ccode\u003eroot\u003c/code\u003e. Но частенько бывает так, что у нас есть пользователь под которым мы логинимся на \u003ccode\u003esmb\u003c/code\u003e/\u003ccode\u003ecifs\u003c/code\u003e сервере и\nлокальный пользователь не \u003ccode\u003eroot\u003c/code\u003e.\u003c/p\u003e","title":"Маппинг пользователей при монтировании smb"},{"content":"Systemd-timers как способ запускать своих скриптов в нужное время. Можно конечно пользоваться старым добрым cron, но таймеры гибче позволяют настроить время и периодичность запуска.\nПочему Для периодического запуска скриптов есть cron, но у него есть несколько проблем, которые могут мешать:\nкрон может быть только периодическим, не получится полноценно описать \u0026ldquo;календарный\u0026rdquo; или событийный вариант запуска ( разве что внутри скрипта проверять дату-вермя); крон срабатывает раз в минуту, то есть минимальный период времени - 1 минута (есть всякого уровня \u0026ldquo;хаки\u0026rdquo;, вроде вызова с предварительным sleep 15, но это хаки); крон просто запускает скрипт, as is (проверка того что скрипт еще работает, все то что можно сделать в декларативной форме в unit-файле systemd - нужно делать самому); крон скрипты не умеют в \u0026ldquo;зависимости\u0026rdquo;; крон скрипты обычно хранятся внутри /etc, а не все могут туда писать. Может еще есть какие мелочи, но это те ограничения с которыми приходилось сталкиваться. Для таймеров же для меня есть два основных недостатка:\nнеобходимо два разных файла (unit - что выполнить, timer - когда выполнить), за исключением временных задач; нет отправки email уведомлений (EMAILTO из cron), тут нужно подумать об этом самому. Для меня минусы не столь значительны и перекрываются плюсами. Поэтому приступим.\nХранение таймеров Итак, что бы создать timer - нужен unit файл. Как он создается, какой он может быть и тд оставим за рамками этого текста. Для нашего пример это будет task.service. Таймеры по месту хранения будут двух типов.\nПроверить таймеры в системе Начнем с того, что таймеров есть два списка: системный и пользовательский. Вызов для работы с ними отличается использованием или не использованием ключа --user.\nДля просмотра всех таймеров в системе можно воспользоваться командой (для всех таймеров текущего пользователя добавить ключ --user):\n1 systemctl list-timers В полученной таблице будут следующие столбцы:\nNEXT - дата и время следующего запуска таймера; LEFT - время до следующего запуска таймера; LAST - дата и время последнего запуска таймера; PASSED - время с последнего запуска таймера; UNIT - имя timer-файла; ACTIVATES - имя unit-файла для запуска. По умолчанию имя timer и unit файлов должны быть одинаковыми (за исключением этих суффиксов), но есть возможность указать кастомное имя timer-файла и привязать его к стандартному unit-файлу. Пример такой записи:\n1 2 3 4 5 6 7 8 9 [Unit] Description=\u0026#34;Таймер вне зависимости от названия файла запустит task.service\u0026#34; [Timer] OnStartupSec=1min Unit=task.service [Install] WantedBy=multi-user.target Где искать файлы Все таймеры хранятся в 3 основных местах:\n/lib/systemd/system - \u0026ldquo;встроенные\u0026rdquo; таймеры; /etc/systemd/system - кастомные системные таймеры; ~/.config/systemd/user - кастомные таймеры пользователя. Типы таймеров по времени запуска По времени запуска есть два типа таймеров:\nRealtime timers - таймеры реального времени привязанные к датам; Monotonic timers - монотонные таймеры привязанные к периодам времени. При работе с таймерами нужно иметь в виду, что со временем в системе могут происходить разного рода особенности, например:\nсон компьютера, при этом время не останавливается; может поменяться временная зона; само время может \u0026ldquo;ускорится\u0026rdquo; или \u0026ldquo;замедлиться\u0026rdquo; для синхронизации. Таймеры реального времени В таймере реального времени главный параметр - OnCalendar внутри блока [Timer]. Параметры может быть:\nOnCalendar=daily - ежедневный запуск (в 00:00:00); OnCalendar=monday *-10-* 17:00 - каждый понедельник октября в 17 часов; OnCalendar=08:00:00 - каждый день в 8 утра. Полное описание формата можно посмотреть в systemd.time(7). Для локального тестирования можно воспользоваться командой:\n1 systemd-analyze calendar \u0026#34;[дата для теста]\u0026#34; Ответ получается такого вида:\n1 2 3 4 5 6 ❯ systemd-analyze calendar \u0026#34;monday *-10-* 17:00\u0026#34; Original form: monday *-10-* 17:00 Normalized form: Mon *-10-* 17:00:00 Next elapse: Mon 2023-10-16 17:00:00 +04 (in UTC): Mon 2023-10-16 13:00:00 UTC From now: 15h left Так же для такого типа таймера есть несколько вспомогательных параметров (основные для использования мной):\nPersistent - значение true или false, при отсутствии времени предыдущего запуска сразу запускает таймер; RandomizedDelaySec - позволяет задать \u0026ldquo;промежуток\u0026rdquo; времени в который можно выполнить таймер, если на одно и то же время ставится много таймеров, то это позволяет \u0026ldquo;размазать\u0026rdquo; их запуск по этому промежутку; FixedRandomDelay - \u0026ldquo;запоминает\u0026rdquo; промежуток для таймера с параметром выше; AccuracySec - это попытка \u0026ldquo;собрать похожие по времени таймеры\u0026rdquo;, что не \u0026ldquo;будить\u0026rdquo; процессор лишний раз. По умолчанию значение здесь 1 минута, для рандомизированно запущенных таймеров лучше всего проставить значение 1us. Монотонные таймеры Основные параметры монотонных таймеров:\nOnActiveSec - период времени с момента активации таймера; OnBootSec - время после загрузки (для контейнеров этот параметр будет эквивалентом OnStartupSec), не работает для пользовательских таймеров; OnStartupSec - время со старта сервис-менеджера, для системных таймеров очень близок к OnBootSec, для контейнеров - это время запуска контейнера, для пользователя - время входа в систему; OnUnitActiveSec - определяет промежуток времени с последней активации таймера, в который таймер должен быть повторно активирован (если за сеанс нужно выполнять задачу несколько раз); OnUnitInactiveSec - то же самое что выше, но учитывает время деактивации. Дополнительные же параметры тут такие:\nOnClockChange - выполнить таймер при смещении времени (синхронизация, установка времени руками); OnTimezoneChange - выполнить таймер при изменении часового пояса. Пример из моей жизни Подгрузка базы паролей pass У pass, который используется для хранения паролей, есть возможность хранить БД под версионированием в git. Но стандартные механизмы умеют только автоматом пушить изменения. Так как у меня есть несколько машин, нужно время от времени эти изменения на каждую из них пулить. Для этого был написан простой скрипт и запущен таймер по нему, который через минуту после включения пулит свежую БД для pass, и далее проверяет обновления каждые 8 часов.\n1 2 3 4 5 6 7 8 9 [Unit] Description=Pass pull git updates [Timer] OnStartupSec=1min OnUnitActiveSec=8h [Install] WantedBy=timers.target Отложенный старт Docker Появилась необходимость отсрочить старт сервиса Docker (и всех контейнеров). Для этого всего лишь убрал сервис докера из автозагрузки:\n1 systemctrl disable docker И добавил таймер для запуска Docker через 5 минут после загрузки системы.\n1 2 3 4 5 6 7 8 [Unit] Description=Docker wait to start [Timer] OnBootSec=5min [Install] WantedBy=timers.target Таймер для certbot идущий в комплекте с пакетом Запускается каждый день два раза в сутки в произвольное время в пределах от 00:00 до 12:00 и от 12:00 до 00:00.\n1 2 3 4 5 6 7 8 9 10 [Unit] Description=This is the timer to set the schedule for automated renewals [Timer] OnCalendar=*-*-* 00/12:00:00 RandomizedDelaySec=12hours Persistent=true [Install] WantedBy=timers.target ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/systemd-timers/","summary":"\u003cp\u003e\u003ccode\u003eSystemd-timers\u003c/code\u003e как способ запускать своих скриптов в нужное время. Можно конечно пользоваться старым\nдобрым \u003ccode\u003ecron\u003c/code\u003e, но таймеры гибче позволяют настроить время и периодичность запуска.\u003c/p\u003e","title":"SystemD Timers"},{"content":"Иногда бывает нужно временно поправить баг или опечатку в стороннем пакете composer. Попробуем найти решение.\nПроблема Есть у нас зависимость от какого-то пакета. Среди файлов этого пакета (внутри vendor) мы нашли баг/опечатку, который мешает нам \u0026ldquo;жить\u0026rdquo; 🙂. А тут у нас еще и CI теперь есть, и все что было поправлено локально (внутри vendor) в нее не попадет. Тут мы начинаем думать\u0026hellip;\nРешения Мы можем \u0026ldquo;подменить\u0026rdquo; реализацию из пакета на свою. Для этого мы находим проблемный класс, пускай это будет файл:\n1 vendor/maintainer/package/src/ImplementationBug.php Нужно скопировать его куда-то в свой проект, пускай это будет папка overrides в корне проекта (или где-то еще, положение не принципиально, но лучше что бы она была вне psr-autoload папок).\nТеперь в composer.json внутри autoload создаем свойство exclude-from-classmap (ссылка на документацию) в виде списка исключений и указываем наш файл там. Так же не забываем добавить пусть к нашему \u0026ldquo;исправленному файлу\u0026rdquo; в папке overrides:\n1 2 3 4 5 6 7 8 9 10 { \u0026#34;autoload\u0026#34;: { \u0026#34;exclude-from-classmap\u0026#34;: [ \u0026#34;vendor/maintainer/package/src/ImplementationBug.php\u0026#34; ], \u0026#34;files\u0026#34;: [ \u0026#34;overrides/ImplementationBug.php\u0026#34; ] } } Важно отметить: namespace в исправленном файле должен остаться прежним, при этом название файла может не совпадать с оригиналом, ведь мы напрямую подгружаем файл. Итого класс имеет \u0026ldquo;оригинальный\u0026rdquo; namespace и имя класса, и при потребности его использовать он уже окажется загружен, без необходимости поиска его через psr-autoload.\nДополнительно Так как этот баг/опечатка могут оказаться не найденными долгое время, лучше добавить issue для мейнтейнера пакета. А еще лучше конечно правильно оформленный PR 😉. Ведь ссылку хоть на issue, хоть на PR можно будет добавить в виде комментария в файл внутри overrides, что бы пришедший после разработчик понял \u0026ldquo;зачем тут это все\u0026rdquo; и поправил когда в основной пакет \u0026ldquo;долетит\u0026rdquo; фикс.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/podmena-faila-v-papke-vendor-cherez-composer/","summary":"\u003cp\u003eИногда бывает нужно временно поправить баг или опечатку в стороннем пакете \u003ccode\u003ecomposer\u003c/code\u003e. Попробуем найти решение.\u003c/p\u003e","title":"Подмена файла в папке vendor через composer"},{"content":"Иногда появляется желание/необходимость прокинуть в виртуальную машину физическое (реальное) железо. Для этого существует такая технология как IOMMU. Приступим.\nТехнические требования Для работы данной технологии нужна поддержка со стороны:\nпроцессора; материнской платы; гипервизора. Процессоры Говорить будем только про x86 процессоры (ARM оставим \u0026ldquo;за бортом\u0026rdquo;, может быть когда-нибудь потом проверю и попробую).\nДля процессоров Intel нам нужны следующие технологии:\nIntel VT-x; Intel VT-d. Для процессоров AMD нужна поддержка:\nAMD-V, AMD-Vi. Материнская плата Тут названия настроек, которые нужно включить и информация о поддержке сильно зависит от производителя материнской платы. Но все это обычно находится в разделе Advanced, и связано с виртуализацией.\nГипервизор Собственно Proxmox 8. В нем есть возможность использовать IOMMU, но по умолчанию она выключена - нужно самому ее ключить. Этим мы и займемся.\nПроверка активности Для того что бы проверить работает ли сейчас режим IOMMU нужно в консоли сервера (ssh или vnc через веб) выполнить следующий код:\n1 dmesg | grep -e DMAR -e IOMMU В выводе мы ищем упоминание включения режима DMAR: IOMMU enabled. Если такое сообщение есть - то IOMMU включен и можно начинать пользоваться. Если режим не включен, то нужно поменять несколько файлов. Так же стоит проверить, что бы был включен режим ремапинга прерываний:\n1 dmesg | grep \u0026#39;remapping\u0026#39; В результате должен быть вывод похожий на:\n1 2 [ 0.134401] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping. [ 0.136043] DMAR-IR: Enabled IRQ remapping in x2apic mode # \u0026lt;==================== Kernel command line В зависимости от варианта установки нужно будет воспользоваться одним из вариантов и добавить в нужный файл параметры:\n1 intel_iommu=on iommu=pt systemd-boot Файл правится добавлением параметров после quiet:\n1 /etc/kernel/cmdline После этого нужно выполнить:\n1 proxmox-boot-tool refresh grub Файл правится добавлением параметров после quiet:\n1 /etc/default/grub После правок выполнить:\n1 update-grub Модули ядра так же стоит добавить подгрузку модулей ядра в файле:\n1 2 3 4 vfio vfio_iommu_type1 vfio_pci vfio_virqfd После чего выполнить команду для обновления initramfs:\n1 update-initramfs -u -k all Проверка После перезагрузки системы результат команды dmesg | grep -e DMAR -e IOMMU должен быть похож на:\n1 [ 0.058451] DMAR: IOMMU enabled Разделение по группам Для возможности проброса конкретных устройств, важно что бы они были разделены по группам. Проверить список групп можно либо в консоли по \u0026ldquo;pcie адресам\u0026rdquo; устройств:\n1 find /sys/kernel/iommu_groups/ -type l Либо в более красивом виде для текущей ноды Proxmox (можно указать руками имя ноды из кластера):\n1 pvesh get /nodes/$(hostname)/hardware/pci --pci-class-blacklist \u0026#34;\u0026#34; Заключени Теперь можно пробовать \u0026ldquo;выдавать\u0026rdquo; виртуалкам настоящие PCIe устройства в пользование. Это могут быть как банальные видеокарты, так и сетевые порты или полноценные NVMe накопители. Именно этим планирую заняться в будущем.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/pcie-passthrough-in-proxmox/","summary":"\u003cp\u003eИногда появляется желание/необходимость прокинуть в виртуальную машину физическое (реальное) железо. Для этого\nсуществует такая технология как \u003ccode\u003eIOMMU\u003c/code\u003e. Приступим.\u003c/p\u003e","title":"Проброс PCIe устройств в Proxmox"},{"content":"Иногда нужно перевести что-то стандартное в Laravel. Например, сообщения валидации, авторизации или завести свои шаблоны сообщений.\nСоздаем структуру Для автоматического создание структуры есть команда (команда скопирует встроенные сообщения для en локализации):\n1 php artisan lang:publish Будут созданы следующие файлы:\nauth.php - сообщения для аутентификации и авторизации; pagination.php - тексты для кнопок/ссылок пагинации; passwords.php - сообщения для процесса сброса пароля; validation.php - сообщения дл встроенных валидаций. Для локализации на свой язык нужно либо скопировать папке en в папку с кодом своего языка, либо создать папку с кодом своего языка и создать \u0026ldquo;соответствующие\u0026rdquo; файлы с заполнением только нужных сообщения.\nТак же сработает если создать папку для своего языка в /resources/lang/[lang_code] руками.\nПереключаем язык приложения Язык приложения можно задать через переменную окружения конфиг файл /config/app.php по ключу locale в нем или вызвав код:\n1 2 3 use Illuminate\\Support\\Facades\\App; App::setLocale($locale); При этом в конфиг файле можно привязаться к переменной окружения, а вариант с php кодом позволит \u0026ldquo;менять\u0026rdquo; локализаци в зависимости от путей роутинга или скажем middleware на сайте.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/laravel-perevody-oshibok-validacii/","summary":"\u003cp\u003eИногда нужно перевести что-то стандартное в Laravel. Например, сообщения валидации, авторизации или завести свои шаблоны\nсообщений.\u003c/p\u003e","title":"Laravel: переводы ошибок валидации"},{"content":"Как то раз заглянул в slow-query.log на проекте и обнаружил там повторяющиеся, двадцатисекундные запросы. Как оказалось причина их оказалась в том, что разработчик не знал/подумал глянуть как запрос строится ORM-кой битрикса. Локально, пока он писал и тестировал все работало замечательно. Но вот прошло около 6 гигабайт данных в таблице - и на первый взгляд простой запрос начал \u0026ldquo;зависать\u0026rdquo; на ~10 секунд 🙁.\nПредыстория Понадобилось найти заказ по значению свойства. Был написан следующий код:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \\Bitrix\\Sale\\Order::getList([ \u0026#39;select\u0026#39; =\u0026gt; [\u0026#39;ID\u0026#39;, \u0026#39;PROPERTY.VALUE\u0026#39;], \u0026#39;filter\u0026#39; =\u0026gt; [ \u0026#39;PROPERTY.CODE\u0026#39; =\u0026gt; \u0026#39;SOME_ORDER_PROPERTY_CODE\u0026#39;, \u0026#39;PROPERTY.VALUE\u0026#39; =\u0026gt; \u0026#39;my value\u0026#39;, ], \u0026#39;runtime\u0026#39; =\u0026gt; [ new \\Bitrix\\Main\\Entity\\ReferenceField( \u0026#39;PROPERTY\u0026#39;, \u0026#39;\\Bitrix\\sale\\Internals\\OrderPropsValueTable\u0026#39;, [\u0026#34;=this.ID\u0026#34; =\u0026gt; \u0026#34;ref.ORDER_ID\u0026#34;], [\u0026#34;join_type\u0026#34; =\u0026gt; \u0026#34;left\u0026#34;] ), ] ]); На первый взгляд простая таблица с данными. Есть несколько столбцов, по одному из них (строковому, что важно) используется фильтрация. Таблицы на первый взгляд тоже не очень большие:\n1 2 3 4 5 6 7 +--------------------------+-----------+ | Table | Size (MB) | +--------------------------+-----------+ | b_sale_order_props_value | 1339 | | b_sale_order | 102 | +--------------------------+-----------+ 2 rows in set (0.01 sec) Смотрим SQL Но вот результат выполнения запроса около 15 (пятнадцати!) секунд. Тут явно что-то не то происходит. Кажется стоило бы глянуть \u0026ldquo;под капот\u0026rdquo; ORM и оценить запрос. Печатаем запрос:\n1 2 3 4 5 6 7 8 SELECT `sale_internals_order`.`ID` AS `ID`, `sale_internals_order_property`.`VALUE` AS `SALE_INTERNALS_ORDER_PROPERTY_VALUE`, `sale_internals_order_property`.`ID` AS `UALIAS_0` FROM `b_sale_order` `sale_internals_order` LEFT JOIN `b_sale_order_props_value` `sale_internals_order_property` ON `sale_internals_order`.`ID` = `sale_internals_order_property`.`ORDER_ID` WHERE UPPER(`sale_internals_order_property`.`CODE`) like upper(\u0026#39;SOME_ORDER_PROPERTY_CODE\u0026#39;) AND UPPER(`sale_internals_order_property`.`VALUE`) like upper(\u0026#39;my value\u0026#39;) А вот и виновник: UPPER. Мы выкидываем возможность глянуть в индекс, что подтверждает EXPLAIN:\n1 2 3 4 5 6 7 +----+-------------+-------------------------------+------------+-------+----------------------+----------------------+---------+---------------------------------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------------------------+------------+-------+----------------------+----------------------+---------+---------------------------------+--------+----------+-------------+ | 1 | SIMPLE | sale_internals_order | NULL | index | PRIMARY | IXS_ORDER_UPDATED_1C | 3 | NULL | 130584 | 100.00 | Using index | | 1 | SIMPLE | sale_internals_order_property | NULL | ref | IX_SOPV_ORD_PROP_UNI | IX_SOPV_ORD_PROP_UNI | 4 | lazurit.sale_internals_order.ID | 35 | 100.00 | Using where | +----+-------------+-------------------------------+------------+-------+----------------------+----------------------+---------+---------------------------------+--------+----------+-------------+ 2 rows in set, 1 warning (0.00 sec) Получается, что мы выбираем все заказы, получаем по ним связанные свойства, потом выполняем приведение этих свойств и их значений в верхний регистр.\nОтступление \u0026ldquo;Зачем\u0026rdquo; Кажется есть понимание ЗАЧЕМ так сделано: так как CMS - универсальная штука, ставящаяся на разного рода хостинги, то нет уверенности, что на очередном хостинге collations могут оказаться настроено с учетом регистра и этого не поменяют, или забудут, или оставят \u0026ldquo;так\u0026rdquo;, или еще какая-то причина, неподвластная разработчикам CMS. Но тогда уже разработчик должен \u0026ldquo;следить\u0026rdquo; за собой, и применять правильное сравнение, если он уверен в значениях для поиска. Встроенный модуль проверки сайта (проверки настроек хостинга), указываем, что нужно использовать регистронезависимое сравнение:\n1 2 3 4 5 6 7 if (defined(\u0026#39;BX_UTF\u0026#39;) \u0026amp;\u0026amp; BX_UTF === true) { if ($character_set_connection != \u0026#39;utf8\u0026#39;) $strError = GetMessage(\u0026#34;SC_CONNECTION_CHARSET_WRONG\u0026#34;, array(\u0026#39;#VAL#\u0026#39; =\u0026gt; \u0026#39;utf8\u0026#39;, \u0026#39;#VAL1#\u0026#39; =\u0026gt; $character_set_connection)); elseif ($collation_connection != \u0026#39;utf8_unicode_ci\u0026#39;) $strError = GetMessage(\u0026#34;SC_CONNECTION_COLLATION_WRONG_UTF\u0026#34;, array(\u0026#39;#VAL#\u0026#39; =\u0026gt; $collation_connection)); } Исправляемся Попробуем приме выше переписать на вариант побыстрее (добавить строгое сравнение в фильтр):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \\Bitrix\\Sale\\Order::getList([ \u0026#39;select\u0026#39; =\u0026gt; [\u0026#39;ID\u0026#39;, \u0026#39;PROPERTY.VALUE\u0026#39;], \u0026#39;filter\u0026#39; =\u0026gt; [ \u0026#39;=PROPERTY.CODE\u0026#39; =\u0026gt; \u0026#39;SOME_ORDER_PROPERTY_CODE\u0026#39;, \u0026#39;=PROPERTY.VALUE\u0026#39; =\u0026gt; \u0026#39;my value\u0026#39;, ], \u0026#39;runtime\u0026#39; =\u0026gt; [ new \\Bitrix\\Main\\Entity\\ReferenceField( \u0026#39;PROPERTY\u0026#39;, \u0026#39;\\Bitrix\\sale\\Internals\\OrderPropsValueTable\u0026#39;, [\u0026#34;=this.ID\u0026#34; =\u0026gt; \u0026#34;ref.ORDER_ID\u0026#34;], [\u0026#34;join_type\u0026#34; =\u0026gt; \u0026#34;left\u0026#34;] ), ] ]); Для проверки своей теории глянем получившийся SQL:\n1 2 3 4 5 6 7 8 SELECT `sale_internals_order`.`ID` AS `ID`, `sale_internals_order_property`.`VALUE` AS `SALE_INTERNALS_ORDER_PROPERTY_VALUE`, `sale_internals_order_property`.`ID` AS `UALIAS_0` FROM `b_sale_order` `sale_internals_order` LEFT JOIN `b_sale_order_props_value` `sale_internals_order_property` ON `sale_internals_order`.`ID` = `sale_internals_order_property`.`ORDER_ID` WHERE `sale_internals_order_property`.`CODE` = \u0026#39;SOME_ORDER_PROPERTY_CODE\u0026#39; AND `sale_internals_order_property`.`VALUE` = \u0026#39;my value\u0026#39;; Снова проверим EXPLAIN:\n1 2 3 4 5 6 7 +----+-------------+-------------------------------+------------+--------+----------------------------------------------------+-------------------------------+---------+------------------------------------------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------------------------+------------+--------+----------------------------------------------------+-------------------------------+---------+------------------------------------------------+------+----------+-------------+ | 1 | SIMPLE | sale_internals_order_property | NULL | ref | ix_perf_b_sale_order_props__1,IX_SOPV_ORD_PROP_UNI | ix_perf_b_sale_order_props__1 | 153 | const | 1 | 10.00 | Using where | | 1 | SIMPLE | sale_internals_order | NULL | eq_ref | PRIMARY | PRIMARY | 4 | lazurit.sale_internals_order_property.ORDER_ID | 1 | 100.00 | Using index | +----+-------------+-------------------------------+------------+--------+----------------------------------------------------+-------------------------------+---------+------------------------------------------------+------+----------+-------------+ 2 rows in set, 1 warning (0.01 sec) А вот и что нам нужно: константный выбор из таблицы свойств, и лишь для выбранных данных присоединение таблицы с заказами. Время выполнения упало, барабанная дробь 🥁, до ~0.06 секунд.\nДополнение Такая же история работает с фильтрацией вхождения в массив вариантов:\n1 2 3 $filter = [ \u0026#39;=PROPERTY.CODE\u0026#39; =\u0026gt; [\u0026#39;first_code\u0026#39;, \u0026#39;second_code\u0026#39;, \u0026#39;third_code\u0026#39;], ]; Такое условие раскладывается на более грустный запрос:\n1 2 3 4 5 6 7 8 9 10 11 12 13 SELECT `sale_internals_order`.`ID` AS `ID`, `sale_internals_order_property`.`VALUE` AS `SALE_INTERNALS_ORDER_PROPERTY_VALUE`, `sale_internals_order_property`.`ID` AS `UALIAS_0` FROM `b_sale_order` `sale_internals_order` LEFT JOIN `b_sale_order_props_value` `sale_internals_order_property` ON `sale_internals_order`.`ID` = `sale_internals_order_property`.`ORDER_ID` WHERE (UPPER(`sale_internals_order_property`.`CODE`) like upper(\u0026#39;first_code\u0026#39;) OR UPPER(`sale_internals_order_property`.`CODE`) like upper(\u0026#39;second_code\u0026#39;) OR UPPER(`sale_internals_order_property`.`CODE`) like upper(\u0026#39;third_code\u0026#39;)) AND UPPER(`sale_internals_order_property`.`VALUE`) like upper(\u0026#39;my value\u0026#39;) Хотя при использовании строгого сравнения:\n1 2 3 $filter = [ \u0026#39;=PROPERTY.CODE\u0026#39; =\u0026gt; [\u0026#39;first_code\u0026#39;, \u0026#39;second_code\u0026#39;, \u0026#39;third_code\u0026#39;], ]; Мы получим запрос через IN (что вроде бы быстрее чем множественный OR):\n1 2 3 4 5 6 7 8 SELECT `sale_internals_order`.`ID` AS `ID`, `sale_internals_order_property`.`VALUE` AS `SALE_INTERNALS_ORDER_PROPERTY_VALUE`, `sale_internals_order_property`.`ID` AS `UALIAS_0` FROM `b_sale_order` `sale_internals_order` LEFT JOIN `b_sale_order_props_value` `sale_internals_order_property` ON `sale_internals_order`.`ID` = `sale_internals_order_property`.`ORDER_ID` WHERE `sale_internals_order_property`.`CODE` in (\u0026#39;first_code\u0026#39;, \u0026#39;second_code\u0026#39;, \u0026#39;third_code\u0026#39;) AND `sale_internals_order_property`.`VALUE` = \u0026#39;my value\u0026#39; ","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/collations-bitrix-orm/","summary":"\u003cp\u003eКак то раз заглянул в slow-query.log на проекте и обнаружил там повторяющиеся, двадцатисекундные запросы. Как оказалось\nпричина их оказалась в том, что разработчик не знал/подумал глянуть как запрос строится ORM-кой битрикса. Локально, пока\nон писал и тестировал все работало замечательно. Но вот прошло около 6 гигабайт данных в таблице - и на первый взгляд\nпростой запрос начал \u0026ldquo;зависать\u0026rdquo; на ~10 секунд 🙁.\u003c/p\u003e","title":"Особенность фильтрации для Bitrix ORM"},{"content":"Вот появилась такая необходимость - нужен DoH в локальной сети. Накидаю туту несколько скриптов/файлов, который самому потом нужно будет \u0026ldquo;только скопировать\u0026rdquo;.\nПредыстория Так уж случилось, что не все dns серверы умеют в DoH из коробки. В моей локальной сети я решил поднять Pi-hole, а он как-раз сам в DoH не умеет. Локальный же провайдер любит у меня заниматься DNS hijacking-ом.\nМожно конечно завернуть весь свой dns трафик по порту скажем в vpn, или настроить отправку через vpn всех запросов на ip выбранных dns-провайдеров. Это решит проблему с подменой, но тут возникает небольшое неудобство: иногда (хоть и редко 🙃) хочется глянуть: подменен ли конкретный адрес у провайдера. И тут приходится думать над временными правилами в файрволе для себя, или отключением отправки всего dns трафика в vpn на время. Это немного не удобно. Да и не все сети (гостевая например) могут быть направлены в свой личный VPN.\nНу а уж глянуть разницу в DNS провайдера и своего DNS, тут вообще без вариантов.\n1 2 3 4 5 6 ❯ diff \u0026lt;(dig @isp.home rutracker.org +short) \u0026lt;(dig rutracker.org +short) 1c1,2 \u0026lt; 188.186.146.207 --- \u0026gt; 104.21.72.173 \u0026gt; 172.67.187.38 И сразу видно - работает ли DoH и что придумал провайдер. Так что решено - далем DoH proxy-dns на ресолвере в сети. Делать будем на примере Cloudflared proxy-dns. Есть варианты с Unbound, BIND, PowerDNS, но так уж \u0026ldquo;исторически сложилось\u0026rdquo;.\nИсходные состояние Исходно у нас есть SBC с установленной Armbian 23.02. На него накачен Pi-hole. Это процесс мы пропустим на данный момент. Сам по себе Pi-hole не умеет отправлять и обрабатывать запросы по DoH, ему нужно помочь.\nCloudflared У приложения Cloudflared есть свой репозиторий на гитхабе: https://github.com/cloudflare/cloudflared. Можно скачать оттуда и обновлять руками время от времени, но перейдя по нескольким ссылкам мы находим репозиторий, с инструкцией для нашего дистрибьютива (построен то он поверх Ubuntu Jammy).\nУстановка Подготавливаем место для ключа-подписи репозитория:\n1 sudo mkdir -p --mode=0755 /usr/share/keyrings Скачиваем ключ:\n1 curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg \u0026gt;/dev/null Добавляем репозиторий:\n1 echo \u0026#39;deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main\u0026#39; | sudo tee /etc/apt/sources.list.d/cloudflared.list Теперь приложение Cloudflared доступно в виде пакета, которое будет обновляться централизованно через менеджер пакетов. Ставим приложение:\n1 sudo apt update \u0026amp;\u0026amp; sudo apt install cloudflared Проверить что приложение установлено можно командой:\n1 cloudflared -v Готовим конфигурацию Создадим папку с настройками:\n1 mkdir -p --mode=0755 /etc/cloudflared Внутри папки создадим файл proxy-dns с параметрами запуска нашего proxy-dns:\n1 2 # Commandline args for cloudflared, using Cloudflare DNS CLOUDFLARED_DNS_OPTS=--port 5353 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query Так же можно добавить параметр --address с указанием интерфейса, на котором будет доступен proxy-dns.\nВ апстримах указываем ссылки для DoH понравившихся нам провайдеров, вот список для примера: https://dnsprivacy.org/public_resolvers/#dns-over-https-doh.\nНастраиваем запуск в виде сервиса Первым делом создадим отдельного пользователя без возможности залогиниться в систему (но можно и от root запустить 🙃):\n1 sudo useradd -s /usr/sbin/nologin -r -M cloudflared Теперь создадим systemd unit (--force ключ нужен для создания нового файла):\n1 sudo systemctl edit --force --full cloudflared-proxy-dns.service И заполнит файл следующим содержимым:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [Unit] Description=cloudflared DNS over HTTPS proxy After=syslog.target network-online.target [Service] Type=simple User=cloudflared EnvironmentFile=/etc/cloudflared/proxy-dns ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_DNS_OPTS Restart=on-failure RestartSec=10 KillMode=process [Install] WantedBy=multi-user.target Далее включаем автозапуск сервиса при старте системы (--now запустит сервис сразу):\n1 systemctl enable --now cloudflared-proxy-dns.service Проверка работы Проверить что сервис \u0026ldquo;жив\u0026rdquo; можно командой dig (порт 5353 был указан в конфигурационном файле /etc/cloudflared/proxy-dns, адрес по умолчанию 127.0.0.1):\n1 dig @127.0.0.1 -p 5353 rutracker.org Финал В итоге у нас получается DoH прокси для работы в локальной сети (на локальном компьютере). Теперь на него можно \u0026quot; натравить\u0026quot; Pi-hole и радоваться жизни.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/cloudflared-proxy-dns/","summary":"\u003cp\u003eВот появилась такая необходимость -\nнужен \u003ca href=\"https://ru.wikipedia.org/wiki/DNS_%D0%BF%D0%BE%D0%B2%D0%B5%D1%80%D1%85_HTTPS\"\u003eDoH\u003c/a\u003e в локальной сети. Накидаю туту\nнесколько скриптов/файлов, который самому потом нужно будет \u0026ldquo;только скопировать\u0026rdquo;.\u003c/p\u003e","title":"Cloudflared proxy-dns"},{"content":"Итак, теперь, когда он у меня \u0026ldquo;на руках\u0026rdquo; - можно расписать что там где и как внутри.\nВнешний вид Внешне представляем собой кусок алюминия (предположительно), с ребрами на верхней грани и двумя \u0026ldquo;туннелями\u0026rdquo; по краям корпуса. Довольно увесистый. Пришел в картонной коробке с удобной зарядкой - часть с вилкой отсоединяется, и всегда можно добыть провод именно под ту розетку, что у тебя рядом. Немного напрягает шум на \u0026ldquo;холостом ходу\u0026rdquo;. Думаю можно будет заменить его на более \u0026ldquo;тихий блок питания\u0026rdquo;, благо на нем есть выходные характеристики:\nнапряжение 12V; сила тока 5A; мощность 60W. Сам блок имеет резиновые проставки-ножки. Опять же грустно, но в комплекте нет vesa крепления (заказывается отдельно).\nНа одной панели есть:\n2 usb3; DisplayPort; HDMI; 4 сетевых порта; вход для питания; винт для провода заземления. На другой стороне:\nкнопка включения; странная кнопка G, утопленная в корпус, непонятно за что отвечающая; утопленная кнопка CLR CMOS, для сброса биоса; еще 2 usb3; слот под microSD карту (TF); и две пластиковые заглушки, на случай вывода туда антенн для WiFi и/или Bluetooth. Внутри корпуса Открывается блок просто - 4 винта снизу корпуса. После чего нужно просто поддеть крышку или ухватиться за резиновую ножку. На нижней крышке практически все отверстия закрыты защитной сеткой (сетка довольно мелкая).\nБатарейка \u0026ldquo;отлично закреплена\u0026rdquo; клеем к блоку usb3 портов. На что стоит обратить внимание, так это на наличие sata3 порта. Насколько я понимаю, его наличие есть только в определенных моделях и i3-N305 не одна из них. Так же для меня оказалось неожиданностью, даже не подумал проверить, что порты для вентиляторов совершенно отличаются от ех, что используются в настольных ПК. Хотя размер позволял поставить туда обычный 4-pin разъем.\nТак же в наличие есть пищалка (зачем?) и перемычки для отключения питания в usb. Сейчас у меня usb порты подают питание даже в случае отключения самого компьютера. Так же в биосе есть возможность установить режим, при котором компьютер сам начнет включаться при подаче питания (пригодится для случаев, когда выключался свет). Вообще в биосе очень много разных настроек, все еще посмотрел, но в памяти отпечатались такие:\nуправление питание процессора; управление авто разгоном процессора управление режимами c-state; управление режимами работы pcie; управление системы охлаждения (обороты вентилятора); управление тем самым включение при подаче питания; Wake-on-LAN; и прочее. Внутри уже установлены m2 nvme ssd и ddr5 память, но это покупалось отдельно. Сам компьютер заказывался как bare-bone.\nПро память: да, на официальном сайте intel про i3-N305 сказано, что он поддерживает до 16GB оперативной памяти. Но в этот компьютер было установлено (для проверки) 32GB DDR5 памяти и никаких проблем с этим не возникло. Память как и положено завелась на частоте 4800MHz.\nВ загруженной системе Так как не все, что есть внутри подписано на текстолите нужно установить операционную систему. Глянуть что видно ей, и что она может нам рассказать о железе.\nВ качестве операционной системы будет использован Proxmox 8.0.3, так как в дальнейшем планируется использовать именно его для виртуализации всего зоопарка имеющегося сейчас софта. Процесс установки мы пропустим, скажу лишь, что с установкой проблем не было - все что нужно определилось само и не пришлось \u0026ldquo;решать проблемы\u0026rdquo;.\nСистема работает на ядре 6.2:\n1 2 root@n305:~# uname -r 6.2.16-4-pve CPU Глянем что нам скажет операционная система про CPU:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 root@n305:~# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 39 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 8 On-line CPU(s) list: 0-7 Vendor ID: GenuineIntel BIOS Vendor ID: Intel(R) Corporation Model name: Intel(R) Core(TM) i3-N305 BIOS Model name: Intel(R) Core(TM) i3-N305 To Be Filled By O.E.M. CPU @ 2.9GHz BIOS CPU family: 206 CPU family: 6 Model: 190 Thread(s) per core: 1 Core(s) per socket: 8 Socket(s): 1 Stepping: 0 CPU(s) scaling MHz: 53% CPU max MHz: 3800.0000 CPU min MHz: 800.0000 BogoMIPS: 3609.60 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology n onstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3 dnowprefetch cpuid_fault epb cat_l2 cdp_l2 ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdt_a rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req umip pku ospke waitpkg gfni vaes vpclmulqdq rdpid movdiri movdir64b fsrm md_clear serialize arch_lbr ibt flush_l1d arch_capabilities Virtualization features: Virtualization: VT-x Caches (sum of all): L1d: 256 KiB (8 instances) L1i: 512 KiB (8 instances) L2: 4 MiB (2 instances) L3: 6 MiB (1 instance) NUMA: NUMA node(s): 1 NUMA node0 CPU(s): 0-7 Vulnerabilities: Itlb multihit: Not affected L1tf: Not affected Mds: Not affected Meltdown: Not affected Mmio stale data: Not affected Retbleed: Not affected Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Spectre v2: Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence Srbds: Not affected Tsx async abort: Not affected PCIe 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 root@n305:~# lspci 00:00.0 Host bridge: Intel Corporation Device 4617 00:02.0 VGA compatible controller: Intel Corporation Alder Lake-N [UHD Graphics] 00:14.0 USB controller: Intel Corporation Device 54ed 00:14.2 RAM memory: Intel Corporation Device 54ef 00:16.0 Communication controller: Intel Corporation Device 54e0 00:1a.0 SD Host controller: Intel Corporation Device 54c4 00:1c.0 PCI bridge: Intel Corporation Device 54b8 00:1c.6 PCI bridge: Intel Corporation Device 54be 00:1d.0 PCI bridge: Intel Corporation Device 54b0 00:1d.1 PCI bridge: Intel Corporation Device 54b1 00:1d.2 PCI bridge: Intel Corporation Device 54b2 00:1f.0 ISA bridge: Intel Corporation Device 5481 00:1f.3 Audio device: Intel Corporation Device 54c8 00:1f.4 SMBus: Intel Corporation Device 54a3 00:1f.5 Serial bus controller: Intel Corporation Device 54a4 01:00.0 Non-Volatile memory controller: ADATA Technology Co., Ltd. XPG GAMMIX S50 NVMe SSD (rev 03) 02:00.0 Ethernet controller: Intel Corporation Ethernet Controller I226-V (rev 04) 03:00.0 Ethernet controller: Intel Corporation Ethernet Controller I226-V (rev 04) 04:00.0 Ethernet controller: Intel Corporation Ethernet Controller I226-V (rev 04) 05:00.0 Ethernet controller: Intel Corporation Ethernet Controller I226-V (rev 04) Да, каждый ethernet порт имеет свою отдельную линию pci-e. И да, это PCIe 2.0:\n1 2 3 root@n305:~# lspci -vv | grep 02:00.0 -A50 | grep -e 02:00.0 -e LnkSta: 02:00.0 Ethernet controller: Intel Corporation Ethernet Controller I226-V (rev 04) LnkSta:\tSpeed 5GT/s, Width x1 Основной же m2 слот - это полноценные 4 линии PCIe 3.0 (downgraded потому как диск с поддержкой версии 4.0, но какой уж был):\n1 2 3 root@n305:~# lspci -vv | grep 01:00.0 -A50 | grep -e 01:00.0 -e LnkSta: 01:00.0 Non-Volatile memory controller: ADATA Technology Co., Ltd. XPG GAMMIX S50 NVMe SSD (rev 03) (prog-if 02 [NVM Express]) LnkSta:\tSpeed 8GT/s (downgraded), Width x4 USB 1 2 3 4 root@n305:~# lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 002: ID 05e3:0751 Genesys Logic, Inc. microSD Card Reader Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Network Так как сейчас у меня нет ни одного свича с 2.5GB портами, то проведем тест в условиях прямого подключения ПК - Мини-ПК.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ❯ iperf3 -c 192.168.77.10 Connecting to host 192.168.77.10, port 5201 [ 5] local 192.168.77.2 port 54820 connected to 192.168.77.10 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 282 MBytes 2.37 Gbits/sec 0 687 KBytes [ 5] 1.00-2.00 sec 281 MBytes 2.36 Gbits/sec 0 718 KBytes [ 5] 2.00-3.00 sec 280 MBytes 2.35 Gbits/sec 0 718 KBytes [ 5] 3.00-4.00 sec 281 MBytes 2.36 Gbits/sec 0 718 KBytes [ 5] 4.00-5.00 sec 280 MBytes 2.35 Gbits/sec 0 718 KBytes [ 5] 5.00-6.00 sec 281 MBytes 2.36 Gbits/sec 0 718 KBytes [ 5] 6.00-7.00 sec 280 MBytes 2.35 Gbits/sec 0 718 KBytes [ 5] 7.00-8.00 sec 281 MBytes 2.36 Gbits/sec 0 718 KBytes [ 5] 8.00-9.00 sec 280 MBytes 2.35 Gbits/sec 0 718 KBytes [ 5] 9.00-10.00 sec 281 MBytes 2.36 Gbits/sec 0 718 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-10.00 sec 2.74 GBytes 2.36 Gbits/sec 0 sender [ 5] 0.00-10.00 sec 2.74 GBytes 2.35 Gbits/sec receiver iperf Done. Стресс тест В простое и особой нагрузки - греется компьютер не слишком сильно. Хотя температурные показатели ssd не радуют.\nА вот во время стресс теста оказалось, что пассивного охлаждения не очень то и хватает. Видно было, как в первую минуту теста потребление было 25W и процессор разгонял все ядра до 3GHz, но после достижения приблизительно 60 градусов - потребление упало и частота сбросилась на 100-150MHz. Видимо для, пусть и мелкого, но все-таки i3 - такого пассивного охлаждения мало.\nДальше, уже через 10 минут - температура особо не менялась, все так же была около 60 градусов, потребление около 20W, а частоты снижены. Видимо придется задуматься об активном охлаждение, хотя бы внутренней части, так как температуры ssd совсем не радуют. Осталось разобраться что же это за разъем такой, для вентилятора.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/topton-i3-n305/","summary":"\u003cp\u003eИтак, теперь, когда он у меня \u0026ldquo;на руках\u0026rdquo; - можно расписать что там где и как внутри.\u003c/p\u003e","title":"Topton i3-n305"},{"content":"Иногда в chromium подобных браузерах начинают очень криво отображаться страницы. Не то что бы \u0026ldquo;не по стандарту\u0026rdquo;, а с пропуском части контента и миганиями.\nИстория В большей своей массе проблемы рендеринга были просто \u0026ldquo;неудобные\u0026rdquo;, временами вообще не давали пользоваться браузером. Усугублялось все тем, что все основные часть браузера \u0026ldquo;показываются\u0026rdquo; через web-view - а значит с этими ошибками.\nВ основном проблема отображения заключались в:\nблоки меняют свое положение; контент мигает; часть или целая страница оказывается \u0026ldquo;не видимой\u0026rdquo;. Из-за редкой повторяемости и простого (оказалось что как повезет) решения, даже не копал в сторону поиска и решения проблемы. Обычно просто переключал флаг Preferred Ozone platform на странице флагов и жил дальше.\nНо в последний раз это оказалось не возможным: из-за вышеописанного (страница флагов - это web-view) она открывалась просто пустой, без каких-либо видимых контролов. Если поводить мышкой над страницей, то можно было увидеть что указатель менялся. Проблема в том, что я не помню наизусть где какой контрол находиться 😥.\nПоиски причин Так как сбой были явно связаны не с версткой, а с процессом рендеринга (и лечились переключение настройками рендеринга), решено было глянуть логи. В логах нашлось интересное:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 // Vertex SKSL #extension GL_NV_shader_noperspective_interpolation: require uniform float4 sk_RTAdjust;in float2 position;in half4 color;flat out half4 vcolor_S0;void main() {// Primitive Processor QuadPerEdgeAAGeometryProcessor vcolor_S0 = color;sk_Position = position.xy01;} // Fragment SKSL #extension GL_NV_shader_noperspective_interpolation: require flat in half4 vcolor_S0;void main() {// Stage 0, QuadPerEdgeAAGeometryProcessor half4 outputColor_S0;outputColor_S0 = vcolor_S0;const half4 outputCoverage_S0 = half4(1);{ // Xfer Processor: Porter Duff sk_FragColor = outputColor_S0 * outputCoverage_S0;}} // Vertex GLSL #version 300 es #extension GL_NV_shader_noperspective_interpolation : require precision mediump float; precision mediump sampler2D; uniform highp vec4 sk_RTAdjust; in highp vec2 position; in mediump vec4 color; flat out mediump vec4 vcolor_S0; void main() { vcolor_S0 = color; gl_Position = vec4(position, 0.0, 1.0); gl_Position = vec4(gl_Position.xy * sk_RTAdjust.xz + gl_Position.ww * sk_RTAdjust.yw, 0.0, gl_Position.w); } // Fragment GLSL #version 300 es #extension GL_NV_shader_noperspective_interpolation : require precision mediump float; precision mediump sampler2D; out mediump vec4 sk_FragColor; flat in mediump vec4 vcolor_S0; void main() { mediump vec4 outputColor_S0; outputColor_S0 = vcolor_S0; { sk_FragColor = outputColor_S0; } } Errors: link failed but did not provide an info log [6430:6430:0716/231329.938336:ERROR:shared_context_state.cc(81)] Skia shader compilation error Это сообщение повторялось раз за разом. При этом глаз зацепился за Skia shader compilation error, что-то про GPU и шейдер. В надежде (ну да 🙃), что это проблема не только у меня, решил поискать \u0026ldquo;собратьев по счастью\u0026rdquo;. Наткнулся на баг: https://bugs.chromium.org/p/chromium/issues/detail?id=1442633.\nРешение Поискал упоминание этого бага и везде, как и в самом баг-репорте, советуют удалить папку с GPU кешем: GPUCache. Найти ее можно по пути:\n1 ~/.config/chromium/Default/GPUCache Для других браузеров пути похожи, только нужно заменить название браузера в пути. В общем-то сбрасывать кеш GPU - задачка не сложная. Но есть особенности:\nпроблема время от времени повторяется; браузеров несколько; лень запоминать пути и прочее. Поэтому добавил в свой zsh функцию-скрипт, что бы было человеческое автодополнение и можно было все \u0026ldquo;починить за пару табов\u0026rdquo;:\n1 2 3 4 5 6 7 function chromium-render-fix() { for browser in chromium BraveSoftware google-chrome yandex-browser do local searchdir=\u0026#34;$HOME/.config/$browser/\u0026#34; [ -d \u0026#34;$searchdir\u0026#34; ] \u0026amp;\u0026amp; find \u0026#34;$searchdir\u0026#34; -type d -name \u0026#34;GPUCache\u0026#34; | xargs rm -rf done } Скрипт удалит кеш еще и для расширений. Дополнительные браузеры можно вписать во второй строке.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/chromium-shader-bug/","summary":"\u003cp\u003eИногда в chromium подобных браузерах начинают очень криво отображаться страницы. Не то что бы \u0026ldquo;не по стандарту\u0026rdquo;, а с\nпропуском части контента и миганиями.\u003c/p\u003e","title":"Проблемы шейдеров в chromium подобных браузерах"},{"content":"Решение о покупке Сейчас у меня довольно-таки сумбурно построенная сеть, в которой есть несколько устройств и все они разные. Настолько разные, что часть их них 32-х битные, часть 64-х битные, разный SoC, разные вендоры, все они построены на базе ARM (а иногда надобно x86). Среди них:\nроутер на базе ipq807x, официальная openWRT на который все никак не выйдет (приходится собирать самому или использовать готовые образы и пакеты); основной одноплатник на базе Samsung Exynos 5 Octa, который в общем-то вроде тянет, но это по сути Galaxy S4, со всеми вытекающими; одноплатник под Pi-hole на базе Allwinner H616, чего хватает (с головой) Pi-hole, но есть ощущение, что это overkill и можно dns вынести в более \u0026ldquo;мелкую виртуалку\u0026rdquo;. К тому же сейчас вся сеть живет на гигабитном канале (единственное устройство, которое поддерживаем 2.5Gb сеть - рабочая станция) и уже хочется чуть \u0026ldquo;расправить плечи\u0026rdquo;.\nВ результате было решено взять что-то, что даст дальнейший толчок развитию:\nбудет x86, с возможностью виртуализации (proxmox, docker); будет с 2.5 Gb сетью (и минимум 4-мя портами, что бы безболезненно заменить роутер); будет пассивно охлаждаться (сейчас вентилятор есть только на основном одноплатнике); желательно будет иметь \u0026ldquo;некий запас по мощности\u0026rdquo; на будущее. Поиск железки После просмотра локального рынка и понимания, что пассивно охлаждаемого более-менее приемлемого по цене варианта нет - я отправился на aliexpress, благо за всё время уже видел там такое решения.\nВообще разнообразие без вентиляторных мини пк там тоже не такое уж и большое, если выставить себе критерии:\nминимум 4 порта; 2.5 Gb сеть; что-то современное (по процу и памяти); не сверх большой бюджет. В основном получается выбор между новыми Celeron N200 и чуть более старыми Celeron J6412 и Pentium N6005.\nРешено было взять самый свежий из них - N200. Пошелкав конфигуратор, заметил что Core i3-N305 стоит не намного то и дороже, но предоставит мне больший запас по CPU.\nЖелезка В итоге был выбран такой мини пк:\nCore i3-N305; Up to 16 GB DDR5 (слот один, но вроде бы можно установить до 32, проверю); 4x intel i266V NIC; 2 PCI-e M2 слота; SATA слот (не уверен тчо пригодится, но все равно приятно). Заключение Так как подробных характеристик и испытаний именно этой модели не нашел на просторах, то думаю описать внутреннее устройство (особенно как раскиданы pci-e линии, думаю интересно, мне до сих пор очень интересно 🙂). Поднять и настроить proxmox 8 (уже вышел). Заменить свой wifi роутер на openWRT на виртуальный OPNsense.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/first-fanless-mini-pc/","summary":"\u003ch2 id=\"решение-о-покупке\"\u003eРешение о покупке\u003c/h2\u003e\n\u003cp\u003eСейчас у меня довольно-таки сумбурно построенная сеть, в которой есть несколько устройств и все они разные. Настолько\nразные, что часть их них 32-х битные, часть 64-х битные, разный SoC, разные вендоры, все они\nпостроены на базе ARM (а иногда надобно x86). Среди них:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eроутер на базе ipq807x, официальная openWRT на который все никак не выйдет (приходится собирать самому или\nиспользовать готовые образы и пакеты);\u003c/li\u003e\n\u003cli\u003eосновной одноплатник на базе Samsung Exynos 5 Octa, который в общем-то вроде тянет, но это по сути Galaxy S4, со всеми\nвытекающими;\u003c/li\u003e\n\u003cli\u003eодноплатник под Pi-hole на базе Allwinner H616, чего хватает (с головой) Pi-hole, но есть ощущение, что это overkill и\nможно dns вынести в более \u0026ldquo;мелкую виртуалку\u0026rdquo;.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eК тому же сейчас вся сеть живет на гигабитном канале (единственное устройство, которое поддерживаем 2.5Gb сеть - рабочая\nстанция) и уже хочется чуть \u0026ldquo;расправить плечи\u0026rdquo;.\u003c/p\u003e","title":"Первый безвентиляторный мини пк"},{"content":"Расскажу что тут вообще происходит.\nЧто? Это будет маленький сайт со статьями-записками или описаниями того, с чем мне приходится работать или сталкиваться по иной причине в жизни (профессиональной или около профессиональной). Технологии, железки, рассуждения на тему и прочее.\nВ основном все для себя любимого. Копал и нашел как настроить что-то под себя - написал, заказал какую-то железку и не нашел про нее никаких статей в ру-сегменте - тоже постараюсь написать, вдруг кому поможет. Применил дома или на работе какую-то новую крутую штуку - тоже напишу.\nЗачем? Тут все по категориям более менее понятно:\nсконфигурировал и разобрался, но используешь редко - напиши, особенно если пришлось поковыряться, сэкономь себе время в будущем; получил в руки железку, и уже сам получил ответы на интересующие по ней вопросы - напиши, вдруг кто-то тоже это ищет, может и найдет у тебя; узнал что-то новое - напиши, сразу станет видно: понял или нет как оно работает. Почему? Потому что могу 😄. Ну а если кому-то это поможет - так вообще просто замечательно.\nВ заключение Пока-что расконсервирую сайт, дальше будет время от времени наполнятся. Ожидать регулярности не стоит 🙃.\n","permalink":"https://405179c2.spwsmr-website.pages.dev/posts/hello/","summary":"\u003cp\u003eРасскажу что тут вообще происходит.\u003c/p\u003e","title":"Приступим..."}]