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

Cloudfront URL config changes #25145

Merged
merged 1 commit into from
Jan 6, 2025
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
4 changes: 4 additions & 0 deletions changes/23823-cloudfront-cdn
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Allow delivery of bootstrap packages and software installers using signed URLs from CloudFront CDN. To enable, configure server settings:
- s3_software_installers_cloudfront_url
- s3_software_installers_cloudfront_url_signing_public_key_id
- s3_software_installers_cloudfront_url_signing_private_key
37 changes: 19 additions & 18 deletions cmd/fleet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,25 @@ func applyDevFlags(cfg *config.FleetConfig) {
cfg.Prometheus.BasicAuth.Password = "insecure"
}

cfg.S3 = config.S3Config{
CarvesBucket: "carves-dev",
CarvesRegion: "minio",
CarvesPrefix: "dev-prefix",
CarvesEndpointURL: "localhost:9000",
CarvesAccessKeyID: "minio",
CarvesSecretAccessKey: "minio123!",
CarvesDisableSSL: true,
CarvesForceS3PathStyle: true,

SoftwareInstallersBucket: "software-installers-dev",
SoftwareInstallersRegion: "minio",
SoftwareInstallersPrefix: "dev-prefix",
SoftwareInstallersEndpointURL: "localhost:9000",
SoftwareInstallersAccessKeyID: "minio",
SoftwareInstallersSecretAccessKey: "minio123!",
SoftwareInstallersDisableSSL: true,
SoftwareInstallersForceS3PathStyle: true,
cfg.S3.CarvesBucket = "carves-dev"
cfg.S3.CarvesRegion = "minio"
cfg.S3.CarvesPrefix = "dev-prefix"
cfg.S3.CarvesEndpointURL = "localhost:9000"
cfg.S3.CarvesAccessKeyID = "minio"
cfg.S3.CarvesSecretAccessKey = "minio123!"
cfg.S3.CarvesDisableSSL = true
cfg.S3.CarvesForceS3PathStyle = true

// Allow the software installers bucket to be overridden in dev mode
if cfg.S3.SoftwareInstallersBucket == "" {
cfg.S3.SoftwareInstallersBucket = "software-installers-dev"
cfg.S3.SoftwareInstallersRegion = "minio"
cfg.S3.SoftwareInstallersPrefix = "dev-prefix"
cfg.S3.SoftwareInstallersEndpointURL = "localhost:9000"
cfg.S3.SoftwareInstallersAccessKeyID = "minio"
cfg.S3.SoftwareInstallersSecretAccessKey = "minio123!"
cfg.S3.SoftwareInstallersDisableSSL = true
cfg.S3.SoftwareInstallersForceS3PathStyle = true
}

cfg.Packaging.S3 = config.S3Config{
Expand Down
2 changes: 2 additions & 0 deletions cmd/fleet/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,8 @@ the way that the Fleet server works.
}
bootstrapPackageStore = bstore
level.Info(logger).Log("msg", "using S3 bootstrap package store", "bucket", config.S3.SoftwareInstallersBucket)

config.S3.ValidateCloudfrontURL(initFatal)
} else {
installerDir := os.TempDir()
if dir := os.Getenv("FLEET_SOFTWARE_INSTALLER_STORE_DIR"); dir != "" {
Expand Down
78 changes: 58 additions & 20 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,16 +316,48 @@ type S3Config struct {
CarvesDisableSSL bool `yaml:"carves_disable_ssl"`
CarvesForceS3PathStyle bool `yaml:"carves_force_s3_path_style"`

SoftwareInstallersBucket string `yaml:"software_installers_bucket"`
SoftwareInstallersPrefix string `yaml:"software_installers_prefix"`
SoftwareInstallersRegion string `yaml:"software_installers_region"`
SoftwareInstallersEndpointURL string `yaml:"software_installers_endpoint_url"`
SoftwareInstallersAccessKeyID string `yaml:"software_installers_access_key_id"`
SoftwareInstallersSecretAccessKey string `yaml:"software_installers_secret_access_key"`
SoftwareInstallersStsAssumeRoleArn string `yaml:"software_installers_sts_assume_role_arn"`
SoftwareInstallersStsExternalID string `yaml:"software_installers_sts_external_id"`
SoftwareInstallersDisableSSL bool `yaml:"software_installers_disable_ssl"`
SoftwareInstallersForceS3PathStyle bool `yaml:"software_installers_force_s3_path_style"`
SoftwareInstallersBucket string `yaml:"software_installers_bucket"`
SoftwareInstallersPrefix string `yaml:"software_installers_prefix"`
SoftwareInstallersRegion string `yaml:"software_installers_region"`
SoftwareInstallersEndpointURL string `yaml:"software_installers_endpoint_url"`
SoftwareInstallersAccessKeyID string `yaml:"software_installers_access_key_id"`
SoftwareInstallersSecretAccessKey string `yaml:"software_installers_secret_access_key"`
SoftwareInstallersStsAssumeRoleArn string `yaml:"software_installers_sts_assume_role_arn"`
SoftwareInstallersStsExternalID string `yaml:"software_installers_sts_external_id"`
SoftwareInstallersDisableSSL bool `yaml:"software_installers_disable_ssl"`
SoftwareInstallersForceS3PathStyle bool `yaml:"software_installers_force_s3_path_style"`
SoftwareInstallersCloudfrontURL string `yaml:"software_installers_cloudfront_url"`
SoftwareInstallersCloudfrontURLSigningPublicKeyID string `yaml:"software_installers_cloudfront_url_signing_public_key_id"`
SoftwareInstallersCloudfrontURLSigningPrivateKey string `yaml:"software_installers_cloudfront_url_signing_private_key"`
}

func (s S3Config) ValidateCloudfrontURL(initFatal func(err error, msg string)) {
if s.SoftwareInstallersCloudfrontURL != "" {
cloudfrontURL, err := url.Parse(s.SoftwareInstallersCloudfrontURL)
if err != nil {
initFatal(err, "S3 software installers cloudfront URL")
return
}
if cloudfrontURL.Scheme != "https" {
initFatal(errors.New("cloudfront url scheme must be https"), "S3 software installers cloudfront URL")
return
}
if s.SoftwareInstallersCloudfrontURLSigningPrivateKey != "" && s.SoftwareInstallersCloudfrontURLSigningPublicKeyID == "" ||
s.SoftwareInstallersCloudfrontURLSigningPrivateKey == "" && s.SoftwareInstallersCloudfrontURLSigningPublicKeyID != "" {
initFatal(errors.New("Couldn't configure. Both `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key` must be set for URL signing."),
"S3 software installers cloudfront URL")
return
}
if s.SoftwareInstallersCloudfrontURLSigningPrivateKey == "" && s.SoftwareInstallersCloudfrontURLSigningPublicKeyID == "" {
initFatal(errors.New("Couldn't configure. Both `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key` must be set when CloudFront distribution URL is set."),
"S3 software installers cloudfront URL")
return
}
} else if s.SoftwareInstallersCloudfrontURLSigningPrivateKey != "" || s.SoftwareInstallersCloudfrontURLSigningPublicKeyID != "" {
initFatal(errors.New("Couldn't configure. `s3_software_installers_cloudfront_url` must be set to use `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key`."),
"S3 software installers cloudfront URL")
return
}
}

func (s S3Config) BucketsAndPrefixesMatch() bool {
Expand Down Expand Up @@ -1197,6 +1229,9 @@ func (man Manager) addConfigs() {
man.addConfigString("s3.software_installers_sts_external_id", "", "Optional unique identifier that can be used by the principal assuming the role to assert its identity.")
man.addConfigBool("s3.software_installers_disable_ssl", false, "Disable SSL (typically for local testing)")
man.addConfigBool("s3.software_installers_force_s3_path_style", false, "Set this to true to force path-style addressing, i.e., `http://s3.amazonaws.com/BUCKET/KEY`")
man.addConfigString("s3.software_installers_cloudfront_url", "", "CloudFront URL for software installers")
man.addConfigString("s3.software_installers_cloudfront_url_signing_public_key_id", "", "CloudFront public key ID for URL signing")
man.addConfigString("s3.software_installers_cloudfront_url_signing_private_key", "", "CloudFront private key for URL signing")

// PubSub
man.addConfigString("pubsub.project", "", "Google Cloud Project to use")
Expand Down Expand Up @@ -1622,16 +1657,19 @@ func (man Manager) loadS3Config() S3Config {
DisableSSL: man.getConfigBool("s3.disable_ssl"),
ForceS3PathStyle: man.getConfigBool("s3.force_s3_path_style"),

SoftwareInstallersBucket: man.getConfigString("s3.software_installers_bucket"),
SoftwareInstallersPrefix: man.getConfigString("s3.software_installers_prefix"),
SoftwareInstallersRegion: man.getConfigString("s3.software_installers_region"),
SoftwareInstallersEndpointURL: man.getConfigString("s3.software_installers_endpoint_url"),
SoftwareInstallersAccessKeyID: man.getConfigString("s3.software_installers_access_key_id"),
SoftwareInstallersSecretAccessKey: man.getConfigString("s3.software_installers_secret_access_key"),
SoftwareInstallersStsAssumeRoleArn: man.getConfigString("s3.software_installers_sts_assume_role_arn"),
SoftwareInstallersStsExternalID: man.getConfigString("s3.software_installers_sts_external_id"),
SoftwareInstallersDisableSSL: man.getConfigBool("s3.software_installers_disable_ssl"),
SoftwareInstallersForceS3PathStyle: man.getConfigBool("s3.software_installers_force_s3_path_style"),
SoftwareInstallersBucket: man.getConfigString("s3.software_installers_bucket"),
SoftwareInstallersPrefix: man.getConfigString("s3.software_installers_prefix"),
SoftwareInstallersRegion: man.getConfigString("s3.software_installers_region"),
SoftwareInstallersEndpointURL: man.getConfigString("s3.software_installers_endpoint_url"),
SoftwareInstallersAccessKeyID: man.getConfigString("s3.software_installers_access_key_id"),
SoftwareInstallersSecretAccessKey: man.getConfigString("s3.software_installers_secret_access_key"),
SoftwareInstallersStsAssumeRoleArn: man.getConfigString("s3.software_installers_sts_assume_role_arn"),
SoftwareInstallersStsExternalID: man.getConfigString("s3.software_installers_sts_external_id"),
SoftwareInstallersDisableSSL: man.getConfigBool("s3.software_installers_disable_ssl"),
SoftwareInstallersForceS3PathStyle: man.getConfigBool("s3.software_installers_force_s3_path_style"),
SoftwareInstallersCloudfrontURL: man.getConfigString("s3.software_installers_cloudfront_url"),
SoftwareInstallersCloudfrontURLSigningPublicKeyID: man.getConfigString("s3.software_installers_cloudfront_url_signing_public_key_id"),
SoftwareInstallersCloudfrontURLSigningPrivateKey: man.getConfigString("s3.software_installers_cloudfront_url_signing_private_key"),
}
}

Expand Down
41 changes: 41 additions & 0 deletions server/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,3 +695,44 @@ e+Z1cALnWREYhEPv4JrR5U0VvqeIdExDD6Ida61yvd7oc59pn0kpfKjozPJr6FsU
// prevent static analysis tools from raising issues due to detection of private key
// in code.
func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") }

func TestValidateCloudfrontURL(t *testing.T) {
t.Parallel()
cases := []struct {
name string
url string
publicKey string
privateKey string
errMatches string
}{
{"happy path", "https://example.com", "public", "private", ""},
{"bad URL", "bozo!://example.com", "public", "private", "parse"},
{"non-HTTPS URL", "http://example.com", "public", "private", "cloudfront url scheme must be https"},
{"missing URL", "", "public", "private", "`s3_software_installers_cloudfront_url` must be set"},
{"missing public key", "https://example.com", "", "private",
"Both `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key` must be set"},
{"missing private key", "https://example.com", "public", "",
"Both `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key` must be set"},
{"missing keys", "https://example.com", "", "",
"Both `s3_software_installers_cloudfront_url_signing_public_key_id` and `s3_software_installers_cloudfront_url_signing_private_key` must be set"},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
s3 := S3Config{
SoftwareInstallersCloudfrontURL: c.url,
SoftwareInstallersCloudfrontURLSigningPublicKeyID: c.publicKey,
SoftwareInstallersCloudfrontURLSigningPrivateKey: c.privateKey,
}
initFatal := func(err error, msg string) {
if c.errMatches != "" {
require.Error(t, err)
require.Regexp(t, c.errMatches, err.Error())
} else {
t.Errorf("unexpected error: %v", err)
}
}
s3.ValidateCloudfrontURL(initFatal)
})
}
}
Loading