Merge pull request #25 from mstg/keykeeper-fixes-1

This commit is contained in:
Mustafa Gezen 2022-08-17 14:44:08 +02:00 committed by GitHub
commit e205ea33b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 163 deletions

View File

@ -38,8 +38,6 @@ import (
"fmt"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/google/uuid"
"io/ioutil"
"os"
"os/exec"
"peridot.resf.org/peridot/db/models"
"peridot.resf.org/utils"
@ -67,14 +65,6 @@ func gpgCmdEnv(cmd *exec.Cmd) *exec.Cmd {
return cmd
}
func (s *Server) deleteGpgKey(keyId string) error {
out, err := logCmdRun(gpgCmdEnv(exec.Command("gpg", "--batch", "--yes", "--delete-secret-and-public-key", keyId)))
if err != nil {
s.log.Errorf("failed to delete gpg key: %s", out.String())
}
return err
}
func (s *Server) importGpgKey(armoredKey string) error {
cmd := gpgCmdEnv(exec.Command("gpg", "--batch", "--yes", "--import", "-"))
cmd.Stdin = strings.NewReader(armoredKey)
@ -85,41 +75,13 @@ func (s *Server) importGpgKey(armoredKey string) error {
return err
}
func (s *Server) importRpmKey(publicKey string) error {
tmpFile, err := ioutil.TempFile("/tmp", "peridot-key-")
if err != nil {
return err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.Write([]byte(publicKey))
if err != nil {
return err
}
cmd := gpgCmdEnv(exec.Command("rpm", "--import", tmpFile.Name()))
out, err := logCmdRun(cmd)
if err != nil {
s.log.Errorf("failed to import rpm key: %s", out.String())
}
return err
}
// WarmGPGKey warms up a specific GPG key
// This involves shelling out to GPG to import the key
func (s *Server) WarmGPGKey(key string, armoredKey string, gpgKey *crypto.Key, db *models.Key) (*LoadedKey, error) {
cachedKey := s.keys[key]
cachedKeyAny, ok := s.keys.Load(key)
// This means that the key is already loaded
// We need to delete and replace it
if cachedKey != nil {
cachedKey.Lock()
defer cachedKey.Unlock()
keyId := gpgKey.GetHexKeyID()
err := s.deleteGpgKey(keyId)
if err != nil {
return nil, err
}
cachedKey.gpgId = keyId
if ok {
return cachedKeyAny.(*LoadedKey), nil
}
err := s.importGpgKey(armoredKey)
@ -127,26 +89,20 @@ func (s *Server) WarmGPGKey(key string, armoredKey string, gpgKey *crypto.Key, d
return nil, err
}
err = s.importRpmKey(db.PublicKey)
if err != nil {
return nil, err
cachedKey := &LoadedKey{
keyUuid: db.ID,
gpgId: gpgKey.GetHexKeyID(),
}
s.keys.Store(key, cachedKey)
if cachedKey == nil {
s.keys[key] = &LoadedKey{
keyUuid: db.ID,
gpgId: gpgKey.GetHexKeyID(),
}
}
return s.keys[key], nil
return cachedKey, nil
}
// EnsureGPGKey ensures that the key is loaded
func (s *Server) EnsureGPGKey(key string) (*LoadedKey, error) {
cachedKey := s.keys[key]
if cachedKey != nil {
return cachedKey, nil
cachedKeyAny, ok := s.keys.Load(key)
if ok {
return cachedKeyAny.(*LoadedKey), nil
}
// Key not found in cache, fetch from database

View File

@ -66,7 +66,7 @@ type Server struct {
worker worker.Worker
temporal client.Client
stores map[string]store.Store
keys map[string]*LoadedKey
keys *sync.Map
defaultStore string
}
@ -82,13 +82,15 @@ func NewServer(db peridotdb.Access, c client.Client) (*Server, error) {
}
return &Server{
log: logrus.New(),
db: db,
storage: storage,
worker: worker.New(c, TaskQueue, worker.Options{}),
log: logrus.New(),
db: db,
storage: storage,
worker: worker.New(c, TaskQueue, worker.Options{
DeadlockDetectionTimeout: 15 * time.Minute,
}),
temporal: c,
stores: map[string]store.Store{"awssm": sm},
keys: map[string]*LoadedKey{},
keys: &sync.Map{},
defaultStore: "awssm",
}, nil
}

View File

@ -114,7 +114,7 @@ func (s *Server) SignArtifactsWorkflow(ctx workflow.Context, artifacts models.Ta
signArtifactCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
ScheduleToStartTimeout: 10 * time.Hour,
StartToCloseTimeout: 24 * time.Hour,
HeartbeatTimeout: time.Minute,
HeartbeatTimeout: 10 * time.Minute,
TaskQueue: TaskQueue,
})
futures = append(futures, peridotworkflow.FutureContext{
@ -188,90 +188,60 @@ func (s *Server) SignArtifactActivity(ctx context.Context, artifactId string, ke
switch ext {
case ".rpm":
rpmSign := func() (*keykeeperpb.SignedArtifact, error) {
var outBuf bytes.Buffer
opts := []string{
"--define", "_gpg_name " + keyName,
"--define", "_peridot_keykeeper_key " + key.keyUuid.String(),
"--addsign", localPath,
}
cmd := gpgCmdEnv(exec.Command("rpm", opts...))
cmd.Stdout = &outBuf
cmd.Stderr = &outBuf
err := cmd.Run()
if err != nil {
s.log.Errorf("failed to sign artifact %s: %v", artifact.Name, err)
statusErr := status.New(codes.Internal, "failed to sign artifact")
statusErr, err2 := statusErr.WithDetails(&errdetails.ErrorInfo{
Reason: "rpmsign-failed",
Domain: "keykeeper.peridot.resf.org",
Metadata: map[string]string{
"logs": outBuf.String(),
"err": err.Error(),
},
})
if err2 != nil {
s.log.Errorf("failed to add error details to status: %v", err2)
}
return nil, statusErr.Err()
}
_, err = s.storage.PutObject(newObjectKey, localPath)
if err != nil {
s.log.Errorf("failed to upload artifact %s: %v", newObjectKey, err)
return nil, fmt.Errorf("failed to upload artifact %s: %v", newObjectKey, err)
}
f, err := os.Open(localPath)
if err != nil {
return nil, err
}
hasher := sha256.New()
_, err = io.Copy(hasher, f)
if err != nil {
return nil, err
}
hash := hex.EncodeToString(hasher.Sum(nil))
err = s.db.CreateTaskArtifactSignature(artifact.ID.String(), key.keyUuid.String(), hash)
if err != nil {
s.log.Errorf("failed to create task artifact signature: %v", err)
return nil, fmt.Errorf("failed to create task artifact signature: %v", err)
}
return &keykeeperpb.SignedArtifact{
Path: newObjectKey,
HashSha256: hash,
}, nil
var outBuf bytes.Buffer
opts := []string{
"--define", "_gpg_name " + keyName,
"--define", "_peridot_keykeeper_key " + key.keyUuid.String(),
"--addsign", localPath,
}
verifySig := func() error {
opts := []string{
"--define", "_gpg_name " + keyName,
"--define", "_peridot_keykeeper_key " + key.keyUuid.String(),
"--checksig", localPath,
cmd := gpgCmdEnv(exec.Command("rpm", opts...))
cmd.Stdout = &outBuf
cmd.Stderr = &outBuf
err := cmd.Run()
if err != nil {
s.log.Errorf("failed to sign artifact %s: %v", artifact.Name, err)
statusErr := status.New(codes.Internal, "failed to sign artifact")
statusErr, err2 := statusErr.WithDetails(&errdetails.ErrorInfo{
Reason: "rpmsign-failed",
Domain: "keykeeper.peridot.resf.org",
Metadata: map[string]string{
"logs": outBuf.String(),
"err": err.Error(),
},
})
if err2 != nil {
s.log.Errorf("failed to add error details to status: %v", err2)
}
cmd := gpgCmdEnv(exec.Command("rpm", opts...))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
s.log.Errorf("failed to verify artifact %s: %v", artifact.Name, err)
return fmt.Errorf("failed to verify artifact %s: %v", artifact.Name, err)
}
return nil
return nil, statusErr.Err()
}
var tries int
for {
res, _ := rpmSign()
err := verifySig()
if err == nil {
return res, nil
}
if err != nil && tries > 3 {
return nil, err
}
tries++
_, err = s.storage.PutObject(newObjectKey, localPath)
if err != nil {
s.log.Errorf("failed to upload artifact %s: %v", newObjectKey, err)
return nil, fmt.Errorf("failed to upload artifact %s: %v", newObjectKey, err)
}
f, err := os.Open(localPath)
if err != nil {
return nil, err
}
hasher := sha256.New()
_, err = io.Copy(hasher, f)
if err != nil {
return nil, err
}
hash := hex.EncodeToString(hasher.Sum(nil))
err = s.db.CreateTaskArtifactSignature(artifact.ID.String(), key.keyUuid.String(), hash)
if err != nil {
s.log.Errorf("failed to create task artifact signature: %v", err)
return nil, fmt.Errorf("failed to create task artifact signature: %v", err)
}
return &keykeeperpb.SignedArtifact{
Path: newObjectKey,
HashSha256: hash,
}, nil
default:
s.log.Infof("skipping artifact %s, extension %s not supported", artifact.Name, ext)
return nil, ErrUnsupportedExtension

View File

@ -41,16 +41,16 @@ import (
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/go-git/go-billy/v5"
"github.com/spf13/viper"
"io"
"io/ioutil"
"os"
"peridot.resf.org/peridot/lookaside"
)
type Storage struct {
bucket string
uploader *s3manager.Uploader
fs billy.Filesystem
bucket string
uploader *s3manager.Uploader
downloader *s3manager.Downloader
fs billy.Filesystem
}
func New(fs billy.Filesystem) (*Storage, error) {
@ -81,34 +81,32 @@ func New(fs billy.Filesystem) (*Storage, error) {
return nil, err
}
uploader := s3manager.NewUploader(sess)
downloader := s3manager.NewDownloader(sess)
return &Storage{
bucket: viper.GetString("s3-bucket"),
uploader: uploader,
fs: fs,
bucket: viper.GetString("s3-bucket"),
uploader: uploader,
downloader: downloader,
fs: fs,
}, nil
}
func (s *Storage) DownloadObject(objectName string, path string) error {
obj, err := s.uploader.S3.GetObject(&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(objectName),
})
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer f.Close()
f, err := s.fs.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
_, err = s.downloader.Download(
f,
&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(objectName),
},
)
_, err = io.Copy(f, obj.Body)
if err != nil {
return err
}
return nil
return err
}
func (s *Storage) ReadObject(objectName string) ([]byte, error) {