74 lines
3.4 KiB
Go
74 lines
3.4 KiB
Go
package preparation
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/aritmolab/thothii/tools/tht/internal/authconfig"
|
|
"github.com/aritmolab/thothii/tools/tht/internal/config"
|
|
"github.com/aritmolab/thothii/tools/tht/internal/safeio"
|
|
)
|
|
|
|
// Credentials creates installation-owned secrets only. External credentials are supplied by
|
|
// the operator. Existing files are checked and retained so interrupted preparation can resume.
|
|
func Credentials(ctx context.Context, directory string) error {
|
|
if err := safeio.ValidatePrivateDirectory(directory); err != nil {
|
|
return fmt.Errorf("use an existing private preparation directory")
|
|
}
|
|
if _, err := safeio.ReadCanonicalPrivateRegular(filepath.Join(directory, "thothii-installation.yaml"), 1<<20); err != nil {
|
|
return fmt.Errorf("prepare installation documents first")
|
|
}
|
|
secrets := filepath.Join(directory, "secrets")
|
|
if err := safeio.EnsurePrivateDirectory(secrets); err != nil {
|
|
return fmt.Errorf("secrets directory must be private and operator-owned")
|
|
}
|
|
for _, name := range []string{"catalog-runtime-password", "catalog-migrator-password", "admin-password"} {
|
|
path := filepath.Join(secrets, name)
|
|
if _, err := os.Lstat(path); err == nil {
|
|
value, err := safeio.ReadCanonicalPrivateRegular(path, 64<<10)
|
|
if err != nil || len(strings.TrimSpace(string(value))) < 32 {
|
|
return fmt.Errorf("existing technical credentials must be private, readable and at least 32 characters; no file was replaced")
|
|
}
|
|
continue
|
|
} else if !errors.Is(err, os.ErrNotExist) {
|
|
return fmt.Errorf("cannot inspect technical credentials")
|
|
}
|
|
value := make([]byte, 32)
|
|
if _, err := rand.Read(value); err != nil {
|
|
return fmt.Errorf("cannot generate random credentials")
|
|
}
|
|
if err := safeio.WriteCanonicalNewPrivateFile(path, []byte(hex.EncodeToString(value)+"\n"), 0o600); err != nil {
|
|
return fmt.Errorf("cannot create credential; existing files are retained")
|
|
}
|
|
}
|
|
for name, contents := range map[string]string{"secrets.env": "# Supply the provider credential; never commit this file.\nOPENAI_API_KEY=CHANGE_ME\n", "pi-auth.json": "{}\n"} {
|
|
path := filepath.Join(secrets, name)
|
|
if _, err := os.Lstat(path); errors.Is(err, os.ErrNotExist) {
|
|
if err := safeio.WriteCanonicalNewPrivateFile(path, []byte(contents), 0o600); err != nil {
|
|
return fmt.Errorf("cannot create external credential template")
|
|
}
|
|
} else if _, err := safeio.ReadCanonicalPrivateRegular(path, 64<<10); err != nil {
|
|
return fmt.Errorf("existing credential template is not private or readable")
|
|
}
|
|
}
|
|
authDirectory := filepath.Join(directory, "auth")
|
|
if _, err := os.Lstat(filepath.Join(authDirectory, "auth.yaml")); err == nil {
|
|
if _, _, err := authconfig.Load(authDirectory); err != nil {
|
|
return fmt.Errorf("existing authentication documents are invalid; repair them explicitly")
|
|
}
|
|
return nil
|
|
}
|
|
installation := config.Installation{Authentication: config.Authentication{ConfigDirectory: authDirectory}}
|
|
if authconfig.Run(ctx, installation, []string{"configure", "--mode", "local", "--public-url", "http://localhost:8080", "--admin-user", "admin", "--password-file", filepath.Join(secrets, "admin-password")}, strings.NewReader(""), io.Discard, io.Discard) != 0 {
|
|
return fmt.Errorf("cannot prepare local administrator; inspect protected auth files and retry without replacing existing credentials")
|
|
}
|
|
return nil
|
|
}
|