.NET 6 is now included in Ubuntu 22.04 (Jammy) , just apt install dotnet6
can be installed. This change is a major improvement and simplification for Ubuntu users. We also released .NET with Chiseled Ubuntu Containers, a new small and secure container product from Canonical. These improvements are the result of a collaboration between Canonical and Microsoft.
Here are the commands to install the .NET 6 SDK on Ubuntu 22.04 :
sudo apt update
sudo apt install dotnet6
We also announced that .NET 6 is available for Chiseled Ubuntu containers . Our friends at Canonical developed a new chisel method to make ultra-small container images. We are very happy about it. The Chiseled Ubuntu image is smaller than the Ubuntu image you used earlier 100MB
!
Here is the command to pull the new ASP.NET Chiseled image:
docker pull mcr.microsoft.com/dotnet/nightly/aspnet:6.0-jammy-chiseled
We've also updated the dotnetapp and aspnetapp samples so you can try out .NET in a Chiseled Ubuntu container.
- These new container images significantly improve the security posture:
- Very small images (reduces size and attack surface)
- No package manager (avoids a whole class of attacks)
- No shell (avoids a whole class of attacks)
- non-root (avoids a whole class of attacks)
Most importantly, Canonical and Microsoft are committed to working together to ensure that new .NET releases work well with new Ubuntu releases. This includes security updates and secure delivery of container images. We are very excited that .NET 6 is available in Ubuntu 22.04 and that Canonical has chosen to work with us as a publishing partner for their Chiseled Ubuntu images. That's what Canonical has to say about the project.
"Ubuntu now has the story from the development side to the production side, starting with the .NET platform, with support for ultra-small container images," said Valentin Viennot, Canonical Product Manager. "We think this is a huge step forward for both our communities; Working with Microsoft's .NET team allows us to go above and beyond.
Canonical and Microsoft
Canonical and Microsoft started working together a few months ago with the goal of making Ubuntu a better .NET development environment.
We have two main goals:
- Simplified with .NET on Ubuntu.
- Shorten the supply chain between Canonical and Microsoft.
Over the years, we have known that many .NET developers use Ubuntu. After our conversation, it became clear that there are things we can do to improve this experience. Let me tell you what we delivered.
.NET in APT
You can now install .NET 6 using APT built by Canonical via source-build . These packages are available for Ubuntu 22.04 (Jammy) and later. This is a good reason to upgrade to Jammy !
Note: Now that .NET 6 is included in Ubuntu, check out our advice on using packages.microsoft.com on Ubuntu 22.04 .
- dotnet6 — .NET 6 SDK (short).
- dotnet-sdk-6.0 — Same as above (full name).
- aspnet-runtime-6.0 — ASP.NET Core
- dotnet-runtime-6.0 — .NET Runtime
I'll show you how to install these images using Docker (same model applies elsewhere):
rich@kamloops:~$ docker run --rm -it ubuntu:jammy
root@7d4dfca0ef55:/# apt update && apt install -y dotnet6
root@7d4dfca0ef55:/# dotnet --version
6.0.108
If this doesn't work, you need to register the following sources in /etc/apt/sources.list:
deb http://archive.ubuntu.com/ubuntu/ jammy-updates universe
Canonical and Microsoft will work together to ensure that these packages are updated in the monthly .NET team release schedule. This includes Microsoft sharing CVE information (description and code) with Canonical prior to public release. Likewise, Canonical will share security information in the other direction.
Notice:
- We are currently missing the Arm64 version. These will come soon. Both companies are strong supporters of Arm64.
- The .NET 7 version is not yet available and may not be available until after .NET 7 GA.
The .NET SDK workload is not available in the package (for any Linux distribution). Also, Linux does not support .NET MAUI workloads.
.NET in a Chiseled Ubuntu container
You can now use .NET in Chiseled Ubuntu containers . Chiseling offers the smallest container footprint while still being the Ubuntu you know and trust. It is similar to traditional distroless with a custom tool for slicing .deb
package.
These images are smaller than the Ubuntu images we currently provide 100MB
and don't include root!
We provide three-tier Chiseled Ubuntu container images for Arm64 and x64 and .NET 6 and 7:
mcr.microsoft.com/dotnet/nightly/runtime-deps:6.0-jammy-chiseled
mcr.microsoft.com/dotnet/nightly/runtime:6.0-jammy-chiseled
mcr.microsoft.com/dotnet/nightly/aspnet:6.0-Jammy-chiseled
Note: Images will be available in our 夜間
repository while chiseled products are in preview. We will make another announcement when they are supported in production. It's going to be sometime this year, but we haven't picked a timeline yet as we've been focusing on the basic enablement.
Canonical also publishes Chiseled Ubuntu container images for .NET via Docker Hub, which include new APT packages:
- https://hub.docker.com/r/ubuntu/dotnet-deps
- https://hub.docker.com/r/ubuntu/dotnet-runtime
- https://hub.docker.com/r/ubuntu/dotnet-aspnet
Let's look at scale wins. All sizes below are uncompressed (on disk, not registry/wire sizes).
First, the runtime-deps
layer.
- Ubuntu 22.04 (Jammy):
112MB
- Chiseled Ubuntu 22.04 (Jammy):
12.9MB
On the other end of the spectrum, the aspnet layer.
- Ubuntu 22.04 (Jammy):
213MB
- Chiseled Ubuntu 22.04 (Jammy):
104MB
What an amazing difference! The folks at Canonical have figured out how to remove 100MB of binaries and other content from these images. When we first started talking, we didn't know we would be talking about such a big difference!
The attentive reader will notice that chiseled aspnet
is smaller than the existing runtime-deps
layer. This is really nice.
It is reasonable to ask what Alpine looks like. This is a newer distro, designed from the start to be ultra-small and componentized. Alpine is 9.84MB
for runtime-deps:6.0-alpine
aspnet:6.0-alpine
and 100MB for ---78409546066e487cd8c0f8c342b59363--- . These are uncompressed and impressive numbers. This is a key reason why Alpine is so popular (and why we've released .NET images for it over the years).
Alpine is great (and we're friends with these people too), but it's not for everyone and every application because it uses musl , a different (and incompatible) libc
variant. This only matters if your application includes native libraries. If not (most .NET applications don't), you don't need to worry about this detail. The .NET product itself is happy to run with musl
or glibc , and every PR runs on dotnet/runtime tests.
From this perspective, this is really good news if you use Ubuntu for development and have always wanted to put a small Ubuntu into production. You now have a direct path from the development box to the cloud without any distribution compatibility issues. It's amazing (and very surprising) to see Ubuntu on the same ballpark as Alpine. Kudos to the folks at Canonical for their tremendous engineering achievements.
It is worth mentioning that Chainguard is looking for minimal container images for future security. The project has run out of distroless GitHub org. We're following this project and it's great to see more interest in smaller, safer container images. We believe minimal + non-root container images are the future.
Like our Alpine image , we chose not to include ICU . It might double the size of the image. This means that we have globalization invariant mode enabled. That's fine for some applications, and a win at scale is great. For others, it's a deal breaker. We may need to adjust this part of the plan based on feedback. We have documented the mode for adding ICU to the image.
Let me demonstrate these images a bit to illustrate how these images are (intentionally) restricted.
% docker run --rm mcr.microsoft.com/dotnet/nightly/runtime-deps:6.0-jammy-chiseled-amd64
docker: Error response from daemon: No command specified.
See 'docker run --help'.
Let's try again.
% docker run --rm mcr.microsoft.com/dotnet/nightly/runtime-deps:6.0-jammy-chiseled-amd64 bash
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknown.
Um? How is this going? They don't work! That's the point. These are container images for similar devices. They are stripped to a minimum. They just do what you designed them to do. That's what makes them safer. If this experience is uncomfortable, you can always use a regular Ubuntu image. We will continue to provide them. They are not going away.
For runtime and aspnet images, we decided to use dotnet --info as ENTRYPOINT to make the experience more friendly and useful.
% docker run --rm mcr.microsoft.com/dotnet/nightly/runtime:6.0-jammy-chiseled
global.json file:
Not found
Host:
Version: 6.0.8
Architecture: arm64
Commit: 55fb7ef977
.NET SDKs installed:
No SDKs were found.
.NET runtimes installed:
Microsoft.NETCore.App 6.0.8 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Download .NET:
https://aka.ms/dotnet-download
Learn about .NET Runtimes and SDKs:
https://aka.ms/dotnet/runtimes-sdk-info
We do not provide chiseled SDK images. Because there is no obvious strong demand. In fact, chiseled SDK images can be difficult to use in some cases. You can continue to use the existing Jammy SDK image: mcr.microsoft.com/dotnet/sdk:6.0-jammy
. If a chiseled SDK image is required, we'd be happy to reconsider.
Use chiseled container images
For most applications, using these new container images will not have any noticeable effect on the appearance of the Dockerfile. We updated our example to use these new container images:
I'll show you how easy it is to use dotnetapp.
Dockerfile is almost no different.
FROM mcr.microsoft.com/dotnet/sdk:7.0-jammy AS build
WORKDIR /source
# 複製 csproj 並恢復為不同的層
COPY *.csproj .
RUN dotnet restore --use-current-runtime
# 複製和釋出應用程式和庫
COPY . .
RUN dotnet publish -c Release -o /app --use-current-runtime --self-contained false --no-restore
# 最後階段/影像
FROM mcr.microsoft.com/dotnet/nightly/runtime:7.0-jammy-chiseled
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "dotnetapp.dll"]
Only the last FROM
statement is really different from our standard Ubuntu Dockerfile.
I will now build the example:
rich@MacBook-Air-2 dotnetapp % pwd
/Users/rich/git/dotnet-docker/samples/dotnetapp
rich@MacBook-Air-2 dotnetapp % docker build -t dotnetapp-chiseled -f Dockerfile.chiseled .
rich@MacBook-Air-2 dotnetapp % docker images | grep dotnetapp-chiseled
dotnetapp-chiseled
latest bf7e125bd182 20 seconds ago 90.5MB
Note: I am not using any .NET trimming functionality. Of course, this image can be made smaller. Let's start the container:
rich@MacBook-Air-2 dotnetapp % docker run --rm dotnetapp-chiseled
42
42 ,d ,d
42 42 42
,adPPYb,42 ,adPPYba, MM42MMM 8b,dPPYba, ,adPPYba, MM42MMM
a8" `Y42 a8" "8a 42 42P' `"8a a8P_____42 42
8b 42 8b d8 42 42 42 8PP""""""" 42
"8a, ,d42 "8a, ,a8" 42, 42 42 "8b, ,aa 42,
`"8bbdP"Y8 `"YbbdP"' "Y428 42 42 `"Ybbd8"' "Y428
.NET 7.0.0-preview.7.22375.6
Linux 5.10.104-linuxkit #1 SMP PREEMPT Thu Mar 17 17:05:54 UTC 2022
OSArchitecture: Arm64
ProcessorCount: 4
TotalAvailableMemoryBytes: 3.83 GiB
Then, let's try to enter:
rich@MacBook-Air-2 dotnetapp % docker run --rm --entrypoint bash dotnetapp-chiseled
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknown.
rich@MacBook-Air-2 dotnetapp % docker run --rm --entrypoint apt dotnetapp-chiseled install -y bash curl
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "apt": executable file not found in $PATH: unknown.
My "red team" skills let me down. Note that docker exec
will have the same result. Now I will describe the Chiseled image in more detail, as you can see in action.
Chiseled Ubuntu Containers
Chiseled Ubuntu containers are Canonical's interpretation of the concept of distroless, originally popularized by Google . In the original implementation, the distribution was stripped and only the necessary packages were installed. Chiseling goes a step further and installs only the necessary directories and files in each package .
Another challenge with the initial implementation was that it was not necessarily supported by either party. Chiseled Ubuntu containers are top-notch Canonical deliverables. This means you can use ultra-small container images and get support as a Canonical customer.
Thank you Google for putting us all on this path.
As mentioned, this approach has a lot of value:
- Very small images (reduces size and attack surface)
- No package manager (avoids a whole class of attacks)
- No shell (avoids a whole class of attacks)
Chiseled Ubuntu containers are currently in preview. We will make a separate announcement when they are stable and supported in production.
non-rooted image
We've configured all new .NET Chiseled Ubuntu containers with a non-root user. These images do not contain the root
user or contain commands that elevate root privileges, such as sudo
or su
. This means that functions and operations that require root
cannot be performed.
In addition to removing shells like bash
, non-root images are an additional security mitigation. Non-root images are logically independent and complement daemons that run without root . Every reduction in privilege helps .
If you need access to privileged resources, you can add root
user in Dockerfile
b77e0a95a7038963b2ff2e7292a235a1---. You won't be blocked, but it's your specific security decision to make.
Chiseled images are device-like, not generic. We feel like they offer us the opportunity to finally deliver a non-rooted image . This informs our future policy. Device-like images will be delivered as a non-root user, while generic images will be delivered according to the base image's policy (possibly configured with root
user). However, this Canonical project inspired us to look for an intermediate option, which is to serve an image that doesn't support root .
secure supply chain
Canonical has a secure process in place to deliver Ubuntu virtual machine images directly to Azure for use by customers. It occurred to us that Canonical could do the same with the Ubuntu container base images we use to build Ubuntu based .NET images (regular and Chiseled). That's what we use now instead of pulling from Docker Hub. We now have an effective zero-distance supply chain for all Canonical assets with known custody/ provenance.
We are doing something similar with shared CVE fixes. We have a shared private virtual mono repository for sharing monthly patches. It is also shared with Red Hat. This means we can work together to make the right fix at the right time in a coordinated way.
.NET container images are not signed yet, but are coming soon. We regularly work to improve our security-focused capabilities.
support
Canonical and Microsoft have been working together to give you a better experience. This includes support. You can report issues in familiar .NET repositories such as dotnet/core and dotnet/runtime . If you want commercial support, you should start with Canonical Support . Canonical is the best place to support Ubuntu packages. Canonical may contact Microsoft as needed to assist with problem resolution.
Security researchers who find vulnerabilities in Canonical-provided .NET packages are still eligible to participate in the Microsoft .NET Bounty Program .
Microsoft continues to maintain .NET packages for Ubuntu in its packages.microsoft.com source code, and we intend to continue to do so in the future. For most users, we recommend using the dotnet6 package that comes with Ubuntu Jammy+. This is what I did. This is the same guide we provide for Red Hat users.
Note: Now that .NET 6 is included in Ubuntu, check out our advice on using packages.microsoft.com on Ubuntu 22.04 .
There are two main reasons for continuing to use Microsoft packages:
- You want to use the .NET version from Microsoft, not any other vendor.
- Microsoft packages target later .NET SDK feature bands (eg
6.0.4xx
), while the source build tracks6.0.1xx
. This is more relevant for Windows users, but may be important for some Linux users.
New packages are available for .NET 6+ and Ubuntu 22.04+. Previous .NET and Ubuntu versions (with new packages) are not supported. You must use an existing packages.microsoft.com
feed to use .NET on earlier Ubuntu versions. Also, Ubuntu 22.04 does not support earlier .NET versions because they do not support OpenSSL v3.
next steps
We've identified many opportunities to make Canonical easier to work with .NET source code . We will focus on these in the short term. These improvements will also benefit other users who build and distribute .NET from source.
We recently set up a release maintenance group for .NET. Canonical is a member of this group. We have started discussing potential source build improvements in this forum. Other distributions (building .NET from source) are welcome to join. For more information, please contact dotnet@microsoft.com .
Canonical is starting to support x64 and will soon add .NET packages for Arm64. This is an exciting time for an industry that needs to support multiple mainline chip architectures. Ubuntu and .NET have a long history of supporting multiple architectures.
.NET has been open source for over 5 years. Our partnership with Canonical felt out of reach in the early stages of our GitHub project. We've learned a lot about how to structure an OSS project so that it becomes a candidate for a Linux distribution. Thanks to other partners who have taught us a lot, especially Fedora and Red Hat . Looking back, it's easy to see that open source, trust, and industry relationships are more important now than when we started. We are delighted and honored to be working with Canonical.