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 }