CVE-2022-46174: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: efs-utils is a set of Utilities for Amazon Elastic File System (EFS). A potential race condition issue exists within the Amazon EFS mount helper in efs-utils versions v1.34.3 and below. When using TLS to mount file systems, the mount helper allocates a local port for stunnel to receive NFS connections prior to applying the TLS tunnel. In affected versions, concurrent mount operations can allocate the same local port, leading to either failed mount operations or an inappropriate mapping from an EFS customer’s local mount points to that customer’s EFS file systems. This issue is patched in version v1.34.4. There is no recommended work around. We recommend affected users update the installed version of efs-utils to v1.34.4 or later.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 4.2
Published: 2022-12-28T07:15Z
Repository: https://github.com/aws/efs-utils
Language: Python
Files changed: amazon-efs-utils.spec, build-deb.sh, config.ini, dist/amazon-efs-utils.control, src/mount_efs/__init__.py, src/watchdog/__init__.py, test/mount_efs_test/test_bootstrap_tls.py, test/mount_efs_test/test_choose_tls_port.py
Commit date: 2022-12-14 06:13:07 +0000
Commit message: Fix potential tlsport selection collision by using state file as
tlsport lock file

This removes all potential concurrency issues during tls port selection
and ensures that concurrent mounts will never select the same port.

Diff: diff --git a/amazon-efs-utils.spec b/amazon-efs-utils.spec
index 2a33e77..35ad03d 100644
--- a/amazon-efs-utils.spec
+++ b/amazon-efs-utils.spec
@@ -33,11 +33,11 @@
 %else
 %global efs_bindir /sbin
 %endif
 
 Name      : amazon-efs-utils
-Version   : 1.34.3
+Version   : 1.34.4
 Release   : 1%{platform}
 Summary   : This package provides utilities for simplifying the use of EFS file systems
 
 Group     : Amazon/Tools
 License   : MIT
@@ -135,10 +135,13 @@ fi
 %endif
 
 %clean
 
 %changelog
+* Tue Dec 13 2022 Ryan Stankiewicz <rjstank@amazon.com> - 1.34.4
+- Fix potential tlsport selection collision by using state file as tlsport lock file.
+
 * Thu Dec 1 2022 Preetham Puneeth Munipalli <tmunipre@amazon.com> - 1.34.3
 - Fix potential tlsport selection race condition by closing socket right before establishing stunnel
 - Fix stunnel constantly restart issue when upgrading from 1.32.1 and before version to latest version
 - Speed up the way to check network availability by using systemctl is-active
 
diff --git a/build-deb.sh b/build-deb.sh
index 29780e6..06f6c3c 100755
--- a/build-deb.sh
+++ b/build-deb.sh
@@ -9,11 +9,11 @@
 
 set -ex
 
 BASE_DIR=$(pwd)
 BUILD_ROOT=${BASE_DIR}/build/debbuild
-VERSION=1.34.3
+VERSION=1.34.4
 RELEASE=1
 DEB_SYSTEM_RELEASE_PATH=/etc/os-release
 
 echo 'Cleaning deb build workspace'
 rm -rf ${BUILD_ROOT}
diff --git a/config.ini b/config.ini
index 7ec49a9..cb5b7ed 100644
--- a/config.ini
+++ b/config.ini
@@ -5,7 +5,7 @@
 # for the specific language governing permissions and limitations under
 # the License.
 #
 
 [global]
-version=1.34.3
+version=1.34.4
 release=1
diff --git a/dist/amazon-efs-utils.control b/dist/amazon-efs-utils.control
index 89bfdb6..35daa5a 100644
--- a/dist/amazon-efs-utils.control
+++ b/dist/amazon-efs-utils.control
@@ -1,8 +1,8 @@
 Package: amazon-efs-utils
 Architecture: all
-Version: 1.34.3
+Version: 1.34.4
 Section: utils
 Depends: python3, nfs-common, stunnel4 (>= 4.56), openssl (>= 1.0.2), util-linux
 Priority: optional
 Copyright: MIT License
 Maintainer: Amazon.com, Inc. <efs-utils@amazon.com>
diff --git a/src/mount_efs/__init__.py b/src/mount_efs/__init__.py
index c6e655c..a8ab583 100755
--- a/src/mount_efs/__init__.py
+++ b/src/mount_efs/__init__.py
@@ -83,11 +83,11 @@ try:
     BOTOCORE_PRESENT = True
 except ImportError:
     BOTOCORE_PRESENT = False
 
 
