Skip to content

Commit abf55e8

Browse files
hoise kex logic to cli
Signed-off-by: eternal-flame-AD <[email protected]>
1 parent dd63957 commit abf55e8

File tree

2 files changed

+72
-58
lines changed

2 files changed

+72
-58
lines changed

v2/cli_flags.go renamed to v2/cli.go

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
package plugin
22

33
import (
4+
"crypto/ed25519"
5+
"crypto/rand"
6+
"crypto/tls"
7+
"crypto/x509"
8+
"crypto/x509/pkix"
9+
"encoding/pem"
10+
"errors"
411
"flag"
512
"os"
613
"slices"
714
"strconv"
815
"strings"
16+
17+
"github.com/gotify/plugin-api/v2/transport"
918
)
1019

11-
type PluginCliFlags struct {
20+
// / PluginCli implements the CLI interface for a Gotify plugin.
21+
type PluginCli struct {
1222
flagSet *flag.FlagSet
1323
KexReqFile *os.File
1424
KexRespFile *os.File
1525
Debug bool
1626
}
1727

18-
func ParsePluginCLIFlags(args []string) (*PluginCliFlags, error) {
28+
// ParsePluginCli parses the CLI arguments and returns a PluginCli instance.
29+
func ParsePluginCli(args []string) (*PluginCli, error) {
1930
flagSet := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
2031
var kexReqFileName string
2132
var kexRespFileName string
@@ -56,15 +67,68 @@ func ParsePluginCLIFlags(args []string) (*PluginCliFlags, error) {
5667
}
5768
}
5869

59-
return &PluginCliFlags{
70+
return &PluginCli{
6071
flagSet: flagSet,
6172
KexReqFile: kexReqFile,
6273
KexRespFile: kexRespFile,
6374
Debug: debug,
6475
}, nil
6576
}
6677

67-
func (f *PluginCliFlags) Close() error {
78+
// Kex performs the key exchange through secure file descriptors provided in the arguments.
79+
func (f *PluginCli) Kex(modulePath string, certPool *x509.CertPool) (certChain []tls.Certificate, err error) {
80+
// perform key exchange through secure file descriptors
81+
_, priv, err := ed25519.GenerateKey(rand.Reader)
82+
if err != nil {
83+
return nil, err
84+
}
85+
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
86+
Subject: pkix.Name{
87+
CommonName: transport.BuildPluginTLSName("*", modulePath),
88+
},
89+
}, priv)
90+
91+
if err != nil {
92+
return nil, err
93+
}
94+
if _, err := f.KexReqFile.Write(pem.EncodeToMemory(&pem.Block{
95+
Type: "CERTIFICATE REQUEST",
96+
Bytes: csrBytes,
97+
})); err != nil {
98+
return nil, err
99+
}
100+
101+
var certificateChain []tls.Certificate
102+
103+
if err := transport.IteratePEMFile(f.KexRespFile, func(block *pem.Block) (continueIterate bool, err error) {
104+
if block.Type == "CERTIFICATE" {
105+
parsedCert, err := x509.ParseCertificate(block.Bytes)
106+
if err != nil {
107+
return false, err
108+
}
109+
certPool.AddCert(parsedCert)
110+
certificateChain = append(certificateChain, tls.Certificate{
111+
Certificate: [][]byte{block.Bytes},
112+
Leaf: parsedCert,
113+
})
114+
return true, nil
115+
}
116+
return true, nil
117+
}); err != nil {
118+
return nil, err
119+
}
120+
121+
if len(certificateChain) == 0 {
122+
return nil, errors.New("no certificate chain found in kex response file")
123+
}
124+
125+
certificateChain[0].PrivateKey = priv
126+
127+
return certificateChain, nil
128+
}
129+
130+
// Close closes any file descriptors associated with the PluginCli instance.
131+
func (f *PluginCli) Close() error {
68132
if err := f.KexReqFile.Close(); err != nil {
69133
return err
70134
}

v2/shim_v1.go

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@ package plugin
22

33
import (
44
"context"
5-
"crypto/ed25519"
6-
"crypto/rand"
75
"crypto/tls"
86
"crypto/x509"
9-
"crypto/x509/pkix"
107
"encoding/json"
11-
"encoding/pem"
128
"errors"
139
"log"
1410
"math"
@@ -105,60 +101,14 @@ type CompatV1Shim struct {
105101
func NewCompatV1Rpc(compatV1 *CompatV1, cliArgs []string) (*CompatV1Shim, error) {
106102
pluginInfo := compatV1.GetPluginInfo()
107103

108-
cliFlags, err := ParsePluginCLIFlags(cliArgs)
104+
cli, err := ParsePluginCli(cliArgs)
109105
if err != nil {
110106
log.Fatalf("Failed to parse CLI flags: %v", err)
111107
}
112-
defer cliFlags.Close()
108+
defer cli.Close()
113109

114110
rootCAs := x509.NewCertPool()
115-
116-
// perform key exchange through secure file descriptors
117-
_, priv, err := ed25519.GenerateKey(rand.Reader)
118-
if err != nil {
119-
return nil, err
120-
}
121-
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
122-
Subject: pkix.Name{
123-
CommonName: transport.BuildPluginTLSName("*", pluginInfo.ModulePath),
124-
},
125-
}, priv)
126-
127-
if err != nil {
128-
return nil, err
129-
}
130-
if _, err := cliFlags.KexReqFile.Write(pem.EncodeToMemory(&pem.Block{
131-
Type: "CERTIFICATE REQUEST",
132-
Bytes: csrBytes,
133-
})); err != nil {
134-
return nil, err
135-
}
136-
137-
var certificateChain []tls.Certificate
138-
139-
if err := transport.IteratePEMFile(cliFlags.KexRespFile, func(block *pem.Block) (continueIterate bool, err error) {
140-
if block.Type == "CERTIFICATE" {
141-
parsedCert, err := x509.ParseCertificate(block.Bytes)
142-
if err != nil {
143-
return false, err
144-
}
145-
rootCAs.AddCert(parsedCert)
146-
certificateChain = append(certificateChain, tls.Certificate{
147-
Certificate: [][]byte{block.Bytes},
148-
Leaf: parsedCert,
149-
})
150-
return true, nil
151-
}
152-
return true, nil
153-
}); err != nil {
154-
return nil, err
155-
}
156-
157-
if len(certificateChain) == 0 {
158-
return nil, errors.New("no certificate chain found in kex response file")
159-
}
160-
161-
certificateChain[0].PrivateKey = priv
111+
certificateChain, err := cli.Kex(pluginInfo.ModulePath, rootCAs)
162112

163113
tlsConfig := &tls.Config{
164114
Certificates: certificateChain,
@@ -171,7 +121,7 @@ func NewCompatV1Rpc(compatV1 *CompatV1, cliArgs []string) (*CompatV1Shim, error)
171121
MinTime: httpTimeout,
172122
PermitWithoutStream: true,
173123
}), grpc.ConnectionTimeout(httpTimeout))
174-
if !cliFlags.Debug {
124+
if !cli.Debug {
175125
gin.SetMode(gin.ReleaseMode)
176126
}
177127

0 commit comments

Comments
 (0)