Commit 0eff0c1f authored by Alain Takoudjou's avatar Alain Takoudjou

certificate_authority: improve the first version, add extensions to crs

parent 1f2862fb
...@@ -49,6 +49,8 @@ setup(name=name, ...@@ -49,6 +49,8 @@ setup(name=name,
'PyRSS2Gen', 'PyRSS2Gen',
'dnspython', 'dnspython',
'pyOpenSSL', # manage ssl certificates 'pyOpenSSL', # manage ssl certificates
'pyasn1', # ASN.1 types and codecs for certificates
'pyasn1-modules',
'requests', # http requests 'requests', # http requests
] + additional_install_requires, ] + additional_install_requires,
extras_require = { extras_require = {
......
...@@ -35,6 +35,8 @@ def parseArguments(): ...@@ -35,6 +35,8 @@ def parseArguments():
help='Path for Certificate file. Defaul: $ca_dir/cacert.pem') help='Path for Certificate file. Defaul: $ca_dir/cacert.pem')
parser.add_argument('--key_file', parser.add_argument('--key_file',
help='Path of key file. Default: $key_dir/cakey.pem') help='Path of key file. Default: $key_dir/cakey.pem')
parser.add_argument('--crl_file',
help='Path of Certificate Revocation List file. Default: $crl_dir/cacrl.pem')
parser.add_argument('--host', parser.add_argument('--host',
default=[], default=[],
help='Host or IP of ca server.') help='Host or IP of ca server.')
...@@ -101,11 +103,13 @@ def start(): ...@@ -101,11 +103,13 @@ def start():
options.cert_file = os.path.join(options.ca_dir, 'cacert.pem') options.cert_file = os.path.join(options.ca_dir, 'cacert.pem')
if not options.key_file: if not options.key_file:
options.key_file = os.path.join(options.ca_dir, 'private', 'cakey.pem') options.key_file = os.path.join(options.ca_dir, 'private', 'cakey.pem')
if not options.crl_file:
options.crl_file = os.path.join(options.crl_dir, 'cacrl.pem')
logger = getLogger(options.debug, options.log_file) logger = getLogger(options.debug, options.log_file)
ca = CertificateAuthority(options.openssl_bin, ca = CertificateAuthority(options.openssl_bin,
openssl_configuration=options.config_file, certificate=options.cert_file, openssl_configuration=options.config_file, certificate=options.cert_file,
key=options.key_file, ca_directory=options.ca_dir) key=options.key_file, crl=options.crl_file, ca_directory=options.ca_dir)
#config = Config() #config = Config()
app.config.update( app.config.update(
...@@ -132,6 +136,8 @@ def start(): ...@@ -132,6 +136,8 @@ def start():
# Generate certificate Authority cert and key # Generate certificate Authority cert and key
ca.checkAuthority() ca.checkAuthority()
# XXX - maybe CRL must be generated from cron every xx hours
ca.genCertificateRevocationList()
app.logger.addHandler(logger) app.logger.addHandler(logger)
app.logger.info("Certificate Authority server started on http://%s:%s" % ( app.logger.info("Certificate Authority server started on http://%s:%s" % (
......
...@@ -9,6 +9,9 @@ import sys ...@@ -9,6 +9,9 @@ import sys
import prettytable import prettytable
import glob import glob
from slapos.certificate_authority.certificate_authority import CertificateBase from slapos.certificate_authority.certificate_authority import CertificateBase
import urllib, urllib2, httplib, socket
CLIENT_IP = '::1'
def parseArguments(): def parseArguments():
""" """
...@@ -28,9 +31,36 @@ def parseArguments(): ...@@ -28,9 +31,36 @@ def parseArguments():
parser.add_argument('--sign', parser.add_argument('--sign',
default=False, action="store_true", default=False, action="store_true",
help='Request sign certificate') help='Request sign certificate')
parser.add_argument('--host',
default='::1',
help='IPv4 or IPv6 host address to use for action sign')
return parser return parser
class BindableHTTPConnection(httplib.HTTPConnection):
def connect(self):
"""Connect to the host and port specified in __init__."""
try:
socket.inet_aton(self.source_ip)
self.sock = socket.socket()
except socket.error:
self.sock = socket.socket(family=socket.AF_INET6)
self.sock.bind((self.source_ip, 0))
if isinstance(self.timeout, float):
self.sock.settimeout(self.timeout)
self.sock.connect((self.host,self.port))
def BindableHTTPConnectionFactory(source_ip):
def _get(host, port=None, strict=None, timeout=0):
bhc=BindableHTTPConnection(host, port=port, strict=strict, timeout=timeout)
bhc.source_ip=source_ip
return bhc
return _get
class BindableHTTPHandler(urllib2.HTTPHandler):
def http_open(self, req):
return self.do_open(BindableHTTPConnectionFactory(CLIENT_IP), req)
def main(): def main():
parser = parseArguments() parser = parseArguments()
options = parser.parse_args() options = parser.parse_args()
...@@ -47,6 +77,8 @@ def main(): ...@@ -47,6 +77,8 @@ def main():
if options.list: if options.list:
exit(listCertificateRequest(options)) exit(listCertificateRequest(options))
elif options.sign and len(options.key_list) > 0: elif options.sign and len(options.key_list) > 0:
global CLIENT_IP
CLIENT_IP = options.host
exit(requestSigncertificate(options)) exit(requestSigncertificate(options))
parser.print_help() parser.print_help()
...@@ -74,7 +106,9 @@ def listCertificateRequest(options): ...@@ -74,7 +106,9 @@ def listCertificateRequest(options):
return 0 return 0
def requestSigncertificate(options): def requestSigncertificate(options):
"""
Sign certificate locally, using web API
"""
req_directory = os.path.join(options.ca_dir, 'req') req_directory = os.path.join(options.ca_dir, 'req')
code = 0 code = 0
x509 = CertificateBase() x509 = CertificateBase()
...@@ -87,14 +121,22 @@ def requestSigncertificate(options): ...@@ -87,14 +121,22 @@ def requestSigncertificate(options):
req = x509.freadCertificateRequest(csr_file) req = x509.freadCertificateRequest(csr_file)
cn = x509.getSubject(req)['CN'] cn = x509.getSubject(req)['CN']
data = {'key': key} values = {'key': key}
logging.info("Signing %s..." % cn) logging.info("Signing %s..." % cn)
response = requests.post('%s/signcert' % options.ca_url, data=data) #response = requests.post('%s/signcert' % options.ca_url, data=data, headers={'host': options.host})
if response.status_code != 200: data = urllib.urlencode(values)
code = -1*response.status_code req = urllib2.Request('%s/signcert' % options.ca_url, data)
logging.error("ERROR %s: Failed to sign certifice from %s.\n%s" % ( opener = urllib2.build_opener(BindableHTTPHandler)
response.status_code, cn, response.text)) try:
urllib2.install_opener(opener)
response = urllib2.urlopen(req)
except urllib2.HTTPError as e:
raise
#if response.status_code != 200:
# code = -1*response.status_code
# logging.error("ERROR %s: Failed to sign certifice from %s.\n%s" % (
# response.status_code, cn, response.text))
else: else:
logging.info("%s is signed, server responded with: %s" % (cn, logging.info("%s is signed, server responded with: %s" % (cn,
response.text)) response.read()))
return code return code
<!doctype html> <!doctype html>
<title>Flaskr</title> <title>Certificate Authority web</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}"> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page> <div class=page>
<h1>Certificate Authority Public</h1> <h1>Certificate Authority Public</h1>
<ul> <ul>
{% for filename in filename_list -%} {% for filename in filename_list -%}
<a href="/get/{{ filename }}">{{ filename }}</a> <li><a href="/get/{{ filename }}">{{ filename }}</a></li>
{% endfor -%} {% endfor -%}
</ul> </ul>
</div> </div>
\ No newline at end of file
<!doctype html>
<title>Certificate Authority Web</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<nav class="navbar navbar-default navbar-fixed-top"></nav>
<div class="container">
</div> <!-- /container -->
{% block body %}{% endblock %}
</div>
\ No newline at end of file
...@@ -42,17 +42,25 @@ def writefile(path, content, mode=0640): ...@@ -42,17 +42,25 @@ def writefile(path, content, mode=0640):
@app.route('/') @app.route('/')
def index(): def index():
# page to list certificates, also connection link # page to list certificates, also connection link
cert_list = [os.path.basename(app.config.ca.certificate)] cert_list = [
os.path.basename(app.config.ca.certificate),
os.path.basename(app.config.ca.ca_crl)
]
cert_list.extend([x for x in os.listdir(app.config.cert_dir)]) cert_list.extend([x for x in os.listdir(app.config.cert_dir)])
return render_template("index.html", filename_list=cert_list) return render_template("index.html", filename_list=cert_list)
@app.route('/get/<string:name>', methods=['GET']) @app.route('/get/<string:name>', methods=['GET'])
def getfile(name): def getfile(name):
ca_name = os.path.basename(app.config.ca.certificate) ca_name = os.path.basename(app.config.ca.certificate)
crl_name = os.path.basename(app.config.ca.ca_crl)
if name == ca_name: if name == ca_name:
return send_file(app.config.ca.certificate, return send_file(app.config.ca.certificate,
attachment_filename=ca_name, attachment_filename=ca_name,
as_attachment=True) as_attachment=True)
elif name == crl_name:
return send_file(app.config.ca.ca_crl,
attachment_filename=crl_name,
as_attachment=True)
else: else:
cert_file = os.path.join(app.config.cert_dir, name) cert_file = os.path.join(app.config.cert_dir, name)
if os.path.exists(cert_file) and os.path.isfile(cert_file): if os.path.exists(cert_file) and os.path.isfile(cert_file):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment