Skip to content

Commit a0995b6

Browse files
author
Jarrod Johnson
committed
Merge remote-tracking branch 'xcat/master'
2 parents 164a168 + ea9be4a commit a0995b6

11 files changed

Lines changed: 234 additions & 119 deletions

File tree

.github/workflows/ci.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
ShellCheck:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v7
15+
- name: Run ShellCheck (errors only)
16+
# Check every tracked file that has a .sh extension or an sh/bash
17+
# shebang. SC2148 (missing shebang) is excluded because many .sh
18+
# files are sourced fragments or dracut hooks; ShellCheck then
19+
# falls back to checking them as bash.
20+
run: |
21+
{
22+
git ls-files '*.sh'
23+
git ls-files | while IFS= read -r f; do
24+
[ -f "$f" ] || continue
25+
head -c 200 "$f" | head -n 1 | \
26+
grep -qE '^#!.*[/ ](sh|bash|dash|ash|ksh)([ \t]|$)' && echo "$f"
27+
done
28+
} | sort -u | xargs -d '\n' shellcheck --severity=error --exclude=SC2148
29+
30+
python-compileall:
31+
name: Python compileall
32+
runs-on: ubuntu-latest
33+
env:
34+
# One entry per Python version shipped by the distros confluent
35+
# targets, limited to versions actions/setup-python still provides
36+
# on current runners (sles15/alma8 ship 3.6, which is unavailable).
37+
# Newline-separated so it feeds both setup-python (multiline input)
38+
# and the shell loop below (word-split on whitespace).
39+
PYTHON_VERSIONS: |
40+
3.8
41+
3.9
42+
3.10
43+
3.12
44+
3.13
45+
3.14
46+
steps:
47+
- uses: actions/checkout@v7
48+
- uses: actions/setup-python@v6
49+
with:
50+
python-version: ${{ env.PYTHON_VERSIONS }}
51+
- name: Compile all Python files
52+
run: |
53+
rc=0
54+
for v in $PYTHON_VERSIONS; do
55+
echo "::group::Python $v"
56+
if "python$v" -W error -m compileall -q -x '/\.git/' .; then
57+
echo "::endgroup::"
58+
else
59+
echo "::endgroup::"
60+
echo "::error::Python $v compileall failed"
61+
rc=1
62+
fi
63+
done
64+
exit "$rc"

confluent_osdeploy/el7-diskless/profiles/default/scripts/image2disk.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ def fixup(rootdir, vols):
173173
for vol in vols:
174174
if vol['mount'] == '/boot/efi':
175175
targdev = vol['targetdisk']
176-
partnum = re.search('(\d+)$', targdev).group(1)
177-
targblock = re.search('(.*)\d+$', targdev).group(1)
176+
partnum = re.search(r'(\d+)$', targdev).group(1)
177+
targblock = re.search(r'(.*)\d+$', targdev).group(1)
178178
if targblock:
179179
if targblock.endswith('p') and 'nvme' in targblock:
180180
targblock = targblock[:-1]
@@ -231,7 +231,7 @@ def install_to_disk(imgpath):
231231
deflvmsize += fs['initsize']
232232
minlvmsize += fs['minsize']
233233
else:
234-
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
234+
plainvols[int(re.search(r'(\d+)$', fs['device'])[0])] = fs
235235
with open('/tmp/installdisk') as diskin:
236236
instdisk = diskin.read()
237237
instdisk = '/dev/' + instdisk

confluent_osdeploy/ubuntu20.04-diskless/profiles/default/scripts/image2disk.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ def fixup(rootdir, vols):
204204
for vol in vols:
205205
if vol['mount'] == '/boot/efi':
206206
targdev = vol['targetdisk']
207-
partnum = re.search('(\d+)$', targdev).group(1)
208-
targblock = re.search('(.*)\d+$', targdev).group(1)
207+
partnum = re.search(r'(\d+)$', targdev).group(1)
208+
targblock = re.search(r'(.*)\d+$', targdev).group(1)
209209
if targblock:
210210
if targblock.endswith('p') and 'nvme' in targblock:
211211
targblock = targblock[:-1]
@@ -263,7 +263,7 @@ def install_to_disk(imgpath):
263263
deflvmsize += fs['initsize']
264264
minlvmsize += fs['minsize']
265265
else:
266-
plainvols[int(re.search('(\d+)$', fs['device'])[0])] = fs
266+
plainvols[int(re.search(r'(\d+)$', fs['device'])[0])] = fs
267267
with open('/tmp/installdisk') as diskin:
268268
instdisk = diskin.read()
269269
instdisk = '/dev/' + instdisk

