Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: redirect dns calls by configuring nsswitch config file in linux #1678

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 83 additions & 6 deletions pkg/service/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
Expand All @@ -35,8 +36,9 @@ import (
var Emoji = "\U0001F430" + " Keploy:"

type tester struct {
logger *zap.Logger
mutex sync.Mutex
logger *zap.Logger
mutex sync.Mutex
hostConfigStr string // hosts string in the nsswitch.conf of linux system. To restore the system hosts configuration after completion of test
}
type TestOptions struct {
MongoPassword string
Expand Down Expand Up @@ -73,12 +75,79 @@ func NewTester(logger *zap.Logger) Tester {
}
}

// setting up the dns routing for the linux system
func (t *tester) setupNsswitchConfig() error {
// Check if the nsswitch.conf present for the system
if _, err := os.Stat("/etc/nsswitch.conf"); err == nil {
// Read the current nsswitch.conf
data, err := os.ReadFile("/etc/nsswitch.conf")
if err != nil {
t.logger.Error("failed to read the nsswitch.conf file from system", zap.Error(err))
return err
}

// Replace the hosts field value if it exists
lines := strings.Split(string(data), "\n")
for i, line := range lines {
if strings.HasPrefix(line, "hosts:") {
t.hostConfigStr = lines[i]
lines[i] = "hosts: files dns"
}
}

// Write the modified nsswitch.conf back to the file
err = os.WriteFile("/etc/nsswitch.conf", []byte(strings.Join(lines, "\n")), 0644)
if err != nil {
t.logger.Error("failed to write the configuration to the nsswitch.conf file to redirect the DNS queries to proxy", zap.Error(err))
return err
}

t.logger.Debug("Successfully written to nsswitch config of linux")
}
return nil
}

// resetNsswitchConfig resets the hosts config of nsswitch of the system
func (t *tester) resetNsswitchConfig() {
data, err := os.ReadFile("/etc/nsswitch.conf")
if err != nil {
t.logger.Error("failed to read the nsswitch.conf file from system", zap.Error(err))
return
}

// Replace the hosts field value if it exists with the actual system hosts value
lines := strings.Split(string(data), "\n")
for i, line := range lines {
if strings.HasPrefix(line, "hosts:") {
lines[i] = t.hostConfigStr
}
}

// Write the modified nsswitch.conf back to the file
err = os.WriteFile("/etc/nsswitch.conf", []byte(strings.Join(lines, "\n")), 0644)
if err != nil {
t.logger.Error("failed to write the configuration to the nsswitch.conf file to redirect the DNS queries to proxy", zap.Error(err))
return
}

t.logger.Debug("Successfully reset the nsswitch config of linux")
}

func (t *tester) InitialiseTest(cfg *TestConfig) (TestEnvironmentSetup, error) {
var (
returnVal TestEnvironmentSetup
err error
)
returnVal.Storage = cfg.Storage

// setting up the dns routing for the fedora distro
if runtime.GOOS == "linux" {
err = t.setupNsswitchConfig()
if err != nil {
return returnVal, err
}
}

// capturing the code coverage for go bianries built by go-version 1.20^
if cfg.WithCoverage {

Expand All @@ -102,7 +171,7 @@ func (t *tester) InitialiseTest(cfg *TestConfig) (TestEnvironmentSetup, error) {
return returnVal, err
} else if err == nil && !dirInfo.IsDir() {
t.logger.Error("the goCoverDir is not a directory. Please provide a valid path to a directory for go coverage binaries.")
return returnVal, fmt.Errorf("the goCoverDir is not a directory. Please provide a valid path to a directory for go coverage binaries.")
return returnVal, fmt.Errorf("the goCoverDir is not a directory. Please provide a valid path to a directory for go coverage binaries")
} else if err != nil && os.IsNotExist(err) {
err := makeDirectory(cfg.CoverageReportPath)
if err != nil {
Expand All @@ -124,7 +193,7 @@ func (t *tester) InitialiseTest(cfg *TestConfig) (TestEnvironmentSetup, error) {
}

stopper := make(chan os.Signal, 1)
signal.Notify(stopper, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL)
signal.Notify(stopper, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)

models.SetMode(models.MODE_TEST)

Expand All @@ -140,7 +209,7 @@ func (t *tester) InitialiseTest(cfg *TestConfig) (TestEnvironmentSetup, error) {

select {
case <-stopper:
return returnVal, errors.New("Keploy was interupted by stopper")
return returnVal, errors.New("keploy was interupted by stopper")
default:
// load the ebpf hooks into the kernel
if err := returnVal.LoadedHooks.LoadHooks(cfg.AppCmd, cfg.AppContainer, 0, context.Background(), nil); err != nil {
Expand All @@ -151,7 +220,7 @@ func (t *tester) InitialiseTest(cfg *TestConfig) (TestEnvironmentSetup, error) {
select {
case <-stopper:
returnVal.LoadedHooks.Stop(true)
return returnVal, errors.New("Keploy was interupted by stopper")
return returnVal, errors.New("keploy was interupted by stopper")
default:
// start the proxy
returnVal.ProxySet = proxy.BootProxy(t.logger, proxy.Option{Port: cfg.Proxyport, MongoPassword: cfg.MongoPassword}, cfg.AppCmd, cfg.AppContainer, 0, "", cfg.PassThroughPorts, returnVal.LoadedHooks, context.Background(), cfg.Delay)
Expand Down Expand Up @@ -237,6 +306,14 @@ func (t *tester) Test(path string, testReportPath string, appCmd string, options
return false
}
initialisedValues, err := t.InitialiseTest(cfg)

// reset the hosts config in nsswitch.conf of the system
defer func() {
if t.hostConfigStr != "" {
t.resetNsswitchConfig()
}
}()

// Recover from panic and gracefully shutdown
defer initialisedValues.LoadedHooks.Recover(pkg.GenerateRandomID())
if err != nil {
Expand Down