2020-07-27 20:43:58 +00:00
|
|
|
package mobileNebula
|
|
|
|
|
|
|
|
import (
|
2020-08-17 16:56:15 +00:00
|
|
|
"bytes"
|
2020-07-27 20:43:58 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2021-04-23 21:23:06 +00:00
|
|
|
"io/ioutil"
|
2020-07-27 20:43:58 +00:00
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
"github.com/DefinedNet/dnapi"
|
2020-08-17 16:56:15 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2020-07-27 20:43:58 +00:00
|
|
|
"github.com/slackhq/nebula"
|
|
|
|
"github.com/slackhq/nebula/cert"
|
2021-12-17 15:53:15 +00:00
|
|
|
nc "github.com/slackhq/nebula/config"
|
|
|
|
"github.com/slackhq/nebula/util"
|
2020-07-27 20:43:58 +00:00
|
|
|
"golang.org/x/crypto/curve25519"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type m map[string]interface{}
|
|
|
|
|
|
|
|
type CIDR struct {
|
|
|
|
Ip string
|
|
|
|
MaskCIDR string
|
|
|
|
MaskSize int
|
|
|
|
Network string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Validity struct {
|
|
|
|
Valid bool
|
|
|
|
Reason string
|
|
|
|
}
|
|
|
|
|
|
|
|
type RawCert struct {
|
|
|
|
RawCert string
|
|
|
|
Cert *cert.NebulaCertificate
|
|
|
|
Validity Validity
|
|
|
|
}
|
|
|
|
|
|
|
|
type KeyPair struct {
|
|
|
|
PublicKey string
|
|
|
|
PrivateKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
func RenderConfig(configData string, key string) (string, error) {
|
|
|
|
var d m
|
|
|
|
|
|
|
|
err := json.Unmarshal([]byte(configData), &d)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
// If this is a managed config, go ahead and return it
|
|
|
|
if rawCfg, ok := d["rawConfig"].(string); ok {
|
|
|
|
yamlCfg, err := dnapi.InsertConfigPrivateKey([]byte(rawCfg), []byte(key))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return "# DN-managed config\n" + string(yamlCfg), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, build the config
|
|
|
|
cfg := newConfig()
|
|
|
|
cfg.PKI.CA, _ = d["ca"].(string)
|
|
|
|
cfg.PKI.Cert, _ = d["cert"].(string)
|
|
|
|
cfg.PKI.Key = key
|
2020-07-27 20:43:58 +00:00
|
|
|
|
|
|
|
i, _ := d["port"].(float64)
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Listen.Port = int(i)
|
2020-07-27 20:43:58 +00:00
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Cipher, _ = d["cipher"].(string)
|
2020-07-27 20:43:58 +00:00
|
|
|
// Log verbosity is not required
|
|
|
|
if val, _ := d["logVerbosity"].(string); val != "" {
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Logging.Level = val
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
i, _ = d["lhDuration"].(float64)
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Lighthouse.Interval = int(i)
|
2020-07-27 20:43:58 +00:00
|
|
|
|
|
|
|
if i, ok := d["mtu"].(float64); ok {
|
|
|
|
mtu := int(i)
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Tun.MTU = &mtu
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Lighthouse.Hosts = make([]string, 0)
|
2020-07-27 20:43:58 +00:00
|
|
|
staticHostmap := d["staticHostmap"].(map[string]interface{})
|
|
|
|
for nebIp, mapping := range staticHostmap {
|
|
|
|
def := mapping.(map[string]interface{})
|
|
|
|
|
|
|
|
isLh := def["lighthouse"].(bool)
|
|
|
|
if isLh {
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Lighthouse.Hosts = append(cfg.Lighthouse.Hosts, nebIp)
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hosts := def["destinations"].([]interface{})
|
|
|
|
realHosts := make([]string, len(hosts))
|
|
|
|
|
|
|
|
for i, h := range hosts {
|
|
|
|
realHosts[i] = h.(string)
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.StaticHostmap[nebIp] = realHosts
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if unsafeRoutes, ok := d["unsafeRoutes"].([]interface{}); ok {
|
2022-11-17 21:43:16 +00:00
|
|
|
cfg.Tun.UnsafeRoutes = make([]configUnsafeRoute, len(unsafeRoutes))
|
2020-07-27 20:43:58 +00:00
|
|
|
for i, r := range unsafeRoutes {
|
|
|
|
rawRoute := r.(map[string]interface{})
|
2022-11-17 21:43:16 +00:00
|
|
|
route := &cfg.Tun.UnsafeRoutes[i]
|
2020-07-27 20:43:58 +00:00
|
|
|
route.Route = rawRoute["route"].(string)
|
|
|
|
route.Via = rawRoute["via"].(string)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:43:16 +00:00
|
|
|
finalConfig, err := yaml.Marshal(cfg)
|
2020-07-27 20:43:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(finalConfig), nil
|
|
|
|
}
|
|
|
|
|
2020-08-17 16:56:15 +00:00
|
|
|
func TestConfig(configData string, key string) error {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
fmt.Println("Recovered in f", r)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
yamlConfig, err := RenderConfig(configData, key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-04-23 21:23:06 +00:00
|
|
|
// We don't want to leak the config into the system logs
|
|
|
|
l := logrus.New()
|
|
|
|
l.SetOutput(bytes.NewBuffer([]byte{}))
|
|
|
|
|
2021-12-17 15:53:15 +00:00
|
|
|
c := nc.NewC(l)
|
|
|
|
err = c.LoadString(yamlConfig)
|
2020-08-17 16:56:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to load config: %s", err)
|
|
|
|
}
|
|
|
|
|
2021-12-17 15:53:15 +00:00
|
|
|
_, err = nebula.Main(c, true, "", l, nil)
|
2020-08-17 16:56:15 +00:00
|
|
|
if err != nil {
|
|
|
|
switch v := err.(type) {
|
2023-09-01 20:06:21 +00:00
|
|
|
case *util.ContextualError:
|
2020-08-17 16:56:15 +00:00
|
|
|
return v.Unwrap()
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-27 20:43:58 +00:00
|
|
|
func GetConfigSetting(configData string, setting string) string {
|
2021-04-23 21:23:06 +00:00
|
|
|
// We don't want to leak the config into the system logs
|
|
|
|
l := logrus.New()
|
|
|
|
l.SetOutput(ioutil.Discard)
|
|
|
|
|
2021-12-17 15:53:15 +00:00
|
|
|
c := nc.NewC(l)
|
|
|
|
c.LoadString(configData)
|
|
|
|
return c.GetString(setting, "")
|
2020-07-27 20:43:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func ParseCIDR(cidr string) (*CIDR, error) {
|
|
|
|
ip, ipNet, err := net.ParseCIDR(cidr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
size, _ := ipNet.Mask.Size()
|
|
|
|
|
|
|
|
return &CIDR{
|
|
|
|
Ip: ip.String(),
|
|
|
|
MaskCIDR: fmt.Sprintf("%d.%d.%d.%d", ipNet.Mask[0], ipNet.Mask[1], ipNet.Mask[2], ipNet.Mask[3]),
|
|
|
|
MaskSize: size,
|
|
|
|
Network: ipNet.IP.String(),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a JSON representation of 1 or more certificates
|
|
|
|
func ParseCerts(rawStringCerts string) (string, error) {
|
|
|
|
var certs []RawCert
|
|
|
|
var c *cert.NebulaCertificate
|
|
|
|
var err error
|
|
|
|
rawCerts := []byte(rawStringCerts)
|
|
|
|
|
|
|
|
for {
|
|
|
|
c, rawCerts, err = cert.UnmarshalNebulaCertificateFromPEM(rawCerts)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
rawCert, err := c.MarshalToPEM()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
rc := RawCert{
|
|
|
|
RawCert: string(rawCert),
|
|
|
|
Cert: c,
|
|
|
|
Validity: Validity{
|
|
|
|
Valid: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Expired(time.Now()) {
|
|
|
|
rc.Validity.Valid = false
|
|
|
|
rc.Validity.Reason = "Certificate is expired"
|
|
|
|
}
|
|
|
|
|
|
|
|
if rc.Validity.Valid && c.Details.IsCA && !c.CheckSignature(c.Details.PublicKey) {
|
|
|
|
rc.Validity.Valid = false
|
|
|
|
rc.Validity.Reason = "Certificate signature did not match"
|
|
|
|
}
|
|
|
|
|
|
|
|
certs = append(certs, rc)
|
|
|
|
|
|
|
|
if rawCerts == nil || strings.TrimSpace(string(rawCerts)) == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rawJson, err := json.Marshal(certs)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(rawJson), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GenerateKeyPair() (string, error) {
|
|
|
|
pub, priv, err := x25519Keypair()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
kp := KeyPair{}
|
|
|
|
kp.PublicKey = string(cert.MarshalX25519PublicKey(pub))
|
|
|
|
kp.PrivateKey = string(cert.MarshalX25519PrivateKey(priv))
|
|
|
|
|
|
|
|
rawJson, err := json.Marshal(kp)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(rawJson), 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
|
|
|
|
}
|
|
|
|
|
2022-08-05 21:42:17 +00:00
|
|
|
func VerifyCertAndKey(rawCert string, pemPrivateKey string) (bool, error) {
|
|
|
|
rawKey, _, err := cert.UnmarshalX25519PrivateKey([]byte(pemPrivateKey))
|
|
|
|
if err != nil {
|
|
|
|
return false, fmt.Errorf("error while unmarshaling private key: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
nebulaCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(rawCert))
|
|
|
|
if err != nil {
|
|
|
|
return false, fmt.Errorf("error while unmarshaling cert: %s", err)
|
|
|
|
}
|
|
|
|
|
2023-05-10 22:05:56 +00:00
|
|
|
if err = nebulaCert.VerifyPrivateKey(nebulaCert.Details.Curve, rawKey); err != nil {
|
2022-08-05 21:42:17 +00:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|