@@ -70,6 +70,7 @@ def __init__(
7070 ssh_config_path : Union [PathLike , Literal ["none" ]] = "none" ,
7171 port : Optional [int ] = None ,
7272 ssh_proxies : Iterable [tuple [SSHConnectionParams , Optional [FilePathOrContent ]]] = (),
73+ batch_mode : bool = False ,
7374 ):
7475 """
7576 :param forwarded_sockets: Connections to the specified local sockets will be
@@ -79,6 +80,14 @@ def __init__(
7980 :param ssh_proxies: pairs of SSH connections params and optional identities,
8081 in order from outer to inner. If an identity is `None`, the `identity` param
8182 is used instead.
83+ :param batch_mode: If enabled, "user interaction such as password prompts and host key
84+ confirmation requests will be disabled", see `ssh_config(5)`, `BatchMode`.
85+ Although this is probably the desired behavior in all use cases, the default value
86+ is `False` for gradual adoption.
87+ Note, this option is only applied to the `destination` and `ssh_proxies`. If you
88+ configured `destination` with `ProxyJump` in the `ssh_config_path` config, the proxy
89+ jump connection will ignore this option -- in that case, you should replace `ProxyJump`
90+ with explicit `ProxyCommand=ssh [...] -o BatchMode=yes` in your config.
8291 """
8392 self .destination = destination
8493 self .forwarded_sockets = list (forwarded_sockets )
@@ -101,6 +110,7 @@ def __init__(
101110 proxy_identity , f"proxy_identity_{ proxy_index } "
102111 )
103112 self .ssh_proxies .append ((proxy_params , proxy_identity_path ))
113+ self .batch_mode = batch_mode
104114 self .log_path = normalize_path (os .path .join (temp_dir .name , "tunnel.log" ))
105115 self .ssh_client_info = get_ssh_client_info ()
106116 self .ssh_exec_path = str (self .ssh_client_info .path )
@@ -145,6 +155,14 @@ def open_command(self) -> List[str]:
145155 command += ["-p" , str (self .port )]
146156 for k , v in self .options .items ():
147157 command += ["-o" , f"{ k } ={ v } " ]
158+ if self .batch_mode :
159+ command += ["-o" , "BatchMode=yes" ]
160+ if "serveraliveinterval" not in map (str .lower , self .options ):
161+ # Revert Debian-specific patch effect:
162+ # > The default is 0, indicating that these messages will not be sent
163+ # > to the server, or 300 if the BatchMode option is set (Debian-specific).
164+ # https://salsa.debian.org/ssh-team/openssh/-/blob/d87b69641b533b892b87e2eea02dbee796682d64/debian/patches/keepalive-extensions.patch#L69-77
165+ command += ["-o" , "ServerAliveInterval=0" ]
148166 if proxy_command := self ._get_proxy_command ():
149167 command += ["-o" , proxy_command ]
150168 for socket_pair in self .forwarded_sockets :
@@ -290,6 +308,14 @@ def _build_proxy_command(
290308 "-o" ,
291309 "UserKnownHostsFile=/dev/null" ,
292310 ]
311+ if self .batch_mode :
312+ # ServerAliveInterval is explained in the open_command() comment
313+ command += [
314+ "-o" ,
315+ "BatchMode=yes" ,
316+ "-o" ,
317+ "ServerAliveInterval=0" ,
318+ ]
293319 if prev_proxy_command is not None :
294320 command += ["-o" , prev_proxy_command .replace ("%" , "%%" )]
295321 command += [
0 commit comments