From 567430790c5b215b522975df3a1eec8fb0ccd3b9 Mon Sep 17 00:00:00 2001 From: Neven1986 Date: Thu, 19 Dec 2019 00:40:25 +0100 Subject: [PATCH] SAML certificate fix and enhancement Problems resolved: - Method create_self_signed_cert() was invoked nowhere. This puts parameter "SAML_SIGN_REQUEST" description in configs/development.py as incorrect - Method create_self_signed_cert() was returning error while trying to write out certificate and private key. File handler was opened for writing out TEXT instead of BINARY data Enhancements: - Two new parameters are introduced SAML_CERT_FILE and SAML_KEY_FILE. User can now explicitly define own certificate and key file anywhere on file-system. - If parameters mentioned in previous bullet aren't explicitly defined, in PowerDNS-Admin root directory self-signed certificate will be created. - Certificates will be used or generated in any case, because in saml.py there are explicit parameters defined which require certificate/key in order to work normally. If they aren't, exception will be thrown. Examples of parameters defined in saml.py requiring certificate: wantAssertionsEncrypted, signMetadata, wantAssertionsSigned. --- configs/development.py | 16 ++++++++++++++-- powerdnsadmin/lib/certutil.py | 4 ++-- powerdnsadmin/services/saml.py | 34 +++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/configs/development.py b/configs/development.py index 0bb6ddf..b62d7c9 100644 --- a/configs/development.py +++ b/configs/development.py @@ -82,8 +82,20 @@ SAML_ENABLED = False # SAML_SP_ENTITY_ID = 'http://' # SAML_SP_CONTACT_NAME = '' # SAML_SP_CONTACT_MAIL = '' -# #Cofigures if SAML tokens should be encrypted. -# #If enabled a new app certificate will be generated on restart + +# Configures the path to certificate file and it's respective private key file +# This pair is used for signing metadata, encrypting tokens and all other signing/encryption +# tasks during communication between iDP and SP +# NOTE: if this two parameters aren't explicitly provided, self-signed certificate-key pair +# will be generated in "PowerDNS-Admin" root directory +# ########################################################################################### +# CAUTION: For production use, usage of self-signed certificates it's highly discouraged. +# Use certificates from trusted CA instead +# ########################################################################################### +# SAML_CERT_FILE = '/etc/pki/powerdns-admin/cert.crt' +# SAML_CERT_KEY = '/etc/pki/powerdns-admin/key.pem' + +# Cofigures if SAML tokens should be encrypted. # SAML_SIGN_REQUEST = False # #Use SAML standard logout mechanism retreived from idp metadata # #If configured false don't care about SAML session on logout. diff --git a/powerdnsadmin/lib/certutil.py b/powerdnsadmin/lib/certutil.py index 8f1b93b..9e09cf6 100644 --- a/powerdnsadmin/lib/certutil.py +++ b/powerdnsadmin/lib/certutil.py @@ -42,7 +42,7 @@ def create_self_signed_cert(): cert.set_pubkey(k) cert.sign(k, 'sha256') - open(CERT_FILE, "wt").write( + open(CERT_FILE, "bw").write( crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) - open(KEY_FILE, "wt").write( + open(KEY_FILE, "bw").write( crypto.dump_privatekey(crypto.FILETYPE_PEM, k)) \ No newline at end of file diff --git a/powerdnsadmin/services/saml.py b/powerdnsadmin/services/saml.py index ac5ad70..040f8b8 100644 --- a/powerdnsadmin/services/saml.py +++ b/powerdnsadmin/services/saml.py @@ -3,7 +3,7 @@ from threading import Thread from flask import current_app import os -from ..lib.certutil import KEY_FILE, CERT_FILE +from ..lib.certutil import KEY_FILE, CERT_FILE, create_self_signed_cert from ..lib.utils import urlparse class SAML(object): @@ -101,12 +101,32 @@ class SAML(object): 'NameIDFormat', 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified') settings['sp']['entityId'] = current_app.config['SAML_SP_ENTITY_ID'] - if os.path.isfile(CERT_FILE): - cert = open(CERT_FILE, "r").readlines() - settings['sp']['x509cert'] = "".join(cert) - if os.path.isfile(KEY_FILE): - key = open(KEY_FILE, "r").readlines() - settings['sp']['privateKey'] = "".join(key) + + + if ('SAML_CERT_FILE' in current_app.config) and ('SAML_KEY_FILE' in current_app.config): + + saml_cert_file = current_app.config['SAML_CERT_FILE'] + saml_key_file = current_app.config['SAML_KEY_FILE'] + + if os.path.isfile(saml_cert_file): + cert = open(saml_cert_file, "r").readlines() + settings['sp']['x509cert'] = "".join(cert) + if os.path.isfile(saml_key_file): + key = open(saml_key_file, "r").readlines() + settings['sp']['privateKey'] = "".join(key) + + else: + + create_self_signed_cert() + + if os.path.isfile(CERT_FILE): + cert = open(CERT_FILE, "r").readlines() + settings['sp']['x509cert'] = "".join(cert) + if os.path.isfile(KEY_FILE): + key = open(KEY_FILE, "r").readlines() + settings['sp']['privateKey'] = "".join(key) + + settings['sp']['assertionConsumerService'] = {} settings['sp']['assertionConsumerService'][ 'binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'