<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Михаил Королёв: заметки с тегом DotNet</title>
<link>https://robonet.me/tags/dotnet/</link>
<description>@robonet mikhail@robonet.me</description>
<author></author>
<language>ru</language>
<generator>Aegea 11.4 (v4171e)</generator>

<itunes:owner>
<itunes:name></itunes:name>
<itunes:email>mikhail@robonet.me</itunes:email>
</itunes:owner>
<itunes:subtitle>@robonet mikhail@robonet.me</itunes:subtitle>
<itunes:image href="https://robonet.me/pictures/userpic/userpic-square@2x.jpg?1704815089" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>Distroless Docker образы для .NET приложений</title>
<guid isPermaLink="false">1</guid>
<link>https://robonet.me/all/distroless-docker/</link>
<pubDate>Sat, 13 Jan 2024 18:54:26 +0300</pubDate>
<author></author>
<comments>https://robonet.me/all/distroless-docker/</comments>
<description>
&lt;p&gt;Пару лет назад пришлось внедрять «коробочное» решение в одну организацию со строгими политиками безопасности для внешних поставщиков, одна из самых неприятных из которых была — необходимость полного отсутствия любых известных уязвимостей в поставке.&lt;/p&gt;
&lt;p&gt;Казалось бы — взять последнюю версию базового образа популярной операционной системы, и все будет хорошо, но в реальной жизни даже в них практически всегда есть известные уязвимости.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;a href="https://snyk.io/test/docker/alpine%3A3.19" class="e2-text-picture-link"&gt;
&lt;img src="https://robonet.me/pictures/distroless-docker.png" width="1998" height="434" alt="" /&gt;
&lt;/a&gt;&lt;div class="e2-text-caption"&gt;Даже в самом «минимальном» базовом образе alpine могут быть уязвимости (Данный пример как раз просто починится с помощью apk -U upgrade, но кого это волнует)&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Так что единственным вариантом для решения этой проблемы раз и на всегда была сборка собственного базового docker образа из «scratch».&lt;/p&gt;
&lt;p&gt;Пример такого образа вы можете посмотреть тут &lt;a href="https://github.com/RoboNET/dotnet-scratch/blob/main/Dockerfile"&gt;https://github.com/RoboNET/dotnet-scratch/blob/main/Dockerfile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Прокомментирую только некоторые моменты&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
RUN adduser \    
    --disabled-password \    
    --gecos "" \    
    --home "/nonexistent" \    
    --shell "/sbin/nologin" \    
    --no-create-home \    
    --uid "${UID}" \    
    "${USER}"
&lt;/code&gt;
&lt;/pre&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
USER $UID:$UID
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Раз уж мы делаем базовый образ в целях безопасности, то надо постараться закрыть как можно большее число векторов атаки. По умолчанию, все команды и ENTRYPOINT в докер образе будет запускаться от root пользователя, так что нам необходимо создать и затем указать, что надо использовать другого пользователя.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
RUN apk add --no-cache \
    icu-libs \
    icu-data-full \
    tzdata
&lt;/code&gt;
&lt;/pre&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
COPY --from=globalization-builder /usr/share/icu /usr/share/icu
COPY --from=globalization-builder /usr/share/zoneinfo /usr/share/zoneinfo
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;По умолчанию alpine не содержит в себе информации о локализации и таймзонах, так что если в вашем приложении необходимо с ними работать, то придется  вручную поставить нужные пакеты и потом не забыть скопировать их в свой образ.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
ENV ASPNETCORE_URLS=http://+:8080 \
    DOTNET_RUNNING_IN_CONTAINER=true \
    DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true \
    TMPDIR=/tmp \
    PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Чтобы дотнет понимал, что работает в контейнере, и знал нужные ему пути, необходимо указать ряд важных переменных окружения.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="dockerfile"&gt;
RUN dotnet publish -c Release \
    -r linux-musl-arm64 -p:PublishReadyToRun=true \
    -p:PublishTrimmed=true -p:PublishSingleFile=true \
    -o out
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Поскольку мы не включаем весь рантайм в приложение, а только необходимые зависимости, нам необходимо собирать наше приложение с включением всего рантайма в билд.&lt;/p&gt;
&lt;h2&gt;А может ли это пригодиться и для других проектов?&lt;/h2&gt;
&lt;p&gt;На самом деле этот подход имеет как и плюсы, так и минусы.&lt;br /&gt;
Из основных плюсов можно выделить:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;отсутствие лишних библиотек и бинарных файлов, что уменьшает вероятность взлома приложения через уязвимые компоненты&lt;/li&gt;
&lt;li&gt;повышение безопасности за счет запуска приложения не от root пользователя, что не позволит как-то вмешаться в оставшиеся зависимости, даже если в нашем приложении как-то смогли выполнить зловредный код через уязвимость&lt;/li&gt;
&lt;li&gt;уменьшение размера приложения, особенно если не добавлять данные о локализации и таймзонах&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Из минусов, с которыми приходилось сталкиваться:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;приходится поддерживать свой собственный базовый образ&lt;/li&gt;
&lt;li&gt;сложнее происходит удаленная отладка&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;И как это использовать?&lt;/h2&gt;
&lt;p&gt;Если хотите начать использовать distroless и rootless образы, то с 8 версии дотнета microsoft собирать рантайм и зависимости в том числе и в distroless образах, например cbl-mariner &lt;a href="https://github.com/dotnet/dotnet-docker/blob/main/src/aspnet/8.0/cbl-mariner2.0-distroless/amd64/Dockerfile"&gt;https://github.com/dotnet/dotnet-docker/blob/main/src/aspnet/8.0/cbl-mariner2.0-distroless/amd64/Dockerfile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;И так-же он внес некоторые изменения в поведении по умолчанию, чтобы повысить безопасности приложений, например использование rootless подхода &lt;a href="https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/"&gt;https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;А если вам надо собрать такие образы под более старые версии, то можете посмотреть &lt;a href="https://github.com/RoboNET/dotnet-scratch/blob/main/examples/Minimal/Dockerfile"&gt;примеры использования моих образов&lt;/a&gt;, или собрать свои на их основе.&lt;/p&gt;
</description>
</item>


</channel>
</rss>