Skip to content

Commit 03c82f6

Browse files
MB-52995 Don't start erlang node when passing password to...
... babysitter Change-Id: I8db0a28cbc30f5919e05b43f62d75fd9602cc130 Reviewed-on: https://review.couchbase.org/c/couchbase-cli/+/179389 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: James Lee <james.lee@couchbase.com>
1 parent d4e4e42 commit 03c82f6

2 files changed

Lines changed: 76 additions & 48 deletions

File tree

cbmgr.py

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import platform
99
import random
1010
import re
11+
import socket
1112
import string
1213
import subprocess
1314
import sys
@@ -1681,55 +1682,47 @@ def __init__(self):
16811682

16821683
def execute(self, opts):
16831684
if opts.send_password is not None:
1684-
path = [CB_BIN_PATH, os.environ['PATH']]
1685-
if os.name == 'posix':
1686-
os.environ['PATH'] = ':'.join(path)
1687-
else:
1688-
os.environ['PATH'] = ';'.join(path)
1689-
1690-
cookiefile = os.path.join(opts.config_path, "couchbase-server.babysitter.cookie")
1691-
if not os.path.isfile(cookiefile):
1685+
portfile = os.path.join(opts.config_path, "couchbase-server.babysitter.smport")
1686+
if not os.path.isfile(portfile):
16921687
_exit_if_errors(["The node is down"])
1693-
cookie = _exit_on_file_read_failure(cookiefile, "Insufficient privileges to send master password - Please"
1694-
" execute this command as a operating system user who has"
1695-
" file system read permission on the Couchbase Server "
1696-
" configuration").rstrip()
1697-
1698-
nodefile = os.path.join(opts.config_path, "couchbase-server.babysitter.node")
1699-
node = _exit_on_file_read_failure(nodefile).rstrip()
1700-
1701-
self.prompt_for_master_pwd(node, cookie, opts.send_password, opts.config_path)
1688+
familyport = _exit_on_file_read_failure(
1689+
portfile, "Insufficient privileges to send master password - Please"
1690+
" execute this command as an operating system user who has"
1691+
" file system read permission on the Couchbase Server "
1692+
" configuration").rstrip()
1693+
[afamilystr, portstr] = familyport.split()
1694+
port = int(portstr)
1695+
afamily = socket.AF_INET6 if afamilystr == "inet6" else socket.AF_INET
1696+
self.prompt_for_master_pwd(afamily, port, opts.send_password)
17021697
else:
17031698
_exit_if_errors(["No parameters set"])
17041699

1705-
def prompt_for_master_pwd(self, node, cookie, password, cb_cfg_path):
1706-
dist_cfg_file = os.path.join(cb_cfg_path, "config", "dist_cfg")
1707-
1700+
def prompt_for_master_pwd(self, afamily, port, password):
17081701
if password == '':
17091702
password = getpass.getpass("\nEnter master password:")
17101703

1711-
name = 'executioner@cb.local'
1712-
args = ['-pa', CB_NS_EBIN_PATH, CB_BABYSITTER_EBIN_PATH, '-noinput', '-name', name, '-proto_dist', 'cb',
1713-
'-eval', 'erlang:set_cookie(node(), list_to_atom(os:getenv("CB_COOKIE"))).', '-epmd_module', 'cb_epmd',
1714-
'-kernel'] + CB_INETRC_OPT + \
1715-
['dist_config_file', f'"{dist_cfg_file}"', '-run', 'encryption_service',
1716-
'remote_set_password', node]
1717-
1718-
rc, out, err = self.run_process("erl", args, extra_env={'SETPASSWORD': password, 'CB_COOKIE': cookie})
1719-
1720-
if rc == 0:
1704+
sock = socket.socket(afamily, socket.SOCK_DGRAM)
1705+
try:
1706+
sock.settimeout(5)
1707+
addr = "::1" if afamily == socket.AF_INET6 else "127.0.0.1"
1708+
sock.sendto(password.encode('utf-8'), (addr, port))
1709+
(result, _) = sock.recvfrom(128)
1710+
except socket.timeout:
1711+
result = b'timeout'
1712+
finally:
1713+
sock.close()
1714+
1715+
if result == b'ok':
17211716
print("SUCCESS: Password accepted. Node started booting.")
1722-
elif rc == 101:
1717+
elif result == b'retry':
17231718
print("Incorrect password.")
1724-
self.prompt_for_master_pwd(node, cookie, '', cb_cfg_path)
1725-
elif rc == 102:
1726-
_exit_if_errors(["Password was already supplied"])
1727-
elif rc == 103:
1728-
_exit_if_errors(["The node is down"])
1729-
elif rc == 104:
1719+
self.prompt_for_master_pwd(afamily, port, '')
1720+
elif result == b'timeout':
1721+
_exit_if_errors(["Timeout"])
1722+
elif result == b'auth_failure':
17301723
_exit_if_errors(["Incorrect password. Node shuts down."])
17311724
else:
1732-
_exit_if_errors([f'Unknown error: {rc} {out}, {err}'])
1725+
_exit_if_errors([f'Unknown error: {result}'])
17331726