-VERSION = "1.34.3"
+VERSION = "1.34.4"
 SERVICE = "elasticfilesystem"
 
 AMAZON_LINUX_2_RELEASE_ID = "Amazon Linux release 2 (Karoo)"
 AMAZON_LINUX_2_PRETTY_NAME = "Amazon Linux 2"
 AMAZON_LINUX_2_RELEASE_VERSIONS = [
@@ -937,11 +937,11 @@ def get_tls_port_range(config):
         )
 
     return lower_bound, upper_bound
 
 
-def choose_tls_port_and_get_bind_sock(config, options):
+def choose_tls_port_and_get_bind_sock(config, options, state_file_dir):
     if "tlsport" in options:
         ports_to_try = [int(options["tlsport"])]
     else:
         lower_bound, upper_bound = get_tls_port_range(config)
 
@@ -949,14 +949,18 @@ def choose_tls_port_and_get_bind_sock(config, options):
 
         # shuffle the ports_to_try to reduce possibility of multiple mounts starting from same port range
         random.shuffle(ports_to_try)
 
     if "netns" not in options:
-        tls_port_sock = find_tls_port_in_range_and_get_bind_sock(ports_to_try)
+        tls_port_sock = find_tls_port_in_range_and_get_bind_sock(
+            ports_to_try, state_file_dir
+        )
     else:
         with NetNS(nspath=options["netns"]):
-            tls_port_sock = find_tls_port_in_range_and_get_bind_sock(ports_to_try)
+            tls_port_sock = find_tls_port_in_range_and_get_bind_sock(
+                ports_to_try, state_file_dir
+            )
 
     if tls_port_sock:
         return tls_port_sock
 
     if "tlsport" in options:
@@ -969,24 +973,48 @@ def choose_tls_port_and_get_bind_sock(config, options):
             "Failed to locate an available port in the range [%d, %d], try specifying a different port range in %s"
             % (lower_bound, upper_bound, CONFIG_FILE)
         )
 
 
-def find_tls_port_in_range_and_get_bind_sock(ports_to_try):
+def find_tls_port_in_range_and_get_bind_sock(ports_to_try, state_file_dir):
     sock = socket.socket()
     for tls_port in ports_to_try:
+        mount = find_existing_mount_using_tls_port(state_file_dir, tls_port)
+        if mount:
+            logging.debug(
+                "Skip binding TLS port %s as it is already assigned to %s",
+                tls_port,
+                mount,
+            )
+            continue
         try:
             logging.info("binding %s", tls_port)
             sock.bind(("localhost", tls_port))
             return sock
         except socket.error as e:
-            logging.info(e)
+            logging.warning(e)
             continue
     sock.close()
     return None
 
 
+def find_existing_mount_using_tls_port(state_file_dir, tls_port):
+    if not os.path.exists(state_file_dir):
+        logging.debug(
+            "State file dir %s does not exist, assuming no existing mount using tls port %s",
+            state_file_dir,
+            tls_port,
+        )
+        return None
+
+    for fname in os.listdir(state_file_dir):
+        if fname.endswith(".%s" % tls_port):
+            return fname
+
+    return None
+
+
 def is_ocsp_enabled(config, options):
     if "ocsp" in options:
         return True
     elif "noocsp" in options:
         return False
