11package executor
22
33import (
4+ "bytes"
45 "context"
56 "errors"
67 "fmt"
@@ -30,6 +31,7 @@ type RunExecutor struct {
3031 tempDir string
3132 homeDir string
3233 workingDir string
34+ sshPort int
3335 uid uint32
3436
3537 run schemas.RunSpec
@@ -74,6 +76,7 @@ func NewRunExecutor(tempDir string, homeDir string, workingDir string, sshPort i
7476 tempDir : tempDir ,
7577 homeDir : homeDir ,
7678 workingDir : workingDir ,
79+ sshPort : sshPort ,
7780 uid : uid ,
7881
7982 mu : mu ,
@@ -322,15 +325,18 @@ func (ex *RunExecutor) execJob(ctx context.Context, jobLogFile io.Writer) error
322325 log .Warning (ctx , "failed to write SSH environment" , "path" , ex .homeDir , "err" , err )
323326 }
324327 }
328+ userSSHDir := ""
329+ uid := - 1
330+ gid := - 1
325331 if user != nil && * user .Uid != 0 {
326332 // non-root user
327- uid : = int (* user .Uid )
328- gid : = int (* user .Gid )
333+ uid = int (* user .Uid )
334+ gid = int (* user .Gid )
329335 homeDir , isHomeDirAccessible := prepareHomeDir (ctx , uid , gid , user .HomeDir )
330336 envMap ["HOME" ] = homeDir
331337 if isHomeDirAccessible {
332338 log .Trace (ctx , "provisioning homeDir" , "path" , homeDir )
333- userSSHDir , err : = prepareSSHDir (uid , gid , homeDir )
339+ userSSHDir , err = prepareSSHDir (uid , gid , homeDir )
334340 if err != nil {
335341 log .Warning (ctx , "failed to prepare ssh dir" , "home" , homeDir , "err" , err )
336342 } else {
@@ -354,6 +360,17 @@ func (ex *RunExecutor) execJob(ctx context.Context, jobLogFile io.Writer) error
354360 } else {
355361 // root user
356362 envMap ["HOME" ] = ex .homeDir
363+ userSSHDir = filepath .Join (ex .homeDir , ".ssh" )
364+ }
365+
366+ if ex .jobSpec .SSHKey != nil && userSSHDir != "" {
367+ err := configureSSH (
368+ ex .jobSpec .SSHKey .Private , ex .jobSpec .SSHKey .Public , ex .clusterInfo .JobIPs , ex .sshPort ,
369+ uid , gid , userSSHDir ,
370+ )
371+ if err != nil {
372+ log .Warning (ctx , "failed to configure SSH" , "err" , err )
373+ }
357374 }
358375
359376 cmd .Env = envMap .Render ()
@@ -712,6 +729,56 @@ func writeSSHEnvironment(env map[string]string, uid int, gid int, envPath string
712729 return nil
713730}
714731
732+ func configureSSH (private string , public string , ips []string , port int , uid int , gid int , sshDir string ) error {
733+ privatePath := filepath .Join (sshDir , "dstack_job" )
734+ privateFile , err := os .OpenFile (privatePath , os .O_TRUNC | os .O_WRONLY | os .O_CREATE , 0o600 )
735+ if err != nil {
736+ return err
737+ }
738+ defer privateFile .Close ()
739+ if err := os .Chown (privatePath , uid , gid ); err != nil {
740+ return err
741+ }
742+ if _ , err := privateFile .WriteString (private ); err != nil {
743+ return err
744+ }
745+
746+ akPath := filepath .Join (sshDir , "authorized_keys" )
747+ akFile , err := os .OpenFile (akPath , os .O_APPEND | os .O_WRONLY | os .O_CREATE , 0o600 )
748+ if err != nil {
749+ return err
750+ }
751+ defer akFile .Close ()
752+ if err := os .Chown (akPath , uid , gid ); err != nil {
753+ return err
754+ }
755+ if _ , err := akFile .WriteString (public ); err != nil {
756+ return err
757+ }
758+
759+ configPath := filepath .Join (sshDir , "config" )
760+ configFile , err := os .OpenFile (configPath , os .O_APPEND | os .O_WRONLY | os .O_CREATE , 0o600 )
761+ if err != nil {
762+ return err
763+ }
764+ defer configFile .Close ()
765+ if err := os .Chown (configPath , uid , gid ); err != nil {
766+ return err
767+ }
768+ var configBuffer bytes.Buffer
769+ for _ , ip := range ips {
770+ configBuffer .WriteString (fmt .Sprintf ("\n Host %s\n " , ip ))
771+ configBuffer .WriteString (fmt .Sprintf (" Port %d\n " , port ))
772+ configBuffer .WriteString (" StrictHostKeyChecking no\n " )
773+ configBuffer .WriteString (" UserKnownHostsFile /dev/null\n " )
774+ configBuffer .WriteString (fmt .Sprintf (" IdentityFile %s\n " , privatePath ))
775+ }
776+ if _ , err := configFile .Write (configBuffer .Bytes ()); err != nil {
777+ return err
778+ }
779+ return nil
780+ }
781+
715782// A makeshift solution to deliver authorized_keys to a non-root user
716783// without modifying the existing API/bootstrap process
717784// TODO: implement key delivery properly, i.e. sumbit keys to and write by the runner,
0 commit comments