techno-tim / k3s-ansible

The easiest way to bootstrap a self-hosted High Availability Kubernetes cluster. A fully automated HA k3s etcd install with kube-vip, MetalLB, and more. Build. Destroy. Repeat.
https://technotim.live/posts/k3s-etcd-ansible/
Apache License 2.0
2.41k stars 1.05k forks source link

Kube config file in sudo root directory instead of ansible_user Home directory #223

Closed Jamison1 closed 1 year ago

Jamison1 commented 1 year ago

First off - Thanks for the great playbook!

When running the playbook and following the video, everything worked great until I went to copy the Kube Config file

scp ansibleuser@192.168.30.38:~/.kube/config ~/.kube/config

I was expecting the config file to be on the master node within my ansible user home directory. It wasn't there but instead it was in the root directory.

It appears that when using become: true the cube config is copied to the root directory instead of the user home directory.

Maybe I have this wrong but in attempting to look this up what I have found so far from really old posts is become: true changes the ansible_user_dir to root instead of the expected ansible_user home directory. The end result is that you can't scp the kube config file from the master node ansible_user directory to the ansible machine.

If you change the path to the master node root directory you receive a permission denied error as you can't scp with sudo.

Potential Fix: add roles for the kube config directory and kube config file without become: true so they are placed in the ansible_user Home directory instead of the ansible_user_dir for sudo root.

Thanks for looking into this.

sleiner commented 1 year ago

Hi @Jamison1 👋

It appears that when using become: true the cube config is copied to the root directory instead of the user home directory.

that depends on the position of become: true. Consider these two playbooks:

t.yaml:

---
- hosts: all
  gather_facts: true
  become: true
  tasks:
    - ansible.builtin.debug:
        msg: "ansible_user_id={{ ansible_user_id }}"

p.yaml:

---
- hosts: all
  gather_facts: true
  tasks:
    - ansible.builtin.debug:
        msg: "ansible_user_id={{ ansible_user_id }}"
      become: true

While the first one (t.yaml) gives you ansible_user_id=root for every host, the second one (p.yaml) gives you the (potentially non-root) login user of your target machines: It depends on whether become is set when Ansible is gathering facts rather than whether become is set for the specific task (or role) that you execute. See also this change.

Apart from the little toy example above, the location of .kube/config after running this repo's site.yml playbook is actually checked during our molecule tests: If I run molecule -vvv test -s single_node and search the output for .kube/config, I can see that for example during verify/from_outside : Download kubecfg, the file is downloaded from /home/vagrant, not /root:

<127.0.0.1> FETCH /home/vagrant/.kube/config TO /var/folders/vx/hn90mz9j7x1bw6j_msm723h80000gn/T/ansible.3xtmqi27kubecfg/config
<127.0.0.1> SSH: EXEC scp -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o Port=2222 -o 'IdentityFile="/Users/simon/.cache/molecule/k3s/single_node/.vagrant/machines/control1/virtualbox/private_key"' -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="vagrant"' -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=60s -o ForwardX11=no -o LogLevel=ERROR -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o 'ControlPath="/Users/simon/.ansible/cp/%h-%p-%r"' '[127.0.0.1]:/home/vagrant/.kube/config' /var/folders/vx/hn90mz9j7x1bw6j_msm723h80000gn/T/ansible.3xtmqi27kubecfg/config

So I'm wondering what's different in your setup such that .kube/config is written to /root...

  1. Can you reproduce the toy example?
  2. If so, Can you reproduce the molecule example for 60adb1de42830d18cc512ad12d0c00ad43fdd232?
  3. If so, are you executing the playbook by running ansible-playbook site.yml (from 60adb1de42830d18cc512ad12d0c00ad43fdd232)? Or are you using a modified version of the playbook? Or even triggering site.yml from another playbook?
Jamison1 commented 1 year ago

Ok I created both files as t.yml and p.yml t.yml gave the expected response of root p.yml gave the unexpected response of root

I then changed p.yml to the following:

---
- hosts: all
  become: false  #Need this set first
  gather_facts: true
  tasks:
    - ansible.builtin.debug:
        msg: "ansible_user_id={{ ansible_user_id }}, ansible_user_dir={{ ansible_user_dir  }}"
      become: true

The response was:

ok: [IP_Address] =>
msg: ansible_user_id=ansible, ansible_user_dir=/home/ansible

Demo VMs are set with cloud-init for ssh with authorized_keys, passwordless login, no root login, no root password, passwordless sudo.

#cloud-config
name: ansible
groups: users,admin,wheel
sudo: ALL=(ALL) NOPASSWD:ALL

Upon logging into [IP_Address] referenced above and testing with id, sudo id, whoami and sudo whoami I get the following:

ansible@ansible:~$ id
uid=1000(ansible) gid=1001(ansible) groups=1001(ansible),100(users),118(admin),1000(wheel)
ansible@ansible:~$ sudo id
uid=0(root) gid=0(root) groups=0(root)
ansible@ansible:~$ whoami
ansible
ansible@ansible:~$ sudo whoami
root

Normal login with authorized key appears to be correctly logged in as ansible user and in order to become root user - sudo must be added at the beginning of the string.

For some reason become:false needs to be set before gather_facts: true in order to recognize the ansible_user_id of ansible instead of root

When running the playbook I am using: ansible-playbook site.yml -i inventory/my-cluster/hosts.ini

Any ideas what I am doing wrong here with my setup?

sleiner commented 1 year ago

Interesting!

for some reason become:false needs to be set before gather_facts: true in order to recognize the ansible_user_id of ansible instead of root.

So, one could suspect that in your setup become: true would somehow be implied. To check if that is actually the case, could you run the same playbook without any explicit become property?

---
- hosts: all
  gather_facts: true
  tasks:
    - ansible.builtin.debug:
        msg: "ansible_user_id={{ ansible_user_id }}"
Jamison1 commented 1 year ago

Upon running that playbook without any explicit become property the response is:

ok: [IP_Address] =>
msg: ansible_user_id=root, ansible_user_dir=/root
sleiner commented 1 year ago

That seems like the DEFAULT_BECOME configuration option, which is actually (IMO erroneously) set in the ansible.cfg file that comes with this repo. Neither my local setup nor the molecule tests in CI use that configuration, so the problem was not caught there. I have opened a PR which should fix this: #224. @Jamison1, can you confirm that .kube/config is uploaded to the expected directory when these changes are applied?

Jamison1 commented 1 year ago

Removing the privilege escalation worked!

Upon running scp ansible@[IP_Address]:~/.kube/config ~/.kube/config the config directory was copied over to the ansible host.

Some users may not be familiar with the export function for KUBECONFIG so the kubectl command can find the kube config file. So for clarity needed to also run export KUBECONFIG=$HOME/.kube/config/config and then kubectl get nodes worked great.

timothystewart6 commented 1 year ago

This was closed by #224