mirror of
https://github.com/ansible-collections/kubernetes.core.git
synced 2026-07-28 10:24:45 +00:00
kubectl: redacted token and password from console log (#159)
** SECURITY_FIX ** for CVE-2020-1753 kubectl connection plugin now redact kubectl_token and kubectl_password from console log. Fixes: #65 Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
2
changelogs/fragments/65_kubectl.yml
Normal file
2
changelogs/fragments/65_kubectl.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
security_fixes:
|
||||||
|
- kubectl - connection plugin now redact kubectl_token and kubectl_password in console log (https://github.com/ansible-collections/community.kubernetes/issues/65).
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = r"""
|
||||||
author:
|
author:
|
||||||
- xuxinkun
|
- xuxinkun
|
||||||
|
|
||||||
@@ -38,7 +38,8 @@ DOCUMENTATION = """
|
|||||||
options:
|
options:
|
||||||
kubectl_pod:
|
kubectl_pod:
|
||||||
description:
|
description:
|
||||||
- Pod name. Required when the host name does not match pod name.
|
- Pod name.
|
||||||
|
- Required when the host name does not match pod name.
|
||||||
default: ''
|
default: ''
|
||||||
vars:
|
vars:
|
||||||
- name: ansible_kubectl_pod
|
- name: ansible_kubectl_pod
|
||||||
@@ -46,7 +47,8 @@ DOCUMENTATION = """
|
|||||||
- name: K8S_AUTH_POD
|
- name: K8S_AUTH_POD
|
||||||
kubectl_container:
|
kubectl_container:
|
||||||
description:
|
description:
|
||||||
- Container name. Required when a pod contains more than one container.
|
- Container name.
|
||||||
|
- Required when a pod contains more than one container.
|
||||||
default: ''
|
default: ''
|
||||||
vars:
|
vars:
|
||||||
- name: ansible_kubectl_container
|
- name: ansible_kubectl_container
|
||||||
@@ -226,7 +228,7 @@ class Connection(ConnectionBase):
|
|||||||
def _build_exec_cmd(self, cmd):
|
def _build_exec_cmd(self, cmd):
|
||||||
""" Build the local kubectl exec command to run cmd on remote_host
|
""" Build the local kubectl exec command to run cmd on remote_host
|
||||||
"""
|
"""
|
||||||
local_cmd = [self.transport_cmd]
|
local_cmd = censored_local_cmd = [self.transport_cmd]
|
||||||
|
|
||||||
# Build command options based on doc string
|
# Build command options based on doc string
|
||||||
doc_yaml = AnsibleLoader(self.documentation).get_single_data()
|
doc_yaml = AnsibleLoader(self.documentation).get_single_data()
|
||||||
@@ -235,28 +237,36 @@ class Connection(ConnectionBase):
|
|||||||
# Translate verify_ssl to skip_verify_ssl, and output as string
|
# Translate verify_ssl to skip_verify_ssl, and output as string
|
||||||
skip_verify_ssl = not self.get_option(key)
|
skip_verify_ssl = not self.get_option(key)
|
||||||
local_cmd.append(u'{0}={1}'.format(self.connection_options[key], str(skip_verify_ssl).lower()))
|
local_cmd.append(u'{0}={1}'.format(self.connection_options[key], str(skip_verify_ssl).lower()))
|
||||||
|
censored_local_cmd.append(u'{0}={1}'.format(self.connection_options[key], str(skip_verify_ssl).lower()))
|
||||||
elif not key.endswith('container') and self.get_option(key) and self.connection_options.get(key):
|
elif not key.endswith('container') and self.get_option(key) and self.connection_options.get(key):
|
||||||
cmd_arg = self.connection_options[key]
|
cmd_arg = self.connection_options[key]
|
||||||
local_cmd += [cmd_arg, self.get_option(key)]
|
local_cmd += [cmd_arg, self.get_option(key)]
|
||||||
|
# Redact password and token from console log
|
||||||
|
if key.endswith(('_token', '_password')):
|
||||||
|
censored_local_cmd += [cmd_arg, '********']
|
||||||
|
|
||||||
extra_args_name = u'{0}_extra_args'.format(self.transport)
|
extra_args_name = u'{0}_extra_args'.format(self.transport)
|
||||||
if self.get_option(extra_args_name):
|
if self.get_option(extra_args_name):
|
||||||
local_cmd += self.get_option(extra_args_name).split(' ')
|
local_cmd += self.get_option(extra_args_name).split(' ')
|
||||||
|
censored_local_cmd += self.get_option(extra_args_name).split(' ')
|
||||||
|
|
||||||
pod = self.get_option(u'{0}_pod'.format(self.transport))
|
pod = self.get_option(u'{0}_pod'.format(self.transport))
|
||||||
if not pod:
|
if not pod:
|
||||||
pod = self._play_context.remote_addr
|
pod = self._play_context.remote_addr
|
||||||
# -i is needed to keep stdin open which allows pipelining to work
|
# -i is needed to keep stdin open which allows pipelining to work
|
||||||
local_cmd += ['exec', '-i', pod]
|
local_cmd += ['exec', '-i', pod]
|
||||||
|
censored_local_cmd += ['exec', '-i', pod]
|
||||||
|
|
||||||
# if the pod has more than one container, then container is required
|
# if the pod has more than one container, then container is required
|
||||||
container_arg_name = u'{0}_container'.format(self.transport)
|
container_arg_name = u'{0}_container'.format(self.transport)
|
||||||
if self.get_option(container_arg_name):
|
if self.get_option(container_arg_name):
|
||||||
local_cmd += ['-c', self.get_option(container_arg_name)]
|
local_cmd += ['-c', self.get_option(container_arg_name)]
|
||||||
|
censored_local_cmd += ['-c', self.get_option(container_arg_name)]
|
||||||
|
|
||||||
local_cmd += ['--'] + cmd
|
local_cmd += ['--'] + cmd
|
||||||
|
censored_local_cmd += ['--'] + cmd
|
||||||
|
|
||||||
return local_cmd
|
return local_cmd, censored_local_cmd
|
||||||
|
|
||||||
def _connect(self, port=None):
|
def _connect(self, port=None):
|
||||||
""" Connect to the container. Nothing to do """
|
""" Connect to the container. Nothing to do """
|
||||||
@@ -269,9 +279,9 @@ class Connection(ConnectionBase):
|
|||||||
""" Run a command in the container """
|
""" Run a command in the container """
|
||||||
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
||||||
|
|
||||||
local_cmd = self._build_exec_cmd([self._play_context.executable, '-c', cmd])
|
local_cmd, censored_local_cmd = self._build_exec_cmd([self._play_context.executable, '-c', cmd])
|
||||||
|
|
||||||
display.vvv("EXEC %s" % (local_cmd,), host=self._play_context.remote_addr)
|
display.vvv("EXEC %s" % (censored_local_cmd,), host=self._play_context.remote_addr)
|
||||||
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd]
|
||||||
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
|
p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
@@ -311,7 +321,7 @@ class Connection(ConnectionBase):
|
|||||||
count = ' count=0'
|
count = ' count=0'
|
||||||
else:
|
else:
|
||||||
count = ''
|
count = ''
|
||||||
args = self._build_exec_cmd([self._play_context.executable, "-c", "dd of=%s bs=%s%s" % (out_path, BUFSIZE, count)])
|
args, dummy = self._build_exec_cmd([self._play_context.executable, "-c", "dd of=%s bs=%s%s" % (out_path, BUFSIZE, count)])
|
||||||
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
|
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
|
||||||
try:
|
try:
|
||||||
p = subprocess.Popen(args, stdin=in_file,
|
p = subprocess.Popen(args, stdin=in_file,
|
||||||
@@ -333,7 +343,7 @@ class Connection(ConnectionBase):
|
|||||||
|
|
||||||
# kubectl doesn't have native support for fetching files from
|
# kubectl doesn't have native support for fetching files from
|
||||||
# running containers, so we use kubectl exec to implement this
|
# running containers, so we use kubectl exec to implement this
|
||||||
args = self._build_exec_cmd([self._play_context.executable, "-c", "dd if=%s bs=%s" % (in_path, BUFSIZE)])
|
args, dummy = self._build_exec_cmd([self._play_context.executable, "-c", "dd if=%s bs=%s" % (in_path, BUFSIZE)])
|
||||||
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
|
args = [to_bytes(i, errors='surrogate_or_strict') for i in args]
|
||||||
actual_out_path = os.path.join(out_dir, os.path.basename(in_path))
|
actual_out_path = os.path.join(out_dir, os.path.basename(in_path))
|
||||||
with open(to_bytes(actual_out_path, errors='surrogate_or_strict'), 'wb') as out_file:
|
with open(to_bytes(actual_out_path, errors='surrogate_or_strict'), 'wb') as out_file:
|
||||||
|
|||||||
Reference in New Issue
Block a user