diff --git a/internal/app/app_api_handlers.go b/internal/app/app_api_handlers.go index 1c17a83..fc6e139 100644 --- a/internal/app/app_api_handlers.go +++ b/internal/app/app_api_handlers.go @@ -1,11 +1,14 @@ package app import ( + "encoding/base64" "encoding/json" + "fmt" "net/http" "net/http/httputil" "net/url" "prx/internal/models" + "prx/internal/pkg" "prx/internal/utils" "time" ) @@ -37,6 +40,34 @@ func (a *App) HandleAddNewProxy(w http.ResponseWriter, req *http.Request) { return } + if body.From == "" || body.To == "" { + a.Response(w, a.Err("validation error %s", fmt.Errorf("redirection records values must not be blank")), http.StatusInternalServerError) + return + } + + if body.Cert == "" || body.Key == "" { + selfsigned, err := pkg.GenerateSelfSignedCertificate(body.From) + if err != nil { + a.Response(w, a.Err("certificate error %s", err), http.StatusInternalServerError) + } + + err = a.Kube.AddNewProxy(models.AddNewProxy{ + From: body.From, + To: body.To, + Cert: base64.StdEncoding.EncodeToString(selfsigned.Crt), + Key: base64.StdEncoding.EncodeToString(selfsigned.Key), + }, a.namespace, a.name) + + if err != nil { + a.Response(w, a.Err("error adding new proxy"), http.StatusInternalServerError) + return + } + + a.setRedirectRecords(body.From, body.To) + + a.Response(w, nil, http.StatusCreated) + } + if err := utils.ValidateFields(body); err != "" { a.Response(w, a.Err("validation error %s", err), http.StatusInternalServerError) return diff --git a/internal/models/certificates.go b/internal/models/certificates.go new file mode 100644 index 0000000..87ec7ff --- /dev/null +++ b/internal/models/certificates.go @@ -0,0 +1,6 @@ +package models + +type Cert struct { + Crt []byte + Key []byte +} diff --git a/internal/pkg/certificate.go b/internal/pkg/certificate.go new file mode 100644 index 0000000..981959e --- /dev/null +++ b/internal/pkg/certificate.go @@ -0,0 +1,68 @@ +package pkg + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "prx/internal/models" + "time" +) + +func GenerateSelfSignedCertificate(fqdn string) (models.Cert, error) { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return models.Cert{}, fmt.Errorf("error generatting private rsa key %w", err) + + } + + serialNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62)) + if err != nil { + return models.Cert{}, fmt.Errorf("error generatting serial number tls template: %w", err) + } + + tmpl := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: fqdn, + Organization: []string{"nated corp"}, + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{fqdn}, + } + + derCert, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv) + if err != nil { + return models.Cert{}, fmt.Errorf("error creating certificate: %w", err) + } + + certBuf := &bytes.Buffer{} + if err := pem.Encode(certBuf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: derCert, + }); err != nil { + return models.Cert{}, fmt.Errorf("error encoding certificate PEM: %w", err) + } + + keyBuf := &bytes.Buffer{} + privBytes := x509.MarshalPKCS1PrivateKey(priv) + if err := pem.Encode(keyBuf, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privBytes, + }); err != nil { + return models.Cert{}, fmt.Errorf("error encoding key PEM: %w", err) + } + + return models.Cert{ + Key: certBuf.Bytes(), + Crt: keyBuf.Bytes(), + }, nil +}