@@ -1299,10 +1327,28 @@ def write_tls_tunnel_state_file(
         json.dump(state, f)
 
     return state_file
 
 
+def rewrite_tls_tunnel_state_file(state, state_file_dir, state_file):
+    with open(os.path.join(state_file_dir, state_file), "w") as f:
+        json.dump(state, f)
+    return state_file
+
+
+def update_tls_tunnel_temp_state_file_with_tunnel_pid(
+    temp_tls_state_file, state_file_dir, stunnel_pid
+):
+    with open(os.path.join(state_file_dir, temp_tls_state_file), "r") as f:
+        state = json.load(f)
+    state["pid"] = stunnel_pid
+    temp_tls_state_file = rewrite_tls_tunnel_state_file(
+        state, state_file_dir, temp_tls_state_file
+    )
+    return temp_tls_state_file
+
+
 def test_tunnel_process(tunnel_proc, fs_id):
     tunnel_proc.poll()
     if tunnel_proc.returncode is not None:
         _, err = tunnel_proc.communicate()
         fatal_error(
@@ -1476,11 +1522,11 @@ def bootstrap_tls(
     mountpoint,
     options,
     state_file_dir=STATE_FILE_DIR,
     fallback_ip_address=None,
 ):
-    tls_port_sock = choose_tls_port_and_get_bind_sock(config, options)
+    tls_port_sock = choose_tls_port_and_get_bind_sock(config, options, state_file_dir)
     tls_port = get_tls_port_from_sock(tls_port_sock)
 
     try:
         # override the tlsport option so that we can later override the port the NFS client uses to connect to stunnel.
         # if the user has specified tlsport=X at the command line this will just re-set tlsport to X.
@@ -1558,10 +1604,22 @@ def bootstrap_tls(
             fallback_ip_address=fallback_ip_address,
         )
         tunnel_args = [_stunnel_bin(), stunnel_config_file]
         if "netns" in options:
             tunnel_args = ["nsenter", "--net=" + options["netns"]] + tunnel_args
+
+        # This temp state file is acting like a tlsport lock file, which is why pid =- 1
+        temp_tls_state_file = write_tls_tunnel_state_file(
+            fs_id,
+            mountpoint,
+            tls_port,
+            -1,
+            tunnel_args,
+            [stunnel_config_file],
+            state_file_dir,
+            cert_details=cert_details,
+        )
     finally:
         # Always close the socket we created when choosing TLS port only until now to
         # 1. avoid concurrent TLS mount port collision 2. enable stunnel process to bind the port
         logging.debug("Closing socket used to choose TLS port %s.", tls_port)
         tls_port_sock.close()
@@ -1575,19 +1633,12 @@ def bootstrap_tls(
         preexec_fn=os.setsid,
         close_fds=True,
     )
     logging.info("Started TLS tunnel, pid: %d", tunnel_proc.pid)
 
-    temp_tls_state_file = write_tls_tunnel_state_file(
-        fs_id,
-        mountpoint,
-        tls_port,
-        tunnel_proc.pid,
-        tunnel_args,
-        [stunnel_config_file],
-        state_file_dir,
-        cert_details=cert_details,
+    update_tls_tunnel_temp_state_file_with_tunnel_pid(
+        temp_tls_state_file, state_file_dir, tunnel_proc.pid
     )
 
     if "netns" not in options:
         test_tlsport(options["tlsport"])
     else:
diff --git a/src/watchdog/__init__.py b/src/watchdog/__init__.py
index be99a21..b2ed27f 100755
--- a/src/watchdog/__init__.py
+++ b/src/watchdog/__init__.py
@@ -54,11 +54,11 @@ AMAZON_LINUX_2_RELEASE_ID = "Amazon Linux release 2 (Karoo)"
 AMAZON_LINUX_2_PRETTY_NAME = "Amazon Linux 2"
 AMAZON_LINUX_2_RELEASE_VERSIONS = [
     AMAZON_LINUX_2_RELEASE_ID,
     AMAZON_LINUX_2_PRETTY_NAME,
 ]
-VERSION = "1.34.3"
+VERSION = "1.34.4"
 SERVICE = "elasticfilesystem"
 
 CONFIG_FILE = "/etc/amazon/efs/efs-utils.conf"
 CONFIG_SECTION = "mount-watchdog"
 MOUNT_CONFIG_SECTION = "mount"
diff --git a/test/mount_efs_test/test_bootstrap_tls.py b/test/mount_efs_test/test_bootstrap_tls.py
index d39c10a..e306076 100644
--- a/test/mount_efs_test/test_bootstrap_tls.py
+++ b/test/mount_efs_test/test_bootstrap_tls.py
@@ -42,10 +42,14 @@ def setup_mocks(mocker):
     mocker.patch("mount_efs.get_target_region", return_value=REGION)
     mocker.patch("mount_efs.write_tls_tunnel_state_file", return_value="~mocktempfile")
     mocker.patch("mount_efs.create_certificate")
     mocker.patch("os.rename")
     mocker.patch("os.kill")
+    mocker.patch(
+        "mount_efs.update_tls_tunnel_temp_state_file_with_tunnel_pid",
+        return_value="~mocktempfile",
+    )
 
     process_mock = MagicMock()
     process_mock.communicate.return_value = (
         "stdout",
         "stderr",
@@ -70,10 +74,14 @@ def setup_mocks_without_popen(mocker):
         "mount_efs.get_dns_name_and_fallback_mount_target_ip_address",
         return_value=(DNS_NAME, None),
     )
     mocker.patch("mount_efs.write_tls_tunnel_state_file", return_value="~mocktempfile")
     mocker.patch("os.kill")
+    mocker.patch(
+        "mount_efs.update_tls_tunnel_temp_state_file_with_tunnel_pid",
+        return_value="~mocktempfile",
+    )
 
     write_config_mock = mocker.patch(
         "mount_efs.write_stunnel_config_file", return_value=EXPECTED_STUNNEL_CONFIG_FILE
     )
     return write_config_mock
@@ -113,10 +121,11 @@ def test_bootstrap_tls_state_file_nonexistent_dir(mocker, tmpdir):
     MOCK_CONFIG.get.side_effect = config_get_side_effect
 
     assert not os.path.exists(state_file_dir)
 
     mocker.patch("mount_efs._stunnel_bin", return_value="/usr/bin/stunnel")
+    mocker.patch("mount_efs.find_existing_mount_using_tls_port", return_value=None)
     with mount_efs.bootstrap_tls(
         MOCK_CONFIG, INIT_SYSTEM, DNS_NAME, FS_ID, MOUNT_POINT, {}, state_file_dir
     ):
         pass
 
diff --git a/test/mount_efs_test/test_choose_tls_port.py b/test/mount_efs_test/test_choose_tls_port.py
index b5f085f..da6e62f 100644
--- a/test/mount_efs_test/test_choose_tls_port.py
+++ b/test/mount_efs_test/test_choose_tls_port.py
@@ -1,13 +1,16 @@
 # Copyright 2017-2018 Amazon.com, Inc. and its affiliates. All Rights Reserved.
 #
 # Licensed under the MIT License. See the LICENSE accompanying this file
 # for the specific language governing permissions and limitations under
 # the License.
-
+import logging
 import random
 import socket
+import sys
+import tempfile
+import unittest
 from unittest.mock import MagicMock
 
 import pytest
 
 import mount_efs
@@ -43,46 +46,74 @@ def _get_config():
         str(DEFAULT_TLS_PORT_RANGE_HIGH),
     )
     return config
 
 
-def test_choose_tls_port_first_try(mocker):
+def test_choose_tls_port_first_try(mocker, tmpdir):
     sock_mock = MagicMock()
     sock_mock.getsockname.return_value = ("local_host", DEFAULT_TLS_PORT)
     mocker.patch("socket.socket", return_value=sock_mock)
     options = {}
 
-    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
+        _get_config(), options, str(tmpdir)
+    )
     tls_port = mount_efs.get_tls_port_from_sock(tls_port_sock)
     assert DEFAULT_TLS_PORT_RANGE_LOW <= tls_port <= DEFAULT_TLS_PORT_RANGE_HIGH
 
 
-def test_choose_tls_port_second_try(mocker):
+def test_choose_tls_port_second_try(mocker, tmpdir):
     bad_sock = MagicMock()
     bad_sock.bind.side_effect = [socket.error, None]
     bad_sock.getsockname.return_value = ("local_host", DEFAULT_TLS_PORT)
     options = {}
 
     mocker.patch("socket.socket", return_value=bad_sock)
 
-    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
+        _get_config(), options, str(tmpdir)
+    )
     tls_port = mount_efs.get_tls_port_from_sock(tls_port_sock)
 
     assert DEFAULT_TLS_PORT_RANGE_LOW <= tls_port <= DEFAULT_TLS_PORT_RANGE_HIGH
     assert 2 == bad_sock.bind.call_count
     assert 1 == bad_sock.getsockname.call_count
 
 
