diff --git a/readconf.c b/readconf.c
index c177202..a9ef70b 100644
--- a/readconf.c
+++ b/readconf.c
@@ -169,6 +169,9 @@ typedef enum {
 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
+#ifdef TCP_STEALTH
+	oTCPStealthSecret,
+#endif
 	oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
 	oPubkeyAcceptedKeyTypes, oProxyJump,
 	oIgnoredUnknownOption, oDeprecated, oUnsupported
@@ -296,6 +299,9 @@ static struct {
 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
 	{ "ignoreunknown", oIgnoreUnknown },
 	{ "proxyjump", oProxyJump },
+#ifdef TCP_STEALTH
+	{ "tcpstealthsecret", oTCPStealthSecret },
+#endif
 
 	{ NULL, oBadOption }
 };
@@ -1657,6 +1663,23 @@ parse_keytypes:
 		charptr = &options->identity_agent;
 		goto parse_string;
 
+#ifdef TCP_STEALTH
+	case oTCPStealthSecret:
+		charptr = &options->tcp_stealth_secret;
+
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing argument.",
+			    filename, linenum);
+
+		if (*activep && *charptr == NULL) {
+			*charptr = xmalloc(64 + 1);
+			memset(*charptr, 0x00, 64 + 1);
+			strncpy(*charptr, arg, 64);
+		}
+
+		break;
+#endif
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
@@ -1853,6 +1876,9 @@ initialize_options(Options * options)
 	options->canonicalize_max_dots = -1;
 	options->canonicalize_fallback_local = -1;
 	options->canonicalize_hostname = -1;
+#ifdef TCP_STEALTH
+	options->tcp_stealth_secret = NULL;
+#endif
 	options->revoked_host_keys = NULL;
 	options->fingerprint_hash = -1;
 	options->update_hostkeys = -1;
diff --git a/readconf.h b/readconf.h
index cef55f7..d745602 100644
--- a/readconf.h
+++ b/readconf.h
@@ -169,6 +169,9 @@ typedef struct {
 	char   *jump_extra;
 
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
+#ifdef TCP_STEALTH
+	char	*tcp_stealth_secret;
+#endif
 }       Options;
 
 #define SSH_CANONICALISE_NO	0
diff --git a/servconf.c b/servconf.c
index 873b0d0..4861e65 100644
--- a/servconf.c
+++ b/servconf.c
@@ -168,6 +168,9 @@ initialize_server_options(ServerOptions *options)
 	options->ip_qos_interactive = -1;
 	options->ip_qos_bulk = -1;
 	options->version_addendum = NULL;
+#ifdef TCP_STEALTH
+	options->tcp_stealth_secret = NULL;
+#endif
 	options->fingerprint_hash = -1;
 }
 
@@ -438,6 +441,9 @@ typedef enum {
 	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
 	sAllowStreamLocalForwarding, sFingerprintHash,
+#ifdef TCP_STEALTH
+	sTCPStealthSecret,
+#endif
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
@@ -579,6 +585,9 @@ static struct {
 	{ "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
 	{ "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
 	{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
+#ifdef TCP_STEALTH
+	{ "tcpstealthsecret", sTCPStealthSecret },
+#endif
 	{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
 	{ NULL, sBadOption, 0 }
 };
@@ -1879,6 +1888,24 @@ process_server_config_line(ServerOptions *options, char *line,
 			options->fingerprint_hash = value;
 		break;
 
+#ifdef TCP_STEALTH
+	case sTCPStealthSecret:
+		charptr = &options->tcp_stealth_secret;
+
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: Missing argument.",
+			    filename, linenum);
+
+		if (*activep && *charptr == NULL) {
+			*charptr = xmalloc(64 + 1);
+			memset(*charptr, 0x00, 64 + 1);
+			strncpy(*charptr, arg, 64);
+		}
+
+		break;
+#endif
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
diff --git a/servconf.h b/servconf.h
index f4137af..79471d3 100644
--- a/servconf.h
+++ b/servconf.h
@@ -194,6 +194,9 @@ typedef struct {
 	u_int	num_auth_methods;
 	char   *auth_methods[MAX_AUTH_METHODS];
 
+#ifdef TCP_STEALTH
+	char    *tcp_stealth_secret;
+#endif
 	int	fingerprint_hash;
 }       ServerOptions;
 
@@ -216,6 +219,12 @@ struct connection_info {
  * NB. an option must appear in servconf.c:copy_set_server_options() or
  * COPY_MATCH_STRING_OPTS here but never both.
  */
+#ifdef TCP_STEALTH
+#define M_CP_STEALTHSCRT(X)	M_CP_STROPT(X);
+#else
+#define M_CP_STEALTHSCRT(X)	
+#endif
+
 #define COPY_MATCH_STRING_OPTS() do { \
 		M_CP_STROPT(banner); \
 		M_CP_STROPT(trusted_user_ca_keys); \
@@ -234,6 +243,7 @@ struct connection_info {
 		M_CP_STRARRAYOPT(deny_groups, num_deny_groups); \
 		M_CP_STRARRAYOPT(accept_env, num_accept_env); \
 		M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \
+		M_CP_STEALTHSCRT(tcp_stealth_secret); \
 	} while (0)
 
 struct connection_info *get_connection_info(int, int);
diff --git a/ssh.0 b/ssh.0
index 67ce809..b94d040 100644
--- a/ssh.0
+++ b/ssh.0
@@ -433,6 +433,20 @@ DESCRIPTION
      -y      Send log information using the syslog(3) system module.  By
              default this information is sent to stderr.
 
+     -z tcp_stealth_secret
+             Specifies the shared secret which is needed to connect to a stealth
+             SSH TCP server. Any string specified will be truncated to or padded
+             with zeroes to 64 bytes. This option needs kernel support and is
+             therefore only available if the required setsockopt() call is
+             available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
+             IMPORTANT: This option should only be used for the purpose of
+             testing as other users could easily read out the secret from the
+             command line arguments. The TCPStealthSecret configuration option
+             is the preferred way of specifying the TCP Stealth secret.
+
      ssh may additionally obtain configuration data from a per-user
      configuration file and a system-wide configuration file.  The file format
      and configuration options are described in ssh_config(5).
diff --git a/ssh.1 b/ssh.1
index 4011c65..4a3b6d4 100644
--- a/ssh.1
+++ b/ssh.1
@@ -64,6 +64,7 @@
 .Op Fl S Ar ctl_path
 .Op Fl W Ar host : Ns Ar port
 .Op Fl w Ar local_tun Ns Op : Ns Ar remote_tun
+.Op Fl z Ar tcp_stealth_secret
 .Oo Ar user Ns @ Oc Ns Ar hostname
 .Op Ar command
 .Ek
@@ -558,6 +559,7 @@ For full details of the options listed below, and their possible values, see
 .It StreamLocalBindUnlink
 .It StrictHostKeyChecking
 .It TCPKeepAlive
+.It TCPStealthSecret
 .It Tunnel
 .It TunnelDevice
 .It UpdateHostKeys
@@ -798,6 +800,21 @@ Send log information using the
 .Xr syslog 3
 system module.
 By default this information is sent to stderr.
+.It Fl z Ar tcp_stealth_secret
+Specifies the shared secret which is needed to connect to a stealth SSH TCP
+server. Any string specified will be truncated to or padded with zeroes to 64
+bytes. This option needs kernel support and is therefore only available if the
+required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+.Pp
+.Cm IMPORTANT:
+This option should only be used for the purpose of testing as other users could
+easily read out the secret from the command line arguments. The
+.Cm TCPStealthSecret
+configuration option is the preferred way of specifying the TCP Stealth secret.
 .El
 .Pp
 .Nm
diff --git a/ssh.c b/ssh.c
index 03a23fb..52be542 100644
--- a/ssh.c
+++ b/ssh.c
@@ -191,6 +191,14 @@ static int remote_forward_confirms_received = 0;
 extern int muxserver_sock;
 extern u_int muxclient_command;
 
+#ifdef TCP_STEALTH
+#define OPT_STEALTH	"[-z tcp_stealth_secret] "
+#define GETOPT_STEALTH	"z:"
+#else
+#define OPT_STEALTH	""
+#define GETOPT_STEALTH	""
+#endif
+
 /* Prints a help message to the user.  This function never returns. */
 
 static void
@@ -203,7 +211,7 @@ usage(void)
 "           [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n"
 "           [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n"
 "           [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
-"           [user@]hostname [command]\n"
+"           " OPT_STEALTH "[user@]hostname [command]\n"
 	);
 	exit(255);
 }
@@ -609,7 +617,7 @@ main(int ac, char **av)
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
+	    "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy" GETOPT_STEALTH)) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -932,6 +940,12 @@ main(int ac, char **av)
 		case 'F':
 			config = optarg;
 			break;
+#ifdef TCP_STEALTH
+		case 'z':
+			options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t));
+			strncpy(options.tcp_stealth_secret, optarg, 64);
+			break;
+#endif
 		default:
 			usage();
 		}
diff --git a/ssh_config.0 b/ssh_config.0
index 8733281..71e5326 100644
--- a/ssh_config.0
+++ b/ssh_config.0
@@ -965,6 +965,15 @@ DESCRIPTION
              To disable TCP keepalive messages, the value should be set to
              M-bM-^@M-^\noM-bM-^@M-^].
 
