Repository: ansible/ansible
Pull request: dont specify default for port, allow ssh/config
 also added general note on how defaults work.
fixes #74524

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Bugfix Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
connection/ssh

Problem statement:
Ignoring ~/.ssh/config configuration file
### Summary

After upgrade from "ansible-base(2.10.8)" to "ansible-core(2.11.0)" Ansible is ignoring "~/.ssh/config" configuration file.

### Issue Type

Bug Report

### Component Name

ansible-core

### Ansible Version

```console
ansible [core 2.11.0] 
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/sbin/ansible
  python version = 3.9.4 (default, Apr 20 2021, 15:51:38) [GCC 10.2.0]
  jinja version = 2.11.3
  libyaml = True
```


### Configuration

```console
Nothing
```


### OS / Environment

Arch Linux

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
Creates an example entry in "~/.ssh/config"
```
Host 192.168.0.1
      Port 2222
```
Run the following ad-hoc command
```
ansible all -i 192.168.0.1, -m setup
```
The output follows
```
192.168.0.1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.0.1 port 22: Connection refused",
"unreachable": true
}
```
As you can notice the output shows "port 22" and not "port 2222".

### Expected Results

Is expected the ansible try to connect to host using the parameters configured at ".ssh/config" file.

### Actual Results

```console
ansible [core 2.11.0] 
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/USER/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/USER/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.4 (default, Apr 20 2021, 15:51:38) [GCC 10.2.0]
  jinja version = 2.11.3
  libyaml = True
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
Parsed 192.168.0.1, inventory source with host_list plugin
Loading callback plugin minimal of type stdout, v2.0 from /usr/lib/python3.9/site-packages/ansible/plugins/callback/minimal.py
Attempting to use 'default' callback.
Skipping callback 'default', as we already have a stdout callback.
Attempting to use 'junit' callback.
Attempting to use 'minimal' callback.
Skipping callback 'minimal', as we already have a stdout callback.
Attempting to use 'oneline' callback.
Skipping callback 'oneline', as we already have a stdout callback.
Attempting to use 'tree' callback.
META: ran handlers
<192.168.0.1> ESTABLISH SSH CONNECTION FOR USER: None
<192.168.0.1> SSH: ansible.cfg set ssh_args: (-C)(-o)(ControlMaster=auto)(-o)(ControlPersist=60s)
<192.168.0.1> SSH: ANSIBLE_HOST_KEY_CHECKING/host_key_checking disabled: (-o)(StrictHostKeyChecking=no)
<192.168.0.1> SSH: ANSIBLE_REMOTE_PORT/remote_port/ansible_port set: (-o)(Port=22)
<192.168.0.1> SSH: ansible_password/ansible_ssh_password not set: (-o)(KbdInteractiveAuthentication=no)(-o)(PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey)(-o)(PasswordAuthentication=no)
<192.168.0.1> SSH: ANSIBLE_TIMEOUT/timeout set: (-o)(ConnectTimeout=10)
<192.168.0.1> SSH: Set ssh_common_args: ()
<192.168.0.1> SSH: Set ssh_extra_args: ()
<192.168.0.1> SSH: found only ControlPersist; added ControlPath: (-o)(ControlPath=/home/USER/.ansible/cp/144d72725f)
<192.168.0.1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/home/USER/.ansible/cp/144d72725f 192.168.0.1 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
<192.168.0.1> (255, b'', b'OpenSSH_8.6p1, OpenSSL 1.1.1k  25 Mar 2021\r\ndebug1: Reading configuration data /home/USER/.ssh/config\r\ndebug1: /home/USER/.ssh/config line 8: Applying options for 192.168.0.1\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug2: resolve_canonicalize: hostname 192.168.0.1 is address\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts\' -> \'/home/USER/.ssh/known_hosts\'\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts2\' -> \'/home/USER/.ssh/known_hosts2\'\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/USER/.ansible/cp/144d72725f" does not exist\r\ndebug3: ssh_connect_direct: entering\r\ndebug1: Connecting to 192.168.0.1 [192.168.0.1] port 22.\r\ndebug3: set_sock_tos: set socket 3 IP_TOS 0x48\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 192.168.0.1 port 22: Connection refused\r\nssh: connect to host 192.168.0.1 port 22: Connection refused\r\n')
<192.168.0.1> ssh_retry: attempt: 1, ssh return code is 255. cmd ([b'ssh', b'-vvv', b'-C', b'-o', b'ControlMaster=auto', b'-o', b'ControlPersist=60s', b'-o', b'StrictHostKeyChecking=no', b'-o', b'Port=22', b'-o', b'KbdInteractiveAuthentication=no', b'-o', b'PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey', b'-o', b'PasswordAuthentication=no', b'-o', b'ConnectTimeout=10', b'-o', b'ControlPath=/home/USER/.ansible/cp/144d72725f', b'192.168.0.1', b"/bin/sh -c 'echo ~ && sleep 0'"]...), pausing for 0 seconds
<192.168.0.1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/home/USER/.ansible/cp/144d72725f 192.168.0.1 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
<192.168.0.1> (255, b'', b'OpenSSH_8.6p1, OpenSSL 1.1.1k  25 Mar 2021\r\ndebug1: Reading configuration data /home/USER/.ssh/config\r\ndebug1: /home/USER/.ssh/config line 8: Applying options for 192.168.0.1\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug2: resolve_canonicalize: hostname 192.168.0.1 is address\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts\' -> \'/home/USER/.ssh/known_hosts\'\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts2\' -> \'/home/USER/.ssh/known_hosts2\'\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/USER/.ansible/cp/144d72725f" does not exist\r\ndebug3: ssh_connect_direct: entering\r\ndebug1: Connecting to 192.168.0.1 [192.168.0.1] port 22.\r\ndebug3: set_sock_tos: set socket 3 IP_TOS 0x48\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 192.168.0.1 port 22: Connection refused\r\nssh: connect to host 192.168.0.1 port 22: Connection refused\r\n')
<192.168.0.1> ssh_retry: attempt: 2, ssh return code is 255. cmd ([b'ssh', b'-vvv', b'-C', b'-o', b'ControlMaster=auto', b'-o', b'ControlPersist=60s', b'-o', b'StrictHostKeyChecking=no', b'-o', b'Port=22', b'-o', b'KbdInteractiveAuthentication=no', b'-o', b'PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey', b'-o', b'PasswordAuthentication=no', b'-o', b'ConnectTimeout=10', b'-o', b'ControlPath=/home/USER/.ansible/cp/144d72725f', b'192.168.0.1', b"/bin/sh -c 'echo ~ && sleep 0'"]...), pausing for 1 seconds
<192.168.0.1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/home/USER/.ansible/cp/144d72725f 192.168.0.1 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
<192.168.0.1> (255, b'', b'OpenSSH_8.6p1, OpenSSL 1.1.1k  25 Mar 2021\r\ndebug1: Reading configuration data /home/USER/.ssh/config\r\ndebug1: /home/USER/.ssh/config line 8: Applying options for 192.168.0.1\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug2: resolve_canonicalize: hostname 192.168.0.1 is address\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts\' -> \'/home/USER/.ssh/known_hosts\'\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts2\' -> \'/home/USER/.ssh/known_hosts2\'\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/USER/.ansible/cp/144d72725f" does not exist\r\ndebug3: ssh_connect_direct: entering\r\ndebug1: Connecting to 192.168.0.1 [192.168.0.1] port 22.\r\ndebug3: set_sock_tos: set socket 3 IP_TOS 0x48\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 192.168.0.1 port 22: Connection refused\r\nssh: connect to host 192.168.0.1 port 22: Connection refused\r\n')
<192.168.0.1> ssh_retry: attempt: 3, ssh return code is 255. cmd ([b'ssh', b'-vvv', b'-C', b'-o', b'ControlMaster=auto', b'-o', b'ControlPersist=60s', b'-o', b'StrictHostKeyChecking=no', b'-o', b'Port=22', b'-o', b'KbdInteractiveAuthentication=no', b'-o', b'PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey', b'-o', b'PasswordAuthentication=no', b'-o', b'ConnectTimeout=10', b'-o', b'ControlPath=/home/USER/.ansible/cp/144d72725f', b'192.168.0.1', b"/bin/sh -c 'echo ~ && sleep 0'"]...), pausing for 3 seconds
<192.168.0.1> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o Port=22 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o ControlPath=/home/USER/.ansible/cp/144d72725f 192.168.0.1 '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
<192.168.0.1> (255, b'', b'OpenSSH_8.6p1, OpenSSL 1.1.1k  25 Mar 2021\r\ndebug1: Reading configuration data /home/USER/.ssh/config\r\ndebug1: /home/USER/.ssh/config line 8: Applying options for 192.168.0.1\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug2: resolve_canonicalize: hostname 192.168.0.1 is address\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts\' -> \'/home/USER/.ssh/known_hosts\'\r\ndebug3: expanded UserKnownHostsFile \'~/.ssh/known_hosts2\' -> \'/home/USER/.ssh/known_hosts2\'\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket "/home/USER/.ansible/cp/144d72725f" does not exist\r\ndebug3: ssh_connect_direct: entering\r\ndebug1: Connecting to 192.168.0.1 [192.168.0.1] port 22.\r\ndebug3: set_sock_tos: set socket 3 IP_TOS 0x48\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 192.168.0.1 port 22: Connection refused\r\nssh: connect to host 192.168.0.1 port 22: Connection refused\r\n')
192.168.0.1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: OpenSSH_8.6p1, OpenSSL 1.1.1k  25 Mar 2021\r\ndebug1: Reading configuration data /home/USER/.ssh/config\r\ndebug1: /home/USER/.ssh/config line 8: Applying options for 192.168.0.1\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug2: resolve_canonicalize: hostname 192.168.0.1 is address\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/USER/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/USER/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master\r\ndebug1: Control socket \"/home/USER/.ansible/cp/144d72725f\" does not exist\r\ndebug3: ssh_connect_direct: entering\r\ndebug1: Connecting to 192.168.0.1 [192.168.0.1] port 22.\r\ndebug3: set_sock_tos: set socket 3 IP_TOS 0x48\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug1: connect to address 192.168.0.1 port 22: Connection refused\r\nssh: connect to host 192.168.0.1 port 22: Connection refused",
    "unreachable": true
}
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

Hints from issue discussion:
Files identified in the description:
None

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
This is likely caused by https://github.com/ansible/ansible/commit/935528e22e5283ee3f63a8772830d3d01f55ed8c

The ssh plugin defines the default port as 22, and with the switch, we would always use that port if one was not provided.  Switching the default to `None` would probably restore previous behavior.
I'm also having trouble with one of my playbooks which it's executing tasks on servers using port other than 22.
`(ansible controller > jump host (2222) > destination (2222)) - does not work`
But it's fine when I'm connecting to other servers using a jump host which is using another port, but destination is using 22.
`(ansible controller > jump host (2222) > destination (22)) - works`
@sivel is probably correct as config states the default is 22 so it will use that and override ssh config, the fix is making the default 'none' and state that 'ssh uses 22 by default' .. which is what we relied on, that will allow .ssh/config to override again.

workaround set port to None in config
```
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 0af9ac73d7..36490eab65 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -179,9 +179,10 @@ DOCUMENTATION = '''
             - name: ansible_ssh_retries
               version_added: '2.7'
       port:
-          description: Remote port to connect to.
+          description:
+            - Remote port to connect to.
+            -  While this plugin defaults to 'none' this is just meant to use the default from the ssh tool (22).
           type: int
-          default: 22
           ini:
             - section: defaults

 ```
            
@bcoca I'm not sure if I can follow you, I have set port to 2222 in my config, how can I connect to my server if I set it to none?

this is my config file (names and hostnames are obsecured)
```
Host anotherserver
  Hostname x.y.z.t
  User root
  Port 2222
Host server
  Hostname a.b.c.d
  User root
  Port 2222
  ProxyJump anotherserver
```
^ that is ansible configuration vs ssh configuration, ansible overrides ssh, if set in ansible (22) it overrides Port 2222
my patch 'fixes' so default is None, so Ansible WONT override
Gotcha. Fixed by removing `default: 22` in `port` section from `plugins/connection/ssh.py` as mentioned in https://github.com/ansible/ansible/issues/74524#issuecomment-830420414.
Thanks @sivel @bcoca!

Commit message: clog

Patch to review:
diff --git a/changelogs/fragments/ssh_porr_default.fix.yml b/changelogs/fragments/ssh_porr_default.fix.yml
new file mode 100644
index 00000000000000..c5d8eedf76dc58
--- /dev/null
+++ b/changelogs/fragments/ssh_porr_default.fix.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - Remove 'default' from ssh pluing as we want to rely on default from ssh itself or ssh/config.
diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py
index 0af9ac73d7222d..52a92341e5d2bd 100644
--- a/lib/ansible/plugins/connection/ssh.py
+++ b/lib/ansible/plugins/connection/ssh.py
@@ -19,6 +19,9 @@
     extends_documentation_fragment:
         - connection_pipelining
     version_added: historical
+    notes:
+        - Many options default to 'None' here but that only means we don't override the ssh tool's defaults and/or configuration.
+          For example, if you specify the port in this plugin it will override any `Port` entry in your `.ssh/config`.
     options:
       host:
           description: Hostname/ip to connect to.
@@ -181,7 +184,6 @@
       port:
           description: Remote port to connect to.
           type: int
-          default: 22
           ini:
             - section: defaults
               key: remote_port


Code review comments:
diff_hunk: @@ -0,0 +1,2 @@
+bugfixes:
+  - Remove 'default' from ssh pluing as we want to rely on default from ssh itself or ssh/config.
path: changelogs/fragments/ssh_porr_default.fix.yml
text: @user1:
```suggestion
  - Remove 'default' from ssh plugin as we want to rely on default from ssh itself or ssh/config.
```