如何从目标主机获取 Ansible 客户端 IP?

huangapple go评论73阅读模式
英文:

How can I get Ansible client IP from target host?

问题

在一个没有互联网访问以执行公共IP地址查找的隔离网络中,我想要从控制节点上运行一个Playbook,针对多个目标主机执行其中一个任务是通过HTTP/HTTPS从控制节点下载文件,而不将控制节点IP硬编码为任务的一部分。例如。

控制节点:192.168.0.5
目标 1:192.168.0.10
目标 2:192.168.0.11
目标 3:192.168.0.12

控制节点可以通过DHCP配置不同的IP,并且ansible_all_ipv4_addresses中可能列出了多个网络接口(其中一些可能对目标主机不可用),因此可能不容易确定目标主机应该从ansible_facts中的哪个网络接口使用,而不探讨通过循环遍历它们并设置超时直到文件被下载完成的方法。看起来似乎最稳健的确定控制节点的公共IP的方法(假设Web服务器正在侦听0.0.0.0)将是确定目标主机从控制节点的已建立连接的源IP(192.168.0.5) - 有没有办法做到这一点?

从控制节点而不是将文件发送到远程主机下载文件的动机是,一些目标主机正在运行Windows,win_copy模块通过WinRM非常慢,因此Ansible文档包含以下注意事项:

> 由于win_copy是通过WinRM运行的,它不是非常高效的传输机制。如果要发送大文件,请考虑将它们托管在Web服务上,然后使用ansible.windows.win_get_url。

英文:

On an isolated network (without internet access to do public IP address lookups), I want to run a playbook from a controller against a number of target hosts where one of the tasks is to download a file via HTTP/HTTPS from the controller without hard-coding the controller IP as part of the task. E.g.

Controller: 192.168.0.5
Target 1: 192.168.0.10
Target 2: 192.168.0.11
Target 3: 192.168.0.12

The controller can have different IPs configured via DHCP, and there could be multiple network interfaces listed in ansible_all_ipv4_addresses (some of which may not be available to the target hosts) so it may not be straight forward to determine which network interface the target hosts should use from ansible_facts on localhost without exploring the idea of looping through them with a timeout until the file has been downloaded. It seems as though the most robust way to determine the public IP of the controller (assuming the web server is listening on 0.0.0.0) would be to determine the originating IP of the established connection (192.168.0.5) from the target host - is there a way to do this?

The motivation for downloading the file from the controller rather than sending it to remote hosts is that some of the target hosts are running Windows and the win_copy module is incredibly slow via WinRM so the Ansible documentation includes the following note:

> Because win_copy runs over WinRM, it is not a very efficient transfer
> mechanism. If sending large files consider hosting them on a web
> service and using ansible.windows.win_get_url instead.

答案1

得分: 1

以下是翻译好的部分:

在我的机器上进行了有限的测试,该机器只有一个IP地址,只有一个目标。但我不明白为什么它在你的情况下不起作用。

给定以下的 inventories/default/hosts.yml 文件:

all:
  hosts:
    target1:
      ansible_host: 192.168.0.10
    target2:
      ansible_host: 192.168.0.11
    target3:
      ansible_host: 192.168.0.12

以下测试playbook应该可以达到你的期望。将虚拟的调试任务替换为 get_url/uri 以启动下载。

注意:

  • 这个playbook 假定你在控制节点上可以访问 ip 命令行工具。
  • 我默认控制节点用于连接目标的IP地址是目标在另一个方向上可以访问的IP地址。如果不是这种情况,那么下面的内容在你的情况下将无法工作。
---
- hosts: all
  gather_facts: false

  tasks:
    - name: 检查控制节点对每个目标目的地的路由
      ansible.builtin.command: ip route get {{ ansible_host }}
      register: route_cmd
      delegate_to: localhost

    - name: 注册每个目标的控制节点出口IP
      ansible.builtin.set_fact:
        controller_ip: "{{ route_cmd.stdout_lines[0] | regex_replace('^.* src (\\d*(\\.\\d*){3}).*$', '\') }}"

    - name: 显示结果
      ansible.builtin.debug:
        msg: "我将从目标 {{ inventory_hostname }} ({{ ansible_host }}) 连接到控制节点,使用IP {{ controller_ip }}"