+     TCPStealthSecret
+             Specifies the shared secret which is needed to connect to a stealth
+             SSH TCP Server. Any string specified will be truncated to or padded
+             with zeroes to 64 bytes. This option needs kernel support and is
+             therefore only available if the required setsockopt() call is
+             available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
      Tunnel  Request tun(4) device forwarding between the client and the
              server.  The argument must be M-bM-^@M-^\yesM-bM-^@M-^], M-bM-^@M-^\point-to-pointM-bM-^@M-^] (layer 3),
              M-bM-^@M-^\ethernetM-bM-^@M-^] (layer 2), or M-bM-^@M-^\noM-bM-^@M-^].  Specifying M-bM-^@M-^\yesM-bM-^@M-^] requests the
diff --git a/ssh_config.5 b/ssh_config.5
index 7630e7b..43ba7b2 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -1670,6 +1670,15 @@ This is important in scripts, and many users want it too.
 .Pp
 To disable TCP keepalive messages, the value should be set to
 .Dq no .
+.It Cm TCPStealthSecret
+Specifies the shared secret which is needed to connect to a stealth SSH TCP
+Server. Any string specified will be truncated to or padded with zeroes to 64
+bytes. This option needs kernel support and is therefore only available if the
+required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
 .It Cm Tunnel
 Request
 .Xr tun 4
diff --git a/sshconnect.c b/sshconnect.c
index 356ec79..807aa2e 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -283,6 +283,17 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
 	}
 	fcntl(sock, F_SETFD, FD_CLOEXEC);
 
+#ifdef TCP_STEALTH
+	if (options.tcp_stealth_secret) {
+		if (setsockopt(sock, IPPROTO_TCP, TCP_STEALTH,
+			       options.tcp_stealth_secret, 64) == -1) {
+			error("setsockopt TCP_STEALTH: %s", strerror(errno));
+			close(sock);
+			return -1;
+		}
+	}
+#endif
+
 	/* Bind the socket to an alternative local IP address */
 	if (options.bind_address == NULL && !privileged)
 		return sock;
diff --git a/sshd.0 b/sshd.0
index 0f7db5f..a7c6b2c 100644
--- a/sshd.0
+++ b/sshd.0
@@ -7,7 +7,7 @@ SYNOPSIS
      sshd [-46DdeiqTt] [-b bits] [-C connection_spec]
           [-c host_certificate_file] [-E log_file] [-f config_file]
           [-g login_grace_time] [-h host_key_file] [-k key_gen_time]
-          [-o option] [-p port] [-u len]
+          [-o option] [-p port] [-u len] [-z tcp_stealth_secret]
 
 DESCRIPTION
      sshd (OpenSSH Daemon) is the daemon program for ssh(1).  Together these
@@ -143,6 +143,21 @@ DESCRIPTION
              in a key file.  Configuration options that require DNS include
              using a USER@HOST pattern in AllowUsers or DenyUsers.
 
+     -z tcp_stealth_secret
+             Turns this SSH server into a Stealth SSH TCP Server. This option
+             specifies the shared secret which is needed by the clients in order
+             to be able to connect to the port the SSH server is listening on.
+             Any string specified will be truncated or padded with zeroes to 64
+             bytes. This option needs kernel support and is therefore only
+             available if the required setsockopt() call is available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
+             IMPORTANT: This option should only be used for the purpose of
+             testing as other users could easily read out the secret from the
+             command line arguments. The TCPStealthSecret configuration option
+             is the preferred way of specifying the TCP Stealth secret.
+
 AUTHENTICATION
      The OpenSSH SSH daemon supports SSH protocols 1 and 2.  The default is to
      use protocol 2 only, though this can be changed via the Protocol option
diff --git a/sshd.8 b/sshd.8
index 6c521f2..47247a3 100644
--- a/sshd.8
+++ b/sshd.8
@@ -55,6 +55,7 @@
 .Op Fl o Ar option
 .Op Fl p Ar port
 .Op Fl u Ar len
+.Op Fl z Ar tcp_stealth_secret
 .Ek
 .Sh DESCRIPTION
 .Nm
@@ -267,6 +268,24 @@ USER@HOST pattern in
 .Cm AllowUsers
 or
 .Cm DenyUsers .
