首页 > 解决方案 > Ansible:在数组中查找字符串并从数组中返回字符串

问题描述

我正在编写一个剧本,它接受用户输入来确定目录中是否存在文件。

这是我到目前为止所拥有的

- name: Encrypt file
  hosts: localhost
  connection: local

  vars:
    working_directory: "{{ playbook_dir }}"
    enc_files: []
    my_file: shared_config

  tasks:

    - name: Get all Decrypted .yaml files
      find:
        paths: "{{ working_directory }}"
        patterns: '*.yaml'
        recurse: yes
        excludes: "*.enc.yaml,decrypt.yaml,encrypt_all.yaml,encrypt_file.yaml"
      register: files

    - name: Add Decrypted files to Array
      set_fact:
        enc_files: "{{ enc_files + [item.path | basename] }}"
      loop: "{{ files.files }}"
      no_log: true

    - debug:
        msg: "{{ enc_files }}"
      when: '"{{ my_file | lower }}" in "{{ enc_files | lower }}"'

我似乎无法开始工作的是,它会根据名称而不是扩展名查找文件是否存在。如果是这样,我想返回带有扩展名的文件来处理它。

这是我现在的树:

├── README.md
├── database
│   └── postgres_config.enc.yaml
├── decrypt.yaml
├── encrypt_all.yaml
├── encrypt_file.yaml
├── infra
│   ├── infra_config.enc.yaml
│   └── infra_config.yaml
├── middleware
│   ├── middleware_config.enc.yaml
│   └── middleware_config.yaml
├── services
│   ├── log_service_config.enc.yaml
│   ├── log_service_config.yaml
│
├── shared
│   ├── shared_config.enc.yaml
│   └── shared_config.yaml

我想要做的是让用户输入shared_configshared_config.yaml返回shared_config.yaml,以便我可以加密该文件。我也在试图找出一种方法,他们可以传递shared config他们的输入(以及任何其他可能的输入,但我以后可以尝试自己弄清楚)。

标签: ansible

解决方案


我想我不会以这种方式处理它(例如列出所有文件,然后查找用户输入是否与文件匹配)。

我可能宁愿以另一种方式处理它:获取用户的输入,然后assert相应的文件确实存在。

然后,您只需使用 Jinja 过滤器和 Python 字符串操作来将用户输入转换为预期路径。


下面是一个建议的剧本,有好处也有陷阱。

优势:

  • 它将与由空格分隔的多块路径一起使用some path将给出some/some_path.yaml
  • 它应该足够健壮以纠正错误的多个空格:some path 将给出some/some_path.yaml
  • 它还接受下划线some_path会给some/some_path.yaml
  • 它应该足够健壮以纠正错误的多个下划线:some___path将给出some/some_path.yaml
  • 以及下划线和空格的混合some path_here multispace___multiunderscore将给出some/some_path_here_multispace_multiunderscore.yaml
  • 它可以在用户输入中指定或不指定扩展名的情况下工作
  • 它应该对横向路径攻击具有一定的弹性

陷阱:

  • 但它不会处理最后一个单词和扩展名之间的空格,因为它会带来太多的下划线:some path .yaml会给some/some_path_.yaml

而且,所以,这里是所说的剧本:

- hosts: all
  gather_facts: no

  vars_prompt:
    - name: file_name
      prompt: Which decrypted file do you need?
      private: no

  pre_tasks:
    - set_fact:
        fqn: "{{ chunk.0 ~ '/' ~ chunk | join('_') if chunk | length > 1 else chunk | join('_') }}"
      vars:
        chunk: "{{ ((file if file.endswith('.yaml') else file ~ '.yaml')  | replace('_',' ')).split() }}"
        file: "{{ file_name | trim }}"

    - assert:
        that:
          - fqn is file  
          - not fqn.startswith('.') # I am just trying to limit transversal path attack here
          - not fqn.startswith('/') # I am just trying to limit transversal path attack here
          - "'enc' != fqn.split('.')[-2]" # this one is a protection against accessing the encrypted files
        msg: "{{ fqn }} is not a file"          

  tasks:
    - debug:
        msg: "Now, do whatever you like best with the file `{{ fqn | basename }}` at `{{ fqn }}` because I am sure it exists"

这是运行它的两个示例:

  • 使用正确的用户输入:
    Which decrypted file do you need?: middleware config
    
    PLAY [all] *********************************************************************
    
    TASK [set_fact] ****************************************************************
    ok: [localhost]
    
    TASK [assert] ******************************************************************
    ok: [localhost] => changed=false 
      msg: All assertions passed
    
    TASK [debug] *******************************************************************
    ok: [localhost] => 
      msg: Now, do whatever you like best with the file `middleware_config.yaml` at `middleware/middleware_config.yaml` because I am sure it exists
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
  • 用户输入错误:
    Which decrypted file do you need?: middleware config
    
    PLAY [all] *********************************************************************
    
    TASK [set_fact] ****************************************************************
    ok: [localhost]
    
    TASK [assert] ******************************************************************
    fatal: [localhost]: FAILED! => changed=false 
      assertion: fqn is file
      evaluated_to: false
      msg: fake/fake_config.yaml is not a file
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
    

推荐阅读