Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use useradd on Alpine when user ID is large #4851

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 41 additions & 5 deletions src/Agent.Worker/ContainerOperationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ private async Task<string> 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}");
}
Expand Down Expand Up @@ -708,7 +708,43 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta
Func<string, string, string, string> addUserWithIdAndGroup;
Func<string, string, string> addUserToGroup;

bool useShadowIfAlpine = false;

if (isAlpineBasedImage)
{
List<string> 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 && !useShadowIfAlpine)
{
addGroup = (groupName) => $"addgroup {groupName}";
addGroupWithId = (groupName, groupId) => $"addgroup -g {groupId} {groupName}";
Expand Down Expand Up @@ -1009,7 +1045,7 @@ private async Task ContainerHealthcheck(IExecutionContext executionContext, Cont
}
}

private async Task<List<string>> DockerExec(IExecutionContext context, string containerId, string command, bool noExceptionOnError=false)
private async Task<List<string>> DockerExec(IExecutionContext context, string containerId, string command, bool noExceptionOnError = false)
{
Trace.Info($"Docker-exec is going to execute: `{command}`; container id: `{containerId}`");
List<string> output = new List<string>();
Expand All @@ -1027,7 +1063,7 @@ private async Task<List<string>> DockerExec(IExecutionContext context, string co
if (exitCode != 0)
{
Trace.Error(message);
if(!noExceptionOnError)
if (!noExceptionOnError)
{
throw new InvalidOperationException(message);
}
Expand All @@ -1046,14 +1082,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
{
Expand Down
1 change: 1 addition & 0 deletions src/Misc/layoutbin/en-US/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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}'",
Expand Down
Loading