mirror of
https://github.com/Luzifer/mondash-checkgpg.git
synced 2024-11-08 08:30:03 +00:00
159 lines
4 KiB
Go
159 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
|
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
|
"github.com/pkg/errors"
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
mondash "github.com/Luzifer/mondash/client"
|
|
)
|
|
|
|
func getKeyFromKeyserver(ctx context.Context, keyID string) (*openpgp.Entity, error) {
|
|
uri, err := url.Parse(cfg.KeyServer)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parse keyserver lookup url")
|
|
}
|
|
|
|
params := url.Values{
|
|
"op": []string{"get"},
|
|
"search": []string{keyID},
|
|
}
|
|
|
|
uri.RawQuery = params.Encode()
|
|
|
|
return getKeyFromURL(ctx, uri.String())
|
|
}
|
|
|
|
func getKeyFromURL(ctx context.Context, keyURL string) (*openpgp.Entity, error) {
|
|
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, keyURL, nil)
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "execute http request")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, errors.Errorf("http status %d", resp.StatusCode)
|
|
}
|
|
|
|
block, err := armor.Decode(resp.Body)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parse armored key")
|
|
}
|
|
|
|
ent, err := openpgp.ReadEntity(packet.NewReader(block.Body))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parse entity")
|
|
}
|
|
|
|
return ent, nil
|
|
}
|
|
|
|
func processKey(ctx context.Context, key string) (keyID, message string, status mondash.Status) {
|
|
var (
|
|
e *openpgp.Entity
|
|
err error
|
|
logger = log.WithField("key", key)
|
|
)
|
|
|
|
switch {
|
|
case strings.HasPrefix(key, "0x"):
|
|
if e, err = getKeyFromKeyserver(ctx, key); err != nil {
|
|
return "", "Key retrieval failed", mondash.StatusUnknown
|
|
}
|
|
|
|
case strings.HasPrefix(key, "http"):
|
|
if e, err = getKeyFromURL(ctx, key); err != nil {
|
|
return "", "Key retrieval failed: " + err.Error(), mondash.StatusUnknown
|
|
}
|
|
|
|
default:
|
|
return "", "Unexpected key source", mondash.StatusUnknown
|
|
}
|
|
|
|
keyID = fmt.Sprintf("0x%016X", e.PrimaryKey.KeyId)
|
|
|
|
if l := len(e.Revocations); l > 0 {
|
|
return keyID, fmt.Sprintf("Key has %d revocation signature(s)", l), mondash.StatusCritical
|
|
}
|
|
|
|
var expiry *time.Time
|
|
for n, id := range e.Identities {
|
|
logger.Debugf("%s %#v", n, id)
|
|
|
|
var idSelfSigExpiry *time.Time
|
|
for _, sig := range id.Signatures {
|
|
if sig.KeyLifetimeSecs == nil || sig.IssuerKeyId != &e.PrimaryKey.KeyId {
|
|
continue
|
|
}
|
|
|
|
idSigExpiry := e.PrimaryKey.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
|
|
logger.WithField("id", n).Debugf("Sig: Identity expires: %s", idSigExpiry)
|
|
|
|
if idSelfSigExpiry == nil || idSigExpiry.After(*idSelfSigExpiry) {
|
|
idSelfSigExpiry = &idSigExpiry
|
|
}
|
|
}
|
|
|
|
if idSelfSigExpiry == nil {
|
|
continue
|
|
}
|
|
|
|
if s := checkExpiry(*idSelfSigExpiry); s != mondash.StatusOK {
|
|
return keyID, fmt.Sprintf("Identity signature for %q has key-expiry in %dh", n, time.Until(*idSelfSigExpiry)/time.Hour), s
|
|
}
|
|
|
|
if expiry == nil || expiry.After(*idSelfSigExpiry) {
|
|
expiry = idSelfSigExpiry
|
|
}
|
|
}
|
|
|
|
for _, sk := range e.Subkeys {
|
|
if sk.Sig.KeyLifetimeSecs == nil {
|
|
// No lifetime assigned to that signature: Ignore that key
|
|
continue
|
|
}
|
|
|
|
if sk.Revoked(time.Now()) {
|
|
// Subkey has been revoked, we don't check that one
|
|
continue
|
|
}
|
|
|
|
skExp := sk.PublicKey.CreationTime.Add(time.Duration(*sk.Sig.KeyLifetimeSecs) * time.Second)
|
|
logger.Debugf("Subkey signature expires: %s", skExp)
|
|
|
|
if s := checkExpiry(skExp); s != mondash.StatusOK {
|
|
return keyID, fmt.Sprintf("Subkey signature has key-expiry in %dh", time.Until(skExp)/time.Hour), s
|
|
}
|
|
|
|
if expiry == nil || expiry.After(skExp) {
|
|
expiry = &skExp
|
|
}
|
|
}
|
|
|
|
if expiry != nil {
|
|
return keyID, fmt.Sprintf("Key looks good (expires in %dh)", time.Until(*expiry)/time.Hour), mondash.StatusOK
|
|
}
|
|
|
|
return keyID, "Key looks good (does not expire)", mondash.StatusOK
|
|
}
|
|
|
|
func checkExpiry(ex time.Time) mondash.Status {
|
|
switch {
|
|
case time.Until(ex) < cfg.CritAt:
|
|
return mondash.StatusCritical
|
|
case time.Until(ex) < cfg.WarnAt:
|
|
return mondash.StatusWarning
|
|
default:
|
|
return mondash.StatusOK
|
|
}
|
|
}
|