confluent_server/builddeb

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,30 @@ if ! grep wheezy /etc/os-release; then
5050
fi
5151
head -n -1 debian/control > debian/control1
5252
mv debian/control1 debian/control
53-
cat > debian/postinst << EOF
54-
if ! getent passwd confluent > /dev/null; then
53+
cat > debian/postinst << \EOF
54+
if ! getent passwd confluent > /dev/null; then
5555
useradd -r confluent -d /var/lib/confluent -s /usr/sbin/nologin
56-
mkdir -p /etc/confluent
57-
chown confluent /etc/confluent
5856
fi
57+
mkdir -p /etc/confluent /var/lib/confluent /var/log/confluent /var/cache/confluent
58+
chown confluent:confluent /etc/confluent /var/lib/confluent /var/log/confluent /var/cache/confluent
59+
60+
sysctl -p /usr/lib/sysctl.d/confluent.conf > /dev/null 2>&1
61+
NEEDCHOWN=0
62+
NEEDSTART=0
63+
[ -n "$(find /etc/confluent /var/log/confluent /var/cache/confluent -uid 0 -print -quit 2>/dev/null)" ] && NEEDCHOWN=1
64+
if [ $NEEDCHOWN = 1 ]; then
65+
if systemctl is-active confluent > /dev/null; then
66+
NEEDSTART=1
67+
systemctl stop confluent
68+
fi
69+
chown -R confluent:confluent /etc/confluent /var/log/confluent /var/cache/confluent
70+
fi
71+
systemctl daemon-reload
72+
if systemctl is-active confluent > /dev/null || [ $NEEDSTART = 1 ]; then systemctl restart confluent > /dev/null 2>&1; fi
73+
if [ ! -e /etc/pam.d/confluent ]; then
74+
ln -s /etc/pam.d/sshd /etc/pam.d/confluent
75+
fi
76+
true
5977
EOF
6078
echo 'export PYBUILD_INSTALL_ARGS=--install-lib=/opt/confluent/lib/python' >> debian/rules
6179
#echo 'Provides: python-'$DPKGNAME >> debian/control

confluent_server/confluent/certutil.py

Lines changed: 109 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -207,85 +207,98 @@ def substitute_cfg(setting, key, val, newval, cfgfile, line):
207207
return False
208208