17341727
def run_process(self, name, args, extra_env=None):
17351728
try:

test/test_cli.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
that validates input correctly"""
33
import json
44
import os
5+
import socket
56
import sys
67
import tempfile
8+
import threading
79
import unittest
810
import urllib
911
from argparse import Namespace
@@ -1867,22 +1869,55 @@ def test_get_group_no_group_name(self):
18671869
class TestMasterPassword(CommandTest):
18681870
def setUp(self):
18691871
self.command = ['couchbase-cli', 'master-password']
1870-
self.cmd_args = ['--config-path', '.', '--send-password', 'password']
1872+
self.cmd_args = ['--config-path', '.', '--send-password', 'asdasd']
18711873
self.server_args = {}
18721874
super(TestMasterPassword, self).setUp()
18731875

1874-
def test_missing_cookie(self):
1876+
def test_missing_portfile(self):
18751877
self.system_exit_run(self.command + self.cmd_args, self.server_args)
18761878
self.assertIn('The node is down', self.str_output)
18771879

1878-
def test_cannot_read_cookie(self):
1879-
cookie = open("couchbase-server.babysitter.cookie", "x")
1880-
cookie.write("cookie-monster")
1881-
cookie.close()
1882-
os.chmod(cookie.name, 0000)
1883-
self.system_exit_run(self.command + self.cmd_args, self.server_args)
1884-
os.remove(cookie.name)
1885-
self.assertIn('ERROR: Insufficient privileges', self.str_output)
1880+
def test_cannot_read_port(self):
1881+
portfile = open('couchbase-server.babysitter.smport', 'x')
1882+
try:
1883+
portfile.write('inet 12345')
1884+
portfile.close()
1885+
os.chmod(portfile.name, 0000)
1886+
self.system_exit_run(self.command + self.cmd_args, self.server_args)
1887+
self.assertIn('ERROR: Insufficient privileges', self.str_output)
1888+
finally:
1889+
os.remove(portfile.name)
1890+
1891+
def test_succ_cmd_ipv4(self):
1892+
self.succ_cmd(socket.AF_INET)
1893+
1894+
def test_succ_cmd_ipv6(self):
1895+
self.succ_cmd(socket.AF_INET6)
1896+
1897+
def succ_cmd(self, addrFamily):
1898+
with socket.socket(addrFamily, socket.SOCK_DGRAM) as sock:
1899+
sock.bind(('', 0))
1900+
port = sock.getsockname()[1]
1901+
portfile = open('couchbase-server.babysitter.smport', 'x')
1902+
try:
1903+
addrFamilyStr = 'inet' if addrFamily == socket.AF_INET else "inet6"
1904+
portfile.write(addrFamilyStr + ' ' + str(port))
1905+
portfile.close()
1906+
1907+
thread = threading.Thread(target=read_password, args=(sock,))
1908+
thread.start()
1909+
1910+
self.no_error_run(self.command + self.cmd_args, self.server_args)
1911+
thread.join()
1912+
self.assertIn('SUCCESS: Password accepted', self.str_output)
1913+
finally:
1914+
os.remove(portfile.name)
1915+
1916+
1917+
def read_password(sock):
1918+
(result, remoteaddr) = sock.recvfrom(128)
1919+
assert(result == b'asdasd')
1920+
sock.sendto(b'ok', remoteaddr)
18861921

18871922

18881923
class TestXdcrReplicate(CommandTest):

0 commit comments

Comments
 (0)