From ca4875a95b4b1cfba90e626551a4e109b454d875 Mon Sep 17 00:00:00 2001 From: DenisRumyantsev Date: Mon, 17 Jun 2024 12:24:58 +0200 Subject: [PATCH 1/2] use useradd on Alpine when userId is large --- .../ContainerOperationProvider.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Agent.Worker/ContainerOperationProvider.cs b/src/Agent.Worker/ContainerOperationProvider.cs index 2362725334..ebf8674763 100644 --- a/src/Agent.Worker/ContainerOperationProvider.cs +++ b/src/Agent.Worker/ContainerOperationProvider.cs @@ -194,7 +194,7 @@ private async Task GetAccessTokenUsingWorkloadIdentityFederation(IExecut Trace.Entering(); var tenantId = string.Empty; - if(!registryEndpoint.Authorization?.Parameters?.TryGetValue(c_tenantId, out tenantId) ?? false) + if (!registryEndpoint.Authorization?.Parameters?.TryGetValue(c_tenantId, out tenantId) ?? false) { throw new InvalidOperationException($"Could not read {c_tenantId}"); } @@ -708,7 +708,14 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta Func addUserWithIdAndGroup; Func addUserToGroup; - if (isAlpineBasedImage) + bool userIdIsLarge = Int64.Parse(container.CurrentUserId) > 256000; + + if (isAlpineBasedImage && userIdIsLarge) + { + await DockerExec(executionContext, container.ContainerId, "apk add shadow"); + } + + if (isAlpineBasedImage && !userIdIsLarge) { addGroup = (groupName) => $"addgroup {groupName}"; addGroupWithId = (groupName, groupId) => $"addgroup -g {groupId} {groupName}"; @@ -1009,7 +1016,7 @@ private async Task ContainerHealthcheck(IExecutionContext executionContext, Cont } } - private async Task> DockerExec(IExecutionContext context, string containerId, string command, bool noExceptionOnError=false) + private async Task> DockerExec(IExecutionContext context, string containerId, string command, bool noExceptionOnError = false) { Trace.Info($"Docker-exec is going to execute: `{command}`; container id: `{containerId}`"); List output = new List(); @@ -1027,7 +1034,7 @@ private async Task> DockerExec(IExecutionContext context, string co if (exitCode != 0) { Trace.Error(message); - if(!noExceptionOnError) + if (!noExceptionOnError) { throw new InvalidOperationException(message); } @@ -1046,14 +1053,14 @@ private static void ThrowIfAlreadyInContainer() { if (PlatformUtil.RunningOnWindows) { - #pragma warning disable CA1416 // SupportedOSPlatform checks not respected in lambda usage +#pragma warning disable CA1416 // SupportedOSPlatform checks not respected in lambda usage // service CExecSvc is Container Execution Agent. ServiceController[] scServices = ServiceController.GetServices(); if (scServices.Any(x => String.Equals(x.ServiceName, "cexecsvc", StringComparison.OrdinalIgnoreCase) && x.Status == ServiceControllerStatus.Running)) { throw new NotSupportedException(StringUtil.Loc("AgentAlreadyInsideContainer")); } - #pragma warning restore CA1416 +#pragma warning restore CA1416 } else { From 2831ca65052c4653700009075cb85dc029c6f27a Mon Sep 17 00:00:00 2001 From: DenisRumyantsev Date: Wed, 19 Jun 2024 08:03:30 +0200 Subject: [PATCH 2/2] renaming --- .../ContainerOperationProvider.cs | 37 +++++++++++++++++-- src/Misc/layoutbin/en-US/strings.json | 1 + 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Agent.Worker/ContainerOperationProvider.cs b/src/Agent.Worker/ContainerOperationProvider.cs index ebf8674763..07a13ff41f 100644 --- a/src/Agent.Worker/ContainerOperationProvider.cs +++ b/src/Agent.Worker/ContainerOperationProvider.cs @@ -708,14 +708,43 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta Func addUserWithIdAndGroup; Func addUserToGroup; - bool userIdIsLarge = Int64.Parse(container.CurrentUserId) > 256000; + bool useShadowIfAlpine = false; - if (isAlpineBasedImage && userIdIsLarge) + if (isAlpineBasedImage) { - await DockerExec(executionContext, container.ContainerId, "apk add shadow"); + List shadowInfoOutput = await DockerExec(executionContext, container.ContainerId, "apk list --installed | grep shadow"); + bool shadowPreinstalled = false; + + foreach (string shadowInfoLine in shadowInfoOutput) + { + if (shadowInfoLine.Contains("{shadow}", StringComparison.Ordinal)) + { + Trace.Info("The 'shadow' package is preinstalled and therefore will be used."); + shadowPreinstalled = true; + break; + } + } + + bool userIdIsOutsideAdduserCommandRange = Int64.Parse(container.CurrentUserId) > 256000; + + if (userIdIsOutsideAdduserCommandRange && !shadowPreinstalled) + { + Trace.Info("User ID is outside the range of the 'adduser' command, therefore the 'shadow' package will be installed and used."); + + try + { + await DockerExec(executionContext, container.ContainerId, "apk add shadow"); + } + catch (InvalidOperationException) + { + throw new InvalidOperationException(StringUtil.Loc("ApkAddShadowFailed")); + } + } + + useShadowIfAlpine = shadowPreinstalled || userIdIsOutsideAdduserCommandRange; } - if (isAlpineBasedImage && !userIdIsLarge) + if (isAlpineBasedImage && !useShadowIfAlpine) { addGroup = (groupName) => $"addgroup {groupName}"; addGroupWithId = (groupName, groupId) => $"addgroup -g {groupId} {groupName}"; diff --git a/src/Misc/layoutbin/en-US/strings.json b/src/Misc/layoutbin/en-US/strings.json index ef6d182260..64ece52636 100644 --- a/src/Misc/layoutbin/en-US/strings.json +++ b/src/Misc/layoutbin/en-US/strings.json @@ -27,6 +27,7 @@ "AgentWithSameNameAlreadyExistInPool": "Pool {0} already contains an agent with name {1}.", "AllowContainerUserRunDocker": "Allow user '{0}' run any docker command without SUDO.", "AlreadyConfiguredError": "Cannot configure the agent because it is already configured. To reconfigure the agent, run 'config.cmd remove' or './config.sh remove' first.", + "ApkAddShadowFailed": "The user ID is outside the range of the 'adduser' command. The alternative command 'useradd' cannot be used because the 'shadow' package is not preinstalled and the attempt to install this package failed. Check network availability or use a docker image with the 'shadow' package preinstalled.", "ArgumentNeeded": "'{0}' has to be specified.", "ArtifactCustomPropertiesNotJson": "Artifact custom properties is not valid JSON: '{0}'", "ArtifactCustomPropertyInvalid": "Artifact custom properties must be prefixed with 'user-'. Invalid property: '{0}'",