209209
async def create_full_ca(certout):
210-
mkdirp('/etc/confluent/tls/ca/private')
211-
keyout = '/etc/confluent/tls/ca/private/cakey.pem'
212-
csrout = '/etc/confluent/tls/ca/ca.csr'
213-
mkdirp('/etc/confluent/tls/ca/newcerts')
214-
with open('/etc/confluent/tls/ca/index.txt', 'w') as idx:
215-
pass
216-
with open('/etc/confluent/tls/ca/index.txt.attr', 'w') as idx:
217-
idx.write('unique_subject = no')
218-
with open('/etc/confluent/tls/ca/serial', 'w') as srl:
219-
srl.write('01')
220-
sslcfg = get_openssl_conf_location()
221-
newcfg = '/etc/confluent/tls/ca/openssl.cfg'
222-
settings = {
223-
'dir': '/etc/confluent/tls/ca',
224-
'certificate': '$dir/cacert.pem',
225-
'private_key': '$dir/private/cakey.pem',
226-
'countryName': 'optional',
227-
'stateOrProvinceName': 'optional',
228-
'organizationName': 'optional',
229-
}
230-
subj = '/CN=Confluent TLS Certificate authority ({0})'.format(socket.gethostname())
231-
if len(subj) > 68:
232-
subj = subj[:68]
233-
with open(sslcfg, 'r') as cfgin:
234-
with open(newcfg, 'w') as cfgfile:
235-
for line in cfgin.readlines():
236-
cfg = line.split('#')[0]
237-
if '=' in cfg:
238-
key, val = cfg.split('=', 1)
239-
for stg in settings:
240-
if substitute_cfg(stg, key, val, settings[stg], cfgfile, line):
241-
break
242-
else:
243-
cfgfile.write(line.strip() + '\n')
244-
continue
245-
cfgfile.write(line.strip() + '\n')
246-
cfgfile.write('\n[CACert]\nbasicConstraints = critical,CA:true\nkeyUsage = critical,keyCertSign,cRLSign\n[ca_confluent]\n')
247-
await util.check_call(
248-
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
249-
keyout)
250-
await util.check_call(
251-
'openssl', 'req', '-new', '-key', keyout, '-out', csrout, '-subj', subj)
252-
await util.check_call(
253-
'openssl', 'ca', '-config', newcfg, '-batch', '-selfsign',
254-
'-extensions', 'CACert', '-extfile', newcfg,
255-
'-notext', '-md', 'sha384', '-startdate',
256-
'19700101010101Z', '-enddate', '21000101010101Z', '-keyfile',
257-
keyout, '-out', '/etc/confluent/tls/ca/cacert.pem', '-in', csrout
258-
)
259-
shutil.copy2('/etc/confluent/tls/ca/cacert.pem', certout)
210+
# The CA is used by the confluent service, which runs as the owner of
211+
# /etc/confluent rather than root; create the CA material as that user
212+
# so the service can use the database for issuing certificates
213+
ouid = normalize_uid()
214+
try:
215+
mkdirp('/etc/confluent/tls/ca/private')
216+
keyout = '/etc/confluent/tls/ca/private/cakey.pem'
217+
csrout = '/etc/confluent/tls/ca/ca.csr'
218+
mkdirp('/etc/confluent/tls/ca/newcerts')
219+
with open('/etc/confluent/tls/ca/index.txt', 'w') as idx:
220+
pass
221+
with open('/etc/confluent/tls/ca/index.txt.attr', 'w') as idx:
222+
idx.write('unique_subject = no')
223+
with open('/etc/confluent/tls/ca/serial', 'w') as srl:
224+
srl.write('01')
225+
sslcfg = get_openssl_conf_location()
226+
newcfg = '/etc/confluent/tls/ca/openssl.cfg'
227+
settings = {
228+
'dir': '/etc/confluent/tls/ca',
229+
'certificate': '$dir/cacert.pem',
230+
'private_key': '$dir/private/cakey.pem',
231+
'countryName': 'optional',
232+
'stateOrProvinceName': 'optional',
233+
'organizationName': 'optional',
234+
}
235+
subj = '/CN=Confluent TLS Certificate authority ({0})'.format(socket.gethostname())
236+
if len(subj) > 68:
237+
subj = subj[:68]
238+
with open(sslcfg, 'r') as cfgin:
239+
with open(newcfg, 'w') as cfgfile:
240+
for line in cfgin.readlines():
241+
cfg = line.split('#')[0]
242+
if '=' in cfg:
243+
key, val = cfg.split('=', 1)
244+
for stg in settings:
245+
if substitute_cfg(stg, key, val, settings[stg], cfgfile, line):
246+
break
247+
else:
248+
cfgfile.write(line.strip() + '\n')
249+
continue
250+
cfgfile.write(line.strip() + '\n')
251+
cfgfile.write('\n[CACert]\nbasicConstraints = critical,CA:true\nkeyUsage = critical,keyCertSign,cRLSign\n[ca_confluent]\n')
252+
await util.check_call(
253+
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
254+
keyout)
255+
await util.check_call(
256+
'openssl', 'req', '-new', '-key', keyout, '-out', csrout, '-subj', subj)
257+
await util.check_call(
258+
'openssl', 'ca', '-config', newcfg, '-batch', '-selfsign',
259+
'-extensions', 'CACert', '-extfile', newcfg,
260+
'-notext', '-md', 'sha384', '-startdate',
261+
'19700101010101Z', '-enddate', '21000101010101Z', '-keyfile',
262+
keyout, '-out', '/etc/confluent/tls/ca/cacert.pem', '-in', csrout
263+
)
264+
shutil.copy2('/etc/confluent/tls/ca/cacert.pem', certout)
265+
finally:
266+
os.seteuid(ouid)
260267
#openssl ca -config openssl.cnf -selfsign -keyfile cakey.pem -startdate 20150214120000Z -enddate 20160214120000Z
261268
#20160107071311Z -enddate 20170106071311Z
262269

263270
async def create_simple_ca(keyout, certout):
271+
# As with create_full_ca, the CA material must be owned by the owner
272+
# of /etc/confluent for use by the confluent service
273+
ouid = normalize_uid()
264274
try:
265-
os.makedirs('/etc/confluent/tls')
266-
except OSError as e:
267-
if e.errno != 17:
268-
raise
269-
sslcfg = get_openssl_conf_location()
270-
tmphdl, tmpconfig = tempfile.mkstemp()
271-
os.close(tmphdl)
272-
shutil.copy2(sslcfg, tmpconfig)
273-
await util.check_call(
274-
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
275-
keyout)
276-
try:
277-
subj = '/CN=Confluent TLS Certificate authority ({0})'.format(socket.gethostname())
278-
if len(subj) > 68:
279-
subj = subj[:68]
280-
with open(tmpconfig, 'a') as cfgfile:
281-
cfgfile.write('\n[CACert]\nbasicConstraints = critical,CA:true\n')
275+
try:
276+
os.makedirs('/etc/confluent/tls')
277+
except OSError as e:
278+
if e.errno != 17:
279+
raise
280+
sslcfg = get_openssl_conf_location()
281+
tmphdl, tmpconfig = tempfile.mkstemp()
282+
os.close(tmphdl)
283+
shutil.copy2(sslcfg, tmpconfig)
282284
await util.check_call(
283-
'openssl', 'req', '-new', '-x509', '-key', keyout, '-days',
284-
'27300', '-out', certout, '-subj', subj,
285-
'-extensions', 'CACert', '-config', tmpconfig
286-
)
285+
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
286+
keyout)
287+
try:
288+
subj = '/CN=Confluent TLS Certificate authority ({0})'.format(socket.gethostname())
289+
if len(subj) > 68:
290+
subj = subj[:68]
291+
with open(tmpconfig, 'a') as cfgfile:
292+
cfgfile.write('\n[CACert]\nbasicConstraints = critical,CA:true\n')
293+
await util.check_call(
294+
'openssl', 'req', '-new', '-x509', '-key', keyout, '-days',
295+
'27300', '-out', certout, '-subj', subj,
296+
'-extensions', 'CACert', '-config', tmpconfig
297+
)
298+
finally:
299+
os.remove(tmpconfig)
287300
finally:
288-
os.remove(tmpconfig)
301+
os.seteuid(ouid)
289302

