fix(setup): validate endpoints and secret files
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
const (
|
||||
descriptorName = "thothii-installation.yaml"
|
||||
environmentName = "operator.env"
|
||||
maxSecretBytes = 64 << 10
|
||||
)
|
||||
|
||||
var installationIDPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]*$`)
|
||||
@@ -182,7 +184,10 @@ func collectAnswers(request Request, input io.Reader, output io.Writer, root str
|
||||
return answers{}, err
|
||||
}
|
||||
}
|
||||
missing := missingSecretFiles(value)
|
||||
missing, missingErr := missingSecretFiles(value)
|
||||
if missingErr != nil {
|
||||
return answers{}, missingErr
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
answer, promptErr := prompt(scanner, output, "Create blank secret-file templates for the missing locations? Type yes to confirm", "no")
|
||||
if promptErr != nil {
|
||||
@@ -251,6 +256,11 @@ func validateAnswers(value answers) error {
|
||||
if value.workspaceAccess != "ssh" && value.workspaceAccess != "https" {
|
||||
return errors.New("workspace repository access must be ssh or https")
|
||||
}
|
||||
for name, endpoint := range map[string]string{"DWH API": value.dwhRESTURL, "LLM API": value.llmURL} {
|
||||
if err := validateServiceEndpoint(name, endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for name, path := range map[string]string{
|
||||
"secret file location": value.secretsFile, "Pi credentials file location": value.piAuthFile,
|
||||
"workspace Git credentials file location": value.gitCredentialsFile, "workspace Git CA file location": value.gitCAFile,
|
||||
@@ -358,20 +368,39 @@ func writeIfAbsent(path string, contents []byte, created *[]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingSecretFiles(value answers) []string {
|
||||
func validateServiceEndpoint(name, endpoint string) error {
|
||||
if endpoint == "" {
|
||||
return nil
|
||||
}
|
||||
parsed, err := url.Parse(endpoint)
|
||||
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" ||
|
||||
parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" {
|
||||
return fmt.Errorf("%s endpoint must be an http(s) URL without user information, password, query, or fragment", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingSecretFiles(value answers) ([]string, error) {
|
||||
paths := configuredSecretPaths(value)
|
||||
missing := make([]string, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
|
||||
exists, err := inspectExistingSecretFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
missing = append(missing, path)
|
||||
}
|
||||
}
|
||||
sort.Strings(missing)
|
||||
return missing
|
||||
return missing, nil
|
||||
}
|
||||
|
||||
func validateOrCreateSecretFiles(value answers, output io.Writer) error {
|
||||
missing := missingSecretFiles(value)
|
||||
missing, err := missingSecretFiles(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -390,6 +419,23 @@ func validateOrCreateSecretFiles(value answers, output io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func inspectExistingSecretFile(path string) (bool, error) {
|
||||
before, err := os.Lstat(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("secret file %s could not be inspected; choose a readable regular file", path)
|
||||
}
|
||||
if !before.Mode().IsRegular() || before.Mode()&os.ModeSymlink != 0 {
|
||||
return false, fmt.Errorf("secret file %s must be a readable regular file, not a directory, symlink, or special file", path)
|
||||
}
|
||||
if _, err := safeio.ReadCanonicalRegular(path, maxSecretBytes); err != nil {
|
||||
return false, fmt.Errorf("secret file %s must be a canonical readable regular file", path)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func configuredSecretPaths(value answers) []string {
|
||||
paths := []string{value.secretsFile, value.piAuthFile}
|
||||
if value.workspaceAccess == "ssh" {
|
||||
|
||||
Reference in New Issue
Block a user