Commit 719959e0 by Vincent Pelletier

wsgi: Become web-friendly

Self-describe site structure in application/hal+json format.
Add Cross-Origin Resource Sharing support: pre-flight request support,
same-origin-only origin access control minimal html page. Access control
decision is stored client-side in a signed & time-limited cookie
supporting multiple concurrent origins. Origins may be pre-allowed (ex:
when caucase GUI is served from a trusted server).
1 parent 7ff81404
......@@ -196,6 +196,26 @@ HTTPS.
It handles its own certificate issuance and renewal, so there is no need to use
`caucase-updater`_ for this service.
CORS
----
caucased implements CORS protection: when receiving a cross-origin request,
it will respond with 401 Unauthorized, with the WWW-Authenticate header set to
a custom scheme ("cors") with an "url" parameter containing an URI template
with one variable field: "return" (more on it later).
Upon receiving this response, the application is expected to render the URL
template and redirect the user to resulting URL. There, the user will be
informed of the cross-origin access attempt, and offered the choice to grant or
deny access to given origin.
Once their decision is made, their browser will receive a cookie remembering
this decision, and they will be redirected to the URL received in the "return"
field received upon above-described redirection.
Then, the application should retry the original request, which will be
accompanied by that cookie.
Backups
-------
......
......@@ -25,6 +25,7 @@ import datetime
from getpass import getpass
import glob
import itertools
import json
import os
import socket
from SocketServer import ThreadingMixIn
......@@ -42,7 +43,7 @@ import pem
from . import exceptions
from . import utils
from . import version
from .wsgi import Application
from .wsgi import Application, CORSTokenManager
from .ca import CertificateAuthority, UserCertificateAuthority, Extension
from .storage import SQLite3Storage
from .http_wsgirequesthandler import WSGIRequestHandler
......@@ -357,6 +358,19 @@ def main(argv=None, until=utils.until):
'default: %(default)s',
)
parser.add_argument(
'--cors-key-store',
default='cors.key',
metavar='PATH',
help='Path to a file containing CORS token keys. default: %(default)s',
)
parser.add_argument(
'--cors-whitelist',
default=[],
nargs='+',
metavar='URL',
help='Origin values to always trust. default: none'
)
parser.add_argument(
'--netloc',
required=True,
help='<host>[:<port>] at which certificate verificators may reach this '
......@@ -600,7 +614,34 @@ def main(argv=None, until=utils.until):
ca_life_period=40, # approx. 10 years
crt_life_time=args.service_crt_validity,
)
application = Application(cau=cau, cas=cas)
if os.path.exists(args.cors_key_store):
with open(args.cors_key_store) as cors_key_file:
cors_secret_list = json.load(cors_key_file)
else:
cors_secret_list = []
def saveCORSKeyList(cors_secret_list):
"""
Update CORS key store when a new key was generated.
"""
with _createKey(args.cors_key_store) as cors_key_file:
json.dump(cors_secret_list, cors_key_file)
application = Application(
cau=cau,
cas=cas,
http_url=urlunsplit((
'http',
'[' + hostname + ']:' + str(http_port),
'/',
None,
None,
)),
https_url=https_base_url,
cors_token_manager=CORSTokenManager(
secret_list=cors_secret_list,
onNewKey=saveCORSKeyList,
),
cors_whitelist=args.cors_whitelist,
)
http_list = []
https_list = []
known_host_set = set()
......
......@@ -52,6 +52,7 @@ setup(
'cryptography>=2.2.1', # everything x509 except...
'pyOpenSSL>=18.0.0', # ...certificate chain validation
'pem>=17.1.0', # Parse PEM files
'PyJWT', # CORS token signature
],
zip_safe=True,
entry_points={
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!