Docker: NuGet Server Windows Server Core
I’ve set up a NuGet Server on a Dockerized Windows Server Core IIS image. This is a multi-stage build that includes an MSBuild stage to compile NuGet Server (ASP.NET) and copy that into the final image. The Dockerfile installs useful utilities like Chocolatey and Vim.
GitHUb: Docker NuGet Server Windows Core
Also, if you’re looking for a more modern NuGet server that runs in .NET Core on Linux… Check out BaGet.
Get Started
Make sure you are running Windows Containers.
git clone [email protected]:mrjamiebowman-blog/Docker-NuGet-Server-Windows-Core.git
cd Docker-NuGet-Server-Windows-Core
cd nugetserver
./build.ps1
./run.ps1
You can access the container by running this command.
docker exec -it nugetserver powershell
MSBuild Image
There are 2 Dockerfiles in this solution. The first Dockerfile in the msbuild folder was used to figure out how to create a build server that could compile ASP.NET 4.5 since NuGet Server is older code.
Some key takeaways here are, I had to use and create a FolderProfile.pubxml file to get this to publish to the correct path, and use a PowerShell script to wrap the msbuild command.
When the msbuild command is ran it causes an enormous amount of output to the screen which returns a non zero response. This causes the Docker build to fail. I was able to over come this by wrapping that process in a PowerShell script.
1 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 |
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 as msbuild LABEL maintainer="@mrjamiebowman" SHELL ["powershell"] # install choco RUN Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) RUN choco install git -y RUN choco install vim -y # set up dirs RUN New-Item -Path C:\source -ItemType Directory -Force RUN New-Item -Path C:\published -ItemType Directory -Force WORKDIR /source # clone and run msbuild RUN git clone https://github.com/NuGet/NuGet.Server.git . RUN nuget restore # wrapping the msbuild command in a powershell scripts returns 0 and does not fail... COPY scripts/msbuild.ps1 . RUN ./msbuild.ps1 COPY scripts/FolderProfile.pubxml /source/src/NuGet.Server/Properties/PublishProfiles/FolderProfile.pubxml COPY scripts/release.ps1 . RUN ./release.ps1 ENTRYPOINT ["powershell"] |
Publishing with MSBuild
At first, I tried several things like running the msbuild command with parameters. This didn’t go so well. Then I tried creating an msbuild script which ultimately was difficult when it came time to compile and publish the ASP.NET code. The easiest way for me was to create a FolderProfile.pubxml file.
MSBuild Command
msbuild NuGet.Server.sln /t:Rebuild /p:Configuration=Release /v:minimal
msbuild NuGet.Server.sln /p:DeployOnBuild=true /p:PublishProfile=FolderProfile
FolderProfile.pubxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="utf-8"?> <!-- This file is used by the publish/package process of your Web project. You can customize the behavior of this process by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. --> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <WebPublishMethod>FileSystem</WebPublishMethod> <PublishProvider>FileSystem</PublishProvider> <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration> <LastUsedPlatform>Any CPU</LastUsedPlatform> <SiteUrlToLaunchAfterPublish /> <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish> <ExcludeApp_Data>False</ExcludeApp_Data> <publishUrl>C:\published</publishUrl> <DeleteExistingFiles>False</DeleteExistingFiles> </PropertyGroup> </Project> |
NuGet Server Image
Everything above is included in the Dockerfile that builds the NuGet Server.
Issues & Challenges
I ran into a few odd issues getting this to work.
ASP.NET 4.5
ASP.NET 4.5 is not installed by default on the IIS image. However, DotNet core is installed. I was able to get past that by running the command below.
1 2 3 |
# install asp.net 4.5 RUN Add-WindowsFeature Web-Asp-Net45 RUN Add-WindowsFeature NET-Framework-45-ASPNET |
Web.config Issues
I ran into a lot of issues with IIS and the web.config file. There were several sections of the web.config that were locked. To unlock them I had to run commands against the appcmd.exe.
RUN & $env:windir\system32\inetsrv\appcmd.exe unlock config -section:system.webServer/handlers
RUN & $env:windir\system32\inetsrv\appcmd.exe unlock config -section:system.webServer/modules
Enabling Directory Browsing
This was useful for debugging and you might need this at some point.
RUN & $env:windir\system32\inetsrv\appcmd set config /section:directoryBrowse /enabled:true
Viewing Errors
IIS will not display errors to the user. There are several ways to overcome this but I found this to be the easiest. While accessing the box and invoking a web request I was able to see the errors.
Invoke-WebRequest 'http://localhost/'
1 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 |
# build server image FROM mcr.microsoft.com/windows/servercore/iis as nugetserver SHELL ["powershell"] # todo: create user for least priviliged # todo: volume for packages # install choco, vim RUN Set-ExecutionPolicy Bpass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) RUN choco install vim -y # install RUN Add-WindowsFeature Web-Asp-Net45 RUN Add-WindowsFeature NET-Framework-45-ASPNET # dirs RUN New-Item -Path C:\setup -ItemType Directory -Force RUN New-Item -Path C:\inetpub\wwwroot\Packages -ItemType Directory -Force # clean iis folder RUN -NoProfile -Command Remove-Item -Recurse C:\inetpub\wwwroot\* # unlock sections in web.config RUN & $env:windir\system32\inetsrv\appcmd.exe unlock config -section:system.webServer/handlers RUN & $env:windir\system32\inetsrv\appcmd.exe unlock config -section:system.webServer/modules # enable directory browsing (useful for debugging) #RUN & $env:windir\system32\inetsrv\appcmd set config /section:directoryBrowse /enabled:true # copy files WORKDIR /inetpub/wwwroot COPY --from=msbuild /published/ . |
Configuring the NuGet Server
API Keys
It’s important to protect your NuGet Server. I would recommend putting this on a private network behind a firewall of some sort but even then someone could push malicious code to the repository. The web.config has 2 keys that can be modified to enforce an API key policy: “requireApiKey“, and “apiKey“.
Docker Volumes
Being that this is a stateless image it’s important to map in a volume to manage the package data. There is a configuration key in the web.config for changing the package path; search for “packagesPath”.
Restarting IIS Site
IIS by default will reload the web.config once all connections drop but if you can’t wait run this command below.
Stop-IISSite -Name 'Default Web Site'
Start-IISSite -Name 'Default Web Site'