github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/pkg/ddevapp/ssh_auth_test.go (about)

     1  package ddevapp_test
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/drud/ddev/pkg/ddevapp"
    11  	"github.com/drud/ddev/pkg/dockerutil"
    12  	"github.com/drud/ddev/pkg/exec"
    13  	"github.com/drud/ddev/pkg/fileutil"
    14  	"github.com/drud/ddev/pkg/globalconfig"
    15  	"github.com/drud/ddev/pkg/testcommon"
    16  	"github.com/drud/ddev/pkg/util"
    17  	"github.com/drud/ddev/pkg/versionconstants"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	asrt "github.com/stretchr/testify/assert"
    21  )
    22  
    23  // TestSSHAuth tests basic ssh authentication
    24  func TestSSHAuth(t *testing.T) {
    25  	assert := asrt.New(t)
    26  	origDir, _ := os.Getwd()
    27  	app := &ddevapp.DdevApp{}
    28  
    29  	runTime := util.TimeTrack(time.Now(), t.Name())
    30  
    31  	//  Add a docker-compose service that has ssh server and mounted authorized_keys
    32  	site := TestSites[0]
    33  	// If running this with GOTEST_SHORT we have to create the directory, tarball etc.
    34  	if site.Dir == "" || !fileutil.FileExists(site.Dir) {
    35  		err := site.Prepare()
    36  		if err != nil {
    37  			t.Fatalf("Prepare() failed on TestSite.Prepare() site=%s, err=%v", site.Name, err)
    38  		}
    39  	}
    40  
    41  	testcommon.ClearDockerEnv()
    42  
    43  	err := app.Init(site.Dir)
    44  	require.NoError(t, err, "app.Init() failed on site %s in dir %s, err=%v", site.Name, site.Dir, err)
    45  	t.Cleanup(func() {
    46  		err = app.Stop(true, false)
    47  		assert.NoError(err)
    48  		err = os.Chdir(origDir)
    49  		assert.NoError(err)
    50  		_ = os.RemoveAll(app.GetConfigPath(".ssh"))
    51  		_ = os.RemoveAll(app.GetConfigPath("docker-compose.sshserver.yaml"))
    52  	})
    53  	srcDdev := filepath.Join(origDir, "testdata", t.Name(), ".ddev")
    54  	err = fileutil.CopyDir(filepath.Join(srcDdev, ".ssh"), app.GetConfigPath(".ssh"))
    55  	require.NoError(t, err)
    56  	err = os.Chmod(app.GetConfigPath(".ssh"), 0700)
    57  	require.NoError(t, err)
    58  	err = os.Chmod(app.GetConfigPath(".ssh/authorized_keys"), 0600)
    59  	require.NoError(t, err)
    60  	err = os.Chmod(app.GetConfigPath(".ssh/id_rsa"), 0600)
    61  	require.NoError(t, err)
    62  	err = fileutil.CopyFile(filepath.Join(srcDdev, "docker-compose.sshserver.yaml"), app.GetConfigPath("docker-compose.sshserver.yaml"))
    63  	require.NoError(t, err)
    64  
    65  	// Start with the testsite stopped (and everything stopped)
    66  	err = app.Stop(true, false)
    67  	assert.NoError(err)
    68  
    69  	// Make absolutely sure the ssh-agent is created from scratch.
    70  	err = ddevapp.RemoveSSHAgentContainer()
    71  	require.NoError(t, err)
    72  
    73  	err = app.Start()
    74  	require.NoError(t, err)
    75  
    76  	err = app.EnsureSSHAgentContainer()
    77  	require.NoError(t, err)
    78  
    79  	// Try a simple ssh (with no auth set up), it should fail with "Permission denied"
    80  	_, stderr, err := app.Exec(&ddevapp.ExecOpts{
    81  		Service: "web",
    82  		Cmd:     "ssh -o BatchMode=yes -o StrictHostKeyChecking=false root@test-ssh-server pwd",
    83  	})
    84  
    85  	assert.Error(err)
    86  	assert.Contains(stderr, "Permission denied")
    87  
    88  	// Add password/key to auth. This is an unfortunate perversion of using docker run directly, copied from
    89  	// ddev auth ssh command, and with an expect script to provide the passphrase.
    90  	uidStr, _, username := util.GetContainerUIDGid()
    91  	sshKeyPath := app.GetConfigPath(".ssh")
    92  	sshKeyPath = dockerutil.MassageWindowsHostMountpoint(sshKeyPath)
    93  
    94  	err = exec.RunInteractiveCommand("docker", []string{"run", "-t", "--rm", "--volumes-from=" + ddevapp.SSHAuthName, "-v", sshKeyPath + ":/home/" + username + "/.ssh", "-u", uidStr, versionconstants.SSHAuthImage + ":" + versionconstants.SSHAuthTag + "-built", "//test.expect.passphrase"})
    95  	require.NoError(t, err)
    96  
    97  	// Try ssh, should succeed
    98  	stdout, _, err := app.Exec(&ddevapp.ExecOpts{
    99  		Service: "web",
   100  		Cmd:     "ssh -o StrictHostKeyChecking=false root@test-ssh-server pwd",
   101  	})
   102  	stdout = strings.Trim(stdout, "\r\n")
   103  	assert.Equal(stdout, "/root")
   104  	assert.NoError(err)
   105  
   106  	err = app.Stop(true, false)
   107  	assert.NoError(err)
   108  
   109  	// Now start it up again; we shouldn't need to add the key this time
   110  	err = app.Start()
   111  	require.NoError(t, err)
   112  
   113  	// Try ssh, should succeed
   114  	stdout, _, err = app.Exec(&ddevapp.ExecOpts{
   115  		Service: "web",
   116  		Cmd:     "ssh -o StrictHostKeyChecking=false root@test-ssh-server pwd",
   117  	})
   118  	stdout = strings.Trim(stdout, "\r\n")
   119  	assert.Equal(stdout, "/root")
   120  	assert.NoError(err)
   121  
   122  	err = app.Stop(true, false)
   123  	assert.NoError(err)
   124  
   125  	runTime()
   126  }
   127  
   128  // TestSshAuthConfigOverride tests that the ~/.ddev/.ssh-auth-compose-compose.yaml can be overridden
   129  // with ~/.ddev/ssh-auth-compose.*.yaml
   130  func TestSshAuthConfigOverride(t *testing.T) {
   131  	assert := asrt.New(t)
   132  	pwd, _ := os.Getwd()
   133  	testDir := testcommon.CreateTmpDir(t.Name())
   134  	_ = os.Chdir(testDir)
   135  	overrideYaml := filepath.Join(globalconfig.GetGlobalDdevDir(), "ssh-auth-compose.override.yaml")
   136  
   137  	// Remove the ddev-ssh-agent, since the start code simply checks to see if it's
   138  	// running and doesn't restart it if it's running
   139  	_ = dockerutil.RemoveContainer("ddev-ssh-agent", 0)
   140  
   141  	testcommon.ClearDockerEnv()
   142  
   143  	app, err := ddevapp.NewApp(testDir, true)
   144  	assert.NoError(err)
   145  	err = app.WriteConfig()
   146  	assert.NoError(err)
   147  	err = fileutil.CopyFile(filepath.Join(pwd, "testdata", t.Name(), "ssh-auth-compose.override.yaml"), overrideYaml)
   148  	assert.NoError(err)
   149  
   150  	answer := fileutil.RandomFilenameBase()
   151  	t.Setenv("ANSWER", answer)
   152  	assert.NoError(err)
   153  	assert.NoError(err)
   154  	t.Cleanup(func() {
   155  		err = app.Stop(true, false)
   156  		assert.NoError(err)
   157  		err = os.Chdir(pwd)
   158  		assert.NoError(err)
   159  		_ = os.RemoveAll(testDir)
   160  		_ = os.Remove(overrideYaml)
   161  	})
   162  
   163  	err = app.Start()
   164  	assert.NoError(err)
   165  
   166  	stdout, _, err := dockerutil.Exec("ddev-ssh-agent", "bash -c 'echo $ANSWER'", "")
   167  	assert.Equal(answer+"\n", stdout)
   168  }