-def test_choose_tls_port_never_succeeds(mocker, capsys):
+@unittest.skipIf(sys.version_info < (3, 6), reason="requires python3.6")
+def test_choose_tls_port_collision(mocker, tmpdir, caplog):
+    """Ensure we don't choose a port that is pending mount"""
+    sock = MagicMock()
+    mocker.patch("socket.socket", return_value=sock)
+    mocker.patch(
+        "random.shuffle",
+        return_value=range(DEFAULT_TLS_PORT_RANGE_LOW, DEFAULT_TLS_PORT_RANGE_HIGH),
+    )
+
+    port_suffix = ".%s" % str(DEFAULT_TLS_PORT_RANGE_LOW)
+    temp_state_file = tempfile.NamedTemporaryFile(
+        suffix=port_suffix, prefix="~", dir=tmpdir
+    )
+
+    options = {}
+    with caplog.at_level(logging.DEBUG):
+        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, tmpdir)
+
+    temp_state_file.close()
+    sock.bind.assert_called_once_with(("localhost", DEFAULT_TLS_PORT_RANGE_LOW + 1))
+    assert "Skip binding TLS port" in caplog.text
+
+
+def test_choose_tls_port_never_succeeds(mocker, tmpdir, capsys):
     bad_sock = MagicMock()
     bad_sock.bind.side_effect = socket.error()
     options = {}
 
     mocker.patch("socket.socket", return_value=bad_sock)
 
     with pytest.raises(SystemExit) as ex:
