@@ -708,10 +708,17 @@ def finalize_initrd(config: Config) -> Iterator[Optional[Path]]:
708708
709709
710710@contextlib .contextmanager
711- def finalize_state (config : Config , cid : int ) -> Iterator [None ]:
711+ def finalize_state (
712+ config : Config ,
713+ cid : Optional [int ] = None ,
714+ proxy_command : Optional [str ] = None ,
715+ ) -> Iterator [None ]:
712716 statedir = INVOKING_USER .runtime_dir () / "mkosi/machine"
713717 statedir .mkdir (parents = True , exist_ok = True )
714718
719+ if proxy_command is None and cid is not None :
720+ proxy_command = f"socat - VSOCK-CONNECT:{ cid } :%p"
721+
715722 with flock (statedir ):
716723 if (p := statedir / f"{ config .machine_or_name ()} .json" ).exists ():
717724 state = json .loads (p .read_text ())
@@ -727,7 +734,7 @@ def finalize_state(config: Config, cid: int) -> Iterator[None]:
727734 {
728735 "Machine" : config .machine_or_name (),
729736 "Pid" : os .getpid (),
730- "ProxyCommand" : f"socat - VSOCK-CONNECT: { cid } :%p" ,
737+ "ProxyCommand" : proxy_command ,
731738 "SshKey" : os .fspath (config .ssh_key ) if config .ssh_key else None ,
732739 },
733740 sort_keys = True ,
@@ -856,10 +863,19 @@ def finalize_register(config: Config) -> bool:
856863 return True
857864
858865
859- def register_machine (config : Config , pid : int , fname : Path , cid : Optional [int ]) -> None :
866+ def register_machine (
867+ config : Config ,
868+ pid : int ,
869+ fname : Path ,
870+ cid : Optional [int ],
871+ ssh_address : Optional [str ] = None ,
872+ ) -> None :
860873 if not finalize_register (config ):
861874 return
862875
876+ if ssh_address is None and cid is not None :
877+ ssh_address = f"vsock/{ cid } "
878+
863879 run (
864880 [
865881 "varlinkctl" ,
@@ -874,7 +890,7 @@ def register_machine(config: Config, pid: int, fname: Path, cid: Optional[int])
874890 "leader" : pid ,
875891 ** ({"rootDirectory" : os .fspath (fname )} if fname .is_dir () else {}),
876892 ** ({"vSockCid" : cid } if cid is not None else {}),
877- ** ({"sshAddress" : f"vsock/ { cid } " } if cid is not None else {}),
893+ ** ({"sshAddress" : ssh_address } if ssh_address is not None else {}),
878894 ** ({"sshPrivateKeyPath" : f"{ config .ssh_key } " } if config .ssh_key else {}),
879895 ** ({"allocateUnit" : True }),
880896 }
@@ -1035,8 +1051,16 @@ def run_qemu(args: Args, config: Config) -> None:
10351051 * shm ,
10361052 ] # fmt: skip
10371053
1054+ ssh_hostfwd_port : Optional [int ] = None
10381055 if config .runtime_network == Network .user :
1039- cmdline += ["-nic" , f"user,model={ config .architecture .default_qemu_nic_model ()} " ]
1056+ # Let the OS pick a free port for SSH host forwarding.
1057+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
1058+ s .bind (("127.0.0.1" , 0 ))
1059+ ssh_hostfwd_port = s .getsockname ()[1 ]
1060+ cmdline += [
1061+ "-nic" ,
1062+ f"user,model={ config .architecture .default_qemu_nic_model ()} ,hostfwd=tcp::{ ssh_hostfwd_port } -:22" ,
1063+ ]
10401064 elif config .runtime_network == Network .interface :
10411065 if os .getuid () != 0 :
10421066 die ("RuntimeNetwork=interface requires root privileges" )
@@ -1318,8 +1342,13 @@ def add_virtiofs_mount(
13181342 cmdline += config .qemu_args
13191343 cmdline += args .cmdline
13201344
1321- if cid is not None :
1322- stack .enter_context (finalize_state (config , cid ))
1345+ proxy_command : Optional [str ] = None
1346+ if cid is None and ssh_hostfwd_port is not None :
1347+ proxy_command = f"socat - TCP4:127.0.0.1:{ ssh_hostfwd_port } "
1348+
1349+ stack .enter_context (
1350+ finalize_state (config , cid = cid , proxy_command = proxy_command )
1351+ )
13231352
13241353 # Reopen stdin, stdout and stderr to give qemu a private copy of them. This is a mitigation for the
13251354 # case when running mkosi under meson and one or two of the three are redirected and their pipe might
@@ -1379,7 +1408,7 @@ def run_ssh(args: Args, config: Config) -> None:
13791408 if not (p := statedir / f"{ config .machine_or_name ()} .json" ).exists ():
13801409 die (
13811410 f"{ p } not found, cannot SSH into virtual machine { config .machine_or_name ()} " ,
1382- hint = "Is the machine running and was it built with Ssh=yes and Vsock=yes ?" ,
1411+ hint = "Is the machine running and was it built with Ssh=yes?" ,
13831412 )
13841413
13851414 state = json .loads (p .read_text ())
0 commit comments