|
7 | 7 | "errors" |
8 | 8 | "fmt" |
9 | 9 | "os" |
| 10 | + "path/filepath" |
| 11 | + "strings" |
10 | 12 |
|
11 | 13 | "github.com/hashicorp/go-cty/cty" |
12 | 14 | "github.com/hashicorp/terraform-plugin-sdk/v2/diag" |
@@ -382,6 +384,7 @@ func ResourceServerCreate(ctx context.Context, d *schema.ResourceData, m any) di |
382 | 384 | if err != nil { |
383 | 385 | return diag.FromErr(err) |
384 | 386 | } |
| 387 | + |
385 | 388 | req.UserData = &userData |
386 | 389 | } |
387 | 390 |
|
@@ -475,18 +478,51 @@ func ResourceServerCreate(ctx context.Context, d *schema.ResourceData, m any) di |
475 | 478 | return ResourceServerRead(ctx, d, m) |
476 | 479 | } |
477 | 480 |
|
478 | | -func LoadUserDataBase64(cloudInit interface{}) ([]byte, error) { |
| 481 | +func LoadUserDataBase64(cloudInit any) ([]byte, error) { |
479 | 482 | value := cloudInit.(string) |
| 483 | + |
480 | 484 | var content []byte |
481 | | - if data, err := os.ReadFile(value); errors.Is(err, os.ErrNotExist) { |
482 | | - content = []byte(value) |
483 | | - } else if err == nil { |
| 485 | + |
| 486 | + // If value refers to an existing file, read it. Otherwise treat value as the content. |
| 487 | + if fi, err := os.Stat(value); err == nil { |
| 488 | + // Only allow regular files |
| 489 | + if !fi.Mode().IsRegular() { |
| 490 | + return nil, fmt.Errorf("cloud_init path is not a regular file: %s", value) |
| 491 | + } |
| 492 | + |
| 493 | + // Resolve absolute path and ensure it is within the current working directory |
| 494 | + absPath, err := filepath.Abs(value) |
| 495 | + if err != nil { |
| 496 | + return nil, err |
| 497 | + } |
| 498 | + |
| 499 | + cwd, err := os.Getwd() |
| 500 | + if err != nil { |
| 501 | + return nil, err |
| 502 | + } |
| 503 | + |
| 504 | + absPath = filepath.Clean(absPath) |
| 505 | + cwd = filepath.Clean(cwd) |
| 506 | + |
| 507 | + if absPath != cwd && !strings.HasPrefix(absPath, cwd+string(os.PathSeparator)) { |
| 508 | + return nil, fmt.Errorf("reading cloud_init from outside working directory is disallowed: %s", value) |
| 509 | + } |
| 510 | + |
| 511 | + data, err := os.ReadFile(absPath) |
| 512 | + if err != nil { |
| 513 | + return nil, err |
| 514 | + } |
| 515 | + |
484 | 516 | content = data |
| 517 | + } else if errors.Is(err, os.ErrNotExist) { |
| 518 | + content = []byte(value) |
485 | 519 | } else { |
486 | 520 | return nil, err |
487 | 521 | } |
| 522 | + |
488 | 523 | encoded := base64.StdEncoding.EncodeToString(content) |
489 | 524 | userData := []byte(encoded) |
| 525 | + |
490 | 526 | return userData, nil |
491 | 527 | } |
492 | 528 |
|
@@ -732,10 +768,12 @@ func ResourceServerUpdate(ctx context.Context, d *schema.ResourceData, m any) di |
732 | 768 |
|
733 | 769 | if d.HasChange("cloud_init") { |
734 | 770 | cloudInit := d.Get("cloud_init").(string) |
| 771 | + |
735 | 772 | userData, err := LoadUserDataBase64(cloudInit) |
736 | 773 | if err != nil { |
737 | 774 | return diag.FromErr(err) |
738 | 775 | } |
| 776 | + |
739 | 777 | req.UserData = &userData |
740 | 778 | hasChanged = true |
741 | 779 | } |
|
0 commit comments