Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add client-side certificate support #1022

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions Nagstamon/Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@
'autologin_key',
'custom_cert_ca_file',
'idp_ecp_endpoint',
'monitor_site'
'monitor_site',
'client_cert_path',
'client_cert_password'
]


Expand Down Expand Up @@ -475,6 +477,17 @@ def _LoadServersMultipleConfig(self):
servers[server].proxy_password = proxy_password
elif servers[server].proxy_password != "":
servers[server].proxy_password = self.DeObfuscate(servers[server].proxy_password)
# client side cert password
if self.keyring_available and self.use_system_keyring:
client_cert_password = keyring.get_password('Nagstamon', '@'.join(('client_cert',
servers[server].client_cert_path))) or ""
if client_cert_password == "":
if servers[server].client_cert_password != "":
servers[server].client_cert_password = self.DeObfuscate(servers[server].client_cert_password)
else:
servers[server].client_cert_password = client_cert_password
elif servers[server].client_cert_password != "":
servers[server].client_cert_password = self.DeObfuscate(servers[server].client_cert_password)

# do only deobfuscating if any autologin_key is set - will be only Centreon/Thruk
if 'autologin_key' in servers[server].__dict__.keys():
Expand Down Expand Up @@ -619,7 +632,7 @@ def SaveMultipleConfig(self, settingsdir, setting):
for option in self.__dict__[settingsdir][s].__dict__:
# obfuscate certain entries in config file - special arrangement for servers
if settingsdir == 'servers':
if option in ['username', 'password', 'proxy_username', 'proxy_password', 'autologin_key']:
if option in ['username', 'password', 'proxy_username', 'proxy_password', 'autologin_key', 'client_cert_password']:
value = self.Obfuscate(self.__dict__[settingsdir][s].__dict__[option])
if option == 'password':
if self.__dict__[settingsdir][s].save_password is False:
Expand Down Expand Up @@ -655,6 +668,22 @@ def SaveMultipleConfig(self, settingsdir, setting):
traceback.print_exc(file=sys.stdout)
sys.exit(1)

value = ''
if option == 'client_cert_password':
if self.keyring_available and self.use_system_keyring:
if self.__dict__[settingsdir][s].client_cert_password != '':
# provoke crash if password saving does not work - this is the case
# on newer Ubuntu releases
try:
keyring.set_password('Nagstamon', '@'.join(('client_cert',
self.__dict__[settingsdir][
s].client_cert_path)),
self.__dict__[settingsdir][s].client_cert_password)
except Exception:
import traceback
traceback.print_exc(file=sys.stdout)
sys.exit(1)

value = ''
config.set(setting + '_' + s, option, str(value))
else:
Expand Down Expand Up @@ -918,6 +947,9 @@ def __init__(self):
self.proxy_address = 'http://proxyserver:port/'
self.proxy_username = 'proxyusername'
self.proxy_password = 'proxypassword'
self.use_client_cert = False
self.client_cert_path = '~/nagstamon.pfx'
self.client_cert_password = 'clientcertpassword'
# defaults to 'basic', other possible values are 'digest' and 'kerberos'
self.authentication = 'basic'
self.timeout = 10
Expand Down
25 changes: 24 additions & 1 deletion Nagstamon/QUI/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5772,7 +5772,12 @@ def __init__(self, dialog):
self.window.input_checkbox_show_options: [self.window.groupbox_options],
self.window.input_checkbox_custom_cert_use: [self.window.label_custom_ca_file,
self.window.input_lineedit_custom_cert_ca_file,
self.window.button_choose_custom_cert_ca_file]}
self.window.button_choose_custom_cert_ca_file],
self.window.input_checkbox_use_client_cert: [self.window.label_client_cert_path,
self.window.input_lineedit_client_cert_path,
self.window.button_choose_client_cert_path,
self.window.label_client_cert_password,
self.window.input_lineedit_client_cert_password]}

self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_use_proxy_from_os]

Expand Down Expand Up @@ -5850,8 +5855,12 @@ def __init__(self, dialog):
self.window.button_choose_custom_cert_ca_file.setText('')
self.window.button_choose_custom_cert_ca_file.setIcon(
self.window.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon))
self.window.button_choose_client_cert_path.setText('')
self.window.button_choose_client_cert_path.setIcon(
self.window.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon))
# connect choose custom cert CA file button with file dialog
self.window.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file)
self.window.button_choose_client_cert_path.clicked.connect(self.choose_client_cert_path)

