diff --git a/changelogs/fragments/random_string_seed.yml b/changelogs/fragments/random_string_seed.yml new file mode 100644 index 0000000000..a90b7d93b5 --- /dev/null +++ b/changelogs/fragments/random_string_seed.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - random_string lookup plugin - allow to specify seed while generating random string (https://github.com/ansible-collections/community.general/issues/5362, https://github.com/ansible-collections/community.general/pull/10710). diff --git a/plugins/lookup/random_string.py b/plugins/lookup/random_string.py index 6e2e463f39..881c13dab6 100644 --- a/plugins/lookup/random_string.py +++ b/plugins/lookup/random_string.py @@ -101,6 +101,14 @@ options: - Returns base64 encoded string. type: bool default: false + seed: + description: + - Seed for random string generator. + - B(Note) that this drastically reduces the security of this plugin. First, when O(seed) is provided, a non-cryptographic random number generator is used. + Second, if the seed does not contain enough entropy, the generated string is weak. + B(Do not use the generated string as a password or a secure token when using this option!) + type: str + version_added: 11.3.0 """ EXAMPLES = r""" @@ -109,6 +117,14 @@ EXAMPLES = r""" var: lookup('community.general.random_string') # Example result: 'DeadBeeF' +- name: Generate random string with seed + ansible.builtin.debug: + var: lookup('community.general.random_string', seed=12345) + # Example result: '6[~(2q5O' + # NOTE: Do **not** use this string as a password or a secure token, + # unless you know exactly what you are doing! + # Specifying seed uses a non-secure random number generator. + - name: Generate random string with length 12 ansible.builtin.debug: var: lookup('community.general.random_string', length=12) @@ -182,7 +198,6 @@ class LookupModule(LookupBase): lower_chars = string.ascii_lowercase upper_chars = string.ascii_uppercase special_chars = string.punctuation - random_generator = random.SystemRandom() self.set_options(var_options=variables, direct=kwargs) @@ -191,6 +206,13 @@ class LookupModule(LookupBase): override_all = self.get_option("override_all") ignore_similar_chars = self.get_option("ignore_similar_chars") similar_chars = self.get_option("similar_chars") + seed = self.get_option("seed") + + if seed is None: + random_generator = random.SystemRandom() + else: + random_generator = random.Random(seed) + values = "" available_chars_set = "" @@ -236,10 +258,11 @@ class LookupModule(LookupBase): remaining_pass_len = length - len(values) values += self.get_random(random_generator, available_chars_set, remaining_pass_len) - # Get pseudo randomization shuffled_values = list(values) - # Randomize the order - random.shuffle(shuffled_values) + if seed is None: + # Get pseudo randomization + # Randomize the order + random.shuffle(shuffled_values) if base64_flag: return [self.b64encode("".join(shuffled_values))] diff --git a/tests/integration/targets/lookup_random_string/test.yml b/tests/integration/targets/lookup_random_string/test.yml index b74116d04a..31b5001ceb 100644 --- a/tests/integration/targets/lookup_random_string/test.yml +++ b/tests/integration/targets/lookup_random_string/test.yml @@ -21,6 +21,8 @@ result11: "{{ query('community.general.random_string', base64=true, length=8) }}" result12: "{{ query('community.general.random_string', upper=false, numbers=false, special=false) }}" # all lower case result13: "{{ query('community.general.random_string', override_all='0', length=2) }}" + result14: "{{ query('community.general.random_string', seed='1234') }}" + result15: "{{ query('community.general.random_string', seed='1234') }}" - name: Raise error when impossible constraints are provided set_fact: @@ -51,3 +53,4 @@ - result13[0] == '00' - impossible_result is failed - "'Available characters cannot' in impossible_result.msg" + - result15[0] == result14[0]