# Buggy code
def sign_in(self, timeout=60, safe=True, tries=1, channel=None):
    """
    Send a sign in request to the master, sets the key information and
    returns a dict containing the master publish interface to bind to
    and the decrypted aes key for transport decryption.

    :param int timeout: Number of seconds to wait before timing out the sign-in request
    :param bool safe: If True, do not raise an exception on timeout. Retry instead.
    :param int tries: The number of times to try to authenticate before giving up.

    :raises SaltReqTimeoutError: If the sign-in request has timed out and :param safe: is not set

    :return: Return a string on failure indicating the reason for failure. On success, return a dictionary
    with the publication port and the shared AES key.

    """
    auth = {}

    auth_timeout = self.opts.get("auth_timeout", None)
    if auth_timeout is not None:
        timeout = auth_timeout
    auth_safemode = self.opts.get("auth_safemode", None)
    if auth_safemode is not None:
        safe = auth_safemode
    auth_tries = self.opts.get("auth_tries", None)
    if auth_tries is not None:
        tries = auth_tries

    m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub)

    auth["master_uri"] = self.opts["master_uri"]

    if not channel:
        channel = salt.transport.client.ReqChannel.factory(self.opts, crypt="clear")

    sign_in_payload = self.minion_sign_in_payload()
    try:
        payload = channel.send(sign_in_payload, tries=tries, timeout=timeout)
    except SaltReqTimeoutError as e:
        if safe:
            log.warning("SaltReqTimeoutError: {0}".format(e))
            return "retry"
        raise SaltClientError("Attempt to authenticate with the salt master failed")

    if "load" in payload:
        if "ret" in payload["load"]:
            if not payload["load"]["ret"]:
                if self.opts["rejected_retry"]:
                    log.error(
                        "The Salt Master has rejected this minion's public "
                        "key.\nTo repair this issue, delete the public key "
                        "for this minion on the Salt Master.\nThe Salt "
                        "Minion will attempt to to re-authenicate."
                    )
                    return "retry"
                else:
                    log.critical(
                        "The Salt Master has rejected this minion's public "
                        "key!\nTo repair this issue, delete the public key "
                        "for this minion on the Salt Master and restart this "
                        "minion.\nOr restart the Salt Master in open mode to "
                        "clean out the keys. The Salt Minion will now exit."
                    )
                    sys.exit(salt.defaults.exitcodes.EX_OK)
            # has the master returned that its maxed out with minions?
            elif payload["load"]["ret"] == "full":
                return "full"
            else:
                log.error(
                    "The Salt Master has cached the public key for this "
                    "node. If this is the first time connecting to this master "
                    "then this key may need to be accepted using 'salt-key -a {0}' on "
                    "the salt master. This salt minion will wait for {1} seconds "
                    "before attempting to re-authenticate.".format(
                        self.opts["id"], self.opts["acceptance_wait_time"]
                    )
                )
                return "retry"
    auth["aes"] = self.verify_master(payload, master_pub="token" in sign_in_payload)
    if not auth["aes"]:
        log.critical(
            "The Salt Master server's public key did not authenticate!\n"
            "The master may need to be updated if it is a version of Salt "
            "lower than {0}, or\n"
            "If you are confident that you are connecting to a valid Salt "
            "Master, then remove the master public key and restart the "
            "Salt Minion.\nThe master public key can be found "
            "at:\n{1}".format(salt.version.__version__, m_pub_fn)
        )
        sys.exit(42)
    if self.opts.get("syndic_master", False):  # Is syndic
        syndic_finger = self.opts.get(
            "syndic_finger", self.opts.get("master_finger", False)
        )
        if syndic_finger:
            if (
                salt.utils.pem_finger(m_pub_fn, sum_type=self.opts["hash_type"])
                != syndic_finger
            ):
                self._finger_fail(syndic_finger, m_pub_fn)
    else:
        if self.opts.get("master_finger", False):
            if (
                salt.utils.pem_finger(m_pub_fn, sum_type=self.opts["hash_type"])
                != self.opts["master_finger"]
            ):
                self._finger_fail(self.opts["master_finger"], m_pub_fn)
    auth["publish_port"] = payload["publish_port"]
    return auth


