diff --git a/server/pkg/publisher/interface.go b/server/pkg/publisher/interface.go index d1d27577..2eb3de11 100644 --- a/server/pkg/publisher/interface.go +++ b/server/pkg/publisher/interface.go @@ -30,4 +30,5 @@ type RepositoryInterface interface { StageTarget(ctx context.Context, pathInsideTargets string, data io.Reader) error CommitStaged(ctx context.Context) error GetTargets(ctx context.Context) ([]string, error) + GetRolePublicKeysFromS3Meta(file, role string) ([]string, error) } diff --git a/server/pkg/publisher/publisher.go b/server/pkg/publisher/publisher.go index ff0a133f..a9e74538 100644 --- a/server/pkg/publisher/publisher.go +++ b/server/pkg/publisher/publisher.go @@ -3,12 +3,14 @@ package publisher import ( "bytes" "context" + "encoding/json" "errors" "fmt" "io" "os" "path" "path/filepath" + "slices" "strings" "sync" "unicode/utf8" @@ -18,6 +20,7 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/sdk/logical" + "github.com/werf/logboek" "github.com/werf/trdl/server/pkg/config" "github.com/werf/trdl/server/pkg/pgp" "github.com/werf/trdl/server/pkg/util" @@ -94,6 +97,9 @@ type setRepositoryKeysOptions struct { InitializeKeys bool } +var TufRepoAlreadyInitializedMsg = `Tuf repository already initialized by another instance of vault-plugin. +Verify the project settings. If settings are correct, consider cleaning up the TUF repository by removing orphaned metadata.` + func (publisher *Publisher) setRepositoryKeys(ctx context.Context, storage logical.Storage, repository RepositoryInterface, opts setRepositoryKeysOptions) error { entry, err := storage.Get(ctx, storageKeyTufRepositoryKeys) if err != nil { @@ -105,6 +111,16 @@ func (publisher *Publisher) setRepositoryKeys(ctx context.Context, storage logic return ErrUninitializedRepositoryKeys } + rootPublicKeysFromS3, err := repository.GetRolePublicKeysFromS3Meta("root.json", "root") + if err != nil { + return fmt.Errorf("unable to get root keys from repository: %w", err) + } + if len(rootPublicKeysFromS3) > 0 { + publisher.logger.Error(TufRepoAlreadyInitializedMsg) + logboek.Context(context.Background()).Default().LogF("%s\n", TufRepoAlreadyInitializedMsg) + return fmt.Errorf("tuf repository already initialized by another instance of vault-plugin") + } + publisher.logger.Debug("Will generate new repository private keys") if err := repository.GenPrivKeys(); err != nil { @@ -132,6 +148,24 @@ func (publisher *Publisher) setRepositoryKeys(ctx context.Context, storage logic return fmt.Errorf("unable to decode keys json by the %q storage key:\n%s---\n%w", storageKeyTufRepositoryKeys, entry.Value, err) } + rootPublicKeysFromS3, err := repository.GetRolePublicKeysFromS3Meta("root.json", "root") + if err != nil { + return fmt.Errorf("unable to get root keys from repository: %w", err) + } + + var data KeyVal + if err := json.Unmarshal(privKeys.Root.Value, &data); err != nil { + return err + } + + if len(rootPublicKeysFromS3) > 0 { + if !slices.Contains(rootPublicKeysFromS3, data.Public) { + publisher.logger.Error(TufRepoAlreadyInitializedMsg) + logboek.Context(context.Background()).Default().LogF("%s\n", TufRepoAlreadyInitializedMsg) + return fmt.Errorf("tuf repository already initialized by another instance of vault-plugin") + } + } + if err := repository.SetPrivKeys(privKeys); err != nil { return fmt.Errorf("unable to set private keys into repository: %w", err) } diff --git a/server/pkg/publisher/repository.go b/server/pkg/publisher/repository.go index abb36a0e..43636d06 100644 --- a/server/pkg/publisher/repository.go +++ b/server/pkg/publisher/repository.go @@ -160,3 +160,51 @@ func (repository *S3Repository) GetTargets(ctx context.Context) ([]string, error } return res, nil } + +type RootMeta struct { + Signed Signed `json:"signed"` +} +type Signed struct { + Keys map[string]Key `json:"keys"` + Roles map[string]RoleDefinition `json:"roles"` +} + +type Key struct { + KeyVal KeyVal `json:"keyval"` +} + +type KeyVal struct { + Public string `json:"public"` +} + +type RoleDefinition struct { + KeyIDs []string `json:"keyids"` +} + +func (repository *S3Repository) GetRolePublicKeysFromS3Meta(file, role string) ([]string, error) { + meta, err := repository.TufRepo.GetMeta() + if err != nil { + return nil, fmt.Errorf("error getting metadata from TUF repo: %w", err) + } + + var rootMeta RootMeta + if err := json.Unmarshal(meta[file], &rootMeta); err != nil { + return nil, fmt.Errorf("error unmarshalling %s: %w", file, err) + } + + rootRole, ok := rootMeta.Signed.Roles[role] + if !ok { + return nil, nil + } + + publicKeys := make([]string, 0, len(rootRole.KeyIDs)) + for _, keyID := range rootRole.KeyIDs { + key, ok := rootMeta.Signed.Keys[keyID] + if !ok { + return nil, fmt.Errorf("key %q not found in keys section", keyID) + } + publicKeys = append(publicKeys, key.KeyVal.Public) + } + + return publicKeys, nil +}