VPN: move certificate signing logit to api service to which controller delegates ops
diff --git a/core/nebula/api/nebula.go b/core/nebula/api/nebula.go
new file mode 100644
index 0000000..b4649f1
--- /dev/null
+++ b/core/nebula/api/nebula.go
@@ -0,0 +1,125 @@
+package main
+
+import (
+ "crypto/ed25519"
+ "crypto/rand"
+ "errors"
+ "io"
+ "net"
+ "time"
+
+ "github.com/slackhq/nebula/cert"
+ "golang.org/x/crypto/curve25519"
+)
+
+type CertificateAuthority struct {
+ PrivateKey []byte `json:"private_key"`
+ Certificate []byte `json:"certificate"`
+}
+
+func CreateCertificateAuthority(name string) (*CertificateAuthority, error) {
+ t := time.Now().Add(time.Duration(-1 * time.Second))
+ rawPub, rawPriv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ nc := cert.NebulaCertificate{
+ Details: cert.NebulaCertificateDetails{
+ Name: name,
+ NotBefore: t,
+ NotAfter: t.Add(time.Duration(8760 * time.Hour)),
+ PublicKey: rawPub,
+ IsCA: true,
+ },
+ }
+ if err := nc.Sign(rawPriv); err != nil {
+ return nil, err
+ }
+ certSerialized, err := nc.MarshalToPEM()
+ if err != nil {
+ return nil, err
+ }
+ privKeySerialzied := cert.MarshalEd25519PrivateKey(rawPriv)
+ return &CertificateAuthority{
+ privKeySerialzied,
+ certSerialized,
+ }, nil
+}
+
+type NebulaNode struct {
+ PrivateKey []byte `json:"private_key,omitempty"`
+ Certificate []byte `json:"certificate"`
+}
+
+func SignNebulaNode(rawCAPrivateKey []byte, rawCACert []byte, nodeName string, nodePublicKey []byte, ip *net.IPNet) (*NebulaNode, error) {
+ caKey, _, err := cert.UnmarshalEd25519PrivateKey(rawCAPrivateKey)
+ if err != nil {
+ return nil, err
+ }
+ caCert, _, err := cert.UnmarshalNebulaCertificateFromPEM(rawCACert)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := caCert.VerifyPrivateKey(caKey); err != nil {
+ return nil, err
+ }
+ issuer, err := caCert.Sha256Sum()
+ if err != nil {
+ return nil, err
+ }
+ if caCert.Expired(time.Now()) {
+ return nil, errors.New("ca certificate is expired")
+ }
+ var pub, priv []byte
+ if nodePublicKey != nil {
+ var err error
+ pub, _, err = cert.UnmarshalX25519PublicKey(nodePublicKey)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ var rawPriv []byte
+ var err error
+ pub, rawPriv, err = x25519Keypair()
+ if err != nil {
+ return nil, err
+ }
+ priv = cert.MarshalX25519PrivateKey(rawPriv)
+ }
+ t := time.Now().Add(time.Duration(-1 * time.Second))
+ nc := cert.NebulaCertificate{
+ Details: cert.NebulaCertificateDetails{
+ Name: nodeName,
+ Ips: []*net.IPNet{ip},
+ NotBefore: t,
+ NotAfter: caCert.Details.NotAfter.Add(time.Duration(-1 * time.Second)),
+ PublicKey: pub,
+ IsCA: false,
+ Issuer: issuer,
+ },
+ }
+ if err := nc.CheckRootConstrains(caCert); err != nil {
+ return nil, err
+ }
+ if err := nc.Sign(caKey); err != nil {
+ return nil, err
+ }
+ certSerialized, err := nc.MarshalToPEM()
+ if err != nil {
+ return nil, err
+ }
+ return &NebulaNode{
+ PrivateKey: priv,
+ Certificate: certSerialized,
+ }, nil
+}
+
+func x25519Keypair() ([]byte, []byte, error) {
+ var pubkey, privkey [32]byte
+ if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
+ return nil, nil, err
+ }
+ curve25519.ScalarBaseMult(&pubkey, &privkey)
+ return pubkey[:], privkey[:], nil
+}