# Traceback (SaltClientError)
[root@ch3ll-cent7 ~]# salt-minion -ldebug
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'
[DEBUG   ] Reading configuration from /etc/salt/minion.d/_schedule.conf
[DEBUG   ] Using cached minion ID from /etc/salt/minion_id: ch3ll-cent7.c7.saltstack.net
[DEBUG   ] Configuration file path: /etc/salt/minion
[WARNING ] Insecure logging configuration detected! Sensitive data may be logged.
[INFO    ] Setting up the Salt Minion "ch3ll-cent7.c7.saltstack.net"
[DEBUG   ] Created pidfile: /var/run/salt-minion.pid
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'
[DEBUG   ] Reading configuration from /etc/salt/minion.d/_schedule.conf
[WARNING ] IMPORTANT: Do not use md5 hashing algorithm! Please set "hash_type" to sha256 in Salt Minion config!
[INFO    ] The Salt Minion is starting up
[INFO    ] Minion is starting as user 'root'
[DEBUG   ] AsyncEventPublisher PUB socket URI: ipc:///var/run/salt/minion/minion_event_9126913ec3_pub.ipc
[DEBUG   ] AsyncEventPublisher PULL socket URI: ipc:///var/run/salt/minion/minion_event_9126913ec3_pull.ipc
[INFO    ] Starting pub socket on ipc:///var/run/salt/minion/minion_event_9126913ec3_pub.ipc
[INFO    ] Starting pull socket on ipc:///var/run/salt/minion/minion_event_9126913ec3_pull.ipc
[DEBUG   ] Minion 'ch3ll-cent7.c7.saltstack.net' trying to tune in
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'ch3ll-cent7.c7.saltstack.net', 'tcp://54.159.114.180:4506')
[DEBUG   ] Generated random reconnect delay between '1000ms' and '11000ms' (2882)
[DEBUG   ] Setting zmq_reconnect_ivl to '2882ms'
[DEBUG   ] Setting zmq_reconnect_ivl_max to '11000ms'
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'ch3ll-cent7.c7.saltstack.net', 'tcp://54.159.114.180:4506', 'clear')
[INFO    ] The Salt Minion is shut down
[WARNING ] /usr/lib/python2.7/site-packages/salt/scripts.py:83: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
log.error('Minion failed to start: {0}'.format(exc.message), exc_info=True)

[ERROR   ] Minion failed to start: Attempt to authenticate with the salt master failed
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/salt/scripts.py", line 81, in minion_process
minion.start()
File "/usr/lib/python2.7/site-packages/salt/cli/daemons.py", line 320, in start
self.minion.tune_in()
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 1699, in tune_in
self.sync_connect_master()
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 760, in sync_connect_master
raise six.reraise(*future_exception)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 767, in connect_master
master, self.pub_channel = yield self.eval_master(self.opts, self.timeout, self.safe)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 503, in eval_master
yield pub_channel.connect()
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/transport/zeromq.py", line 338, in connect
yield self.auth.authenticate()
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "<string>", line 3, in raise_exc_info
SaltClientError: Attempt to authenticate with the salt master failed
[WARNING ] ** Restarting minion **
[INFO    ] Sleeping random_reauth_delay of 6 seconds
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'

