oliverpool: 1 well-known: support openpgp web key discovery 6 files changed, 313 insertions(+), 3 deletions(-)
https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40799#%3CCT18V3ZK9ZW7.2VDLU3CMFNATN@taiga%3E
I think we can go ahead and take it, yeah.
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/44509/mbox | git am -3Learn more about email & git
will allow clients to discover the public key automatically, using: gpg --locate-keys outgoing@sr.ht Internet-Draft https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service#name-web-key-directory --- Initial discussion: https://lists.sr.ht/~sircmpwn/sr.ht-discuss/%3Cdd965bea-a130-4675-8cdf-742286607079%40app.fastmail.com%3E First patch (on meta.sr.ht frontend): https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40794 Second patch (on meta.sr.ht backend): https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/40799 Current patch: hub.sr.ht backend. It fixes the discrepancy noted by ~bitfehler (with an added test) To be publicly accessible, adjustements must be made to sr.ht-nginx/hub.sr.ht.conf, like (untested): location /.well-known/openpgpkey { proxy_pass http://127.0.0.1:5114; } --- api/openpgpkey/opengpgkey_test.go | 78 +++++++++++ api/openpgpkey/openpgpkey.go | 141 ++++++++++++++++++++ api/openpgpkey/testdata/expected_served_key | Bin 0 -> 1169 bytes api/openpgpkey/testdata/pubkey | 80 +++++++++++ api/server.go | 8 ++ go.mod | 9 +- 6 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 api/openpgpkey/opengpgkey_test.go create mode 100644 api/openpgpkey/openpgpkey.go create mode 100644 api/openpgpkey/testdata/expected_served_key create mode 100644 api/openpgpkey/testdata/pubkey diff --git a/api/openpgpkey/opengpgkey_test.go b/api/openpgpkey/opengpgkey_test.go new file mode 100644 index 0000000..67d6306 --- /dev/null +++ b/api/openpgpkey/opengpgkey_test.go @@ -0,0 +1,78 @@ +package openpgpkey + +import ( + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/vaughan0/go-ini" +) + +func TestNewWellKnown(t *testing.T) { + router := chi.NewRouter() + err := MountWebKeyDirectoryRoutes(ini.File{ + "sr.ht": ini.Section{ + "global-domain": "sr.ht", + }, + "mail": ini.Section{ + "pgp-pubkey": "testdata/pubkey", + }, + }, router) + assert.NoError(t, err) + + resp := httptest.NewRecorder() + router.ServeHTTP(resp, httptest.NewRequest("GET", "/.well-known/openpgpkey/hu/4y36rkzdjnzmk3oxaekyi5biowgr5kcz", nil)) // admin in zbase32 + assert.Equal(t, http.StatusOK, resp.Result().StatusCode) + assert.Equal(t, "application/octet-stream", resp.Header().Get("Content-Type")) + + // reference file created with $(gpgconf --list-dirs libexecdir)/gpg-wks-client -v --install-key -C . ./pubkey admin@sr.ht + expectedKey, err := os.ReadFile("testdata/expected_served_key") + assert.NoError(t, err) + servedKey := resp.Body.Bytes() + assert.NoError(t, os.WriteFile("testdata/served_key", servedKey, 0o666)) + if assert.Len(t, servedKey, len(expectedKey)) { + // 13 bytes are different because gpg used the "new-format tag byte" + // https://superuser.com/a/1610202 + diff := 0 + for i, got := range servedKey { + expected := expectedKey[i] + if got != expected { + diff++ + } + } + if assert.Equal(t, 13, diff) { + // everything look fine, remove the testdata/served_key + assert.NoError(t, os.Remove("testdata/served_key")) + } else { + // assert display a nice hex diff + assert.Equal(t, expectedKey, servedKey) + } + } +} + +func TestEncodeLocalPart(t *testing.T) { + assert.Equal(t, "4y36rkzdjnzmk3oxaekyi5biowgr5kcz", encodeLocalPart("admin")) + assert.Equal(t, "4y36rkzdjnzmk3oxaekyi5biowgr5kcz", encodeLocalPart("Admin")) + assert.Equal(t, "e5a4bxki1ktx1jncwco5nkcofedmkxod", encodeLocalPart("git")) +} + +func TestGetGlobalDomain(t *testing.T) { + globalDomain, err := getGlobalDomain(ini.File{ + "sr.ht": ini.Section{ + "global-domain": "sr.ht", + }, + }, "meta.sr.ht") + assert.NoError(t, err) + assert.Equal(t, "sr.ht", globalDomain) + + globalDomain, err = getGlobalDomain(ini.File{ + "meta.sr.ht": ini.Section{ + "origin": "http://meta.sr.ht.local", + }, + }, "meta.sr.ht") + assert.NoError(t, err) + assert.Equal(t, "sr.ht.local", globalDomain) +} diff --git a/api/openpgpkey/openpgpkey.go b/api/openpgpkey/openpgpkey.go new file mode 100644 index 0000000..a43aeed --- /dev/null +++ b/api/openpgpkey/openpgpkey.go @@ -0,0 +1,141 @@ +package openpgpkey + +import ( + "crypto/sha1" + "encoding/base32" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + + "github.com/ProtonMail/go-crypto/openpgp" + "github.com/go-chi/chi" + "github.com/vaughan0/go-ini" +) + +// MountWebKeyDirectoryRoutes mounts the routes to serve the public key (binary encoded) under the well-known URLs: +// - /.well-known/openpgpkey/hu/<zbase32-hash> +// - /.well-known/openpgpkey/hu/policy +// +// See https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service +func MountWebKeyDirectoryRoutes(conf ini.File, router chi.Router) error { + pubKeyPath, ok := conf.Get("mail", "pgp-pubkey") + if !ok { + return errors.New("expected [mail]pgp-pubkey in config") + } + + pubKeyFile, err := os.Open(pubKeyPath) + if err != nil { + return fmt.Errorf("failed to open [mail]pgp-pubkey: %v", err) + } + defer pubKeyFile.Close() + + keyring, err := openpgp.ReadArmoredKeyRing(pubKeyFile) + if err != nil { + return err + } + + globalDomain, err := getGlobalDomain(conf, "meta.sr.ht") + if err != nil { + return err + } + + for _, entity := range keyring { + for _, identidy := range entity.Identities { + local, domain, found := stringsCut(identidy.UserId.Email, "@") + if !found || domain != globalDomain { + continue + } + serializePublicKey := wkdSerializer(entity, identidy) + router.Get("/.well-known/openpgpkey/hu/"+encodeLocalPart(local), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/octet-stream") + serializePublicKey(w) + }) + } + } + router.Get("/.well-known/openpgpkey/hu/policy", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte("# Policy flags for the OpenPGP Web Key Directory")) + })) + return nil +} + +func wkdSerializer(entity *openpgp.Entity, identidy *openpgp.Identity) func(io.Writer) error { + return func(w io.Writer) error { + if err := entity.PrimaryKey.Serialize(w); err != nil { + return err + } + if err := identidy.UserId.Serialize(w); err != nil { + return err + } + for _, sig := range identidy.Signatures { + if err := sig.Serialize(w); err != nil { + return err + } + } + for _, subkey := range entity.Subkeys { + if err := subkey.PublicKey.Serialize(w); err != nil { + return err + } + for _, revocation := range subkey.Revocations { + if err := revocation.Serialize(w); err != nil { + return err + } + } + if err := subkey.Sig.Serialize(w); err != nil { + return err + } + + } + return nil + } +} + +var zbase32Encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769").WithPadding(base32.NoPadding) + +func encodeLocalPart(s string) string { + hash := sha1.New() + hash.Write([]byte(strings.ToLower(s))) + shaOne := hash.Sum(nil) + return zbase32Encoding.EncodeToString(shaOne) +} + +// Gets the global domain from the config. If it's not defined, assume that +// the given site is a sub-domain of the global domain, i.e. it is of the +// form `blah.globaldomain.com`. +func getGlobalDomain(conf ini.File, site string) (string, error) { + globalDomain, ok := conf.Get("sr.ht", "global-domain") + if ok { + return globalDomain, nil + } + serviceOrigin, ok := conf.Get(site, "origin") + if !ok { + return "", fmt.Errorf("expected [sr.ht]global-domain or [%s]origin in config", site) + } + serviceURL, err := url.Parse(serviceOrigin) + if err != nil { + return "", fmt.Errorf("failed to parse URL [%s]origin in config: %v", site, err) + } + _, globalDomain, _ = stringsCut(serviceURL.Hostname(), ".") + return globalDomain, nil +} + +// strings.Cut is available starting with go1.18 +// Copied from https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/strings/strings.go;l=1262 +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license. +// +// stringsCut slices s around the first instance of sep, +// returning the text before and after sep. +// The found result reports whether sep appears in s. +// If sep does not appear in s, cut returns s, "", false.
We're on Go 1.20, so we can use this from the stdlib.
+func stringsCut(s, sep string) (before, after string, found bool) { + if i := strings.Index(s, sep); i >= 0 { + return s[:i], s[i+len(sep):], true + } + return s, "", false +} diff --git a/api/openpgpkey/testdata/expected_served_key b/api/openpgpkey/testdata/expected_served_key new file mode 100644 index 0000000000000000000000000000000000000000..0650ef3865a8a907fe71bdb19ca7b0978a29be49 GIT binary patch literal 1169 zcmV;C1aAA80SyFL3eeO62mrjW#%(2aeVVDQPfuTXRMLH@P(mU}#bq0>%LAM3x7F^G zqdA!-Si!~^Wa|bYx3q)|3_*%I9n@y&8r4d3h6QDY_n=p)^E&W2@haX9GVNb$u5C&E zB?g+PWoh-$0|6M))e2^douh}W<|U?ERnPdG3g6hf3Z+Q*wFEPRNxH5b(7t+A9=ikf znWz`*@Zxl17j>FPb|!F9S;7Ig{<DXcgF+f$-XvP(0w=1y$~MAAT<=N?bF^Dy!8Xf~ zas-@J_hNmmPEUISx$z-jN&e6vk_LjGlOZt32Cc(BkC)&wg)7>N;#rjhrW$N|R0u%; z(vqEtwp-V~BaS7Kaq9pP0RREC6LWGdXmlVvVPtJ-Za{N#E@*T<i2*kR69EVSAq4_h z3eeO78v_Lk2?z%Q1{DYi3JU@R76JnS0v-VZ7k~f?2@qwM1kt04ldgtb2melFzvfjw zBtWw@<nKoHfnx4jhG}$eB_on1#h6I?QR{=ex%nG)4+j)TjWtAE^+;9=qeDJqk@>!7 zd`q&{!R7ijoK^6OKzr}v<XFV%Tq!alqmu9-ak79QA(p16_kSyvGZI~@A4uBfRmlH1 z<eMouju`q~p@U?f#>#0t>!l<k1u{px{=(lCLn?2c-zNW}@=ce{+-CCcg}`Y&7eay` zUQz3=VM8e{rXw86D`oeH#Y%p;CxmX3#8XC-yZyzH{=ttnXrtmgZmJ;B#x3kQ>?N!W zuwJ^#<@a8JQaK~GVP6x5R~-j_LMmO%Q#x<;NKmHx&A-M6Ik8z0KO4CL4Fp&U(9{74 z0LoH4T40{<vdU#z^HiLEVUg42rlOrgnP+5kB=$zCM-d`;JLP-Hdml(b)RFqzQ3-_) zrPy9)j1?JHVL~(-3f8=Uz0VlfO}=urF)*L2@0YDKyk`RUsgNcg@|z|32-@F<>wSru zPKg$NQAB}}cs;nthIYY3dCMpzhtB>bw%n{!vS<|dwK=8uWiam3(l|aOiEqcG6##;9 z)fV;scF>B&)w~fP{?LVWlVU`9ky2V2ozGk3HUaE~!_n3ZLI7gNwk&8mzLVa*dN9{U zZ@D|$cTcKYGbgS0DqCz-=|5m*^t_wJ)gk-dU*PgDgq;JM1Gt^A-pz6^C6?3P0{{^L z00D^s9|RZy2mlEM0$2*r)B+m}0162ZWtRleql%NR466tLv!lJb9I<Jq{Luu;loJ|Z zh}>!~WHMUPL}#cGM~|zes^MIzRL^aJC|75$)JK<zwloRMQDTfBqu`fp1Z6+H>mRXd zSUI<r{h!tVVtQ{PxZqx3FQSQI|3C7bw+lEKz0RR@U~}=u-Afx8L72=VmCKJOgVN+b zLvk~j2X~vA{7B?&LL@aWiy)J;#5ndtH|M@w<}&E%P2~5_6k&mpg96vRZ@%FOhfa+8 zbt;z^18lqOdkE$qY@-(Jj0B%^yI-{ii;50GO72Y8m{Dq2B%MVyVdv4boas<VFVjY{ j{Cw%3E7_NImj#fO)ZM0?L)bW-EI|b3r%2Loooy2*?miwD literal 0 HcmV?d00001 diff --git a/api/openpgpkey/testdata/pubkey b/api/openpgpkey/testdata/pubkey new file mode 100644 index 0000000..27c1a2c --- /dev/null +++ b/api/openpgpkey/testdata/pubkey @@ -0,0 +1,80 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFgK0NQBCAC8sMZtJXV9mqmtT09feFTSfahQQiJJxWUbr8sDm+231e6TozmZ +JljBxhhk6wYht7SEDAxBijod1GbpGtVKc4YFZYb3oFep8zrwOPEq3g4y7V9rrm1J +/SUGmqhlafXQAwEY0tUKZoudo4es5iWmW1XP+JwK39i7CqVI97UEM4NJuq4d0L56 +VR67A/aZqBfr8OJ0Yxd1mkd2JnBRWcIBt/6zh5aDQhpg3iRa5QInqr3KNsJGXO9K +C3O0W2PBNsuQcgScVfdifa1OT3sDufEhYEn+0CCSBoKfkyEwyAatwz6Pl+AyhSva +i+JZlQSmGmx6VAhBANKSnYm2W9e/I44lknHrABEBAAG0E3NyLmh0IDxhZG1pbkBz +ci5odD6JATcEEwEIACEFAlgK0NQCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA +CgkQZZcE0aOKk66GXAf/TmS/5lU+JECzNeTvRvWBYu5ahml0biUjkibFmEj6UeuD +vLn5G3UPBxRIjTVEXPVIVgujQz5kkfm+Z3xLstbB5fo1nFXwikB77+LkWMTpXCky +IqOS8CBxsoAgIZamp/d/K5YzEl2qH0ja5lXI/zjkmyk5jhj6XaGDZJ/Gymk866Uk +IwUyR7z+wt8VQypvnt8m/6LyTZfO3Gby74XAaT0XQoIfXlHrrmFDKS6mIxzKK2X3 +iMVKfrknhG6TxFNGk7v9xZH+wY82aKPiO26qINHGLew57CWsDLBeusrl916BUjkj +tmFfE4ZXHQd+QipdzFM6b/VIUKb7zb/GBzmxWRE/G7RIc3IuaHQgb3V0Z29pbmcg +KEdlbmVyYWwgcHVycG9zZSBvdXRnb2luZyBzci5odCBhZGRyZXNzKSA8b3V0Z29p +bmdAc3IuaHQ+iQFOBBMBCAA4FiEERHtp5LNL6QvIKaDpZZcE0aOKk64FAlwreIwC +GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQZZcE0aOKk64s7Af/RHO4hxQi +rx4+W1Kqe3f6An9KcrFQ1hDUzWDm5KKZE7RztD1/9BoFXK4UOyPB1F+Ng0gCknLI +65ISSsiC9CbS0adOf5nAO5cUbwSv2A1D/TgznVf1dZvdBQ1YuCyV53UXFryZJ4d7 +NgPQPOrzaQE7rQutbk/wlNSKn7t3rkJjVaSPAofdjG0HCb2Uc/oFSCNG6aY3SqBv +yr0ys57CrdhKqg2DVQcsZk8elSEQu4DIFZm5xsM7wxN57qA3Xshm347t4tWH+IpT +i9sqjIBCa+TEy5frhkgj/ykIl9AKrIWs27KEvCogXy6aiwbQS8K9+Mp4d8TKmv12 +nO79CM5j7vFLa7RGc3IuaHQgdG9kbyAoQWRkcmVzcyBmb3IgYXV0b21hdGVkIGVt +YWlscyBmcm9tIHRvZG8uc3IuaHQpIDx0b2RvQHNyLmh0PokBTgQTAQgAOBYhBER7 +aeSzS+kLyCmg6WWXBNGjipOuBQJcK3ihAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B +AheAAAoJEGWXBNGjipOuexMH/jBckRZTSVT9CciFzAslsuqRRehJ4Tae5EW4X5Ou +JGnmFVDb+nn1T14I+I+GhA3URYuCe1pEFiJfg368pXqGDbh/9JSmzWd9PZtGGlI6 +JApQlPYxapfkv3UrXtF0T7Rj0yTTgf6vkjSaHdp05bCvatTO7UAD01s9/TCHloeP +Mxkx9e4BKfhO2y/aVeNFW9EASKG46q/2c5UyUEjoVCYCV14L3vMcbXNO0hQOeY6o +WQD245tfNRPUMNumCfdE+FF8PWYGkhypPf9Os4FfsHQsj12bi3CNEa7wrVuMwRU3 +qTWqkc+0c/HMU471DIh6YH7dxFgLTqDtZ7SqoFufmwJ+qTS0THNyLmh0IGJ1aWxk +cyAoQWRkcmVzcyBmb3IgYXV0b21hdGVkIGVtYWlscyBmcm9tIGJ1aWxkcy5zci5o +dCkgPGJ1aWxkc0Bzci5odD6JAU4EEwEIADgWIQREe2nks0vpC8gpoOlllwTRo4qT +rgUCXCt4sgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBllwTRo4qTrjzo +B/oCCFAGAhrQJADDH7ALYd6K7kQgtHjOE++u2ZAjbbu5CBgIcao5UKe0t3u8xVDK +E0gy/v2Y/vyu1VacaXWhvV52dm+YA6UYMDzlyTngQ6zXP2MmmOXYm9o7hks83Acj +8zyVxtuoSiTEcXBxyGbcFa4TN8ptuiEJEKnRnpi67xMrniaXfBTAE4BDgD3qpfRd +zh3Ya+/pqd/f6yWCok8rQeHennqbyrdSrjzlwrpwHj63aTp0CsbLjsv7JToo8AIo +GoyGgXaMj1991DJFE2kxmd3aSFVEPGH2bgLk21wadjSMBfVgWQy/d0hBpU0h77+e +iGkNWsmQXE0TrNKs96YSPktUtENzci5odCBnaXQgKEFkZHJlc3MgZm9yIGF1dG9t +YXRlZCBlbWFpbHMgZnJvbSBnaXQuc3IuaHQpIDxnaXRAc3IuaHQ+iQFOBBMBCAA4 +FiEERHtp5LNL6QvIKaDpZZcE0aOKk64FAlwreMMCGwMFCwkIBwIGFQoJCAsCBBYC +AwECHgECF4AACgkQZZcE0aOKk67CfggAlkf1xyVLFBNC/UyaYq6iYotcVQ9oW82O +dLZ00NckXllixvmcd9s+l5xmKluGPOivY2/tM2ongxfe3orPrr4/JIQXAeZIcq7z +uz8OJDI/efPFC+x/BMb2eb0bpDGamcTMp6AiJGZd4fF2IcBGcWUpFSbaXzy8fE/d +iVEIk970HGSxB8NchA7TY1AKzGty5qy4lslfGc7eA9G8ZfoNQ96MGJFIPvY4jT4L +71tH+baqRN3/8By0mmxLCiW8rQDVGgcPicZENWWioCIYLlj7TixqTLLtK8nCLQ8a +y8uc/9Yk+whJDah+t8wBAuIjCysa/lZPcxoWekDyZ6pLGyI2T/QRSbRGc3IuaHQg +bWV0YSAoQWRkcmVzcyBmb3IgYXV0b21hdGVkIGVtYWlscyBmcm9tIG1ldGEuc3Iu +aHQpIDxtZXRhQHNyLmh0PokBTgQTAQgAOBYhBER7aeSzS+kLyCmg6WWXBNGjipOu +BQJcK3jVAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEGWXBNGjipOuS9AH +/RVimfI35uuI4zGu5v4T/FNO7uSvLiKtG64iYoQCBJSOw0wx/rU/wWfEZ5w8wPZB +TdcFVB/qEVCkE8vISmvK70fLiBtOKq8tmZvgFd9f7V9LWUDyoh4xOShFM+nPWpJW +aweKK/tfclXAU4NIfgtFEn+XuSqrtt3VmQWeYiR8+ToRb3lcO8CxLW/AFZrVzRZu +X9nQMyCr6mK6Xxg8c17CXSHJRSfFEvRzES7q/spSJT26xlaY8X1V82GFRpBPdeIV +EEd/6vbPqNJ6kyZioYB/PyEkTxMLh7sRM8l2xKYcqLgW7vXKW2L82vmFdzdPCQMg +xxWhdUhYWD9RIdoT/n8CEHW0UnNyLmh0IGRpc3BhdGNoIChBZGRyZXNzIGZvciBh +dXRvbWF0ZWQgZW1haWxzIGZyb20gZGlzcGF0Y2guc3IuaHQpIDxkaXNwYXRjaEBz +ci5odD6JAU4EEwEIADgWIQREe2nks0vpC8gpoOlllwTRo4qTrgUCXCt46gIbAwUL +CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBllwTRo4qTruoEB/9UJapNcs9gci37 +XJCEHB/G0+9HL7lcGjbBSfMhA2BCuirfS3k347Ug+hLzld23wqFSP2wbr+muKHxL +1rQ9CIJ5lVwWOmZQLovl/8W/irQRh4SRnJtxWpCpXKoYhDH0bPoUbYjR9rezWWb0 +rKw6KpWiEhYAOZWZ/vnv9uT70P7RP0+D5biI5snuS/O2sJLVqt0YDpOPcyF3nFg5 +KHre7DzzBNoIZjrMOld8KbQ9YjOG14kokhXFQRbkrPhisx2uQdaCJN1fn4nbn8EU +DMXk28ZtutEAJjoGnaWksYi1VtYm/4Z8dWMhRByAx0fNKbl3FhzEUuL8ef2nxfcA +2KMtUu2IuQENBFgK0NQBCADKUjxaYJ7vssplWvNUnH5hkdPlpqKdQ5lnZHMk9kaq +RxEieDvle8l7H0hC1JH621EJhQ+l2F5njBUZVmFCNBoK1ryAvc8Y2E2+crYxMJ+r +75etNLxnAvepkCYf8psl+Qja34brfYmaTokWflFEgZF4PbjIhnbBRHnLKCWHzv4l +ttysU7JoFPe1OaX4ZTDu09I4PiSJb8ekFQCCcdUW9f520IrF1bwRIP7QhXWTYkR5 +kVJaGZ3PW+Q2AeyFw9HWDEIAYse2LGg6vpPevnow10ZvuTvbd0+qWzMnrfcqW2xV +6T9gZvS8m8XVIfveX+DyL4SdA5sDuJ2w3s1yLyWW094DABEBAAGJAR8EGAEIAAkF +AlgK0NQCGwwACgkQZZcE0aOKk64MqwgAs6O9uhyxaaf80QTKlBMaYYjcai9kMlrS +RGeoEUePq6Wq4VypVM9tgShXZ67UR5eJtjQJzFFijCCj4JdsBGU/vesfsWpYObeW +/Z/WAGJ6byK44F5gL6KJYf8/8p23CzgZvc6hdGBz8cfdSxsZQZjMI5XLjyeD0uQ/ +Q3IzmQd3m5r8SORtQiQ1L4sgk7PEOPZDN+e+XOYy6OlN5PfPFGGBkYMC171vvuEI +h06M+nUqlxcDbLvsewjmH2yjFuyMBJ9zu1+1BouKDkFK7kzXmFFqWCSdRTVh59G0 +nOlQSC/TRrL8fOmfK9mXdZcFkJXU3aadQ9g4nSxBBOanSNJvnW0TJw== +=zmAw +-----END PGP PUBLIC KEY BLOCK----- diff --git a/api/server.go b/api/server.go index 3a38b97..7684f5e 100644 --- a/api/server.go +++ b/api/server.go @@ -1,6 +1,8 @@ package main import ( + "log" + "git.sr.ht/~sircmpwn/core-go/config" "git.sr.ht/~sircmpwn/core-go/server" work "git.sr.ht/~sircmpwn/dowork" @@ -8,6 +10,7 @@ import ( "git.sr.ht/~sircmpwn/hub.sr.ht/api/account" "git.sr.ht/~sircmpwn/hub.sr.ht/api/graph" "git.sr.ht/~sircmpwn/hub.sr.ht/api/graph/api" + "git.sr.ht/~sircmpwn/hub.sr.ht/api/openpgpkey" ) func main() { @@ -30,5 +33,10 @@ func main() { WithSchema(schema, scopes). WithQueues(accountQueue) + err := openpgpkey.MountWebKeyDirectoryRoutes(appConfig, gsrv.Router()) + if err != nil { + log.Fatalf("openpgpkey.MountWebKeyDirectoryRoutes failed: %v", err) + } + gsrv.Run() } diff --git a/go.mod b/go.mod index 0010975..d2afaae 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,10 @@ require ( git.sr.ht/~sircmpwn/core-go v0.0.0-20221025082458-3e69641ef307 git.sr.ht/~sircmpwn/dowork v0.0.0-20210820133136-d3970e97def3 github.com/99designs/gqlgen v0.17.20 + github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 + github.com/go-chi/chi v4.1.2+incompatible + github.com/stretchr/testify v1.7.1 + github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec github.com/vektah/gqlparser/v2 v2.5.1 ) @@ -13,11 +17,11 @@ require ( git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3 // indirect git.sr.ht/~sircmpwn/go-bare v0.0.0-20210227202403-5dae5c48f917 // indirect github.com/Masterminds/squirrel v1.4.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emersion/go-message v0.15.0 // indirect github.com/emersion/go-pgpmail v0.2.0 // indirect @@ -25,7 +29,6 @@ require ( github.com/emersion/go-smtp v0.15.1-0.20211103212524-30169acc42e7 // indirect github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect github.com/fernet/fernet-go v0.0.0-20191111064656-eff2850e6001 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-redis/redis/v8 v8.11.4 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -36,13 +39,13 @@ require ( github.com/lib/pq v1.8.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.7.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.10.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/urfave/cli/v2 v2.8.1 // indirect - github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect github.com/vektah/gqlparser v1.3.1 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect -- 2.42.0
builds.sr.ht <builds@sr.ht>hub.sr.ht/patches: FAILED in 3m39s [well-known: support openpgp web key discovery][0] from [oliverpool][1] [0]: https://lists.sr.ht/~sircmpwn/sr.ht-dev/patches/44509 [1]: mailto:git@olivier.pfad.fr ✗ #1054601 FAILED hub.sr.ht/patches/archlinux.yml https://builds.sr.ht/~sircmpwn/job/1054601 ✓ #1054600 SUCCESS hub.sr.ht/patches/alpine.yml https://builds.sr.ht/~sircmpwn/job/1054600 ✓ #1054602 SUCCESS hub.sr.ht/patches/debian.yml https://builds.sr.ht/~sircmpwn/job/1054602