mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2024-12-29 06:25:40 +00:00
added application certificate handling for signed SAML messages
This commit is contained in:
parent
050b822636
commit
92d7ca3870
@ -11,7 +11,6 @@ login_manager = LoginManager()
|
|||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
|
||||||
def enable_github_oauth(GITHUB_ENABLE):
|
def enable_github_oauth(GITHUB_ENABLE):
|
||||||
if not GITHUB_ENABLE:
|
if not GITHUB_ENABLE:
|
||||||
return None, None
|
return None, None
|
||||||
@ -89,5 +88,9 @@ def enable_google_oauth(GOOGLE_ENABLE):
|
|||||||
|
|
||||||
google = enable_google_oauth(app.config.get('GOOGLE_OAUTH_ENABLE'))
|
google = enable_google_oauth(app.config.get('GOOGLE_OAUTH_ENABLE'))
|
||||||
|
|
||||||
|
|
||||||
from app import views, models
|
from app import views, models
|
||||||
|
|
||||||
|
if app.config.get('SAML_ENABLED') and app.config.get('SAML_ENCRYPT'):
|
||||||
|
from app.lib import certutil
|
||||||
|
if not certutil.check_certificate():
|
||||||
|
certutil.create_self_signed_cert()
|
||||||
|
48
app/lib/certutil.py
Normal file
48
app/lib/certutil.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from OpenSSL import crypto
|
||||||
|
from datetime import datetime
|
||||||
|
import pytz
|
||||||
|
import os
|
||||||
|
CRYPT_PATH = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../")
|
||||||
|
CERT_FILE = CRYPT_PATH + "/saml_cert.crt"
|
||||||
|
KEY_FILE = CRYPT_PATH + "/saml_cert.key"
|
||||||
|
|
||||||
|
|
||||||
|
def check_certificate():
|
||||||
|
if not os.path.isfile(CERT_FILE):
|
||||||
|
return False
|
||||||
|
st_cert = open(CERT_FILE, 'rt').read()
|
||||||
|
cert = crypto.load_certificate(crypto.FILETYPE_PEM, st_cert)
|
||||||
|
now = datetime.now(pytz.utc)
|
||||||
|
begin = datetime.strptime(cert.get_notBefore(), "%Y%m%d%H%M%SZ").replace(tzinfo=pytz.UTC)
|
||||||
|
begin_ok = begin < now
|
||||||
|
end = datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ").replace(tzinfo=pytz.UTC)
|
||||||
|
end_ok = end > now
|
||||||
|
if begin_ok and end_ok:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_self_signed_cert():
|
||||||
|
|
||||||
|
# create a key pair
|
||||||
|
k = crypto.PKey()
|
||||||
|
k.generate_key(crypto.TYPE_RSA, 2048)
|
||||||
|
|
||||||
|
# create a self-signed cert
|
||||||
|
cert = crypto.X509()
|
||||||
|
cert.get_subject().C = "DE"
|
||||||
|
cert.get_subject().ST = "NRW"
|
||||||
|
cert.get_subject().L = "Dortmund"
|
||||||
|
cert.get_subject().O = "Dummy Company Ltd"
|
||||||
|
cert.get_subject().OU = "Dummy Company Ltd"
|
||||||
|
cert.get_subject().CN = "PowerDNS-Admin"
|
||||||
|
cert.set_serial_number(1000)
|
||||||
|
cert.gmtime_adj_notBefore(0)
|
||||||
|
cert.gmtime_adj_notAfter(10*365*24*60*60)
|
||||||
|
cert.set_issuer(cert.get_subject())
|
||||||
|
cert.set_pubkey(k)
|
||||||
|
cert.sign(k, 'sha256')
|
||||||
|
|
||||||
|
open(CERT_FILE, "wt").write(
|
||||||
|
crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||||
|
open(KEY_FILE, "wt").write(
|
||||||
|
crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
|
@ -6,6 +6,7 @@ import urlparse
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
|
from certutil import *
|
||||||
from distutils.version import StrictVersion
|
from distutils.version import StrictVersion
|
||||||
from datetime import datetime,timedelta
|
from datetime import datetime,timedelta
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -193,6 +194,7 @@ def email_to_gravatar_url(email, size=100):
|
|||||||
hash_string = hashlib.md5(email).hexdigest()
|
hash_string = hashlib.md5(email).hexdigest()
|
||||||
return "https://s.gravatar.com/avatar/%s?s=%s" % (hash_string, size)
|
return "https://s.gravatar.com/avatar/%s?s=%s" % (hash_string, size)
|
||||||
|
|
||||||
|
|
||||||
def prepare_flask_request(request):
|
def prepare_flask_request(request):
|
||||||
# If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
|
# If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
|
||||||
url_data = urlparse.urlparse(request.url)
|
url_data = urlparse.urlparse(request.url)
|
||||||
@ -204,7 +206,7 @@ def prepare_flask_request(request):
|
|||||||
'get_data': request.args.copy(),
|
'get_data': request.args.copy(),
|
||||||
'post_data': request.form.copy(),
|
'post_data': request.form.copy(),
|
||||||
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
|
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
|
||||||
# 'lowercase_urlencoding': True,
|
'lowercase_urlencoding': True,
|
||||||
'query_string': request.query_string
|
'query_string': request.query_string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,8 +222,10 @@ def init_saml_auth(req):
|
|||||||
settings['sp'] = {}
|
settings['sp'] = {}
|
||||||
settings['sp']['NameIDFormat'] = idp_data['sp']['NameIDFormat']
|
settings['sp']['NameIDFormat'] = idp_data['sp']['NameIDFormat']
|
||||||
settings['sp']['entityId'] = app.config['SAML_SP_ENTITY_ID']
|
settings['sp']['entityId'] = app.config['SAML_SP_ENTITY_ID']
|
||||||
settings['sp']['privateKey'] = ''
|
cert = open(CERT_FILE, "r").readlines()
|
||||||
settings['sp']['x509cert'] = ''
|
key = open(KEY_FILE, "r").readlines()
|
||||||
|
settings['sp']['privateKey'] = "".join(key)
|
||||||
|
settings['sp']['x509cert'] = "".join(cert)
|
||||||
settings['sp']['assertionConsumerService'] = {}
|
settings['sp']['assertionConsumerService'] = {}
|
||||||
settings['sp']['assertionConsumerService']['binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
settings['sp']['assertionConsumerService']['binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
||||||
settings['sp']['assertionConsumerService']['url'] = own_url+'/saml/authorized'
|
settings['sp']['assertionConsumerService']['url'] = own_url+'/saml/authorized'
|
||||||
@ -233,19 +237,19 @@ def init_saml_auth(req):
|
|||||||
settings['strict'] = True
|
settings['strict'] = True
|
||||||
settings['debug'] = app.config['SAML_DEBUG']
|
settings['debug'] = app.config['SAML_DEBUG']
|
||||||
settings['security'] = {}
|
settings['security'] = {}
|
||||||
settings['security']['digestAlgorithm'] = 'http://www.w3.org/2000/09/xmldsig#sha1'
|
settings['security']['digestAlgorithm'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
|
||||||
settings['security']['metadataCacheDuration'] = None
|
settings['security']['metadataCacheDuration'] = None
|
||||||
settings['security']['metadataValidUntil'] = None
|
settings['security']['metadataValidUntil'] = None
|
||||||
settings['security']['requestedAuthnContext'] = True
|
settings['security']['requestedAuthnContext'] = True
|
||||||
settings['security']['signatureAlgorithm'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
|
settings['security']['signatureAlgorithm'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
|
||||||
settings['security']['wantAssertionsEncrypted'] = False
|
settings['security']['wantAssertionsEncrypted'] = False
|
||||||
settings['security']['wantAttributeStatement'] = True
|
settings['security']['wantAttributeStatement'] = True
|
||||||
settings['security']['wantNameId'] = True
|
settings['security']['wantNameId'] = True
|
||||||
settings['security']['authnRequestsSigned'] = False
|
settings['security']['authnRequestsSigned'] = app.config['SAML_SIGN_REQUEST']
|
||||||
settings['security']['logoutRequestSigned'] = False
|
settings['security']['logoutRequestSigned'] = app.config['SAML_SIGN_REQUEST']
|
||||||
settings['security']['logoutResponseSigned'] = False
|
settings['security']['logoutResponseSigned'] = app.config['SAML_SIGN_REQUEST']
|
||||||
settings['security']['nameIdEncrypted'] = False
|
settings['security']['nameIdEncrypted'] = False
|
||||||
settings['security']['signMetadata'] = False
|
settings['security']['signMetadata'] = True
|
||||||
settings['security']['wantAssertionsSigned'] = True
|
settings['security']['wantAssertionsSigned'] = True
|
||||||
settings['security']['wantMessagesSigned'] = True
|
settings['security']['wantMessagesSigned'] = True
|
||||||
settings['security']['wantNameIdEncrypted'] = False
|
settings['security']['wantNameIdEncrypted'] = False
|
||||||
|
@ -412,8 +412,10 @@ def saml_logout():
|
|||||||
clear_session()
|
clear_session()
|
||||||
if url is not None:
|
if url is not None:
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
elif app.config.get('SAML_LOGOUT_URL'):
|
elif app.config.get('SAML_LOGOUT_URL') is not None:
|
||||||
return redirect(app.config.get('SAML_LOGOUT_URL'))
|
return redirect(app.config.get('SAML_LOGOUT_URL'))
|
||||||
|
else:
|
||||||
|
return redirect(url_for('login'))
|
||||||
else:
|
else:
|
||||||
return render_template('errors/SAML.html', errors=errors)
|
return render_template('errors/SAML.html', errors=errors)
|
||||||
|
|
||||||
|
@ -92,6 +92,9 @@ SAML_METADATA_CACHE_LIFETIME = 1
|
|||||||
SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
|
SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
|
||||||
SAML_SP_CONTACT_NAME = '<contact name>'
|
SAML_SP_CONTACT_NAME = '<contact name>'
|
||||||
SAML_SP_CONTACT_MAIL = '<contact mail>'
|
SAML_SP_CONTACT_MAIL = '<contact mail>'
|
||||||
|
#Cofigures if SAML tokens should be encrypted.
|
||||||
|
#If enabled a new app certificate will be generated on restart
|
||||||
|
SAML_SIGN_REQUEST = False
|
||||||
#Use SAML standard logout mechanism retreived from idp metadata
|
#Use SAML standard logout mechanism retreived from idp metadata
|
||||||
#If configured false don't care about SAML session on logout.
|
#If configured false don't care about SAML session on logout.
|
||||||
#Logout from PowerDNS-Admin only and keep SAML session authenticated.
|
#Logout from PowerDNS-Admin only and keep SAML session authenticated.
|
||||||
|
Loading…
Reference in New Issue
Block a user