{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Михаил Королёв: заметки с тегом DotNet",
    "_rss_description": "@robonet mikhail@robonet.me",
    "_rss_language": "ru",
    "_itunes_email": "mikhail@robonet.me",
    "_itunes_categories_xml": "",
    "_itunes_image": "https:\/\/robonet.me\/pictures\/userpic\/userpic-square@2x.jpg?1704815089",
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/robonet.me\/tags\/dotnet\/",
    "feed_url": "https:\/\/robonet.me\/tags\/dotnet\/json\/",
    "icon": "https:\/\/robonet.me\/pictures\/userpic\/userpic@2x.jpg?1704815089",
    "authors": [
        {
            "name": "Михаил Королёв",
            "url": "https:\/\/robonet.me\/",
            "avatar": "https:\/\/robonet.me\/pictures\/userpic\/userpic@2x.jpg?1704815089"
        }
    ],
    "items": [
        {
            "id": "1",
            "url": "https:\/\/robonet.me\/all\/distroless-docker\/",
            "title": "Distroless Docker образы для .NET приложений",
            "content_html": "<p>Пару лет назад пришлось внедрять «коробочное» решение в одну организацию со строгими политиками безопасности для внешних поставщиков, одна из самых неприятных из которых была — необходимость полного отсутствия любых известных уязвимостей в поставке.<\/p>\n<p>Казалось бы — взять последнюю версию базового образа популярной операционной системы, и все будет хорошо, но в реальной жизни даже в них практически всегда есть известные уязвимости.<\/p>\n<div class=\"e2-text-picture\">\n<a href=\"https:\/\/snyk.io\/test\/docker\/alpine%3A3.19\" class=\"e2-text-picture-link\">\n<img src=\"https:\/\/robonet.me\/pictures\/distroless-docker.png\" width=\"1998\" height=\"434\" alt=\"\" \/>\n<\/a><div class=\"e2-text-caption\">Даже в самом «минимальном» базовом образе alpine могут быть уязвимости (Данный пример как раз просто починится с помощью apk -U upgrade, но кого это волнует)<\/div>\n<\/div>\n<p>Так что единственным вариантом для решения этой проблемы раз и на всегда была сборка собственного базового docker образа из «scratch».<\/p>\n<p>Пример такого образа вы можете посмотреть тут <a href=\"https:\/\/github.com\/RoboNET\/dotnet-scratch\/blob\/main\/Dockerfile\">https:\/\/github.com\/RoboNET\/dotnet-scratch\/blob\/main\/Dockerfile<\/a><\/p>\n<p>Прокомментирую только некоторые моменты<\/p>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nRUN adduser \\    \n    --disabled-password \\    \n    --gecos \"\" \\    \n    --home \"\/nonexistent\" \\    \n    --shell \"\/sbin\/nologin\" \\    \n    --no-create-home \\    \n    --uid \"${UID}\" \\    \n    \"${USER}\"\n<\/code>\n<\/pre>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nUSER $UID:$UID\n<\/code>\n<\/pre>\n<p>Раз уж мы делаем базовый образ в целях безопасности, то надо постараться закрыть как можно большее число векторов атаки. По умолчанию, все команды и ENTRYPOINT в докер образе будет запускаться от root пользователя, так что нам необходимо создать и затем указать, что надо использовать другого пользователя.<\/p>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nRUN apk add --no-cache \\\n    icu-libs \\\n    icu-data-full \\\n    tzdata\n<\/code>\n<\/pre>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nCOPY --from=globalization-builder \/usr\/share\/icu \/usr\/share\/icu\nCOPY --from=globalization-builder \/usr\/share\/zoneinfo \/usr\/share\/zoneinfo\n<\/code>\n<\/pre>\n<p>По умолчанию alpine не содержит в себе информации о локализации и таймзонах, так что если в вашем приложении необходимо с ними работать, то придется  вручную поставить нужные пакеты и потом не забыть скопировать их в свой образ.<\/p>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nENV ASPNETCORE_URLS=http:\/\/+:8080 \\\n    DOTNET_RUNNING_IN_CONTAINER=true \\\n    DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true \\\n    TMPDIR=\/tmp \\\n    PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT\/tools\n<\/code>\n<\/pre>\n<p>Чтобы дотнет понимал, что работает в контейнере, и знал нужные ему пути, необходимо указать ряд важных переменных окружения.<\/p>\n<pre class=\"e2-text-code\"><code class=\"dockerfile\">\nRUN dotnet publish -c Release \\\n    -r linux-musl-arm64 -p:PublishReadyToRun=true \\\n    -p:PublishTrimmed=true -p:PublishSingleFile=true \\\n    -o out\n<\/code>\n<\/pre>\n<p>Поскольку мы не включаем весь рантайм в приложение, а только необходимые зависимости, нам необходимо собирать наше приложение с включением всего рантайма в билд.<\/p>\n<h2>А может ли это пригодиться и для других проектов?<\/h2>\n<p>На самом деле этот подход имеет как и плюсы, так и минусы.<br \/>\nИз основных плюсов можно выделить:<\/p>\n<ul>\n<li>отсутствие лишних библиотек и бинарных файлов, что уменьшает вероятность взлома приложения через уязвимые компоненты<\/li>\n<li>повышение безопасности за счет запуска приложения не от root пользователя, что не позволит как-то вмешаться в оставшиеся зависимости, даже если в нашем приложении как-то смогли выполнить зловредный код через уязвимость<\/li>\n<li>уменьшение размера приложения, особенно если не добавлять данные о локализации и таймзонах<\/li>\n<\/ul>\n<p>Из минусов, с которыми приходилось сталкиваться:<\/p>\n<ul>\n<li>приходится поддерживать свой собственный базовый образ<\/li>\n<li>сложнее происходит удаленная отладка<\/li>\n<\/ul>\n<h2>И как это использовать?<\/h2>\n<p>Если хотите начать использовать distroless и rootless образы, то с 8 версии дотнета microsoft собирать рантайм и зависимости в том числе и в distroless образах, например cbl-mariner <a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/main\/src\/aspnet\/8.0\/cbl-mariner2.0-distroless\/amd64\/Dockerfile\">https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/main\/src\/aspnet\/8.0\/cbl-mariner2.0-distroless\/amd64\/Dockerfile<\/a><\/p>\n<p>И так-же он внес некоторые изменения в поведении по умолчанию, чтобы повысить безопасности приложений, например использование rootless подхода <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/securing-containers-with-rootless\/\">https:\/\/devblogs.microsoft.com\/dotnet\/securing-containers-with-rootless\/<\/a><\/p>\n<p>А если вам надо собрать такие образы под более старые версии, то можете посмотреть <a href=\"https:\/\/github.com\/RoboNET\/dotnet-scratch\/blob\/main\/examples\/Minimal\/Dockerfile\">примеры использования моих образов<\/a>, или собрать свои на их основе.<\/p>\n",
            "date_published": "2024-01-13T18:54:26+03:00",
            "date_modified": "2024-01-13T19:21:15+03:00",
            "tags": [
                "Docker",
                "DotNet"
            ],
            "image": "https:\/\/robonet.me\/pictures\/distroless-docker.png",
            "_date_published_rfc2822": "Sat, 13 Jan 2024 18:54:26 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "1",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": [
                    "https:\/\/robonet.me\/pictures\/distroless-docker.png"
                ]
            }
        }
    ],
    "_e2_version": 4171,
    "_e2_ua_string": "Aegea 11.4 (v4171e)"
}