# Buggy code
def gen_keys(keydir, keyname, keysize, user=None):
    """
    Generate a RSA public keypair for use with salt

    :param str keydir: The directory to write the keypair to
    :param str keyname: The type of salt server for whom this key should be written. (i.e. 'master' or 'minion')
    :param int keysize: The number of bits in the key
    :param str user: The user on the system who should own this keypair

    :rtype: str
    :return: Path on the filesystem to the RSA private key
    """
    base = os.path.join(keydir, keyname)
    priv = "{0}.pem".format(base)
    pub = "{0}.pub".format(base)

    gen = RSA.generate(bits=keysize, e=65537)
    if os.path.isfile(priv):
        # Between first checking and the generation another process has made
        # a key! Use the winner's key
        return priv
    cumask = os.umask(191)
    with salt.utils.fopen(priv, "wb+") as f:
        f.write(gen.exportKey("PEM"))
    os.umask(cumask)
    with salt.utils.fopen(pub, "wb+") as f:
        f.write(gen.publickey().exportKey("PEM"))
    os.chmod(priv, 256)
    if user:
        try:
            import pwd

            uid = pwd.getpwnam(user).pw_uid
            os.chown(priv, uid, -1)
            os.chown(pub, uid, -1)
        except (KeyError, ImportError, OSError):
            # The specified user was not found, allow the backup systems to
            # report the error
            pass
    return priv


# Traceback (AssertionError)
salt-cloud --script-args='-D' -P  -p ec2_ubuntu_2GB_public_ca s1 s2 s3 s4
[INFO    ] salt-cloud starting
[INFO    ] Starting new HTTPS connection (1): ec2.us-west-1.amazonaws.com
api01:
----------
Provisioning:
VM being provisioned in parallel. PID: 6650
proxy01:
----------
Provisioning:
VM being provisioned in parallel. PID: 6652
webapp01:
----------
Provisioning:
Process Process-4:
VM being provisioned in parallel. PID: 6651
Process Process-3:
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self.run()
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self.run()
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python2.7/dist-packages/salt/cloud/__init__.py", line 1189, in create
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python2.7/dist-packages/salt/cloud/__init__.py", line 1189, in create
Process Process-5:
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self.run()
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python2.7/dist-packages/salt/cloud/__init__.py", line 1189, in create
self.opts
self.opts
self.opts
File "/usr/lib/python2.7/dist-packages/salt/utils/cloud.py", line 159, in gen_keys
File "/usr/lib/python2.7/dist-packages/salt/utils/cloud.py", line 159, in gen_keys
File "/usr/lib/python2.7/dist-packages/salt/utils/cloud.py", line 159, in gen_keys
salt.crypt.gen_keys(tdir, 'minion', keysize)
File "/usr/lib/python2.7/dist-packages/salt/crypt.py", line 88, in gen_keys
gen = RSA.generate(bits=keysize, e=65537)
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 508, in generate
salt.crypt.gen_keys(tdir, 'minion', keysize)
File "/usr/lib/python2.7/dist-packages/salt/crypt.py", line 88, in gen_keys
gen = RSA.generate(bits=keysize, e=65537)
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 508, in generate
salt.crypt.gen_keys(tdir, 'minion', keysize)
File "/usr/lib/python2.7/dist-packages/salt/crypt.py", line 88, in gen_keys
gen = RSA.generate(bits=keysize, e=65537)
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/RSA.py", line 508, in generate
obj = _RSA.generate_py(bits, rf, progress_func, e)    # TODO: Don't use legacy _RSA module
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/_RSA.py", line 50, in generate_py
obj = _RSA.generate_py(bits, rf, progress_func, e)    # TODO: Don't use legacy _RSA module
obj = _RSA.generate_py(bits, rf, progress_func, e)    # TODO: Don't use legacy _RSA module
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/_RSA.py", line 50, in generate_py
File "/usr/lib/python2.7/dist-packages/Crypto/PublicKey/_RSA.py", line 50, in generate_py
p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
File "/usr/lib/python2.7/dist-packages/Crypto/Util/number.py", line 265, in getStrongPrime
p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
File "/usr/lib/python2.7/dist-packages/Crypto/Util/number.py", line 265, in getStrongPrime
File "/usr/lib/python2.7/dist-packages/Crypto/Util/number.py", line 265, in getStrongPrime
randfunc)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
randfunc)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
randfunc)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
return self._singleton.read(bytes)
return self._singleton.read(bytes)
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
return _UserFriendlyRNG.read(self, bytes)
return _UserFriendlyRNG.read(self, bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
return _UserFriendlyRNG.read(self, bytes)
retval = self._fa.random_data(N)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
retval = self._fa.random_data(N)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
retval = self._fa.random_data(N)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
return self.generator.pseudo_random_data(bytes)
return self.generator.pseudo_random_data(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
return self.generator.pseudo_random_data(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
for i in xrange(num_full_blocks):
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
return _UserFriendlyRNG.read(self, bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
retval = self._fa.random_data(N)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
return self.generator.pseudo_random_data(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
for i in xrange(num_full_blocks):
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
return _UserFriendlyRNG.read(self, bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 137, in read
self._check_pid()
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 153, in _check_pid
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()
for i in xrange(num_full_blocks):
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
return self._singleton.read(bytes)
for i in xrange(num_full_blocks):
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
return _UserFriendlyRNG.read(self, bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
return _UserFriendlyRNG.read(self, bytes)
retval = self._fa.random_data(N)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 132, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
retval = self._fa.random_data(N)
return self.generator.pseudo_random_data(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaAccumulator.py", line 148, in random_data
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
for i in xrange(num_full_blocks):
return self.generator.pseudo_random_data(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/Fortuna/FortunaGenerator.py", line 93, in pseudo_random_data
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
for i in xrange(num_full_blocks):
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 202, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
return _UserFriendlyRNG.read(self, bytes)
return self._singleton.read(bytes)
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 178, in read
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 137, in read
return _UserFriendlyRNG.read(self, bytes)
self._check_pid()
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 153, in _check_pid
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 137, in read
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()
self._check_pid()
File "/usr/lib/python2.7/dist-packages/Crypto/Random/_UserFriendlyRNG.py", line 153, in _check_pid
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
AssertionError: PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()

# Fixed code
def gen_keys(keydir, keyname, keysize, user=None):
    """
    Generate a RSA public keypair for use with salt

    :param str keydir: The directory to write the keypair to
    :param str keyname: The type of salt server for whom this key should be written. (i.e. 'master' or 'minion')
    :param int keysize: The number of bits in the key
    :param str user: The user on the system who should own this keypair

    :rtype: str
    :return: Path on the filesystem to the RSA private key
    """
    base = os.path.join(keydir, keyname)
    priv = "{0}.pem".format(base)
    pub = "{0}.pub".format(base)

    salt.utils.reinit_crypto()
    gen = RSA.generate(bits=keysize, e=65537)
    if os.path.isfile(priv):
        # Between first checking and the generation another process has made
        # a key! Use the winner's key
        return priv
    cumask = os.umask(191)
    with salt.utils.fopen(priv, "wb+") as f:
        f.write(gen.exportKey("PEM"))
    os.umask(cumask)
    with salt.utils.fopen(pub, "wb+") as f:
        f.write(gen.publickey().exportKey("PEM"))
    os.chmod(priv, 256)
    if user:
        try:
            import pwd

            uid = pwd.getpwnam(user).pw_uid
            os.chown(priv, uid, -1)
            os.chown(pub, uid, -1)
        except (KeyError, ImportError, OSError):
            # The specified user was not found, allow the backup systems to
            # report the error
            pass
    return priv