290303
async def create_certificate(keyout=None, certout=None, csrfile=None, subj=None, san=None, backdate=True, days=None):
291304
now_utc = datetime.datetime.now(datetime.timezone.utc)
@@ -413,12 +426,30 @@ async def create_certificate(keyout=None, certout=None, csrfile=None, subj=None,
413426
shutil.copy2(cacfgfile, tmpcafile)
414427
os.close(tmphdl)
415428
cacfgfile = tmpcafile
416-
await util.check_call(
417-
'openssl', 'ca', '-config', cacfgfile, '-rand_serial',
418-
'-in', csrfile, '-out', certout, '-batch', '-notext',
419-
'-startdate', startdate, '-enddate', enddate, '-md', 'sha384',
420-
'-extfile', extconfig, '-subj', subj
421-
)
429+
os.chmod(cacfgfile, 0o644)
430+
os.chmod(csrfile, 0o644)
431+
# openssl ca rewrites the CA database (index, serial) as the
432+
# invoking user; run it as the owner of /etc/confluent so the
433+
# database remains usable by the confluent service. The chmodded
434+
# temporary inputs hold no secrets, and the certificate is
435+
# written to a temporary path first, as certout may only be
436+
# writable by the original user (e.g. a web server certificate
437+
# path during osdeploy initialize -t)
438+
os.chmod(extconfig, 0o644)
439+
ouid = normalize_uid()
440+
try:
441+
tmphdl, tmpcertout = tempfile.mkstemp()
442+
os.close(tmphdl)
443+
await util.check_call(
444+
'openssl', 'ca', '-config', cacfgfile, '-rand_serial',
445+
'-in', csrfile, '-out', tmpcertout, '-batch', '-notext',
446+
'-startdate', startdate, '-enddate', enddate, '-md', 'sha384',
447+
'-extfile', extconfig, '-subj', subj
448+
)
449+
finally:
450+
os.seteuid(ouid)
451+
shutil.copy(tmpcertout, certout)
452+
os.remove(tmpcertout)
422453
for keycopy in tlsmateriallocation.get('keys', []):
423454
if keycopy != keyout:
424455
shutil.copy2(keyout, keycopy)

confluent_server/confluent/collective/manager.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,28 +206,30 @@ async def follow_leader(remote, leader):
206206
exitcause = await cfm.follow_channel(remote)
207207
newleader = exitcause.get('newleader', None)
208208
finally:
209+
handled = False
209210
if cleanexit:
210211
log.log({'info': 'Previous following cleanly closed',
211212
'subsystem': 'collective'})
212-
return
213-
if newleader:
213+
handled = True
214+
if not handled and newleader:
214215
log.log(
215216
{'info': 'Previous leader directed us to join new leader {}'.format(newleader)})
216217
try:
217218
if await connect_to_leader(None, get_myname(), newleader):
218-
return
219+
handled = True
219220
except Exception:
220221
log.log({'error': 'Unknown error attempting to connect to {}, check trace log'.format(newleader), 'subsystem': 'collective'})
221222
cfm.logException()
222-
log.log({'info': 'Current leader ({0}) has disappeared, restarting '
223-
'collective membership'.format(leader), 'subsystem': 'collective'})
224-
# The leader has folded, time to startup again...
225-
follower = None
226-
await cfm.stop_following()
227-
currentleader = None
228-
if retrythread is None: # start a recovery
229-
retrythread = tasks.spawn_task_after(
230-
random.random(), start_collective)
223+
if not handled:
224+
log.log({'info': 'Current leader ({0}) has disappeared, restarting '
225+
'collective membership'.format(leader), 'subsystem': 'collective'})
226+
# The leader has folded, time to startup again...
227+
follower = None
228+
await cfm.stop_following()
229+
currentleader = None
230+
if retrythread is None: # start a recovery
231+
retrythread = tasks.spawn_task_after(
232+
random.random(), start_collective)
231233

232234
async def _create_tls_connection(host, port):
233235
cloop = asyncio.get_running_loop()

0 commit comments

Comments
 (0)