-        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))
 
     assert 0 != ex.value.code
 
     out, err = capsys.readouterr()
     assert "Failed to locate an available port" in err
@@ -91,47 +122,49 @@ def test_choose_tls_port_never_succeeds(mocker, capsys):
         DEFAULT_TLS_PORT_RANGE_HIGH - DEFAULT_TLS_PORT_RANGE_LOW
         == bad_sock.bind.call_count
     )
 
 
-def test_choose_tls_port_option_specified(mocker):
+def test_choose_tls_port_option_specified(mocker, tmpdir):
     sock_mock = MagicMock()
     sock_mock.getsockname.return_value = ("local_host", DEFAULT_TLS_PORT)
     mocker.patch("socket.socket", return_value=sock_mock)
     options = {"tlsport": DEFAULT_TLS_PORT}
 
-    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
+        _get_config(), options, str(tmpdir)
+    )
     tls_port = mount_efs.get_tls_port_from_sock(tls_port_sock)
 
     assert DEFAULT_TLS_PORT == tls_port
 
 
-def test_choose_tls_port_option_specified_unavailable(mocker, capsys):
+def test_choose_tls_port_option_specified_unavailable(mocker, tmpdir, capsys):
     bad_sock = MagicMock()
     bad_sock.bind.side_effect = socket.error()
     options = {"tlsport": 1000}
 
     mocker.patch("socket.socket", return_value=bad_sock)
 
     with pytest.raises(SystemExit) as ex:
-        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))
 
     assert 0 != ex.value.code
 
     out, err = capsys.readouterr()
     assert "Specified port [1000] is unavailable" in err
 
     assert 1 == bad_sock.bind.call_count
 
 
-def test_choose_tls_port_under_netns(mocker, capsys):
+def test_choose_tls_port_under_netns(mocker, tmpdir):
     mocker.patch("builtins.open")
     setns_mock = mocker.patch("mount_efs.setns", return_value=(None, None))
     mocker.patch("socket.socket", return_value=MagicMock())
     options = {"netns": "/proc/1000/ns/net"}
 
-    mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
+    mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))
     utils.assert_called(setns_mock)
 
 
 def test_verify_tls_port(mocker):
     sock = MagicMock()

Vulnerable code: Version   : 1.34.3
VERSION=1.34.3
version=1.34.3
Version: 1.34.3
VERSION = "1.34.3"
def choose_tls_port_and_get_bind_sock(config, options):
        tls_port_sock = find_tls_port_in_range_and_get_bind_sock(ports_to_try)
            tls_port_sock = find_tls_port_in_range_and_get_bind_sock(ports_to_try)
def find_tls_port_in_range_and_get_bind_sock(ports_to_try):
            logging.info(e)
    tls_port_sock = choose_tls_port_and_get_bind_sock(config, options)
    temp_tls_state_file = write_tls_tunnel_state_file(
        fs_id,
        mountpoint,
        tls_port,
        tunnel_proc.pid,
        tunnel_args,
        [stunnel_config_file],
        state_file_dir,
        cert_details=cert_details,
VERSION = "1.34.3"

def test_choose_tls_port_first_try(mocker):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
def test_choose_tls_port_second_try(mocker):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
def test_choose_tls_port_never_succeeds(mocker, capsys):
        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
def test_choose_tls_port_option_specified(mocker):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
def test_choose_tls_port_option_specified_unavailable(mocker, capsys):
        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
def test_choose_tls_port_under_netns(mocker, capsys):
    mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options)
Fixed code: Version   : 1.34.4
* Tue Dec 13 2022 Ryan Stankiewicz <rjstank@amazon.com> - 1.34.4
- Fix potential tlsport selection collision by using state file as tlsport lock file.

VERSION=1.34.4
version=1.34.4
Version: 1.34.4
VERSION = "1.34.4"
def choose_tls_port_and_get_bind_sock(config, options, state_file_dir):
        tls_port_sock = find_tls_port_in_range_and_get_bind_sock(
            ports_to_try, state_file_dir
        )
            tls_port_sock = find_tls_port_in_range_and_get_bind_sock(
                ports_to_try, state_file_dir
            )
