diff --git a/tool/fdpass-teleport/.gitattributes b/tool/fdpass-teleport/.gitattributes
new file mode 100644
index 0000000000000..b1d9e273eb1ba
--- /dev/null
+++ b/tool/fdpass-teleport/.gitattributes
@@ -0,0 +1 @@
+/Cargo.lock -merge
diff --git a/tool/fdpass-teleport/.gitignore b/tool/fdpass-teleport/.gitignore
new file mode 100644
index 0000000000000..ea8c4bf7f35f6
--- /dev/null
+++ b/tool/fdpass-teleport/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/tool/fdpass-teleport/Cargo.lock b/tool/fdpass-teleport/Cargo.lock
new file mode 100644
index 0000000000000..8fa06fae09e11
--- /dev/null
+++ b/tool/fdpass-teleport/Cargo.lock
@@ -0,0 +1,95 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "eyre"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
+[[package]]
+name = "fdpass-teleport"
+version = "0.1.0"
+dependencies = [
+ "nix",
+ "simple-eyre",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "simple-eyre"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b561532e8ffe7ecf09108c4f662896a9ec3eac4999eba84015ec3dcb8cc630a"
+dependencies = [
+ "eyre",
+ "indenter",
+]
diff --git a/tool/fdpass-teleport/Cargo.toml b/tool/fdpass-teleport/Cargo.toml
new file mode 100644
index 0000000000000..7ccfb2f201fcb
--- /dev/null
+++ b/tool/fdpass-teleport/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "fdpass-teleport"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+
+[dependencies]
+nix = { version = "0.29.0", features = ["socket", "uio", "fs"] }
+simple-eyre = "0.3.1"
+
+[profile.release]
+codegen-units = 1
+lto = "fat"
+panic = "abort"
+strip = "symbols"
diff --git a/tool/fdpass-teleport/src/main.rs b/tool/fdpass-teleport/src/main.rs
new file mode 100644
index 0000000000000..13331f4411bf0
--- /dev/null
+++ b/tool/fdpass-teleport/src/main.rs
@@ -0,0 +1,109 @@
+// Teleport
+// Copyright (C) 2024 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use nix::{
+ errno::Errno,
+ fcntl::{self, OFlag},
+ libc,
+ sys::socket::{self, ControlMessage, MsgFlags},
+};
+use simple_eyre::eyre::{self, OptionExt, Result, WrapErr};
+use std::{
+ env,
+ io::{IoSlice, Write},
+ os::{
+ fd::AsRawFd,
+ unix::{ffi::OsStrExt, net::UnixStream},
+ },
+ process,
+};
+
+fn main() -> Result<()> {
+ simple_eyre::install()?;
+
+ let mut args = env::args_os();
+
+ if args.len() != 3 {
+ let pkg_name = env!("CARGO_PKG_NAME");
+ let version = option_env!("VERSION").unwrap_or("unknown");
+
+ let argv0 = args.next();
+ let argv0 = match &argv0 {
+ Some(o) => o.to_string_lossy(),
+ None => pkg_name.into(),
+ };
+
+ eprint!(
+ "{pkg_name} (version {version})\n\nusage: {argv0} \n",
+ );
+ process::exit(libc::EXIT_FAILURE);
+ }
+
+ let mux_path = args.nth(1).ok_or_eyre("missing mux path")?;
+ let mut target = args.next().ok_or_eyre("missing connection target")?;
+ target.push("\0");
+
+ // in OpenSSH ProxyCommand+ProxyUseFdPass the program is executed with a
+ // unix domain socket as stdout, which we can check with a getsockname();
+ // we'll get a ENOTSOCK if stdout is not a socket, or EINVAL if the address
+ // returned by getsockname() is not AF_UNIX (i.e. stdout is a socket but not
+ // a unix domain socket)
+ socket::getsockname::(libc::STDOUT_FILENO)
+ .wrap_err("stdout is not a unix socket")?;
+
+ // to not have to bother with poll() for the later sendmsg() we have to
+ // confirm that the socket is set to blocking mode, or we might end up
+ // busylooping with EAGAIN; OpenSSH at the time of writing (9.7) will give
+ // the ProxyCommand a blocking socketpair() half, so we should be good
+ let fl = fcntl::fcntl(libc::STDOUT_FILENO, fcntl::F_GETFL)
+ .wrap_err("could not check stdout for blocking mode")?;
+ let fl = OFlag::from_bits_retain(fl);
+ if fl.contains(OFlag::O_NONBLOCK) {
+ let mut fl = fl;
+ fl.set(OFlag::O_NONBLOCK, false);
+ fcntl::fcntl(libc::STDOUT_FILENO, fcntl::F_SETFL(fl))
+ .wrap_err("could not set stdout to blocking mode")?;
+ }
+
+ let mut mux_conn = UnixStream::connect(mux_path).wrap_err("could not connect to mux")?;
+ // we added a final NUL to target above
+ mux_conn
+ .write_all(target.as_bytes())
+ .wrap_err("could not send connection target to mux")?;
+
+ // we can now pass the connection to OpenSSH (or whoever launched us) over
+ // stdout, sending a byte of actual data together with the connection's file
+ // descriptor; logic lifted from OpenBSD's netcat fdpass code
+ // (https://github.com/openbsd/src/blob/master/usr.bin/nc/netcat.c)
+ loop {
+ match socket::sendmsg::<()>(
+ libc::STDOUT_FILENO,
+ &[IoSlice::new(&[0])],
+ &[ControlMessage::ScmRights(&[mux_conn.as_raw_fd()])],
+ MsgFlags::empty(),
+ None,
+ ) {
+ Ok(1) => break,
+ Ok(s) => eyre::bail!("unexpected sendmsg return value {s}"),
+ Err(Errno::EAGAIN | Errno::EINTR) => continue,
+ Err(e) => Err(e).wrap_err("could not pass connection to stdout")?,
+ };
+ }
+
+ // returning would close and deallocate things, and there's just no need for
+ // that
+ process::exit(libc::EXIT_SUCCESS);
+}