3
0
Fork 0

Run check config on app boot, fix early error reporting on android

This commit is contained in:
Nate Brown 2020-08-17 11:56:15 -05:00
parent 50d50f690b
commit 1a4cbceda0
10 changed files with 144 additions and 45 deletions

View File

@ -2,11 +2,12 @@ package net.defined.mobile_nebula
import android.app.Activity import android.app.Activity
import android.content.ComponentName import android.content.ComponentName
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.net.VpnService import android.net.VpnService
import android.os.* import android.os.*
import androidx.annotation.NonNull; import androidx.annotation.NonNull
import com.google.gson.Gson import com.google.gson.Gson
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
@ -28,9 +29,15 @@ class MainActivity: FlutterActivity() {
private var activeSiteId: String? = null private var activeSiteId: String? = null
companion object {
private var appContext: Context? = null
fun getContext(): Context? { return appContext }
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
appContext = context
//TODO: Initializing in the constructor leads to a context lacking info we need, figure out the right way to do this //TODO: Initializing in the constructor leads to a context lacking info we need, figure out the right way to do this
sites = Sites(context, flutterEngine) sites = Sites(flutterEngine)
// Bind against our service to detect which site is running on app boot // Bind against our service to detect which site is running on app boot
val intent = Intent(this, NebulaVpnService::class.java) val intent = Intent(this, NebulaVpnService::class.java)

View File

@ -43,28 +43,37 @@ class NebulaVpnService : VpnService() {
return Service.START_NOT_STICKY return Service.START_NOT_STICKY
} }
startVpn(intent?.getStringExtra("path"), intent?.getStringExtra("id")) val path = intent?.getStringExtra("path")
return super.onStartCommand(intent, flags, startId) val id = intent?.getStringExtra("id")
}
private fun startVpn(path: String?, id: String?) {
if (running) { if (running) {
return announceExit(id, "Trying to run nebula but it is already running") announceExit(id, "Trying to run nebula but it is already running")
//TODO: can we signal failure?
return super.onStartCommand(intent, flags, startId)
} }
//TODO: if we fail to start, android will attempt a restart lacking all the intent data we need. //TODO: if we fail to start, android will attempt a restart lacking all the intent data we need.
// Link active site config in Main to avoid this // Link active site config in Main to avoid this
site = Site(File(path)) site = Site(File(path))
var ipNet: CIDR
if (site!!.cert == null) { if (site!!.cert == null) {
return announceExit(id, "Site is missing a certificate") announceExit(id, "Site is missing a certificate")
//TODO: can we signal failure?
return super.onStartCommand(intent, flags, startId)
} }
// We don't actually start here. In order to properly capture boot errors we wait until an IPC connection is made
return super.onStartCommand(intent, flags, startId)
}
private fun startVpn() {
var ipNet: CIDR
try { try {
ipNet = mobileNebula.MobileNebula.parseCIDR(site!!.cert!!.cert.details.ips[0]) ipNet = mobileNebula.MobileNebula.parseCIDR(site!!.cert!!.cert.details.ips[0])
} catch (err: Exception) { } catch (err: Exception) {
return announceExit(id, err.message ?: "$err") return announceExit(site!!.id, err.message ?: "$err")
} }
val builder = Builder() val builder = Builder()
@ -91,7 +100,7 @@ class NebulaVpnService : VpnService() {
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Got an error $e") Log.e(TAG, "Got an error $e")
vpnInterface?.close() vpnInterface?.close()
announceExit(id, e.message) announceExit(site!!.id, e.message)
return stopSelf() return stopSelf()
} }
@ -130,7 +139,7 @@ class NebulaVpnService : VpnService() {
//TODO: how do we limit what can talk to us? //TODO: how do we limit what can talk to us?
//TODO: Make sure replyTo is actually a messenger //TODO: Make sure replyTo is actually a messenger
when (msg.what) { when (msg.what) {
MSG_REGISTER_CLIENT -> mClients.add(msg.replyTo) MSG_REGISTER_CLIENT -> register(msg)
MSG_UNREGISTER_CLIENT -> mClients.remove(msg.replyTo) MSG_UNREGISTER_CLIENT -> mClients.remove(msg.replyTo)
MSG_IS_RUNNING -> isRunning() MSG_IS_RUNNING -> isRunning()
MSG_LIST_HOSTMAP -> listHostmap(msg) MSG_LIST_HOSTMAP -> listHostmap(msg)
@ -142,11 +151,29 @@ class NebulaVpnService : VpnService() {
} }
} }
private fun register(msg: Message) {
mClients.add(msg.replyTo)
if (!running) {
startVpn()
}
}
private fun protect(msg: Message): Boolean {
if (nebula != null) {
return false
}
msg.replyTo.send(Message.obtain(null, msg.what))
return true
}
private fun isRunning() { private fun isRunning() {
sendSimple(MSG_IS_RUNNING, if (running) 1 else 0) sendSimple(MSG_IS_RUNNING, if (running) 1 else 0)
} }
private fun listHostmap(msg: Message) { private fun listHostmap(msg: Message) {
if (protect(msg)) { return }
val res = nebula!!.listHostmap(msg.what == MSG_LIST_PENDING_HOSTMAP) val res = nebula!!.listHostmap(msg.what == MSG_LIST_PENDING_HOSTMAP)
var m = Message.obtain(null, msg.what) var m = Message.obtain(null, msg.what)
m.data.putString("data", res) m.data.putString("data", res)
@ -154,6 +181,8 @@ class NebulaVpnService : VpnService() {
} }
private fun getHostInfo(msg: Message) { private fun getHostInfo(msg: Message) {
if (protect(msg)) { return }
val res = nebula!!.getHostInfoByVpnIp(msg.data.getString("vpnIp"), msg.data.getBoolean("pending")) val res = nebula!!.getHostInfoByVpnIp(msg.data.getString("vpnIp"), msg.data.getBoolean("pending"))
var m = Message.obtain(null, msg.what) var m = Message.obtain(null, msg.what)
m.data.putString("data", res) m.data.putString("data", res)
@ -161,6 +190,8 @@ class NebulaVpnService : VpnService() {
} }
private fun setRemoteForTunnel(msg: Message) { private fun setRemoteForTunnel(msg: Message) {
if (protect(msg)) { return }
val res = nebula!!.setRemoteForTunnel(msg.data.getString("vpnIp"), msg.data.getString("addr")) val res = nebula!!.setRemoteForTunnel(msg.data.getString("vpnIp"), msg.data.getString("addr"))
var m = Message.obtain(null, msg.what) var m = Message.obtain(null, msg.what)
m.data.putString("data", res) m.data.putString("data", res)
@ -168,6 +199,8 @@ class NebulaVpnService : VpnService() {
} }
private fun closeTunnel(msg: Message) { private fun closeTunnel(msg: Message) {
if (protect(msg)) { return }
val res = nebula!!.closeTunnel(msg.data.getString("vpnIp")) val res = nebula!!.closeTunnel(msg.data.getString("vpnIp"))
var m = Message.obtain(null, msg.what) var m = Message.obtain(null, msg.what)
m.data.putBoolean("data", res) m.data.putBoolean("data", res)

View File

@ -15,7 +15,7 @@ data class SiteContainer(
val updater: SiteUpdater val updater: SiteUpdater
) )
class Sites(private var context: Context, private var engine: FlutterEngine) { class Sites(private var engine: FlutterEngine) {
private var sites: HashMap<String, SiteContainer> = HashMap() private var sites: HashMap<String, SiteContainer> = HashMap()
init { init {
@ -23,6 +23,7 @@ class Sites(private var context: Context, private var engine: FlutterEngine) {
} }
fun refreshSites(activeSite: String? = null) { fun refreshSites(activeSite: String? = null) {
val context = MainActivity.getContext()!!
val sitesDir = context.filesDir.resolve("sites") val sitesDir = context.filesDir.resolve("sites")
if (!sitesDir.isDirectory) { if (!sitesDir.isDirectory) {
sitesDir.delete() sitesDir.delete()
@ -57,7 +58,7 @@ class Sites(private var context: Context, private var engine: FlutterEngine) {
fun deleteSite(id: String) { fun deleteSite(id: String) {
sites.remove(id) sites.remove(id)
val siteDir = context.filesDir.resolve("sites").resolve(id) val siteDir = MainActivity.getContext()!!.filesDir.resolve("sites").resolve(id)
siteDir.deleteRecursively() siteDir.deleteRecursively()
//TODO: make sure you stop the vpn //TODO: make sure you stop the vpn
//TODO: make sure you relink the active site if this is the active site //TODO: make sure you relink the active site if this is the active site
@ -77,7 +78,6 @@ class SiteUpdater(private var site: Site, engine: FlutterEngine): EventChannel.S
site.connected = connected site.connected = connected
site.status = status site.status = status
val d = mapOf("connected" to site.connected, "status" to site.status) val d = mapOf("connected" to site.connected, "status" to site.status)
if (err != null) { if (err != null) {
eventSink?.error("", err, d) eventSink?.error("", err, d)
} else { } else {
@ -209,6 +209,14 @@ class Site {
ca = arrayOf() ca = arrayOf()
errors.add("Error while loading certificate authorities: ${err.message}") errors.add("Error while loading certificate authorities: ${err.message}")
} }
if (errors.isEmpty()) {
try {
mobileNebula.MobileNebula.testConfig(config, getKey(MainActivity.getContext()!!))
} catch (err: Exception) {
errors.add("Config test error: ${err.message}")
}
}
} }
fun getKey(context: Context): String? { fun getKey(context: Context): String? {

View File

@ -173,9 +173,9 @@ struct Site: Codable {
init(proto: NETunnelProviderProtocol) throws { init(proto: NETunnelProviderProtocol) throws {
let dict = proto.providerConfiguration let dict = proto.providerConfiguration
let rawConfig = dict?["config"] as? Data ?? Data() let config = dict?["config"] as? Data ?? Data()
let decoder = JSONDecoder() let decoder = JSONDecoder()
let incoming = try decoder.decode(IncomingSite.self, from: rawConfig) let incoming = try decoder.decode(IncomingSite.self, from: config)
self.init(incoming: incoming) self.init(incoming: incoming)
} }
@ -241,6 +241,22 @@ struct Site: Codable {
logVerbosity = incoming.logVerbosity ?? "info" logVerbosity = incoming.logVerbosity ?? "info"
mtu = incoming.mtu ?? 1300 mtu = incoming.mtu ?? 1300
logFile = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.net.defined.mobileNebula")?.appendingPathComponent(id).appendingPathExtension("log").path logFile = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.net.defined.mobileNebula")?.appendingPathComponent(id).appendingPathExtension("log").path
if (errors.isEmpty) {
do {
let encoder = JSONEncoder()
let rawConfig = try encoder.encode(incoming)
let key = try getKey()
let strConfig = String(data: rawConfig, encoding: .utf8)
var err: NSError?
MobileNebulaTestConfig(strConfig, key, &err)
if (err != nil) {
throw err!
}
} catch {
errors.append("Config test error: \(error.localizedDescription)")
}
}
} }
// Gets the private key from the keystore, we don't always need it in memory // Gets the private key from the keystore, we don't always need it in memory

View File

@ -529,7 +529,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula; PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -559,7 +559,7 @@
INFOPLIST_FILE = NebulaNetworkExtension/Info.plist; INFOPLIST_FILE = NebulaNetworkExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2; IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_LDFLAGS = ""; OTHER_LDFLAGS = "";
@ -594,7 +594,7 @@
INFOPLIST_FILE = NebulaNetworkExtension/Info.plist; INFOPLIST_FILE = NebulaNetworkExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2; IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_LDFLAGS = ""; OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula.NebulaNetworkExtension; PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula.NebulaNetworkExtension;
@ -626,7 +626,7 @@
INFOPLIST_FILE = NebulaNetworkExtension/Info.plist; INFOPLIST_FILE = NebulaNetworkExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.2; IPHONEOS_DEPLOYMENT_TARGET = 13.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_LDFLAGS = ""; OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula.NebulaNetworkExtension; PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula.NebulaNetworkExtension;
@ -768,7 +768,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula; PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -801,7 +801,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "$(PROJECT_DIR)/Flutter",
); );
MARKETING_VERSION = 0.0.31; MARKETING_VERSION = 0.0.33;
PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula; PRODUCT_BUNDLE_IDENTIFIER = net.defined.mobileNebula;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"

View File

@ -45,8 +45,10 @@ func NewNebula(configData string, key string, logFile string, tunFd int) (*Nebul
if err != nil { if err != nil {
switch v := err.(type) { switch v := err.(type) {
case nebula.ContextualError: case nebula.ContextualError:
return nil, v.RealError v.Log(l)
return nil, v.Unwrap()
default: default:
l.WithError(err).Error("Failed to start")
return nil, err return nil, err
} }
} }
@ -81,7 +83,7 @@ func (n *Nebula) ListHostmap(pending bool) (string, error) {
} }
func (n *Nebula) GetHostInfoByVpnIp(vpnIp string, pending bool) (string, error) { func (n *Nebula) GetHostInfoByVpnIp(vpnIp string, pending bool) (string, error) {
b, err := json.Marshal(n.c.GetHostInfoByVpnIp(stringIpToInt(vpnIp), pending)) b, err := json.Marshal(n.c.GetHostInfoByVpnIP(stringIpToInt(vpnIp), pending))
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -95,6 +95,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vishvananda/netlink v1.0.1-0.20190522153524-00009fb8606a h1:Bt1IVPhiCDMqwGrc2nnbIN4QKvJGx6SK2NzWBmW00ao= github.com/vishvananda/netlink v1.0.1-0.20190522153524-00009fb8606a h1:Bt1IVPhiCDMqwGrc2nnbIN4QKvJGx6SK2NzWBmW00ao=
github.com/vishvananda/netlink v1.0.1-0.20190522153524-00009fb8606a/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.0.1-0.20190522153524-00009fb8606a/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
@ -156,3 +157,4 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,6 +1,7 @@
package mobileNebula package mobileNebula
import ( import (
"bytes"
"crypto/rand" "crypto/rand"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -9,6 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula" "github.com/slackhq/nebula"
"github.com/slackhq/nebula/cert" "github.com/slackhq/nebula/cert"
"golang.org/x/crypto/curve25519" "golang.org/x/crypto/curve25519"
@ -108,6 +110,39 @@ func RenderConfig(configData string, key string) (string, error) {
return string(finalConfig), nil return string(finalConfig), nil
} }
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
}
config := nebula.NewConfig()
err = config.LoadString(yamlConfig)
if err != nil {
return fmt.Errorf("failed to load config: %s", err)
}
// We don't want to leak the config into the system logs
l := logrus.New()
l.SetOutput(bytes.NewBuffer([]byte{}))
_, err = nebula.Main(config, true, "", l, nil)
if err != nil {
switch v := err.(type) {
case nebula.ContextualError:
return v.Unwrap()
default:
return err
}
}
return nil
}
func GetConfigSetting(configData string, setting string) string { func GetConfigSetting(configData string, setting string) string {
config := nebula.NewConfig() config := nebula.NewConfig()
config.LoadString(configData) config.LoadString(configData)