private string BuildTestAppImage(string stageTarget, string contextDir, params string[] customBuildArgs) { string tag = _imageData.GetIdentifier(stageTarget); List <string> buildArgs = new List <string>(); buildArgs.Add($"sdk_image={_imageData.GetImage(DotNetImageType.SDK, _dockerHelper)}"); DotNetImageType runtimeImageType = _isWeb ? DotNetImageType.Aspnet : DotNetImageType.Runtime; buildArgs.Add($"runtime_image={_imageData.GetImage(runtimeImageType, _dockerHelper)}"); if (DockerHelper.IsLinuxContainerModeEnabled) { buildArgs.Add($"runtime_deps_image={_imageData.GetImage(DotNetImageType.Runtime_Deps, _dockerHelper)}"); } if (customBuildArgs != null) { buildArgs.AddRange(customBuildArgs); } _dockerHelper.Build( tag: tag, target: stageTarget, contextDir: contextDir, buildArgs: buildArgs.ToArray()); return(tag); }
public void VerifyPackageCache(ProductImageData imageData) { string verifyCacheCommand = null; if (imageData.Version.Major == 2) { if (DockerHelper.IsLinuxContainerModeEnabled) { verifyCacheCommand = "test -d /usr/share/dotnet/sdk/NuGetFallbackFolder"; } else { verifyCacheCommand = "CMD /S /C PUSHD \"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder\""; } } else { OutputHelper.WriteLine(".NET Core SDK images >= 3.0 don't include a package cache."); } if (verifyCacheCommand != null) { // Simple check to verify the NuGet package cache was created DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), command: verifyCacheCommand, name: imageData.GetIdentifier("PackageCache")); } }
protected void VerifyCommonInsecureFiles(ProductImageData imageData) { if (imageData.Version < new Version("3.1") || (imageData.OS.Contains("alpine") && imageData.IsArm)) { return; } string worldWritableDirectoriesWithoutStickyBitCmd = @"find / -xdev -type d \( -perm -0002 -a ! -perm -1000 \)"; string worldWritableFilesCmd = "find / -xdev -type f -perm -o+w"; string noUserOrGroupFilesCmd; if (imageData.OS.Contains("alpine")) { // BusyBox in Alpine doesn't support the more convenient -nouser and -nogroup options for the find command noUserOrGroupFilesCmd = @"find / -xdev -exec stat -c %U-%n {} \+ | { grep ^UNKNOWN || true; }"; } else { noUserOrGroupFilesCmd = @"find / -xdev \( -nouser -o -nogroup \)"; } string command = $"/bin/sh -c \"{worldWritableDirectoriesWithoutStickyBitCmd} && {worldWritableFilesCmd} && {noUserOrGroupFilesCmd}\""; string output = DockerHelper.Run( image: imageData.GetImage(ImageType, DockerHelper), name: imageData.GetIdentifier($"InsecureFiles-{ImageType}"), command: command ); Assert.Empty(output); }
private void PowerShellScenario_Execute(ProductImageData imageData, string optionalArgs) { if (imageData.Version.Major < 3) { OutputHelper.WriteLine("PowerShell does not exist in pre-3.0 images, skip testing"); return; } // Disable this test for Arm-based Alpine on 6.0 until PowerShell has support (https://github.com/PowerShell/PowerShell/issues/14667, https://github.com/PowerShell/PowerShell/issues/12937) if (imageData.Version.Major == 6 && imageData.OS.Contains("alpine") && imageData.IsArm) { OutputHelper.WriteLine("PowerShell does not have Alpine arm images, skip testing"); return; } // A basic test which executes an arbitrary command to validate PS is functional string output = DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), name: imageData.GetIdentifier($"pwsh"), optionalRunArgs: optionalArgs, command: $"pwsh -c (Get-Childitem env:DOTNET_RUNNING_IN_CONTAINER).Value" ); Assert.Equal(output, bool.TrueString, ignoreCase: true); }
public static void Validate( IEnumerable <EnvironmentVariableInfo> variables, DotNetImageType imageType, ProductImageData imageData, DockerHelper dockerHelper) { const char delimiter = '|'; IEnumerable <string> echoParts; string invokeCommand; char delimiterEscape; if (DockerHelper.IsLinuxContainerModeEnabled) { echoParts = variables.Select(envVar => $"${envVar.Name}"); invokeCommand = $"/bin/sh -c"; delimiterEscape = '\\'; } else { echoParts = variables.Select(envVar => $"%{envVar.Name}%"); invokeCommand = $"CMD /S /C"; delimiterEscape = '^'; } string combinedValues = dockerHelper.Run( image: imageData.GetImage(imageType, dockerHelper), name: imageData.GetIdentifier($"env"), command: $"{invokeCommand} \"echo {string.Join($"{delimiterEscape}{delimiter}", echoParts)}\""); string[] values = combinedValues.Split(delimiter); Assert.Equal(variables.Count(), values.Count()); for (int i = 0; i < values.Count(); i++) { EnvironmentVariableInfo variable = variables.ElementAt(i); string actualValue; // Process unset variables in Windows if (!DockerHelper.IsLinuxContainerModeEnabled && string.Equals(values[i], $"%{variable.Name}%", StringComparison.Ordinal)) { actualValue = string.Empty; } else { actualValue = values[i]; } if (variable.AllowAnyValue) { Assert.NotEmpty(actualValue); } else { Assert.Equal(variable.ExpectedValue, actualValue); } } }
private IEnumerable <string> GetInstalledRpmPackages(ProductImageData imageData) { // Get list of installed RPM packages string command = $"bash -c \"rpm -qa | sort\""; string installedPackages = DockerHelper.Run( image: imageData.GetImage(ImageType, DockerHelper), command: command, name: imageData.GetIdentifier("PackageInstallation")); return(installedPackages.Split(Environment.NewLine)); }
private void PowerShellScenario_Execute(ProductImageData imageData, string optionalArgs) { if (imageData.Version.Major < 3) { OutputHelper.WriteLine("PowerShell does not exist in pre-3.0 images, skip testing"); return; } // A basic test which executes an arbitrary command to validate PS is functional string output = DockerHelper.Run( image: imageData.GetImage(DotNetImageType.SDK, DockerHelper), name: imageData.GetIdentifier($"pwsh"), optionalRunArgs: optionalArgs, command: $"pwsh -c (Get-Childitem env:DOTNET_RUNNING_IN_CONTAINER).Value" ); Assert.Equal(output, bool.TrueString, ignoreCase: true); }
public void VerifyDistrolessRunsAsNonRootUser(ProductImageData imageData) { if (!imageData.IsDistroless) { return; } string command = $"bash -c \"echo $EUID\""; string imageTag = DockerHelper.BuildDistrolessHelper(ImageType, imageData, "bash"); string userId = DockerHelper.Run( image: imageTag, command: command, name: imageData.GetIdentifier("NonRootUser")); Assert.NotEqual("0", userId); }
private IEnumerable <SdkContentFileInfo> GetActualSdkContents(ProductImageData imageData) { string dotnetPath; if (DockerHelper.IsLinuxContainerModeEnabled) { dotnetPath = "/usr/share/dotnet"; } else { dotnetPath = "Program Files\\dotnet"; } string powerShellCommand = $"Get-ChildItem -File -Force -Recurse '{dotnetPath}' " + "| Get-FileHash -Algorithm SHA512 " + "| select @{name='Value'; expression={$_.Hash + ' ' +$_.Path}} " + "| select -ExpandProperty Value"; string command = $"pwsh -Command \"{powerShellCommand}\""; string containerFileList = DockerHelper.Run( image: imageData.GetImage(ImageType, DockerHelper), command: command, name: imageData.GetIdentifier("DotnetFolder")); IEnumerable <SdkContentFileInfo> actualDotnetFiles = containerFileList .Replace("\r\n", "\n") .Split("\n") .Select(output => { string[] outputParts = output.Split(" "); return(new SdkContentFileInfo(outputParts[1], outputParts[0])); }) .OrderBy(fileInfo => fileInfo.Path) .ToArray(); return(actualDotnetFiles); }
private IEnumerable <string> GetInstalledRpmPackages(ProductImageData imageData) { // Get list of installed RPM packages string command = $"bash -c \"rpm -qa | sort\""; string imageTag; if (imageData.IsDistroless) { imageTag = DockerHelper.BuildDistrolessHelper(ImageType, imageData, "bash", "rpm"); } else { imageTag = imageData.GetImage(ImageType, DockerHelper); } string installedPackages = DockerHelper.Run( image: imageTag, command: command, name: imageData.GetIdentifier("PackageInstallation")); return(installedPackages.Split(Environment.NewLine)); }
/// <summary> /// Builds a helper image intended to test distroless scenarios. /// </summary> /// <remarks> /// Because distroless containers do not contain a shell, and potentially other packages necessary for testing, /// this helper image adds the necessary packages to a distroless image. /// </remarks> public string BuildDistrolessHelper(DotNetImageType imageType, ProductImageData imageData, params string[] requiredPackages) { string dockerfile; if (imageData.OS.Contains("mariner")) { dockerfile = Path.Combine(TestArtifactsDir, "Dockerfile.cbl-mariner-distroless"); } else { throw new NotImplementedException($"Distroless helper not implemented for OS '{imageData.OS}'"); } string baseImageTag = imageData.GetImage(imageType, this); string installerImageTag = baseImageTag.Replace("-distroless", string.Empty); string tag = imageData.GetIdentifier("distroless-helper"); Build(tag, dockerfile, null, TestArtifactsDir, false, $"installer_image={installerImageTag}", $"base_image={baseImageTag}", $"\"required_packages={string.Join(" ", requiredPackages)}\""); return(tag); }
public void VerifyShellNotInstalledForDistroless(ProductImageData imageData) { if (!imageData.IsDistroless) { OutputHelper.WriteLine("Skipping test for non-distroless platform."); return; } string imageTag = imageData.GetImage(ImageType, DockerHelper); // Attempting to execute the container's shell should result in an exception. // There should be no shell installed in distroless containers. InvalidOperationException ex = Assert.Throws <InvalidOperationException>(() => DockerHelper.Run( image: imageTag, name: imageData.GetIdentifier($"env"), optionalRunArgs: $"--entrypoint /bin/sh") ); Assert.Contains("Exit code: 127", ex.Message); }