# fill authentication combobox
self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Bearer'])
Expand Down Expand Up @@ -6159,6 +6168,20 @@ def choose_custom_cert_ca_file(self):
# only take filename if QFileDialog gave something useful back
if file != '':
self.window.input_lineedit_custom_cert_ca_file.setText(file)

@Slot()
def choose_client_cert_path(self):
"""
show dialog for selection of client certificate
"""
filter = 'PFX files (*.pfx);;p12 files (*.p12);;All Files (*)'
file = dialogs.file_chooser.getOpenFileName(self.window,
directory=os.path.expanduser('~'),
filter=filter)[0]

# only take filename if QFileDialog gave something useful back
if file != '':
self.window.input_lineedit_client_cert_path.setText(file)

@Slot()
def checkmk_view_hosts_reset(self):
Expand Down
8 changes: 8 additions & 0 deletions Nagstamon/Servers/Generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

from bs4 import BeautifulSoup
import requests
from requests_pkcs12 import Pkcs12Adapter

# check ECP authentication support availability
try:
Expand Down Expand Up @@ -164,6 +165,9 @@ def __init__(self, **kwds):
self.proxy_address = ''
self.proxy_username = ''
self.proxy_password = ''
self.use_client_cert = False
self.client_cert_path = ''
self.client_cert_password = ''
self.auth_type = ''
self.encoding = None
self.hosts = dict()
Expand Down Expand Up @@ -342,6 +346,10 @@ def create_session(self):
# add proxy information
self.proxify(session)

# add client certificates
if self.use_client_cert is True:
session.mount(self.monitor_url, Pkcs12Adapter(pkcs12_filename=self.client_cert_path, pkcs12_password=self.client_cert_password))

return session

def proxify(self, requester):
Expand Down
61 changes: 61 additions & 0 deletions Nagstamon/resources/qui/settings_server.ui
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,63 @@
</item>
</layout>
</item>
<item row="7" column="1" colspan="3">
<widget class="QCheckBox" name="input_checkbox_use_client_cert">
<property name="text">
<string>Use client side certificate</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_client_cert_path">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cert Path (PKCS12):</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="label_client_cert_password">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cert Password:</string>
</property>
</widget>
</item>
<item row="8" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="input_lineedit_client_cert_path"/>
</item>
<item>
<widget class="QPushButton" name="button_choose_client_cert_path">
<property name="text">
<string>Choose file...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="2">
<widget class="QLineEdit" name="input_lineedit_client_cert_password">
<property name="text">
<string>1234567890</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="36" column="2" colspan="2">
<widget class="QLineEdit" name="input_lineedit_disabled_backends">
<property name="text">
Expand Down Expand Up @@ -810,6 +867,10 @@
<tabstop>button_choose_custom_cert_ca_file</tabstop>
<tabstop>input_combobox_authentication</tabstop>
<tabstop>input_spinbox_timeout</tabstop>
<tabstop>input_checkbox_use_client_cert</tabstop>
<tabstop>input_lineedit_client_cert_path</tabstop>
<tabstop>button_choose_client_cert_path</tabstop>
<tabstop>input_lineedit_client_cert_password</tabstop>
<tabstop>input_checkbox_use_autologin</tabstop>
<tabstop>input_lineedit_monitor_site</tabstop>
<tabstop>input_lineedit_autologin_key</tabstop>
Expand Down
1 change: 1 addition & 0 deletions build/requirements/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ python-dateutil
requests
requests-kerberos
requests-ecp
requests-pkcs12
setuptools
1 change: 1 addition & 0 deletions build/requirements/macos.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pysocks
python-dateutil
requests
requests-ecp
requests-pkcs12
# gssapi instead kerberos
requests-gssapi
setuptools
1 change: 1 addition & 0 deletions build/requirements/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pysocks
python-dateutil
requests
requests-ecp
requests-pkcs12
# maybe requests-gssapi might become usable with https://github.com/pythongssapi/python-gssapi/issues/244#issuecomment-827404287
requests-kerberos
setuptools
Expand Down