mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-21 16:01:22 +02:00
implement godoxy-agent
This commit is contained in:
201
agent/pkg/certs/certs.go
Normal file
201
agent/pkg/certs/certs.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
CertsDNSName = "godoxy.agent"
|
||||
|
||||
caCertPath = "certs/ca.crt"
|
||||
caKeyPath = "certs/ca.key"
|
||||
srvCertPath = "certs/agent.crt"
|
||||
srvKeyPath = "certs/agent.key"
|
||||
)
|
||||
|
||||
func loadCerts(certPath, keyPath string) (*tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
return &cert, err
|
||||
}
|
||||
|
||||
func write(b []byte, f *os.File) error {
|
||||
_, err := f.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
func saveCerts(certDER []byte, key *rsa.PrivateKey, certPath, keyPath string) ([]byte, []byte, error) {
|
||||
certPEM, keyPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}),
|
||||
pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||
|
||||
if certPath == "" || keyPath == "" {
|
||||
return certPEM, keyPEM, nil
|
||||
}
|
||||
|
||||
certFile, err := os.Create(certPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer certFile.Close()
|
||||
|
||||
keyFile, err := os.Create(keyPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer keyFile.Close()
|
||||
|
||||
return certPEM, keyPEM, errors.Join(
|
||||
write(certPEM, certFile),
|
||||
write(keyPEM, keyFile),
|
||||
)
|
||||
}
|
||||
|
||||
func checkExists(certPath, keyPath string) bool {
|
||||
certExists, err := utils.FileExists(certPath)
|
||||
if err != nil {
|
||||
E.LogFatal("cert error", err)
|
||||
}
|
||||
keyExists, err := utils.FileExists(keyPath)
|
||||
if err != nil {
|
||||
E.LogFatal("key error", err)
|
||||
}
|
||||
return certExists && keyExists
|
||||
}
|
||||
|
||||
func InitCerts() (ca *tls.Certificate, srv *tls.Certificate, isNew bool, err error) {
|
||||
if checkExists(caCertPath, caKeyPath) && checkExists(srvCertPath, srvKeyPath) {
|
||||
logging.Info().Msg("Loading existing certs...")
|
||||
ca, err = loadCerts(caCertPath, caKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
srv, err = loadCerts(srvCertPath, srvKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
logging.Info().Msg("Verifying agent cert...")
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
roots.AddCert(ca.Leaf)
|
||||
|
||||
srvCert, err := x509.ParseCertificate(srv.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
// check if srv is signed by ca
|
||||
if _, err := srvCert.Verify(x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
}); err == nil {
|
||||
logging.Info().Msg("OK")
|
||||
return ca, srv, false, nil
|
||||
}
|
||||
logging.Error().Msg("Agent cert and CA cert mismatch, regenerating")
|
||||
}
|
||||
|
||||
// Create the CA's certificate
|
||||
caTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"GoDoxy"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(1000, 0, 0), // 1000 years
|
||||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
caCertDER, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
certPEM, keyPEM, err := saveCerts(caCertDER, caKey, caCertPath, caKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
ca = &cert
|
||||
|
||||
// Generate a new private key for the server certificate
|
||||
serverKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
srvTemplate := caTemplate
|
||||
srvTemplate.Issuer = srvTemplate.Subject
|
||||
srvTemplate.DNSNames = append(srvTemplate.DNSNames, CertsDNSName)
|
||||
|
||||
srvCertDER, err := x509.CreateCertificate(rand.Reader, &srvTemplate, &caTemplate, &serverKey.PublicKey, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
certPEM, keyPEM, err = saveCerts(srvCertDER, serverKey, srvCertPath, srvKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
cert, err = tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
srv = &cert
|
||||
|
||||
return ca, srv, true, nil
|
||||
}
|
||||
|
||||
func NewClientCert(ca *tls.Certificate) ([]byte, []byte, error) {
|
||||
// Generate the SSL's private key
|
||||
sslKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Create the SSL's certificate
|
||||
sslTemplate := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(2),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"GoDoxy"},
|
||||
CommonName: CertsDNSName,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(1000, 0, 0), // 1000 years
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
|
||||
// Sign the certificate with the CA
|
||||
sslCertDER, err := x509.CreateCertificate(rand.Reader, sslTemplate, ca.Leaf, &sslKey.PublicKey, ca.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return saveCerts(sslCertDER, sslKey, "", "")
|
||||
}
|
||||
76
agent/pkg/certs/zip.go
Normal file
76
agent/pkg/certs/zip.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
)
|
||||
|
||||
func writeFile(zipWriter *zip.Writer, name string, data []byte) error {
|
||||
w, err := zipWriter.CreateHeader(&zip.FileHeader{
|
||||
Name: name,
|
||||
Method: zip.Deflate,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func readFile(f *zip.File) ([]byte, error) {
|
||||
r, err := f.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
|
||||
func ZipCert(ca, crt, key []byte) ([]byte, error) {
|
||||
data := bytes.NewBuffer(nil)
|
||||
zipWriter := zip.NewWriter(data)
|
||||
defer zipWriter.Close()
|
||||
|
||||
if err := writeFile(zipWriter, "ca.pem", ca); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeFile(zipWriter, "cert.pem", crt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeFile(zipWriter, "key.pem", key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := zipWriter.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.Bytes(), nil
|
||||
}
|
||||
|
||||
func AgentCertsFilename(host string) string {
|
||||
return filepath.Join(common.AgentCertsBasePath, host+".zip")
|
||||
}
|
||||
|
||||
func ExtractCert(data []byte) (ca, crt, key []byte, err error) {
|
||||
zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for _, file := range zipReader.File {
|
||||
switch file.Name {
|
||||
case "ca.pem":
|
||||
ca, err = readFile(file)
|
||||
case "cert.pem":
|
||||
crt, err = readFile(file)
|
||||
case "key.pem":
|
||||
key, err = readFile(file)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
return ca, crt, key, nil
|
||||
}
|
||||
Reference in New Issue
Block a user