# Fixed code
def sign_in(self, timeout=60, safe=True, tries=1, channel=None):
    """
    Send a sign in request to the master, sets the key information and
    returns a dict containing the master publish interface to bind to
    and the decrypted aes key for transport decryption.

    :param int timeout: Number of seconds to wait before timing out the sign-in request
    :param bool safe: If True, do not raise an exception on timeout. Retry instead.
    :param int tries: The number of times to try to authenticate before giving up.

    :raises SaltReqTimeoutError: If the sign-in request has timed out and :param safe: is not set

    :return: Return a string on failure indicating the reason for failure. On success, return a dictionary
    with the publication port and the shared AES key.

    """
    auth = {}

    auth_timeout = self.opts.get("auth_timeout", None)
    if auth_timeout is not None:
        timeout = auth_timeout
    auth_safemode = self.opts.get("auth_safemode", None)
    if auth_safemode is not None:
        safe = auth_safemode
    auth_tries = self.opts.get("auth_tries", None)
    if auth_tries is not None:
        tries = auth_tries

    m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub)

    auth["master_uri"] = self.opts["master_uri"]

    if not channel:
        channel = salt.transport.client.ReqChannel.factory(self.opts, crypt="clear")

    sign_in_payload = self.minion_sign_in_payload()
    try:
        payload = channel.send(sign_in_payload, tries=tries, timeout=timeout)
    except SaltReqTimeoutError as e:
        if safe:
            log.warning("SaltReqTimeoutError: {0}".format(e))
            return "retry"
        raise SaltClientError(
            "Attempt to authenticate with the salt master failed with timeout error"
        )

    if "load" in payload:
        if "ret" in payload["load"]:
            if not payload["load"]["ret"]:
                if self.opts["rejected_retry"]:
                    log.error(
                        "The Salt Master has rejected this minion's public "
                        "key.\nTo repair this issue, delete the public key "
                        "for this minion on the Salt Master.\nThe Salt "
                        "Minion will attempt to to re-authenicate."
                    )
                    return "retry"
                else:
                    log.critical(
                        "The Salt Master has rejected this minion's public "
                        "key!\nTo repair this issue, delete the public key "
                        "for this minion on the Salt Master and restart this "
                        "minion.\nOr restart the Salt Master in open mode to "
                        "clean out the keys. The Salt Minion will now exit."
                    )
                    sys.exit(salt.defaults.exitcodes.EX_OK)
            # has the master returned that its maxed out with minions?
            elif payload["load"]["ret"] == "full":
                return "full"
            else:
                log.error(
                    "The Salt Master has cached the public key for this "
                    "node. If this is the first time connecting to this master "
                    "then this key may need to be accepted using 'salt-key -a {0}' on "
                    "the salt master. This salt minion will wait for {1} seconds "
                    "before attempting to re-authenticate.".format(
                        self.opts["id"], self.opts["acceptance_wait_time"]
                    )
                )
                return "retry"
    auth["aes"] = self.verify_master(payload, master_pub="token" in sign_in_payload)
    if not auth["aes"]:
        log.critical(
            "The Salt Master server's public key did not authenticate!\n"
            "The master may need to be updated if it is a version of Salt "
            "lower than {0}, or\n"
            "If you are confident that you are connecting to a valid Salt "
            "Master, then remove the master public key and restart the "
            "Salt Minion.\nThe master public key can be found "
            "at:\n{1}".format(salt.version.__version__, m_pub_fn)
        )
        sys.exit(42)
    if self.opts.get("syndic_master", False):  # Is syndic
        syndic_finger = self.opts.get(
            "syndic_finger", self.opts.get("master_finger", False)
        )
        if syndic_finger:
            if (
                salt.utils.pem_finger(m_pub_fn, sum_type=self.opts["hash_type"])
                != syndic_finger
            ):
                self._finger_fail(syndic_finger, m_pub_fn)
    else:
        if self.opts.get("master_finger", False):
            if (
                salt.utils.pem_finger(m_pub_fn, sum_type=self.opts["hash_type"])
                != self.opts["master_finger"]
            ):
                self._finger_fail(self.opts["master_finger"], m_pub_fn)
    auth["publish_port"] = payload["publish_port"]
    return auth