英文:

Limited test on my machine which has a single ip and with a single target. But I don't see why it would not work in your scenario.

Given the following inventories/default/hosts.yml

all:
  hosts:
    target1:
      ansible_host: 192.168.0.10
    target2:
      ansible_host: 192.168.0.11
    target3:
      ansible_host: 192.168.0.12

The following test playbook should do what you expect. Replace the dummy debug task with get_url/uri to initiate the download.

Notes:

  • this playbook infers you have access to the ip command line tool on the controller.
  • I took for granted that the controller IP used to connect to target is the one that the target has access to in the other direction. If this isn't the case then the below will not work in your situation.
---
- hosts: all
  gather_facts: false

  tasks:
    - name: Check route on controller for each target destination
      ansible.builtin.command: ip route get {{ ansible_host }}
      register: route_cmd
      delegate_to: localhost

    - name: Register the controller outgoing ip for each target
      ansible.builtin.set_fact:
        controller_ip: "{{ route_cmd.stdout_lines[0] | regex_replace('^.* src (\\d*(\\.\\d*){3}).*$', '\') }}"

    - name: Show result
      ansible.builtin.debug:
        msg: "I would connect from target {{ inventory_hostname }} ({{ ansible_host }}) 
          to controller using ip {{ controller_ip }}"

</details>



# 答案2
**得分**: 0

是的,当然可以,至少如果托管节点仅运行Linux操作系统的话。可以参考示例 [Find the IP address of the client in an SSH session](https://stackoverflow.com/a/50703599/6771046)。以下是一个简单的示例Playbook:

```lang-yaml
---
- hosts: test
  become: false
  gather_facts: false

  tasks:

  - name: 获取控制节点连接的IP
    shell:
      cmd:  who | cut -d "(" -f2 | cut -d ")" -f1
    changed_when: false
    check_mode: false
    register: connected_from

  - name: 显示连接来源
    debug:
      msg: "{{ connected_from.stdout }}"

或者更简单的方式,如果已经收集了Ansible facts

---
- hosts: test
  become: false
  gather_facts: true
  gather_subset:
    - "env"
    - "!all"
    - "!min"

  tasks:

  - name: 显示连接来源
    debug:
       msg: "{{ ansible_env.SSH_CONNECTION.split(' ') | first }}"

这将提供所需的信息。

任务 [显示连接来源] ******
ok: [test.example.com] =>
  msg: 192.0.2.1

背景信息

请注意,此方法不适用于运行Windows操作系统且使用WinRM连接的托管节点,详见:

英文:

> It seems as though the most robust way to determine the public IP of the Control Node ... would be to determine the originating IP of the established connection ... on the Manged Node - Is there a way to do this?

Yes, of course, at least if the Managed Nodes are Linux only. See in example Find the IP address of the client in an SSH session, so a minimal example playbook

---
- hosts: test
  become: false
  gather_facts: false

  tasks:

  - name: Gather Control Node Connection IP
    shell:
      cmd:  who | cut -d &quot;(&quot; -f2 | cut -d &quot;)&quot; -f1
    changed_when: false
    check_mode: false
    register: connected_from

  - name: Show Connected From
    debug:
      msg: &quot;{{ connected_from.stdout }}&quot;

or even simpler and if Ansible facts are gathered

---
- hosts: test
  become: false
  gather_facts: true
  gather_subset:
    - &quot;env&quot;
    - &quot;!all&quot;
    - &quot;!min&quot;

  tasks:

  - name: Show Connected From
    debug:
       msg: &quot;{{ ansible_env.SSH_CONNECTION.split(&#39; &#39;) | first }}&quot;
    # when: ansible_os_family != &#39;Windows&#39; # and if more facts were gathered

would result into the requested information.

TASK [Show Connected From] ******
ok: [test.example.com] =&gt;
  msg: 192.0.2.1

Background Information

The approach will not work for a Managed Node running a Windows OS and a WinRM Connection, see also

huangapple
  • 本文由 发表于 2023年6月1日 23:34:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76383547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定