Browse Source

Automatic ActivityPub key generation, doesn't need config anymore

master
Jan-Lukas Else 4 months ago
parent
commit
e62e4f32d6
  1. 49
      activityPub.go
  2. 44
      activityPub_test.go
  3. 2
      config.go
  4. 2
      example-config.yml

49
activityPub.go

@ -1,6 +1,8 @@
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"database/sql"
"encoding/json"
@ -11,7 +13,6 @@ import (
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
@ -54,15 +55,7 @@ func (a *goBlog) initActivityPub() error {
a.webfingerAccts[a.apIri(blog)] = acct
}
// Read key and prepare signing
pkfile, err := os.ReadFile(a.cfg.ActivityPub.KeyPath)
if err != nil {
return err
}
privateKeyDecoded, _ := pem.Decode(pkfile)
if privateKeyDecoded == nil {
return errors.New("failed to decode private key")
}
a.apPrivateKey, err = x509.ParsePKCS1PrivateKey(privateKeyDecoded.Bytes)
err := a.loadActivityPubPrivateKey()
if err != nil {
return err
}
@ -422,3 +415,39 @@ func (a *goBlog) apIri(b *configBlog) string {
func apRequestIsSuccess(code int) bool {
return code == http.StatusOK || code == http.StatusCreated || code == http.StatusAccepted || code == http.StatusNoContent
}
// Load or generate key for ActivityPub communication
func (a *goBlog) loadActivityPubPrivateKey() error {
// Check if already loaded
if a.apPrivateKey != nil {
return nil
}
// Check if already generated
if keyData, err := a.db.retrievePersistentCache("activitypub_key"); err == nil && keyData != nil {
privateKeyDecoded, _ := pem.Decode(keyData)
if privateKeyDecoded == nil {
log.Println("failed to decode cached private key")
// continue
} else {
key, err := x509.ParsePKCS1PrivateKey(privateKeyDecoded.Bytes)
if err == nil && key != nil {
a.apPrivateKey = key
return nil
}
}
}
// Generate and cache key
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
encodedKey := x509.MarshalPKCS1PrivateKey(key)
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encodedKey})
err = a.db.cachePersistently("activitypub_key", pemEncoded)
if err != nil {
return err
}
a.apPrivateKey = key
// Return key
return nil
}

44
activityPub_test.go

@ -0,0 +1,44 @@
package main
import (
"crypto/x509"
"encoding/pem"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_loadActivityPubPrivateKey(t *testing.T) {
app := &goBlog{
cfg: &config{
Db: &configDb{
File: filepath.Join(t.TempDir(), "test.db"),
},
},
}
_ = app.initDatabase(false)
// Generate
err := app.loadActivityPubPrivateKey()
require.NoError(t, err)
assert.NotNil(t, app.apPrivateKey)
oldEncodedKey := x509.MarshalPKCS1PrivateKey(app.apPrivateKey)
oldPemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: oldEncodedKey})
// Reset and reload
err = app.loadActivityPubPrivateKey()
require.NoError(t, err)
assert.NotNil(t, app.apPrivateKey)
newEncodedKey := x509.MarshalPKCS1PrivateKey(app.apPrivateKey)
newPemEncoded := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: newEncodedKey})
assert.Equal(t, string(oldPemEncoded), string(newPemEncoded))
}

2
config.go

@ -232,7 +232,6 @@ type configRegexRedirect struct {
type configActivityPub struct {
Enabled bool `mapstructure:"enabled"`
KeyPath string `mapstructure:"keyPath"`
TagsTaxonomies []string `mapstructure:"tagsTaxonomies"`
}
@ -288,7 +287,6 @@ func (a *goBlog) initConfig() error {
viper.SetDefault("micropub.photoParam", "images")
viper.SetDefault("micropub.photoDescriptionParam", "imagealts")
viper.SetDefault("micropub.locationParam", "location")
viper.SetDefault("activityPub.keyPath", "data/private.pem")
viper.SetDefault("activityPub.tagsTaxonomies", []string{"tags"})
// Unmarshal config
a.cfg = &config{}

2
example-config.yml

@ -69,6 +69,8 @@ hooks:
# ActivityPub
activityPub:
enabled: true # Enable ActivityPub
tagsTaxonomies: # Post taxonomies to use as "Hashtags"
- tags
# Webmention
webmention:

Loading…
Cancel
Save