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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.24.0

require (
github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c
github.com/Azure/azure-extension-platform v0.0.0-20260107210613-2a62cc200c34
github.com/Azure/azure-extension-platform v0.0.0-20260312212104-6da8a253549a
github.com/Azure/azure-sdk-for-go v63.2.0+incompatible
github.com/ahmetalpbalkan/go-httpbin v0.0.0-20160706084156-8817b883dae1
github.com/go-kit/kit v0.12.0
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c h
github.com/Azure/azure-extension-foundation v0.0.0-20250620154556-caff9e3c3c5c/go.mod h1:sNC6lMTUkXwjrQ+nttr6GXhDfvSGT7t3UDq30BEYzu8=
github.com/Azure/azure-extension-platform v0.0.0-20260107210613-2a62cc200c34 h1:7bEC4DJC4w0gx7SBy7M7Q2qi6ckmHcnnlFJzo+X/gi4=
github.com/Azure/azure-extension-platform v0.0.0-20260107210613-2a62cc200c34/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260303193429-96e5f13d68a7 h1:sckOZUC/OwKKF/bneOiik8bCf4wiOtEBpyN26IUCBMQ=
github.com/Azure/azure-extension-platform v0.0.0-20260303193429-96e5f13d68a7/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260304003136-336d3e56fa20 h1:d5WpxK2xd2htsET+EQhZa/WjauDV0RLZef15x+JLShI=
github.com/Azure/azure-extension-platform v0.0.0-20260304003136-336d3e56fa20/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260304003541-160ebdf80e13 h1:t+jiig+xE6PQjBQOASaMZfFeo8Ln5ClUCxB/SFSJ6Dw=
github.com/Azure/azure-extension-platform v0.0.0-20260304003541-160ebdf80e13/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260304004038-0cf99cda38d7 h1:TUZKvOXi2MUEQhLYsTqSIyLqxQy2U08cP9VHSNCF2Bk=
github.com/Azure/azure-extension-platform v0.0.0-20260304004038-0cf99cda38d7/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260304193358-17aecbaff233 h1:XOj8YJeGOdLZiXdSrXbdYFi8dDJnUi7xXlg7CvD26EI=
github.com/Azure/azure-extension-platform v0.0.0-20260304193358-17aecbaff233/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260305214320-4828fb38d797 h1:9YWYQi8rHmaMm1cdwVhS5FAL/ivPDzvOEyxsmHymc4w=
github.com/Azure/azure-extension-platform v0.0.0-20260305214320-4828fb38d797/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-extension-platform v0.0.0-20260312212104-6da8a253549a h1:VbxYR5y5uVJaNKQBzDnFBKoHqEjBmix+27/mPvOTqb4=
github.com/Azure/azure-extension-platform v0.0.0-20260312212104-6da8a253549a/go.mod h1:0458BvQsi5ch6kn+KZtI5m88Z3L9UFXdoY1+6nKdivY=
github.com/Azure/azure-sdk-for-go v63.2.0+incompatible h1:OIqkK/zTGqVUuzpEvY0B1YSYDRAFC/j+y0w2GovCggI=
github.com/Azure/azure-sdk-for-go v63.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
Expand Down
50 changes: 47 additions & 3 deletions main/cmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strconv"
"time"

"github.com/Azure/azure-extension-platform/pkg/extensionpolicysettings"
"github.com/Azure/azure-extension-platform/pkg/utils"
vmextension "github.com/Azure/azure-extension-platform/vmextension"
"github.com/Azure/custom-script-extension-linux/pkg/errorutil"
Expand Down Expand Up @@ -39,6 +40,7 @@ const (
fullName = "Microsoft.Azure.Extensions.CustomScript"
maxTailLen = 4 * 1024 // length of max stdout/stderr to be transmitted in .status file
maxTelemetryTailLen int = 1800
policyFileName = "waagent_runtime_policy.json"
)

var (
Expand Down Expand Up @@ -112,6 +114,19 @@ func min(a, b int) int {
return b
}

type CSEExtensionPolicySettings struct {
RequireSigning bool `json:"requireSigning"`
FileRootCertCA string `json:"fileRootCertCA,omitempty"` // optional field for customer that want to specify a root cert for script signature verification. This is a path to a cert file on the VM that the extension can use to verify script signatures. The customer is responsible for ensuring the cert is there and updated as needed (e.g. if the cert expires). The customer can choose to use this field or not based on their needs.
AllowedScripts []string `json:"allowedScripts"`
}

func (cseps CSEExtensionPolicySettings) ValidateFormat() error {
if cseps.RequireSigning && len(cseps.FileRootCertCA) == 0 {
Comment thread
alsanmsft marked this conversation as resolved.
return errors.New("invalid policy settings: if RequireSigning is true, fileRootCertCA must be provided")
}
return nil
}

func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, *vmextension.ErrorWithClarification) {
// parse the extension handler settings (not available prior to 'enable')
cfg, ewc := parseAndValidateSettings(ctx, h.HandlerEnvironment.ConfigFolder, seqNum)
Expand All @@ -121,8 +136,36 @@ func enable(ctx *log.Context, h HandlerEnvironment, seqNum int) (string, *vmexte
return "", ewc
}

// If policy file exists, load the policy.
// If the policy is invalid, we log error and exit.
// If the policy file does not exist, proceed as normal.
var ExtensionPolicyManagerPtr *extensionpolicysettings.ExtensionPolicySettingsManager[CSEExtensionPolicySettings]
policyPath := filepath.Join(h.HandlerEnvironment.ConfigFolder, policyFileName)

if _, err := os.Stat(policyPath); err == nil {
ExtensionPolicyManagerPtr, err = extensionpolicysettings.NewExtensionPolicySettingsManager[CSEExtensionPolicySettings](policyPath)
if err != nil {
return "", vmextension.NewErrorWithClarificationPtr(errorutil.ExtensionPolicySettings_policyLoadFailed, errors.Wrap(err, "failed to create extension policy settings manager"))
}
err = ExtensionPolicyManagerPtr.LoadExtensionPolicySettings()
if err != nil {
return "", vmextension.NewErrorWithClarificationPtr(errorutil.ExtensionPolicySettings_policyLoadFailed, errors.Wrap(err, "failed to load extension policy settings"))
} else {
settings, err := ExtensionPolicyManagerPtr.GetSettings()
if err != nil {
return "", vmextension.NewErrorWithClarificationPtr(errorutil.ExtensionPolicySettings_policyLoadFailed, errors.Wrap(err, "failed to get extension policy settings"))
}
ctx.Log("message", "successfully loaded extension policy settings", "settings", fmt.Sprintf("%+v", settings))
}
} else if os.IsNotExist(err) {
ctx.Log("message", "extension policy settings file does not exist, proceeding with default extension behavior.", "path", policyPath)
ExtensionPolicyManagerPtr = nil
} else {
return "", vmextension.NewErrorWithClarificationPtr(errorutil.ExtensionPolicySettings_policyLoadFailed, errors.Wrap(err, "error while checking for extension policy settings file. Stat failed with an error other than file not existing"))
}

dir := filepath.Join(dataDir, downloadDir, fmt.Sprintf("%d", seqNum))
if ewc := downloadFiles(ctx, dir, cfg); ewc != nil {
if ewc := downloadFiles(ctx, dir, cfg, ExtensionPolicyManagerPtr); ewc != nil {
ewc.Err = errors.Wrap(ewc.Err, "processing file downloads failed")
return "", ewc
}
Expand Down Expand Up @@ -180,7 +223,8 @@ func checkAndSaveSeqNum(ctx log.Logger, seq int, mrseqPath string) (shouldExit b

// downloadFiles downloads the files specified in cfg into dir (creates if does
// not exist) and takes storage credentials specified in cfg into account.
func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) *vmextension.ErrorWithClarification {
// If extension policy settings is provided, they are passed on to downloadAndProcessURL for file validation.
func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings, eps *extensionpolicysettings.ExtensionPolicySettingsManager[CSEExtensionPolicySettings]) *vmextension.ErrorWithClarification {
// - prepare the output directory for files and the command output
// - create the directory if missing
ctx.Log("event", "creating output directory", "path", dir)
Expand All @@ -205,7 +249,7 @@ func downloadFiles(ctx *log.Context, dir string, cfg handlerSettings) *vmextensi
for i, f := range cfg.fileUrls() {
ctx := ctx.With("file", i)
ctx.Log("event", "download start")
if ewc := downloadAndProcessURL(ctx, f, dir, &cfg); ewc != nil {
if ewc := downloadAndProcessURL(ctx, f, dir, &cfg, eps); ewc != nil {
ctx.Log("event", "download failed", "error", ewc.Err)
return vmextension.NewErrorWithClarificationPtr(ewc.ErrorCode, errors.Wrapf(ewc.Err, "failed to download file[%d]", i))
}
Expand Down
Loading
Loading