Skip to content

Commit fa78cec

Browse files
committed
Add filesystem locks for interacting with YubiKeys
1 parent afa09eb commit fa78cec

4 files changed

Lines changed: 47 additions & 12 deletions

File tree

sign_node/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def __init__(self, config_file=None, **cmd_args):
7575
'immudb_address': None,
7676
'immudb_public_key_file': None,
7777
'files_sign_cert_path': '/etc/pki/ima/ima-sign.key',
78+
'locks_dir_path': '/tmp/gpg_locks',
7879
}
7980
schema = {
8081
"development_mode": {"type": "boolean", "default": False},
@@ -104,6 +105,7 @@ def __init__(self, config_file=None, **cmd_args):
104105
'type': 'string', 'required': False,
105106
'coerce': normalize_path,
106107
},
108+
'locks_dir_path': {'type': 'string', 'required': True},
107109
}
108110
super(SignNodeConfig, self).__init__(
109111
default_config, config_file, schema, **cmd_args

sign_node/package_sign.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
import pexpect
99

10+
from sign_node.utils.locking import exclusive_lock
11+
from sign_node.utils.pgp_utils import restart_gpg_agent
12+
1013
__all__ = [
1114
"sign_rpm_package",
1215
"PackageSignError",
@@ -19,8 +22,14 @@ class PackageSignError(Exception):
1922
pass
2023

2124

22-
def sign_rpm_package(path, keyid, password, sign_files=False,
23-
sign_files_cert_path='/etc/pki/ima/ima-sign.key'):
25+
def sign_rpm_package(
26+
path,
27+
keyid,
28+
password,
29+
sign_files=False,
30+
sign_files_cert_path='/etc/pki/ima/ima-sign.key',
31+
locks_dir_path: str = '/tmp/gpg_locks',
32+
):
2433
"""
2534
Signs an RPM package.
2635
@@ -55,19 +64,23 @@ def sign_rpm_package(path, keyid, password, sign_files=False,
5564
logging.debug('Deleting signature from %s', pkg_path)
5665
code, out, err = plumbum.local['rpmsign'].run(
5766
args=('--delsign', pkg_path),
58-
retcode=None
67+
retcode=None,
5968
)
6069
logging.debug('Command result: %d, %s\n%s', code, out, err)
6170
if code != 0:
6271
full_out = '\n'.join((out, err))
63-
raise PackageSignError(f'Cannot delete package signature: {full_out}')
64-
out, status = pexpect.run(
65-
command=final_cmd,
66-
events={"Enter passphrase:.*": f"{password}\r"},
67-
env={"LC_ALL": "en_US.UTF-8"},
68-
timeout=100000,
69-
withexitstatus=True,
70-
)
72+
raise PackageSignError(
73+
f'Cannot delete package signature: {full_out}'
74+
)
75+
with exclusive_lock(locks_dir_path, keyid):
76+
out, status = pexpect.run(
77+
command=final_cmd,
78+
events={"Enter passphrase:.*": f"{password}\r"},
79+
env={"LC_ALL": "en_US.UTF-8"},
80+
timeout=100000,
81+
withexitstatus=True,
82+
)
83+
restart_gpg_agent()
7184
if status is None:
7285
message = (
7386
f"The RPM signing command is failed with timeout."
@@ -79,7 +92,10 @@ def sign_rpm_package(path, keyid, password, sign_files=False,
7992
logging.error(
8093
"The RPM signing command is failed with %s exit code."
8194
"\nCommand: %s\nOutput:\n%s.\nTraceback: %s",
82-
status, final_cmd, out, traceback.format_exc()
95+
status,
96+
final_cmd,
97+
out,
98+
traceback.format_exc(),
8399
)
84100
raise PackageSignError(
85101
f"RPM sign failed with {status} exit code.\n"

sign_node/signer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ def download_package(pkg: dict):
444444
pgp_key_password,
445445
sign_files=sign_files,
446446
sign_files_cert_path=self.__config.files_sign_cert_path,
447+
locks_dir_path=self.__config.locks_dir_path,
447448
)
448449
packages_to_sign = []
449450
if packages_to_sign:
@@ -453,6 +454,7 @@ def download_package(pkg: dict):
453454
pgp_key_password,
454455
sign_files=sign_files,
455456
sign_files_cert_path=self.__config.files_sign_cert_path,
457+
locks_dir_path=self.__config.locks_dir_path,
456458
)
457459
finish_time = datetime.utcnow()
458460
stats['sign_packages_time'] = self.timedelta_seconds(

sign_node/utils/locking.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import contextlib
2+
import fcntl
3+
from pathlib import Path
4+
5+
6+
@contextlib.contextmanager
7+
def exclusive_lock(path: str, filename: str):
8+
locks_dir = Path(path)
9+
locks_dir.mkdir(exist_ok=True, parents=True)
10+
with Path(locks_dir, filename).open('w+') as file:
11+
fcntl.flock(file, fcntl.LOCK_EX)
12+
try:
13+
yield
14+
finally:
15+
fcntl.flock(file, fcntl.LOCK_UN)

0 commit comments

Comments
 (0)