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.
Make sure you are running Windows Containers.
git clone firstname.lastname@example.org: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
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.
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 NuGet.Server.sln /t:Rebuild /p:Configuration=Release /v:minimal msbuild NuGet.Server.sln /p:DeployOnBuild=true /p:PublishProfile=FolderProfile
<?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 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.
# install asp.net 4.5 RUN Add-WindowsFeature Web-Asp-Net45 RUN Add-WindowsFeature NET-Framework-45-ASPNET
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
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.
# 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
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“.
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'