Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
go-version: '1.27.0'

- name: Build
run: go build -v ./...
Expand Down
52 changes: 24 additions & 28 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,53 +1,49 @@
module github.com/Azure/run-command-handler-linux

go 1.23.0

toolchain go1.24.5
go 1.27.0

require (
github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c
github.com/Azure/azure-extension-platform v0.0.0-20260410171604-91b4725acbb1
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20200921172446-862fbad56b77
github.com/ahmetb/go-httpbin v0.0.0-20200921172446-862fbad56b77
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.0
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20240315150752-da45896c98cb
github.com/ahmetb/go-httpbin v0.0.0-20240315150752-da45896c98cb
github.com/go-kit/kit v0.13.0
github.com/google/uuid v1.6.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.9.0
golang.org/x/text v0.23.0
github.com/stretchr/testify v1.12.1
golang.org/x/text v0.41.0
)

require (
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest v0.11.30 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/Azure/go-autorest/logger v0.2.2 // indirect
github.com/Azure/go-autorest/tracing v0.6.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.9.0 // indirect
github.com/andybalholm/brotli v1.2.2 // indirect
github.com/dnaeon/go-vcr v1.2.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/gorilla/context v0.0.0-20160525203319-aed02d124ae4 // indirect
github.com/gorilla/mux v0.0.0-20160605233521-9fa818a44c2b // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2
replace github.com/go-kit/kit => github.com/go-kit/kit v0.1.1-0.20160721083846-b076b44dbec2
113 changes: 54 additions & 59 deletions go.sum

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions internal/cmds/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ func copyFiles(ctx log.Logger, fileExtensionSuffix string, extensionSubdirectory
errMessage := "Failed to open '%s' file '%s' for reading. Contact ICM team AzureRT\\Extensions for this service error."
ctx.Log("message", fmt.Sprintf(errMessage, fileExtensionSuffix, sourceFileFullPath))
extensionEvents.LogErrorEvent("copyfiles", errMessage)
return fileNamesMigrated, errors.Wrapf(sourceFileOpenError, errMessage)
return fileNamesMigrated, errors.Wrap(sourceFileOpenError, errMessage)
}
defer sourceFile.Close()

Expand All @@ -747,7 +747,7 @@ func copyFiles(ctx log.Logger, fileExtensionSuffix string, extensionSubdirectory
errMessage := "Failed to create '%s' file '%s'. Contact ICM team AzureRT\\Extensions for this service error."
ctx.Log("message", fmt.Sprintf(errMessage, fileExtensionSuffix, destinationFileFullPath))
extensionEvents.LogErrorEvent("copyfiles", errMessage)
return fileNamesMigrated, errors.Wrapf(destFileCreateError, errMessage)
return fileNamesMigrated, errors.Wrap(destFileCreateError, errMessage)
}
defer destFile.Close()

Expand All @@ -757,7 +757,7 @@ func copyFiles(ctx log.Logger, fileExtensionSuffix string, extensionSubdirectory
fileExtensionSuffix, sourceFileFullPath, destinationFileFullPath)
ctx.Log("message", errMessage)
extensionEvents.LogErrorEvent("copyfiles", errMessage)
return fileNamesMigrated, errors.Wrapf(copyError, errMessage)
return fileNamesMigrated, errors.Wrap(copyError, errMessage)
} else {
message := fmt.Sprintf("File '%s' was copied successfully to '%s'", sourceFileFullPath, destinationFileFullPath)
ctx.Log("message", message)
Expand Down
16 changes: 8 additions & 8 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ func Exec(ctx *log.Context, cmd, workdir string, stdout, stderr io.WriteCloser,
if sourceScriptFileOpenError != nil {
errMessage := "Failed to open source script. Contact ICM team AzureRT\\Extensions for this service error."
ctx.Log("message", errMessage+fmt.Sprintf(" Source script file is '%s'", scriptPath))
return constants.ExitCode_RunAsOpenSourceScriptFileFailed, errors.Wrapf(sourceScriptFileOpenError, errMessage)
return constants.ExitCode_RunAsOpenSourceScriptFileFailed, errors.Wrap(sourceScriptFileOpenError, errMessage)
}

destScriptFile, destScriptCreateError := os.Create(runAsScriptFilePath)
if destScriptCreateError != nil {
errMessage := "Failed to create script for Run As in Run As directory. Contact ICM team AzureRT\\Extensions for this service error."
ctx.Log("message", errMessage+fmt.Sprintf(" Destination runAs script file is '%s'", runAsScriptFilePath))
return constants.ExitCode_RunAsCreateRunAsScriptFileFailed, errors.Wrapf(destScriptCreateError, errMessage)
return constants.ExitCode_RunAsCreateRunAsScriptFileFailed, errors.Wrap(destScriptCreateError, errMessage)
}
_, runAsScriptCopyError := io.Copy(destScriptFile, sourceScriptFile)
if runAsScriptCopyError != nil {
errMessage := fmt.Sprintf("Failed to copy script file '%s' to Run As path '%s'. Contact ICM team AzureRT\\Extensions for this service error.", scriptPath, runAsScriptFilePath)
ctx.Log("message", errMessage)
return constants.ExitCode_RunAsCopySourceScriptToRunAsScriptFileFailed, errors.Wrapf(runAsScriptCopyError, errMessage)
return constants.ExitCode_RunAsCopySourceScriptToRunAsScriptFileFailed, errors.Wrap(runAsScriptCopyError, errMessage)
}
sourceScriptFile.Close()
destScriptFile.Close()
Expand All @@ -84,28 +84,28 @@ func Exec(ctx *log.Context, cmd, workdir string, stdout, stderr io.WriteCloser,
if lookupUserError != nil {
errMessage := fmt.Sprintf("Failed to lookup RunAs user '%s'. Looks like user does not exist. For RunAs to work properly, contact admin of VM and make sure RunAs user is added on the VM and user has access to resources accessed by the Run Command (Directories, Files, Network etc.). Refer: https://aka.ms/RunCommandManagedLinux", cfg.PublicSettings.RunAsUser)
ctx.Log("message", errMessage)
return constants.ExitCode_RunAsLookupUserFailed, errors.Wrapf(lookupUserError, errMessage)
return constants.ExitCode_RunAsLookupUserFailed, errors.Wrap(lookupUserError, errMessage)
}

lookedUpUserUid, lookedUpUserUidErr := strconv.Atoi(lookedUpUser.Uid)
if lookedUpUserUidErr != nil {
errMessage := "Failed to determine RunAs user's Uid and Guid . Contact ICM team AzureRT\\Extensions for this service error."
ctx.Log("message", errMessage)
return constants.ExitCode_RunAsLookupUserUidFailed, errors.Wrapf(lookedUpUserUidErr, errMessage)
return constants.ExitCode_RunAsLookupUserUidFailed, errors.Wrap(lookedUpUserUidErr, errMessage)
}

runAsScriptChownError := os.Chown(runAsScriptFilePath, lookedUpUserUid, os.Getegid())
if runAsScriptChownError != nil {
errMessage := fmt.Sprintf("Failed to change owner of file '%s' to RunAs user '%s'. Contact ICM team AzureRT\\Extensions for this service error.", runAsScriptFilePath, cfg.PublicSettings.RunAsUser)
ctx.Log("message", errMessage)
return constants.ExitCode_RunAsScriptFileChangeOwnerFailed, errors.Wrapf(runAsScriptChownError, errMessage)
return constants.ExitCode_RunAsScriptFileChangeOwnerFailed, errors.Wrap(runAsScriptChownError, errMessage)
}

runAsScriptChmodError := os.Chmod(runAsScriptFilePath, 0550)
if runAsScriptChmodError != nil {
errMessage := fmt.Sprintf("Failed to change permissions to execute for file '%s' for RunAs user '%s'. Contact ICM team AzureRT\\Extensions for this service error.", runAsScriptFilePath, cfg.PublicSettings.RunAsUser)
ctx.Log("message", errMessage)
return constants.ExitCode_RunAsScriptFileChangePermissionsFailed, errors.Wrapf(runAsScriptChmodError, errMessage)
return constants.ExitCode_RunAsScriptFileChangePermissionsFailed, errors.Wrap(runAsScriptChmodError, errMessage)
}

// echo pipes the RunAsPassword to sudo -S for RunAsUser instead of prompting the password interactively from user and blocking.
Expand Down Expand Up @@ -141,7 +141,7 @@ func Exec(ctx *log.Context, cmd, workdir string, stdout, stderr io.WriteCloser,
}
}

return exitCode, errors.Wrapf(err, "failed to execute command")
return exitCode, errors.Wrap(err, "failed to execute command")
}

func SetEnvironmentVariables(cfg *handlersettings.HandlerSettings) (string, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func DownloadAndProcessScript(ctx *log.Context, url, downloadDir string, cfg *ha
func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, fileName string, scriptSAS string, sourceManagedIdentity *handlersettings.RunCommandManagedIdentity) (string, error) {
var err error
if !urlutil.IsValidUrl(url) {
return "", fmt.Errorf(url + " is not a valid url") // url does not contain SAS to se can log it
return "", fmt.Errorf("%s is not a valid url", url) // url does not contain SAS to se can log it
}

targetFilePath := filepath.Join(downloadDir, fileName)
Expand All @@ -69,7 +69,7 @@ func downloadAndProcessURL(ctx *log.Context, url, downloadDir string, fileName s

//If there was an error downloading using SAS URI or SAS was not provided, download using managedIdentity or publicly.
if scriptSASDownloadErr != nil || scriptSAS == "" {
ctx.Log("info",fmt.Sprintf("Downloading script using SAS token failed: %v. Attempting download using managed identity or public access.", scriptSASDownloadErr))
ctx.Log("info", fmt.Sprintf("Downloading script using SAS token failed: %v. Attempting download using managed identity or public access.", scriptSASDownloadErr))
downloaders, getDownloadersError := getDownloaders(url, sourceManagedIdentity, download.ProdMsiDownloader{})
if getDownloadersError == nil {
const mode = 0500 // we assume users download scripts to execute
Expand Down
2 changes: 1 addition & 1 deletion misc/manifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ExtensionImage xmlns="http://schemas.microsoft.com/windowsazure">
<ProviderNameSpace>Microsoft.CPlat.Core</ProviderNameSpace>
<Type>RunCommandHandlerLinux</Type>
<Version>1.3.29</Version>
<Version>1.3.30</Version>
<Label>Microsoft Azure Run Command Extension for Linux Virtual Machines</Label>
<HostingResources>VmRole</HostingResources>
<MediaLink></MediaLink>
Expand Down
3 changes: 2 additions & 1 deletion pkg/download/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func Download(ctx *log.Context, downloader Downloader) (int, io.ReadCloser, erro

if len(requestId) > 0 {
errString += fmt.Sprintf(" (Service request ID: %s)", requestId)

}
return response.StatusCode, nil, fmt.Errorf(errString)
return response.StatusCode, nil, errors.New(errString)
}
2 changes: 1 addition & 1 deletion pkg/download/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func WithRetries(ctx *log.Context, downloaders []Downloader, sf SleepFunc) (io.R
}

if downloadErrors != nil {
downloadErrors = errors.Wrapf(downloadErrors, fmt.Sprintf("Attempt %d: %s ", n+1, err.Error()))
downloadErrors = errors.Wrap(downloadErrors, fmt.Sprintf("Attempt %d: %s ", n+1, err.Error()))
} else {
downloadErrors = err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/urlutil/urlerrorhelper.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package urlutil

import (
"fmt"
"errors"
"net/url"
"strings"
)
Expand All @@ -14,7 +14,7 @@ func RemoveUrlFromErr(err error) error {
strSegments[i] = "[REDACTED]"
}
}
return fmt.Errorf(strings.Join(strSegments, " "))
return errors.New(strings.Join(strSegments, " "))
}

func IsValidUrl(urlstring string) bool {
Expand Down
Loading