anchor/anchor/app.py
Tim Kelsey ef2160e82e Anchor can now be installed and invoked as simply "anchor"
This installs stuff in the right places to run anchor from the
included startup scripts. The config is installed into /etc/anchor

This will work from within a venv or without.

The anchor config.py file has been moved into the project package
so that it will install with the other stuff. Eventually we should
strip it out as much as possible and move the details into the JSON
file.

Change-Id: Iffaa7669ce8118fbd41011f9e965704c2ad51b44
2016-09-08 17:00:37 +00:00

238 lines
8.0 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import absolute_import
import logging
import os
import sys
import paste
from paste import translogger # noqa
import pecan
from anchor import audit
from anchor import errors
from anchor import jsonloader
logger = logging.getLogger(__name__)
def config_check_domains(validator_set):
for name, step in validator_set.items():
if 'allowed_domains' in step:
for domain in step['allowed_domains']:
if not domain.startswith('.'):
raise errors.ConfigValidationException(
"Domain that does not start with "
"a '.' <{}>".format(domain))
def validate_config(conf):
for old_name in ['auth', 'ca', 'validators']:
if old_name in conf.config:
raise errors.ConfigValidationException(
"The config seems to be for an old version of Anchor. Please "
"check documentation.")
if not conf.config.get('registration_authority'):
raise errors.ConfigValidationException(
"No registration authorities present")
if not conf.config.get('signing_ca'):
raise errors.ConfigValidationException(
"No signing CA configurations present")
if not conf.config.get('authentication'):
raise errors.ConfigValidationException(
"No authentication methods present")
for name in conf.registration_authority.keys():
logger.info("Checking config for registration authority: %s", name)
validate_registration_authority_config(name, conf)
for name in conf.signing_ca.keys():
logger.info("Checking config for signing ca: %s", name)
validate_signing_ca_config(name, conf)
for name in conf.authentication.keys():
logger.info("Checking config for authentication method: %s", name)
validate_authentication_config(name, conf)
validate_audit_config(conf)
def validate_audit_config(conf):
valid_targets = ('messaging', 'log')
if not conf.config.get('audit'):
# no audit configuration - that's ok
return
audit_conf = conf.audit
if audit_conf.get('target', 'log') not in valid_targets:
raise errors.ConfigValidationException(
"Audit target not known (expected one of %s)" % (
", ".join(valid_targets),))
if audit_conf.get('target') == 'messaging':
if audit_conf.get('url') is None:
raise errors.ConfigValidationException("Audit url required")
def validate_authentication_config(name, conf):
auth_conf = conf.authentication[name]
default_user = "myusername"
default_secret = "simplepassword"
if not auth_conf.get('backend'):
raise errors.ConfigValidationException(
"Authentication method %s doesn't define backend" % name)
if auth_conf['backend'] not in ('static', 'keystone', 'ldap'):
raise errors.ConfigValidationException(
"Authentication backend % unknown" % (auth_conf['backend'],))
# Check for anchor being run with default user/secret
if auth_conf['backend'] == 'static':
if auth_conf['user'] == default_user:
logger.warning("default user for static auth in use")
if auth_conf['secret'] == default_secret:
logger.warning("default secret for static auth in use")
def validate_signing_ca_config(name, conf):
ca_conf = conf.signing_ca[name]
backend_name = ca_conf.get('backend')
if not backend_name:
raise errors.ConfigValidationException(
"Backend type not defined for RA '%s'" % name)
sign_func = jsonloader.conf.get_signing_backend(backend_name)
if not sign_func:
raise errors.ConfigValidationException(
"Backend '%s' could not be found" % backend_name)
if hasattr(sign_func, "_config_validator"):
sign_func._config_validator(name, ca_conf)
def validate_registration_authority_config(ra_name, conf):
ra_conf = conf.registration_authority[ra_name]
auth_name = ra_conf.get('authentication')
if not auth_name:
raise errors.ConfigValidationException(
"No authentication configured for registration authority: %s" %
ra_name)
if not conf.authentication.get(auth_name):
raise errors.ConfigValidationException(
"Authentication method %s configured for registration authority "
"%s doesn't exist" % (auth_name, ra_name))
ca_name = ra_conf.get('signing_ca')
if not ca_name:
raise errors.ConfigValidationException(
"No signing CA configuration present for registration authority: "
"%s" % ra_name)
if not conf.signing_ca.get(ca_name):
raise errors.ConfigValidationException(
"Signing CA %s configured for registration authority %s doesn't "
"exist" % (ca_name, ra_name))
if not ra_conf.get("validators"):
raise errors.ConfigValidationException(
"No validators configured for registration authority: %s" %
ra_name)
ra_validators = ra_conf['validators']
for step in ra_validators.keys():
try:
jsonloader.conf.get_validator(step)
except KeyError:
raise errors.ConfigValidationException(
"Unknown validator <{}> found (for registration "
"authority {})".format(step, ra_name))
config_check_domains(ra_validators)
logger.info("Validators OK for registration authority: %s", ra_name)
ra_fixups = ra_conf.get('fixups', {})
for step in ra_fixups.keys():
try:
jsonloader.conf.get_fixup(step)
except KeyError:
raise errors.ConfigValidationException(
"Unknown fixup <{}> found (for registration "
"authority {})".format(step, ra_name))
logger.info("Fixups OK for registration authority: %s", ra_name)
def load_config():
"""Attempt to find and load a JSON configuration file.
We will search in various locations in order for a valid config file
to use:
- the contents of 'ANCHOR_CONF' environment variable
- a local 'config.json' file in the invocation folder
- a HOME/.config/anchor/config.json file
- a /etc/anchor/config.json file
"""
config_name = 'ANCHOR_CONF'
local_config_path = 'config.json'
user_config_path = os.path.join(
os.environ['HOME'], '.config', 'anchor', 'config.json')
prefix = os.environ.get('VIRTUAL_ENV', os.sep)
sys_config_path = os.path.join(prefix, 'etc', 'anchor', 'config.json')
if 'registration_authority' not in jsonloader.conf.config:
config_path = ""
if config_name in os.environ:
config_path = os.environ[config_name]
elif os.path.isfile(local_config_path):
config_path = local_config_path
elif os.path.isfile(user_config_path):
config_path = user_config_path
elif os.path.isfile(sys_config_path):
config_path = sys_config_path
logger = logging.getLogger("anchor")
logger.info("using config: {}".format(config_path))
jsonloader.conf.load_file_data(config_path)
jsonloader.conf.load_extensions()
def setup_app(config):
# initial logging, will be re-configured later
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
app_conf = dict(config.app)
load_config()
validate_config(jsonloader.conf)
audit.init_audit()
app = pecan.make_app(
app_conf.pop('root'),
logging=config.logging,
**app_conf
)
return paste.translogger.TransLogger(app, setup_console_handler=False)