mirror of https://github.com/jlelse/GoBlog
Automatic ActivityPub key generation, doesn't need config anymore
This commit is contained in:
parent
eeba1967e1
commit
e62e4f32d6
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -11,7 +13,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -54,15 +55,7 @@ func (a *goBlog) initActivityPub() error {
|
||||||
a.webfingerAccts[a.apIri(blog)] = acct
|
a.webfingerAccts[a.apIri(blog)] = acct
|
||||||
}
|
}
|
||||||
// Read key and prepare signing
|
// Read key and prepare signing
|
||||||
pkfile, err := os.ReadFile(a.cfg.ActivityPub.KeyPath)
|
err := a.loadActivityPubPrivateKey()
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -422,3 +415,39 @@ func (a *goBlog) apIri(b *configBlog) string {
|
||||||
func apRequestIsSuccess(code int) bool {
|
func apRequestIsSuccess(code int) bool {
|
||||||
return code == http.StatusOK || code == http.StatusCreated || code == http.StatusAccepted || code == http.StatusNoContent
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
}
|
|
@ -232,7 +232,6 @@ type configRegexRedirect struct {
|
||||||
|
|
||||||
type configActivityPub struct {
|
type configActivityPub struct {
|
||||||
Enabled bool `mapstructure:"enabled"`
|
Enabled bool `mapstructure:"enabled"`
|
||||||
KeyPath string `mapstructure:"keyPath"`
|
|
||||||
TagsTaxonomies []string `mapstructure:"tagsTaxonomies"`
|
TagsTaxonomies []string `mapstructure:"tagsTaxonomies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +287,6 @@ func (a *goBlog) initConfig() error {
|
||||||
viper.SetDefault("micropub.photoParam", "images")
|
viper.SetDefault("micropub.photoParam", "images")
|
||||||
viper.SetDefault("micropub.photoDescriptionParam", "imagealts")
|
viper.SetDefault("micropub.photoDescriptionParam", "imagealts")
|
||||||
viper.SetDefault("micropub.locationParam", "location")
|
viper.SetDefault("micropub.locationParam", "location")
|
||||||
viper.SetDefault("activityPub.keyPath", "data/private.pem")
|
|
||||||
viper.SetDefault("activityPub.tagsTaxonomies", []string{"tags"})
|
viper.SetDefault("activityPub.tagsTaxonomies", []string{"tags"})
|
||||||
// Unmarshal config
|
// Unmarshal config
|
||||||
a.cfg = &config{}
|
a.cfg = &config{}
|
||||||
|
|
|
@ -69,6 +69,8 @@ hooks:
|
||||||
# ActivityPub
|
# ActivityPub
|
||||||
activityPub:
|
activityPub:
|
||||||
enabled: true # Enable ActivityPub
|
enabled: true # Enable ActivityPub
|
||||||
|
tagsTaxonomies: # Post taxonomies to use as "Hashtags"
|
||||||
|
- tags
|
||||||
|
|
||||||
# Webmention
|
# Webmention
|
||||||
webmention:
|
webmention:
|
||||||
|
|
Loading…
Reference in New Issue