def find_tls_port_in_range_and_get_bind_sock(ports_to_try, state_file_dir):
        mount = find_existing_mount_using_tls_port(state_file_dir, tls_port)
        if mount:
            logging.debug(
                "Skip binding TLS port %s as it is already assigned to %s",
                tls_port,
                mount,
            )
            continue
            logging.warning(e)
def find_existing_mount_using_tls_port(state_file_dir, tls_port):
    if not os.path.exists(state_file_dir):
        logging.debug(
            "State file dir %s does not exist, assuming no existing mount using tls port %s",
            state_file_dir,
            tls_port,
        )
        return None

    for fname in os.listdir(state_file_dir):
        if fname.endswith(".%s" % tls_port):
            return fname

    return None


def rewrite_tls_tunnel_state_file(state, state_file_dir, state_file):
    with open(os.path.join(state_file_dir, state_file), "w") as f:
        json.dump(state, f)
    return state_file


def update_tls_tunnel_temp_state_file_with_tunnel_pid(
    temp_tls_state_file, state_file_dir, stunnel_pid
):
    with open(os.path.join(state_file_dir, temp_tls_state_file), "r") as f:
        state = json.load(f)
    state["pid"] = stunnel_pid
    temp_tls_state_file = rewrite_tls_tunnel_state_file(
        state, state_file_dir, temp_tls_state_file
    )
    return temp_tls_state_file


    tls_port_sock = choose_tls_port_and_get_bind_sock(config, options, state_file_dir)

        # This temp state file is acting like a tlsport lock file, which is why pid =- 1
        temp_tls_state_file = write_tls_tunnel_state_file(
            fs_id,
            mountpoint,
            tls_port,
            -1,
            tunnel_args,
            [stunnel_config_file],
            state_file_dir,
            cert_details=cert_details,
        )
    update_tls_tunnel_temp_state_file_with_tunnel_pid(
        temp_tls_state_file, state_file_dir, tunnel_proc.pid
VERSION = "1.34.4"
    mocker.patch(
        "mount_efs.update_tls_tunnel_temp_state_file_with_tunnel_pid",
        return_value="~mocktempfile",
    )
    mocker.patch(
        "mount_efs.update_tls_tunnel_temp_state_file_with_tunnel_pid",
        return_value="~mocktempfile",
    )
    mocker.patch("mount_efs.find_existing_mount_using_tls_port", return_value=None)
import logging
import sys
import tempfile
import unittest
def test_choose_tls_port_first_try(mocker, tmpdir):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
        _get_config(), options, str(tmpdir)
    )
def test_choose_tls_port_second_try(mocker, tmpdir):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
        _get_config(), options, str(tmpdir)
    )
@unittest.skipIf(sys.version_info < (3, 6), reason="requires python3.6")
def test_choose_tls_port_collision(mocker, tmpdir, caplog):
    """Ensure we don't choose a port that is pending mount"""
    sock = MagicMock()
    mocker.patch("socket.socket", return_value=sock)
    mocker.patch(
        "random.shuffle",
        return_value=range(DEFAULT_TLS_PORT_RANGE_LOW, DEFAULT_TLS_PORT_RANGE_HIGH),
    )

    port_suffix = ".%s" % str(DEFAULT_TLS_PORT_RANGE_LOW)
    temp_state_file = tempfile.NamedTemporaryFile(
        suffix=port_suffix, prefix="~", dir=tmpdir
    )

    options = {}
    with caplog.at_level(logging.DEBUG):
        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, tmpdir)

    temp_state_file.close()
    sock.bind.assert_called_once_with(("localhost", DEFAULT_TLS_PORT_RANGE_LOW + 1))
    assert "Skip binding TLS port" in caplog.text


def test_choose_tls_port_never_succeeds(mocker, tmpdir, capsys):
        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))
def test_choose_tls_port_option_specified(mocker, tmpdir):
    tls_port_sock = mount_efs.choose_tls_port_and_get_bind_sock(
        _get_config(), options, str(tmpdir)
    )
def test_choose_tls_port_option_specified_unavailable(mocker, tmpdir, capsys):
        mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))
def test_choose_tls_port_under_netns(mocker, tmpdir):
    mount_efs.choose_tls_port_and_get_bind_sock(_get_config(), options, str(tmpdir))