+.It Fl z Ar tcp_stealth_secret
+Turns this SSH server into a stealth SSH TCP server. This option specifies the
+shared secret which is needed by the clients in order to be able to connect to
+the port the SSH server is listening on.  Any string specified will be truncated
+or padded with zeroes to 64 bytes. This option needs kernel support and is
+therefore only available if the required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+
+.Cm IMPORTANT:
+This option should only be used for the purpose of
+testing as other users could easily read out the secret from the
+command line arguments. The
+.Cm TCPStealthSecret
+configuration option
+is the preferred way of specifying the TCP Stealth secret.
 .El
 .Sh AUTHENTICATION
 The OpenSSH SSH daemon supports SSH protocols 1 and 2.
diff --git a/sshd.c b/sshd.c
index 799c771..8ae854a 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1006,6 +1006,14 @@ drop_connection(int startups)
 	return (r < p) ? 1 : 0;
 }
 
+#ifdef TCP_STEALTH
+#define OPT_STEALTH	" [-z tcp_stealth_secret]"
+#define GETOPT_STEALTH	"z:"
+#else
+#define OPT_STEALTH	""
+#define GETOPT_STEALTH	""
+#endif
+
 static void
 usage(void)
 {
@@ -1021,7 +1029,7 @@ usage(void)
 "usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
 "            [-E log_file] [-f config_file] [-g login_grace_time]\n"
 "            [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n"
-"            [-u len]\n"
+"            [-u len]" OPT_STEALTH "\n"
 	);
 	exit(1);
 }
@@ -1210,6 +1218,14 @@ server_listen(void)
 		if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
 		    &on, sizeof(on)) == -1)
 			error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+#ifdef TCP_STEALTH
+		if (options.tcp_stealth_secret != NULL) {
+			if (setsockopt(listen_sock, IPPROTO_TCP, TCP_STEALTH,
+			    options.tcp_stealth_secret, 64) == -1)
+				error("setsockopt TCP_STEALTH: %s",
+				      strerror(errno));
+		}
+#endif
 
 		/* Only communicate in IPv6 over AF_INET6 sockets. */
 		if (ai->ai_family == AF_INET6)
@@ -1567,7 +1583,8 @@ main(int ac, char **av)
 
 	/* Parse command-line arguments. */
 	while ((opt = getopt(ac, av,
-	    "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) {
+	    "z:C:E:b:c:f:g:h:k:o:p:u:" \
+	    "46DQRTdeiqrt" GETOPT_STEALTH)) != -1) {
 		switch (opt) {
 		case '4':
 			options.address_family = AF_INET;
@@ -1679,6 +1696,12 @@ main(int ac, char **av)
 				exit(1);
 			free(line);
 			break;
+#ifdef TCP_STEALTH
+		case 'z':
+			options.tcp_stealth_secret = xcalloc(64 + 1, sizeof(u_int8_t));
+			strncpy(options.tcp_stealth_secret, optarg, 64);
+			break;
+#endif
 		case '?':
 		default:
 			usage();
diff --git a/sshd_config.0 b/sshd_config.0
index 85379dc..5bd072e 100644
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -918,6 +918,19 @@ DESCRIPTION
              To disable TCP keepalive messages, the value should be set to
              M-bM-^@M-^\noM-bM-^@M-^].
 
+     TCPStealthSecret
+             Turns this SSH server into a stealth SSH TCP server. This
+             configuration option specifies the shared secret needed by the
+             clients in order to be able to connect to the port the SSH server
+             is listening on. This means that port scanners will receive a
+             TCP RST and thus will not recognize this TCP port being open.
+
+             Any string specified will be truncated or padded with zeroes to 64
+             bytes. This option needs kernel support and is therefore only
+             available if the required setsockopt() call is available.
+             See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/
+             for details.
+
      TrustedUserCAKeys
              Specifies a file containing public keys of certificate
              authorities that are trusted to sign user certificates for
diff --git a/sshd_config.5 b/sshd_config.5
index 1bc26ec..7551a44 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -1520,6 +1520,18 @@ This avoids infinitely hanging sessions.
 .Pp
 To disable TCP keepalive messages, the value should be set to
 .Dq no .
+.It Cm TCPStealthSecret
+Turns this SSH server into a stealth SSH TCP server. This configuration option
+specifies the shared secret needed by the clients in order to be able to connect
+to the port the SSH server is listening on. This means that port scanners will
+receive a TCP RST and thus will not recognize this TCP port being open.  Any
+string specified will be truncated or padded with zeroes to 64 bytes. This
+option needs kernel support and is therefore only available if the required
+.Xr setsockopt 2
+call is available.
+.Pp
+See http://datatracker.ietf.org/doc/draft-kirsch-ietf-tcp-stealth/ for details.
+
 .It Cm TrustedUserCAKeys
 Specifies a file containing public keys of certificate authorities that are
 trusted to sign user certificates for authentication, or