Skip to content

Commit

Permalink
Add keystone audit middleware API logging
Browse files Browse the repository at this point in the history
This commit adds Keystone audit middleware API logging to
the Glance charm in versions Yoga and newer to allow users to
configure their environment for CADF compliance. This feature can
be enabled/disabled and is set to 'disabled' by default to avoid
bloat in log files. The logging output writes to
/var/log/glance/glance-api.log.
This commit builds on previous discussions:
juju/charm-helpers#808.

Related-Pr: juju/charm-helpers#893
func-test-pr: openstack-charmers/zaza-openstack-tests#1212
Closes-Bug: 1856555
Change-Id: Ied08b56cf3c4fa30827d43a50ca7b552db0fa82b
  • Loading branch information
MylesJP committed Jun 7, 2024
1 parent 75d3636 commit 66a167e
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 3 deletions.
5 changes: 5 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ options:
default: False
description: |
Setting this to True will allow supporting services to log to syslog.
audit-middleware:
type: boolean
default: False
description: |
Enable Keystone auditing middleware for logging API calls.
openstack-origin:
type: string
default: bobcat
Expand Down
19 changes: 16 additions & 3 deletions hooks/glance_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@
CHARM = "glance"

GLANCE_CONF_DIR = "/etc/glance"
GLANCE_AUDIT_MAP = "%s/api_audit_map.conf" % GLANCE_CONF_DIR
GLANCE_REGISTRY_CONF = "%s/glance-registry.conf" % GLANCE_CONF_DIR
GLANCE_API_CONF = "%s/glance-api.conf" % GLANCE_CONF_DIR
GLANCE_SWIFT_CONF = "%s/glance-swift.conf" % GLANCE_CONF_DIR
GLANCE_REGISTRY_PASTE = os.path.join(GLANCE_CONF_DIR,
'glance-registry-paste.ini')
GLANCE_API_PASTE = os.path.join(GLANCE_CONF_DIR,
'glance-api-paste.ini')
GLANCE_API_PASTE = os.path.join(GLANCE_CONF_DIR, 'api-paste.ini')
GLANCE_POLICY_FILE = os.path.join(GLANCE_CONF_DIR, "policy.json")
# NOTE(ajkavanagh): from Ussuri, glance switched to policy-in-code; this is the
# policy.yaml file (as there is not packaged policy.json or .yaml) that is used
Expand Down Expand Up @@ -204,6 +204,7 @@ def ceph_config_file():
config_file=GLANCE_API_CONF),
context.MemcacheContext(),
glance_contexts.GlanceImageImportContext(),
context.KeystoneAuditMiddleware(service=CHARM),
glance_contexts.ExternalS3Context()],
'services': ['glance-api']
}),
Expand All @@ -218,6 +219,14 @@ def ceph_config_file():
'hook_contexts': [],
'services': ['glance-api', 'glance-registry']
}),
(GLANCE_AUDIT_MAP, {
'hook_contexts': [context.KeystoneAuditMiddleware(service=CHARM)],
'services': ['glance-api']
}),
(GLANCE_API_PASTE, {
'hook_contexts': [context.KeystoneAuditMiddleware(service=CHARM)],
'services': ['glance-api']
}),
(ceph_config_file(), {
'hook_contexts': [context.CephContext()],
'services': ['glance-api', 'glance-registry']
Expand Down Expand Up @@ -257,7 +266,9 @@ def register_configs():

confs = [GLANCE_REGISTRY_CONF,
GLANCE_API_CONF,
HAPROXY_CONF]
HAPROXY_CONF,
GLANCE_API_PASTE,
GLANCE_AUDIT_MAP]

if relation_ids('ceph'):
mkdir(os.path.dirname(ceph_config_file()))
Expand Down Expand Up @@ -403,6 +414,8 @@ def restart_map():
cmp_release = CompareOpenStackReleases(os_release('glance-common'))

for f, ctxt in CONFIG_FILES.items():
if f == GLANCE_AUDIT_MAP and cmp_release < 'yoga':
continue
svcs = []
for svc in ctxt['services']:
if cmp_release >= 'stein' and svc == 'glance-registry':
Expand Down
86 changes: 86 additions & 0 deletions templates/yoga/api-paste.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Use this pipeline for no auth or image caching - DEFAULT
[pipeline:glance-api]
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context rootapp

# Use this pipeline for image caching and no auth
[pipeline:glance-api-caching]
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache rootapp

# Use this pipeline for caching w/ management interface but no auth
[pipeline:glance-api-cachemanagement]
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp

# Use this pipeline for keystone auth
[pipeline:glance-api-keystone]
{% if audit_middleware and service_name -%}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context rootapp
{% else %}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context rootapp
{% endif %}

# Use this pipeline for keystone auth with image caching
[pipeline:glance-api-keystone+caching]
{% if audit_middleware and service_name -%}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context cache rootapp
{% else %}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context cache rootapp
{% endif %}

# Use this pipeline for keystone auth with caching and cache management
[pipeline:glance-api-keystone+cachemanagement]
{% if audit_middleware and service_name -%}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken audit context cache cachemanage rootapp
{% else %}
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context cache cachemanage rootapp
{% endif %}

[composite:rootapp]
paste.composite_factory = glance.api:root_app_factory
/: apiversions
/v2: apiv2app

[app:apiversions]
paste.app_factory = glance.api.versions:create_resource

[app:apiv2app]
paste.app_factory = glance.api.v2.router:API.factory

[filter:healthcheck]
paste.filter_factory = oslo_middleware:Healthcheck.factory
backends = disable_by_file
disable_by_file_path = /etc/glance/healthcheck_disable

[filter:versionnegotiation]
paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory

[filter:cache]
paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory

[filter:cachemanage]
paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory

[filter:context]
paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory

[filter:unauthenticated-context]
paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory

[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
delay_auth_decision = true

[filter:gzip]
paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory

[filter:osprofiler]
paste.filter_factory = osprofiler.web:WsgiMiddleware.factory

[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = glance
oslo_config_program = glance-api

[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware:HTTPProxyToWSGI.factory

{% include "section-filter-audit" %}
16 changes: 16 additions & 0 deletions templates/yoga/api_audit_map.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = None

# possible end path of api requests
[path_keywords]
detail = None
file = None
images = image
members = member
tags = tag

# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
image = service/storage/image
89 changes: 89 additions & 0 deletions templates/yoga/glance-api.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[DEFAULT]
verbose = {{ verbose }}
use_syslog = {{ use_syslog }}
debug = {{ debug }}
workers = {{ workers }}
bind_host = {{ bind_host }}

{% if ext -%}
bind_port = {{ ext }}
{% elif bind_port -%}
bind_port = {{ bind_port }}
{% else -%}
bind_port = 9292
{% endif -%}

{% if transport_url %}
transport_url = {{ transport_url }}
{% endif %}

log_file = /var/log/glance/api.log
backlog = 4096

{% if expose_image_locations -%}
show_multiple_locations = {{ expose_image_locations }}
show_image_direct_url = {{ expose_image_locations }}
{% endif -%}

{% if api_config_flags -%}
{% for key, value in api_config_flags.items() -%}
{{ key }} = {{ value }}
{% endfor -%}
{% endif -%}

delayed_delete = False
scrub_time = 43200
scrubber_datadir = /var/lib/glance/scrubber
image_cache_dir = /var/lib/glance/image-cache/
db_enforce_mysql_charset = False

{% if image_size_cap -%}
image_size_cap = {{ image_size_cap }}
{% endif -%}

{% if enabled_backends %}
enabled_backends = {{ enabled_backends }}
{% endif %}

[glance_store]
{% if default_store_backend %}
default_backend = {{ default_store_backend }}
{% endif %}

[image_format]
disk_formats = {{ disk_formats }}
{% if container_formats -%}
container_formats = {{ container_formats }}
{% endif -%}

{% include "section-keystone-authtoken-v3only" %}

{% if auth_host -%}
[paste_deploy]
flavor = keystone
config_file = /etc/glance/api-paste.ini
{% endif %}

[barbican]
auth_endpoint = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v3

{% include "parts/section-database" %}

{% include "section-oslo-messaging-rabbit" %}

{% include "section-oslo-notifications" %}

{% include "section-oslo-middleware" %}

{% include "parts/section-storage" %}

{% for name, cfg in enabled_backend_configs.items() %}
[{{name}}]
{% for key, val in cfg.items() -%}
{{ key }} = {{ val }}
{% endfor -%}
{% endfor%}

{% include "parts/section-image-import" %}

{% include "section-audit-middleware-notifications" %}
3 changes: 3 additions & 0 deletions tests/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ tests:
- zaza.openstack.charm_tests.glance.tests.GlanceCephRGWBackendTest
- zaza.openstack.charm_tests.glance.tests.GlanceExternalS3Test
- zaza.openstack.charm_tests.glance.tests.GlanceCinderBackendTest
- zaza.openstack.charm_tests.audit.tests.KeystoneAuditMiddlewareTest
- zaza.openstack.charm_tests.policyd.tests.GlanceTests
- zaza.openstack.charm_tests.ceph.tests.CheckPoolTypes
- zaza.openstack.charm_tests.ceph.tests.BlueStoreCompressionCharmOperation
Expand All @@ -35,6 +36,8 @@ tests:
- zaza.openstack.charm_tests.policyd.tests.GlanceTests

tests_options:
audit-middleware:
service: glance
tempest:
full_run:
smoke: true
Expand Down
27 changes: 27 additions & 0 deletions unit_tests/test_glance_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def test_restart_map_rocky(self):
(utils.GLANCE_API_CONF, ['glance-api']),
(utils.GLANCE_SWIFT_CONF, ['glance-api']),
(utils.GLANCE_POLICY_FILE, ['glance-api', 'glance-registry']),
(utils.GLANCE_API_PASTE, ['glance-api']),
(utils.ceph_config_file(), ['glance-api', 'glance-registry']),
(utils.HAPROXY_CONF, ['haproxy']),
(utils.HTTPS_APACHE_CONF, ['apache2']),
Expand All @@ -181,6 +182,31 @@ def test_restart_map_stein(self):
(utils.GLANCE_API_CONF, ['glance-api']),
(utils.GLANCE_SWIFT_CONF, ['glance-api']),
(utils.GLANCE_POLICY_FILE, ['glance-api']),
(utils.GLANCE_API_PASTE, ['glance-api']),
(utils.ceph_config_file(), ['glance-api']),
(utils.HAPROXY_CONF, ['haproxy']),
(utils.HTTPS_APACHE_CONF, ['apache2']),
(utils.HTTPS_APACHE_24_CONF, ['apache2']),
(utils.APACHE_PORTS_CONF, ['apache2']),
(utils.MEMCACHED_CONF, ['memcached']),
])
self.assertEqual(ex_map, utils.restart_map())
self.enable_memcache.return_value = False
del ex_map[utils.MEMCACHED_CONF]
self.assertEqual(ex_map, utils.restart_map())

def test_restart_map_yoga(self):
self.enable_memcache.return_value = True
self.config.side_effect = None
self.service_name.return_value = 'glance'
self.os_release.return_value = 'yoga'

ex_map = OrderedDict([
(utils.GLANCE_API_CONF, ['glance-api']),
(utils.GLANCE_SWIFT_CONF, ['glance-api']),
(utils.GLANCE_POLICY_FILE, ['glance-api']),
(utils.GLANCE_AUDIT_MAP, ['glance-api']),
(utils.GLANCE_API_PASTE, ['glance-api']),
(utils.ceph_config_file(), ['glance-api']),
(utils.HAPROXY_CONF, ['haproxy']),
(utils.HTTPS_APACHE_CONF, ['apache2']),
Expand All @@ -205,6 +231,7 @@ def test_restart_map_stein_ssl(self, isdir):
(utils.GLANCE_API_CONF, ['glance-api']),
(utils.GLANCE_SWIFT_CONF, ['glance-api']),
(utils.GLANCE_POLICY_FILE, ['glance-api']),
(utils.GLANCE_API_PASTE, ['glance-api']),
(utils.ceph_config_file(), ['glance-api']),
(utils.HAPROXY_CONF, ['haproxy']),
(utils.HTTPS_APACHE_CONF, ['apache2']),
Expand Down

0 comments on commit 66a167e

Please sign in to comment.