Add cluster config settings

This commit is contained in:
Daniel A. Wozniak 2023-08-19 15:34:31 -07:00 committed by Gareth J. Greenaway
parent ade2eaa057
commit 8764aa9eea
11 changed files with 572 additions and 361 deletions

View file

@ -55,7 +55,10 @@ class ReqServerChannel:
def __init__(self, opts, transport):
self.opts = opts
self.transport = transport
self.event = None
self.event = salt.utils.event.get_master_event(
self.opts, self.opts["sock_dir"], listen=False
)
self.master_key = salt.crypt.MasterKeys(self.opts)
def pre_fork(self, process_manager):
"""
@ -187,7 +190,10 @@ class ReqServerChannel:
The server equivalent of ReqChannel.crypted_transfer_decode_dictentry
"""
# encrypt with a specific AES key
pubfn = os.path.join(self.opts["pki_dir"], "minions", target)
if self.master_key.cluster_key:
pubfn = os.path.join(self.opts["cluster_pki_dir"], "minions", target)
else:
pubfn = os.path.join(self.opts["pki_dir"], "minions", target)
key = salt.crypt.Crypticle.generate_key_string()
pcrypt = salt.crypt.Crypticle(self.opts, key)
try:
@ -212,10 +218,9 @@ class ReqServerChannel:
tosign = salt.payload.dumps(
{"key": pret["key"], "pillar": ret, "nonce": nonce}
)
master_pem_path = os.path.join(self.opts["pki_dir"], "master.pem")
signed_msg = {
"data": tosign,
"sig": salt.crypt.sign_message(master_pem_path, tosign),
"sig": salt.crypt.sign_message(self.master_key.rsa_path, tosign),
}
pret[dictkey] = pcrypt.dumps(signed_msg)
else:
@ -223,12 +228,11 @@ class ReqServerChannel:
return pret
def _clear_signed(self, load):
master_pem_path = os.path.join(self.opts["pki_dir"], "master.pem")
tosign = salt.payload.dumps(load)
return {
"enc": "clear",
"load": tosign,
"sig": salt.crypt.sign_message(master_pem_path, tosign),
"sig": salt.crypt.sign_message(self.master_key.rsa_path, tosign),
}
def _update_aes(self):
@ -326,18 +330,21 @@ class ReqServerChannel:
else:
return {"enc": "clear", "load": {"ret": "full"}}
pki_dir = self.opts["pki_dir"]
if self.opts["cluster_id"]:
if self.opts["cluster_pki_dir"]:
pki_dir = self.opts["cluster_pki_dir"]
# Check if key is configured to be auto-rejected/signed
auto_reject = self.auto_key.check_autoreject(load["id"])
auto_sign = self.auto_key.check_autosign(
load["id"], load.get("autosign_grains", None)
)
pubfn = os.path.join(self.opts["pki_dir"], "minions", load["id"])
pubfn_pend = os.path.join(self.opts["pki_dir"], "minions_pre", load["id"])
pubfn_rejected = os.path.join(
self.opts["pki_dir"], "minions_rejected", load["id"]
)
pubfn_denied = os.path.join(self.opts["pki_dir"], "minions_denied", load["id"])
pubfn = os.path.join(pki_dir, "minions", load["id"])
pubfn_pend = os.path.join(pki_dir, "minions_pre", load["id"])
pubfn_rejected = os.path.join(pki_dir, "minions_rejected", load["id"])
pubfn_denied = os.path.join(pki_dir, "minions_denied", load["id"])
if self.opts["open_mode"]:
# open mode is turned on, nuts to checks and overwrite whatever
# is there
@ -740,6 +747,7 @@ class PubServerChannel:
self.event = salt.utils.event.get_event("master", opts=self.opts, listen=False)
self.ckminions = salt.utils.minions.CkMinions(self.opts)
self.present = {}
self.master_key = salt.crypt.MasterKeys(self.opts)
def close(self):
self.transport.close()
@ -771,6 +779,7 @@ class PubServerChannel:
secrets = kwargs.get("secrets", None)
if secrets is not None:
salt.master.SMaster.secrets = secrets
self.master_key = salt.crypt.MasterKeys(self.opts)
self.transport.publish_daemon(
self.publish_payload, self.presence_callback, self.remove_presence_callback
)
@ -861,9 +870,10 @@ class PubServerChannel:
)
payload["load"] = crypticle.dumps(load)
if self.opts["sign_pub_messages"]:
master_pem_path = os.path.join(self.opts["pki_dir"], "master.pem")
log.debug("Signing data packet")
payload["sig"] = salt.crypt.sign_message(master_pem_path, payload["load"])
payload["sig"] = salt.crypt.sign_message(
self.master_key.rsa_path, payload["load"]
)
int_payload = {"payload": salt.payload.dumps(payload)}
# If topics are upported, target matching has to happen master side

View file

@ -153,12 +153,35 @@ class Master(
self.config["syndic_dir"],
self.config["sqlite_queue_dir"],
]
pki_dir = self.config["pki_dir"]
if (
self.config["cluster_pki_dir"]
and self.config["cluster_pki_dir"] != self.config["pki_dir"]
):
v_dirs.extend(
[
self.config["cluster_pki_dir"],
os.path.join(self.config["cluster_pki_dir"], "minions"),
os.path.join(self.config["cluster_pki_dir"], "minions_pre"),
os.path.join(
self.config["cluster_pki_dir"], "minions_denied"
),
os.path.join(
self.config["cluster_pki_dir"], "minions_autosign"
),
os.path.join(
self.config["cluster_pki_dir"], "minions_rejected"
),
]
)
pki_dir = [self.config["pki_dir"], self.config["cluster_pki_dir"]]
verify_env(
v_dirs,
self.config["user"],
permissive=self.config["permissive_pki_access"],
root_dir=self.config["root_dir"],
pki_dir=self.config["pki_dir"],
pki_dir=pki_dir,
)
# Clear out syndics from cachedir
for syndic_file in os.listdir(self.config["syndic_dir"]):

View file

@ -185,6 +185,14 @@ VALID_OPTS = immutabletypes.freeze(
"pki_dir": str,
# A unique identifier for this daemon
"id": str,
# When defined we operate this master as a part of a cluster.
"cluster_id": str,
# Defines the other masters in the cluster.
"cluster_peers": list,
# Use this location instead of pki dir for cluster. This allows users
# to define where minion keys and the cluster private key will be
# stored.
"cluster_pki_dir": str,
# Use a module function to determine the unique identifier. If this is
# set and 'id' is not set, it will allow invocation of a module function
# to determine the value of 'id'. For simple invocations without function
@ -409,6 +417,8 @@ VALID_OPTS = immutabletypes.freeze(
"permissive_pki_access": bool,
# The passphrase of the master's private key
"key_pass": (type(None), str),
# The passphrase of the master cluster's private key
"cluster_key_pass": (type(None), str),
# The passphrase of the master's private signing key
"signing_key_pass": (type(None), str),
# The path to a directory to pull in configuration file includes
@ -1544,6 +1554,7 @@ DEFAULT_MASTER_OPTS = immutabletypes.freeze(
"verify_env": True,
"permissive_pki_access": False,
"key_pass": None,
"cluster_key_pass": None,
"signing_key_pass": None,
"default_include": "master.d/*.conf",
"winrepo_dir": os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo"),
@ -4034,6 +4045,19 @@ def apply_master_config(overrides=None, defaults=None):
prepend_root_dir(opts, prepend_root_dirs)
# When a cluster id is defined, make sure the other nessicery bits a
# defined.
if "cluster_id" not in opts:
opts["cluster_id"] = None
if opts["cluster_id"] is not None:
if not opts.get("cluster_peers", None):
opts["cluster_peers"] = []
if not opts.get("cluster_pki_dir", None):
opts["cluster_pki_dir"] = opts["pki_dir"]
else:
opts["cluster_peers"] = []
opts["cluster_pki_dir"] = None
# Enabling open mode requires that the value be set to True, and
# nothing else!
opts["open_mode"] = opts["open_mode"] is True

View file

@ -371,11 +371,27 @@ class MasterKeys(dict):
def __init__(self, opts):
super().__init__()
self.opts = opts
self.pub_path = os.path.join(self.opts["pki_dir"], "master.pub")
self.rsa_path = os.path.join(self.opts["pki_dir"], "master.pem")
self.master_pub_path = os.path.join(self.opts["pki_dir"], "master.pub")
self.master_rsa_path = os.path.join(self.opts["pki_dir"], "master.pem")
key_pass = salt.utils.sdb.sdb_get(self.opts["key_pass"], self.opts)
self.key = self.__get_keys(passphrase=key_pass)
self.master_key = self.__get_keys(passphrase=key_pass)
self.cluster_pub_path = None
self.cluster_rsa_path = None
self.cluster_key = None
if self.opts["cluster_id"]:
self.cluster_pub_path = os.path.join(
self.opts["cluster_pki_dir"], "cluster.pub"
)
self.cluster_rsa_path = os.path.join(
self.opts["cluster_pki_dir"], "cluster.pem"
)
key_pass = salt.utils.sdb.sdb_get(self.opts["cluster_key_pass"], self.opts)
self.cluster_key = self.__get_keys(
name="cluster",
passphrase=key_pass,
pki_dir=self.opts["cluster_pki_dir"],
)
self.pub_signature = None
@ -433,15 +449,35 @@ class MasterKeys(dict):
def __getstate__(self):
return {"opts": self.opts}
def __get_keys(self, name="master", passphrase=None):
@property
def key(self):
if self.cluster_key:
return self.cluster_key
return self.master_key
@property
def pub_path(self):
if self.cluster_pub_path:
return self.cluster_pub_path
return self.master_pub_path
@property
def rsa_path(self):
if self.cluster_rsa_path:
return self.cluster_rsa_path
return self.master_rsa_path
def __get_keys(self, name="master", passphrase=None, pki_dir=None):
"""
Returns a key object for a key in the pki-dir
"""
path = os.path.join(self.opts["pki_dir"], name + ".pem")
if pki_dir is None:
pki_dir = self.opts["pki_dir"]
path = os.path.join(pki_dir, name + ".pem")
if not os.path.exists(path):
log.info("Generating %s keys: %s", name, self.opts["pki_dir"])
log.info("Generating %s keys: %s", name, pki_dir)
gen_keys(
self.opts["pki_dir"],
pki_dir,
name,
self.opts["keysize"],
self.opts.get("user"),
@ -465,7 +501,14 @@ class MasterKeys(dict):
Return the string representation of a public key
in the pki-directory
"""
path = os.path.join(self.opts["pki_dir"], name + ".pub")
if self.cluster_pub_path:
path = self.cluster_pub_path
else:
path = self.master_pub_path
# XXX We should always have a key present when this is called, if not
# it's an error.
# if not os.path.isfile(path):
# raise RuntimeError(f"The key {path} does not exist.")
if not os.path.isfile(path):
key = self.__get_keys()
if HAS_M2:
@ -476,6 +519,9 @@ class MasterKeys(dict):
with salt.utils.files.fopen(path) as rfh:
return rfh.read()
def get_ckey_paths(self):
return self.cluster_pub_path, self.cluster_rsa_path
def get_mkey_paths(self):
return self.pub_path, self.rsa_path

View file

@ -332,7 +332,11 @@ class AutoKey:
"""
Check a keyid for membership in a autosign directory.
"""
autosign_dir = os.path.join(self.opts["pki_dir"], "minions_autosign")
if self.opts["cluster_id"]:
pki_dir = self.opts["cluster_pki_dir"]
else:
pki_dir = self.opts["pki_dir"]
autosign_dir = os.path.join(pki_dir, "minions_autosign")
# cleanup expired files
expire_minutes = self.opts.get("autosign_timeout", 120)

View file

@ -309,6 +309,9 @@ class Key:
def __init__(self, opts, io_loop=None):
self.opts = opts
self.pki_dir = self.opts["pki_dir"]
if self.opts["cluster_id"]:
self.pki_dir = self.opts["cluster_pki_dir"]
kind = self.opts.get("__role", "") # application kind
if kind not in salt.utils.kinds.APPL_KINDS:
emsg = f"Invalid application kind = '{kind}'."
@ -330,11 +333,11 @@ class Key:
"""
Return the minion keys directory paths
"""
minions_accepted = os.path.join(self.opts["pki_dir"], self.ACC)
minions_pre = os.path.join(self.opts["pki_dir"], self.PEND)
minions_rejected = os.path.join(self.opts["pki_dir"], self.REJ)
minions_accepted = os.path.join(self.pki_dir, self.ACC)
minions_pre = os.path.join(self.pki_dir, self.PEND)
minions_rejected = os.path.join(self.pki_dir, self.REJ)
minions_denied = os.path.join(self.opts["pki_dir"], self.DEN)
minions_denied = os.path.join(self.pki_dir, self.DEN)
return minions_accepted, minions_pre, minions_rejected, minions_denied
def _get_key_attrs(self, keydir, keyname, keysize, user):
@ -342,10 +345,10 @@ class Key:
if "gen_keys_dir" in self.opts:
keydir = self.opts["gen_keys_dir"]
else:
keydir = self.opts["pki_dir"]
keydir = self.pki_dir
if not keyname:
if "gen_keys" in self.opts:
keyname = self.opts["gen_keys"]
keyname = self.pki_dir
else:
keyname = "minion"
if not keysize:
@ -380,7 +383,7 @@ class Key:
return f"Public-key {pub} does not exist"
# default to master.pub
else:
mpub = self.opts["pki_dir"] + "/" + "master.pub"
mpub = self.pki_dir + "/" + "master.pub"
if os.path.isfile(mpub):
pub = mpub
@ -390,7 +393,7 @@ class Key:
return f"Private-key {priv} does not exist"
# default to master_sign.pem
else:
mpriv = self.opts["pki_dir"] + "/" + "master_sign.pem"
mpriv = self.pki_dir + "/" + "master_sign.pem"
if os.path.isfile(mpriv):
priv = mpriv
@ -399,22 +402,17 @@ class Key:
log.debug(
"Generating new signing key-pair .%s.* in %s",
self.opts["master_sign_key_name"],
self.opts["pki_dir"],
self.pki_dir,
)
salt.crypt.gen_keys(
self.opts["pki_dir"],
self.pki_dir,
self.opts["master_sign_key_name"],
keysize or self.opts["keysize"],
self.opts.get("user"),
self.passphrase,
)
priv = (
self.opts["pki_dir"]
+ "/"
+ self.opts["master_sign_key_name"]
+ ".pem"
)
priv = self.pki_dir + "/" + self.opts["master_sign_key_name"] + ".pem"
else:
return "No usable private-key found"
@ -428,7 +426,7 @@ class Key:
if not os.path.isdir(signature_path):
log.debug("target directory %s does not exist", signature_path)
else:
signature_path = self.opts["pki_dir"]
signature_path = self.pki_dir
sign_path = signature_path + "/" + self.opts["master_pubkey_signature"]
@ -525,9 +523,9 @@ class Key:
Return a dict of local keys
"""
ret = {"local": []}
for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(self.opts["pki_dir"])):
for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(self.pki_dir)):
if fn_.endswith(".pub") or fn_.endswith(".pem"):
path = os.path.join(self.opts["pki_dir"], fn_)
path = os.path.join(self.pki_dir, fn_)
ret["local"].append(fn_)
return ret
@ -600,7 +598,7 @@ class Key:
for status, keys in self.name_match(match).items():
ret[status] = {}
for key in salt.utils.data.sorted_ignorecase(keys):
path = os.path.join(self.opts["pki_dir"], status, key)
path = os.path.join(self.pki_dir, status, key)
with salt.utils.files.fopen(path, "r") as fp_:
ret[status][key] = salt.utils.stringutils.to_unicode(fp_.read())
return ret
@ -613,7 +611,7 @@ class Key:
for status, keys in self.list_keys().items():
ret[status] = {}
for key in salt.utils.data.sorted_ignorecase(keys):
path = os.path.join(self.opts["pki_dir"], status, key)
path = os.path.join(self.pki_dir, status, key)
with salt.utils.files.fopen(path, "r") as fp_:
ret[status][key] = salt.utils.stringutils.to_unicode(fp_.read())
return ret
@ -639,7 +637,7 @@ class Key:
invalid_keys = []
for keydir in keydirs:
for key in matches.get(keydir, []):
key_path = os.path.join(self.opts["pki_dir"], keydir, key)
key_path = os.path.join(self.pki_dir, keydir, key)
try:
salt.crypt.get_rsa_pub_key(key_path)
except salt.exceptions.InvalidKeyError:
@ -649,7 +647,7 @@ class Key:
try:
shutil.move(
key_path,
os.path.join(self.opts["pki_dir"], self.ACC, key),
os.path.join(self.pki_dir, self.ACC, key),
)
eload = {"result": True, "act": "accept", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
@ -668,8 +666,8 @@ class Key:
for key in keys[self.PEND]:
try:
shutil.move(
os.path.join(self.opts["pki_dir"], self.PEND, key),
os.path.join(self.opts["pki_dir"], self.ACC, key),
os.path.join(self.pki_dir, self.PEND, key),
os.path.join(self.pki_dir, self.ACC, key),
)
eload = {"result": True, "act": "accept", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
@ -713,7 +711,7 @@ class Key:
"master AES key is rotated or auth is revoked "
"with 'saltutil.revoke_auth'.".format(key)
)
os.remove(os.path.join(self.opts["pki_dir"], status, key))
os.remove(os.path.join(self.pki_dir, status, key))
eload = {"result": True, "act": "delete", "id": key}
self.event.fire_event(
eload, salt.utils.event.tagify(prefix="key")
@ -738,7 +736,7 @@ class Key:
for status, keys in self.list_keys().items():
for key in keys[self.DEN]:
try:
os.remove(os.path.join(self.opts["pki_dir"], status, key))
os.remove(os.path.join(self.pki_dir, status, key))
eload = {"result": True, "act": "delete", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
except OSError:
@ -753,7 +751,7 @@ class Key:
for status, keys in self.list_keys().items():
for key in keys:
try:
os.remove(os.path.join(self.opts["pki_dir"], status, key))
os.remove(os.path.join(self.pki_dir, status, key))
eload = {"result": True, "act": "delete", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
except OSError:
@ -787,8 +785,8 @@ class Key:
for key in matches.get(keydir, []):
try:
shutil.move(
os.path.join(self.opts["pki_dir"], keydir, key),
os.path.join(self.opts["pki_dir"], self.REJ, key),
os.path.join(self.pki_dir, keydir, key),
os.path.join(self.pki_dir, self.REJ, key),
)
eload = {"result": True, "act": "reject", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
@ -809,8 +807,8 @@ class Key:
for key in keys[self.PEND]:
try:
shutil.move(
os.path.join(self.opts["pki_dir"], self.PEND, key),
os.path.join(self.opts["pki_dir"], self.REJ, key),
os.path.join(self.pki_dir, self.PEND, key),
os.path.join(self.pki_dir, self.REJ, key),
)
eload = {"result": True, "act": "reject", "id": key}
self.event.fire_event(eload, salt.utils.event.tagify(prefix="key"))
@ -836,9 +834,9 @@ class Key:
ret[status] = {}
for key in keys:
if status == "local":
path = os.path.join(self.opts["pki_dir"], key)
path = os.path.join(self.pki_dir, key)
else:
path = os.path.join(self.opts["pki_dir"], status, key)
path = os.path.join(self.pki_dir, status, key)
ret[status][key] = salt.utils.crypt.pem_finger(path, sum_type=hash_type)
return ret
@ -854,9 +852,9 @@ class Key:
ret[status] = {}
for key in keys:
if status == "local":
path = os.path.join(self.opts["pki_dir"], key)
path = os.path.join(self.pki_dir, key)
else:
path = os.path.join(self.opts["pki_dir"], status, key)
path = os.path.join(self.pki_dir, status, key)
ret[status][key] = salt.utils.crypt.pem_finger(path, sum_type=hash_type)
return ret

View file

@ -289,13 +289,13 @@ class Maintenance(salt.utils.process.SignalHandlingProcess):
else:
acc = "accepted"
for fn_ in os.listdir(os.path.join(self.opts["pki_dir"], acc)):
for fn_ in os.listdir(os.path.join(self.pki_dir, acc)):
if not fn_.startswith("."):
keys.append(fn_)
log.debug("Writing master key cache")
# Write a temporary file securely
with salt.utils.atomicfile.atomic_open(
os.path.join(self.opts["pki_dir"], acc, ".key_cache"), mode="wb"
os.path.join(self.pki_dir, acc, ".key_cache"), mode="wb"
) as cache_file:
salt.payload.dump(keys, cache_file)
@ -1309,6 +1309,10 @@ class AESFuncs(TransportMethods):
)
self.__setup_fileserver()
self.masterapi = salt.daemons.masterapi.RemoteFuncs(opts)
if "cluster_id" in self.opts and self.opts["cluster_id"]:
self.pki_dir = self.opts["cluster_pki_dir"]
else:
self.pki_dir = self.opts.get("pki_dir", "")
def __setup_fileserver(self):
"""
@ -1341,8 +1345,7 @@ class AESFuncs(TransportMethods):
"""
if not salt.utils.verify.valid_id(self.opts, id_):
return False
pub_path = os.path.join(self.opts["pki_dir"], "minions", id_)
pub_path = os.path.join(self.pki_dir, "minions", id_)
try:
pub = salt.crypt.get_rsa_pub_key(pub_path)
except OSError:
@ -1764,7 +1767,7 @@ class AESFuncs(TransportMethods):
log.trace("Verifying signed event publish from minion")
sig = load.pop("sig")
this_minion_pubkey = os.path.join(
self.opts["pki_dir"], "minions/{}".format(load["id"])
self.pki_dir, "minions/{}".format(load["id"])
)
serialized_load = salt.serializers.msgpack.serialize(load)
if not salt.crypt.verify_signature(

View file

@ -216,6 +216,10 @@ class CkMinions:
self.acc = "minions"
else:
self.acc = "accepted"
if self.opts.get("cluster_id", None) is not None:
self.pki_dir = self.opts.get("cluster_pki_dir", "")
else:
self.pki_dir = self.opts.get("pki_dir", "")
def _check_nodegroup_minions(self, expr, greedy): # pylint: disable=unused-argument
"""
@ -261,7 +265,7 @@ class CkMinions:
Respects cache if configured
"""
minions = []
pki_cache_fn = os.path.join(self.opts["pki_dir"], self.acc, ".key_cache")
pki_cache_fn = os.path.join(self.pki_dir, self.acc, ".key_cache")
try:
os.makedirs(os.path.dirname(pki_cache_fn))
except OSError:
@ -273,7 +277,7 @@ class CkMinions:
return salt.payload.load(fn_)
else:
for fn_ in salt.utils.data.sorted_ignorecase(
os.listdir(os.path.join(self.opts["pki_dir"], self.acc))
os.listdir(os.path.join(self.pki_dir, self.acc))
):
if not fn_.startswith("."):
minions.append(fn_)
@ -301,7 +305,7 @@ class CkMinions:
if greedy:
minions = []
for fn_ in salt.utils.data.sorted_ignorecase(
os.listdir(os.path.join(self.opts["pki_dir"], self.acc))
os.listdir(os.path.join(self.pki_dir, self.acc))
):
if not fn_.startswith("."):
minions.append(fn_)
@ -447,7 +451,7 @@ class CkMinions:
if greedy:
mlist = []
for fn_ in salt.utils.data.sorted_ignorecase(
os.listdir(os.path.join(self.opts["pki_dir"], self.acc))
os.listdir(os.path.join(self.pki_dir, self.acc))
):
if not fn_.startswith("."):
mlist.append(fn_)
@ -677,7 +681,7 @@ class CkMinions:
"""
mlist = []
for fn_ in salt.utils.data.sorted_ignorecase(
os.listdir(os.path.join(self.opts["pki_dir"], self.acc))
os.listdir(os.path.join(self.pki_dir, self.acc))
):
if not fn_.startswith("."):
mlist.append(fn_)

View file

@ -298,15 +298,26 @@ def verify_env(
# If acls are enabled, the pki_dir needs to remain readable, this
# is still secure because the private keys are still only readable
# by the user running the master
if dir_ == pki_dir:
smode = stat.S_IMODE(mode.st_mode)
if smode != 448 and smode != 488:
if os.access(dir_, os.W_OK):
os.chmod(dir_, 448)
else:
log.critical(
'Unable to securely set the permissions of "%s".', dir_
)
if isinstance(pki_dir, str):
if dir_ == pki_dir:
smode = stat.S_IMODE(mode.st_mode)
if smode != 448 and smode != 488:
if os.access(dir_, os.W_OK):
os.chmod(dir_, 448)
else:
log.critical(
'Unable to securely set the permissions of "%s".', dir_
)
else:
if dir_ in pki_dir:
smode = stat.S_IMODE(mode.st_mode)
if smode != 448 and smode != 488:
if os.access(dir_, os.W_OK):
os.chmod(dir_, 448)
else:
log.critical(
'Unable to securely set the permissions of "%s".', dir_
)
if skip_extra is False:
# Run the extra verification checks
@ -539,6 +550,10 @@ def valid_id(opts, id_):
try:
if any(x in id_ for x in ("/", "\\", "\0")):
return False
if opts.get("cluster_id", None) is not None:
pki_dir = opts["cluster_pki_dir"]
else:
pki_dir = opts["pki_dir"]
return bool(clean_path(opts["pki_dir"], id_))
except (AttributeError, KeyError, TypeError, UnicodeDecodeError):
return False

View file

@ -478,23 +478,25 @@ def test_serverside_exception(temp_salt_minion, temp_salt_master):
assert ret == "Server-side exception handling payload"
def test_req_server_chan_encrypt_v2(pki_dir):
def test_req_server_chan_encrypt_v2(master_opts, pki_dir):
loop = tornado.ioloop.IOLoop.current()
opts = {
"worker_threads": 1,
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"zmq_monitor": False,
"mworker_queue_niceness": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("master")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
}
server = salt.channel.server.ReqServerChannel.factory(opts)
master_opts.update(
{
"worker_threads": 1,
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"zmq_monitor": False,
"mworker_queue_niceness": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("master")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
}
)
server = salt.channel.server.ReqServerChannel.factory(master_opts)
dictkey = "pillar"
nonce = "abcdefg"
pillar_data = {"pillar1": "meh"}
@ -511,7 +513,7 @@ def test_req_server_chan_encrypt_v2(pki_dir):
else:
cipher = PKCS1_OAEP.new(key)
aes = cipher.decrypt(ret["key"])
pcrypt = salt.crypt.Crypticle(opts, aes)
pcrypt = salt.crypt.Crypticle(master_opts, aes)
signed_msg = pcrypt.loads(ret[dictkey])
assert "sig" in signed_msg
@ -527,23 +529,25 @@ def test_req_server_chan_encrypt_v2(pki_dir):
server.close()
def test_req_server_chan_encrypt_v1(pki_dir):
def test_req_server_chan_encrypt_v1(master_opts, pki_dir):
loop = tornado.ioloop.IOLoop.current()
opts = {
"worker_threads": 1,
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"zmq_monitor": False,
"mworker_queue_niceness": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("master")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
}
server = salt.channel.server.ReqServerChannel.factory(opts)
master_opts.update(
{
"worker_threads": 1,
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"zmq_monitor": False,
"mworker_queue_niceness": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("master")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
}
)
server = salt.channel.server.ReqServerChannel.factory(master_opts)
dictkey = "pillar"
nonce = "abcdefg"
pillar_data = {"pillar1": "meh"}
@ -563,31 +567,33 @@ def test_req_server_chan_encrypt_v1(pki_dir):
else:
cipher = PKCS1_OAEP.new(key)
aes = cipher.decrypt(ret["key"])
pcrypt = salt.crypt.Crypticle(opts, aes)
pcrypt = salt.crypt.Crypticle(master_opts, aes)
data = pcrypt.loads(ret[dictkey])
assert data == pillar_data
finally:
server.close()
def test_req_chan_decode_data_dict_entry_v1(pki_dir):
def test_req_chan_decode_data_dict_entry_v1(minion_opts, master_opts, pki_dir):
mockloop = MagicMock()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
client = salt.channel.client.ReqChannel.factory(opts, io_loop=mockloop)
client = salt.channel.client.ReqChannel.factory(minion_opts, io_loop=mockloop)
try:
dictkey = "pillar"
target = "minion"
@ -607,24 +613,26 @@ def test_req_chan_decode_data_dict_entry_v1(pki_dir):
server.close()
async def test_req_chan_decode_data_dict_entry_v2(pki_dir):
async def test_req_chan_decode_data_dict_entry_v2(minion_opts, master_opts, pki_dir):
mockloop = MagicMock()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=mockloop)
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop)
dictkey = "pillar"
target = "minion"
@ -632,7 +640,7 @@ async def test_req_chan_decode_data_dict_entry_v2(pki_dir):
# Mock auth and message client.
auth = client.auth
auth._crypticle = salt.crypt.Crypticle(opts, AES_KEY)
auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY)
client.auth = MagicMock()
client.auth.mpub = auth.mpub
client.auth.authenticated = True
@ -679,24 +687,28 @@ async def test_req_chan_decode_data_dict_entry_v2(pki_dir):
server.close()
async def test_req_chan_decode_data_dict_entry_v2_bad_nonce(pki_dir):
async def test_req_chan_decode_data_dict_entry_v2_bad_nonce(
minion_opts, master_opts, pki_dir
):
mockloop = MagicMock()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=mockloop)
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop)
dictkey = "pillar"
badnonce = "abcdefg"
@ -705,7 +717,7 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_nonce(pki_dir):
# Mock auth and message client.
auth = client.auth
auth._crypticle = salt.crypt.Crypticle(opts, AES_KEY)
auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY)
client.auth = MagicMock()
client.auth.mpub = auth.mpub
client.auth.authenticated = True
@ -751,24 +763,28 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_nonce(pki_dir):
server.close()
async def test_req_chan_decode_data_dict_entry_v2_bad_signature(pki_dir):
async def test_req_chan_decode_data_dict_entry_v2_bad_signature(
minion_opts, master_opts, pki_dir
):
mockloop = MagicMock()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=mockloop)
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop)
dictkey = "pillar"
badnonce = "abcdefg"
@ -777,7 +793,7 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_signature(pki_dir):
# Mock auth and message client.
auth = client.auth
auth._crypticle = salt.crypt.Crypticle(opts, AES_KEY)
auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY)
client.auth = MagicMock()
client.auth.mpub = auth.mpub
client.auth.authenticated = True
@ -839,24 +855,28 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_signature(pki_dir):
server.close()
async def test_req_chan_decode_data_dict_entry_v2_bad_key(pki_dir):
async def test_req_chan_decode_data_dict_entry_v2_bad_key(
minion_opts, master_opts, pki_dir
):
mockloop = MagicMock()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=mockloop)
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop)
dictkey = "pillar"
badnonce = "abcdefg"
@ -865,7 +885,7 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_key(pki_dir):
# Mock auth and message client.
auth = client.auth
auth._crypticle = salt.crypt.Crypticle(opts, AES_KEY)
auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY)
client.auth = MagicMock()
client.auth.mpub = auth.mpub
client.auth.authenticated = True
@ -895,7 +915,7 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_key(pki_dir):
# Now encrypt with a different key
key = salt.crypt.Crypticle.generate_key_string()
pcrypt = salt.crypt.Crypticle(opts, key)
pcrypt = salt.crypt.Crypticle(minion_opts, key)
pubfn = os.path.join(master_opts["pki_dir"], "minions", "minion")
pub = salt.crypt.get_rsa_pub_key(pubfn)
ret[dictkey] = pcrypt.dumps(signed_msg)
@ -934,25 +954,27 @@ async def test_req_chan_decode_data_dict_entry_v2_bad_key(pki_dir):
server.close()
async def test_req_serv_auth_v1(pki_dir):
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"master_sign_pubkey": False,
"publish_port": 4505,
"auth_mode": 1,
}
async def test_req_serv_auth_v1(minion_opts, master_opts, pki_dir):
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"master_sign_pubkey": False,
"publish_port": 4505,
"auth_mode": 1,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -960,7 +982,7 @@ async def test_req_serv_auth_v1(pki_dir):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
@ -990,25 +1012,27 @@ async def test_req_serv_auth_v1(pki_dir):
server.close()
async def test_req_serv_auth_v2(pki_dir):
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"master_sign_pubkey": False,
"publish_port": 4505,
"auth_mode": 1,
}
async def test_req_serv_auth_v2(minion_opts, master_opts, pki_dir):
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"master_sign_pubkey": False,
"publish_port": 4505,
"auth_mode": 1,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1016,7 +1040,7 @@ async def test_req_serv_auth_v2(pki_dir):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
@ -1048,26 +1072,28 @@ async def test_req_serv_auth_v2(pki_dir):
server.close()
async def test_req_chan_auth_v2(pki_dir, io_loop):
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
async def test_req_chan_auth_v2(minion_opts, master_opts, pki_dir, io_loop):
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1075,15 +1101,15 @@ async def test_req_chan_auth_v2(pki_dir, io_loop):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
master_opts["master_sign_pubkey"] = False
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
server.master_key = salt.crypt.MasterKeys(server.opts)
opts["verify_master_pubkey_sign"] = False
opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=io_loop)
minion_opts["verify_master_pubkey_sign"] = False
minion_opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop)
signin_payload = client.auth.minion_sign_in_payload()
pload = client._package_load(signin_payload)
try:
@ -1101,26 +1127,30 @@ async def test_req_chan_auth_v2(pki_dir, io_loop):
server.close()
async def test_req_chan_auth_v2_with_master_signing(pki_dir, io_loop):
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
async def test_req_chan_auth_v2_with_master_signing(
minion_opts, master_opts, pki_dir, io_loop
):
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1128,7 +1158,7 @@ async def test_req_chan_auth_v2_with_master_signing(pki_dir, io_loop):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
master_opts["master_sign_pubkey"] = True
master_opts["master_use_pubkey_signature"] = False
master_opts["signing_key_pass"] = True
@ -1137,17 +1167,17 @@ async def test_req_chan_auth_v2_with_master_signing(pki_dir, io_loop):
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
server.master_key = salt.crypt.MasterKeys(server.opts)
opts["verify_master_pubkey_sign"] = True
opts["always_verify_signature"] = True
opts["master_sign_key_name"] = "master_sign"
opts["master"] = "master"
minion_opts["verify_master_pubkey_sign"] = True
minion_opts["always_verify_signature"] = True
minion_opts["master_sign_key_name"] = "master_sign"
minion_opts["master"] = "master"
assert (
pki_dir.joinpath("minion", "minion_master.pub").read_text()
== pki_dir.joinpath("master", "master.pub").read_text()
)
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=io_loop)
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop)
signin_payload = client.auth.minion_sign_in_payload()
pload = client._package_load(signin_payload)
assert "version" in pload
@ -1196,28 +1226,32 @@ async def test_req_chan_auth_v2_with_master_signing(pki_dir, io_loop):
server.close()
async def test_req_chan_auth_v2_new_minion_with_master_pub(pki_dir, io_loop):
async def test_req_chan_auth_v2_new_minion_with_master_pub(
minion_opts, master_opts, pki_dir, io_loop
):
pki_dir.joinpath("master", "minions", "minion").unlink()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1225,15 +1259,15 @@ async def test_req_chan_auth_v2_new_minion_with_master_pub(pki_dir, io_loop):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
master_opts["master_sign_pubkey"] = False
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
server.master_key = salt.crypt.MasterKeys(server.opts)
opts["verify_master_pubkey_sign"] = False
opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=io_loop)
minion_opts["verify_master_pubkey_sign"] = False
minion_opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop)
signin_payload = client.auth.minion_sign_in_payload()
pload = client._package_load(signin_payload)
try:
@ -1249,7 +1283,9 @@ async def test_req_chan_auth_v2_new_minion_with_master_pub(pki_dir, io_loop):
server.close()
async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig(pki_dir, io_loop):
async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig(
minion_opts, master_opts, pki_dir, io_loop
):
pki_dir.joinpath("master", "minions", "minion").unlink()
@ -1261,25 +1297,27 @@ async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig(pki_dir, io_l
mapub.unlink()
mapub.write_text(MASTER2_PUB_KEY.strip())
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1287,15 +1325,15 @@ async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig(pki_dir, io_l
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
master_opts["master_sign_pubkey"] = False
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
server.master_key = salt.crypt.MasterKeys(server.opts)
opts["verify_master_pubkey_sign"] = False
opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=io_loop)
minion_opts["verify_master_pubkey_sign"] = False
minion_opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop)
signin_payload = client.auth.minion_sign_in_payload()
pload = client._package_load(signin_payload)
try:
@ -1311,29 +1349,33 @@ async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig(pki_dir, io_l
server.close()
async def test_req_chan_auth_v2_new_minion_without_master_pub(pki_dir, io_loop):
async def test_req_chan_auth_v2_new_minion_without_master_pub(
minion_opts, master_opts, pki_dir, io_loop
):
pki_dir.joinpath("master", "minions", "minion").unlink()
pki_dir.joinpath("minion", "minion_master.pub").unlink()
opts = {
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
minion_opts.update(
{
"master_uri": "tcp://127.0.0.1:4506",
"interface": "127.0.0.1",
"ret_port": 4506,
"ipv6": False,
"sock_dir": ".",
"pki_dir": str(pki_dir.joinpath("minion")),
"id": "minion",
"__role": "minion",
"keysize": 4096,
"max_minions": 0,
"auto_accept": False,
"open_mode": False,
"key_pass": None,
"publish_port": 4505,
"auth_mode": 1,
"acceptance_wait_time": 3,
"acceptance_wait_time_max": 3,
}
)
SMaster.secrets["aes"] = {
"secret": multiprocessing.Array(
ctypes.c_char,
@ -1341,15 +1383,15 @@ async def test_req_chan_auth_v2_new_minion_without_master_pub(pki_dir, io_loop):
),
"reload": salt.crypt.Crypticle.generate_key_string,
}
master_opts = dict(opts, pki_dir=str(pki_dir.joinpath("master")))
master_opts.update(pki_dir=str(pki_dir.joinpath("master")))
master_opts["master_sign_pubkey"] = False
server = salt.channel.server.ReqServerChannel.factory(master_opts)
server.auto_key = salt.daemons.masterapi.AutoKey(server.opts)
server.cache_cli = False
server.master_key = salt.crypt.MasterKeys(server.opts)
opts["verify_master_pubkey_sign"] = False
opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(opts, io_loop=io_loop)
minion_opts["verify_master_pubkey_sign"] = False
minion_opts["always_verify_signature"] = False
client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop)
signin_payload = client.auth.minion_sign_in_payload()
pload = client._package_load(signin_payload)
try:

View file

@ -187,3 +187,45 @@ def test_dropfile_contents(tmp_path, master_opts):
salt.crypt.dropfile(str(tmp_path), master_opts["user"], master_id=master_opts["id"])
with salt.utils.files.fopen(str(tmp_path / ".dfn"), "r") as fp:
assert master_opts["id"] == fp.read()
def test_master_keys_without_cluster_id(tmp_path, master_opts):
master_opts["pki_dir"] = str(tmp_path)
assert master_opts["cluster_id"] is None
assert master_opts["cluster_pki_dir"] is None
mkeys = salt.crypt.MasterKeys(master_opts)
expected_master_pub = str(tmp_path / "master.pub")
expected_master_rsa = str(tmp_path / "master.pem")
assert expected_master_pub == mkeys.master_pub_path
assert expected_master_rsa == mkeys.master_rsa_path
assert mkeys.cluster_pub_path is None
assert mkeys.cluster_rsa_path is None
assert mkeys.pub_path == expected_master_pub
assert mkeys.rsa_path == expected_master_rsa
assert mkeys.key == mkeys.master_key
def test_master_keys_with_cluster_id(tmp_path, master_opts):
master_pki_path = tmp_path / "master_pki"
cluster_pki_path = tmp_path / "cluster_pki"
# The paths need to exist
master_pki_path.mkdir()
cluster_pki_path.mkdir()
master_opts["pki_dir"] = str(master_pki_path)
master_opts["cluster_id"] = "cluster1"
master_opts["cluster_pki_dir"] = str(cluster_pki_path)
mkeys = salt.crypt.MasterKeys(master_opts)
expected_master_pub = str(master_pki_path / "master.pub")
expected_master_rsa = str(master_pki_path / "master.pem")
expected_cluster_pub = str(cluster_pki_path / "cluster.pub")
expected_cluster_rsa = str(cluster_pki_path / "cluster.pem")
assert expected_master_pub == mkeys.master_pub_path
assert expected_master_rsa == mkeys.master_rsa_path
assert expected_cluster_pub == mkeys.cluster_pub_path
assert expected_cluster_rsa == mkeys.cluster_rsa_path
assert mkeys.pub_path == expected_cluster_pub
assert mkeys.rsa_path == expected_cluster_rsa
assert mkeys.key == mkeys.cluster_key