Enable audit middleware

We're enabling the audit middleware for the following API services:

* cinder
* nova
* neutron
* glance
* heat
* barbican
* octavia
* gnocchi
  * config.verbose was deprecated a long time ago and Gnocchi doesn't
    support a separate "logging.conf" file
     * as such, Gnocchi can't be configured to use "info" level logging
     * the audit logs will only be emitted in debug mode
  * cf001e8428/gnocchi/service.py (L72-L79)
* newly defined audit map (the other services had already existing
  definitions, which were updated):
  * aodh
  * designate
  * magnum
  * masakari
    * updated wsgi log configuration

The following services do not support the audit middleware,
the support for api paste was dropped:
* placement
* watcher
* keystone (has its own pycadf audit implementation)
  * emits notifications using oslo.notifications
  * configurable driver, possible options include "messagingv2" and "log"
  * we can't just use the log driver since the user may request amqp
    notifications using:
      "juju config keystone enable-telemetry-notifications=True"
  * we'll listen for amqp notifications using a separate service

Reference:
* audit middleware configuration: https://docs.openstack.org/keystonemiddleware/latest/audit.html
* api map samples: https://opendev.org/openstack/pycadf/src/tag/3.1.1/etc/pycadf

Change-Id: I28a261b85067704221d6ab3d949c5d2a27a4a9d7
This commit is contained in:
Lucian Petrut
2025-05-26 14:34:33 +00:00
parent d8094c2a11
commit 58a4c980bb
61 changed files with 1529 additions and 16 deletions

View File

@@ -288,6 +288,36 @@ class AodhOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
"""Healthcheck path for ingress relation."""
return "/healthcheck"
@property
def container_configs(self) -> List[sunbeam_core.ContainerConfigFile]:
"""Container configurations files."""
return [
sunbeam_core.ContainerConfigFile(
"/etc/aodh/aodh.conf",
"root",
"aodh",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/aodh/api-paste.ini",
"root",
"aodh",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/aodh/api_audit_map.conf",
"root",
"aodh",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",
"aodh",
0o640,
),
]
def get_pebble_handlers(
self,
) -> List[sunbeam_chandlers.ServicePebbleHandler]:

View File

@@ -1,5 +1,6 @@
[DEFAULT]
debug = {{ options.debug }}
api_paste_confg = /etc/aodh/api-paste.ini
{% if amqp.transport_url -%}
transport_url = {{ amqp.transport_url }}
@@ -20,3 +21,6 @@ alarm_histories_delete_batch_size = {{ options.alarm_histories_delete_batch_size
{% include "parts/section-service-credentials" %}
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -0,0 +1,57 @@
[composite:aodh+noauth]
use = egg:Paste#urlmap
/ = aodhversions_pipeline
/v2 = aodhv2_noauth_pipeline
/healthcheck = healthcheck
[composite:aodh+keystone]
use = egg:Paste#urlmap
/ = aodhversions_pipeline
/v2 = aodhv2_keystone_pipeline
/healthcheck = healthcheck
[app:healthcheck]
use = egg:oslo.middleware#healthcheck
oslo_config_project = aodh
[pipeline:aodhversions_pipeline]
pipeline = cors http_proxy_to_wsgi aodhversions
[app:aodhversions]
paste.app_factory = aodh.api.app:app_factory
root = aodh.api.controllers.root.VersionsController
[pipeline:aodhv2_keystone_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler authtoken audit aodhv2
[pipeline:aodhv2_noauth_pipeline]
pipeline = cors http_proxy_to_wsgi request_id osprofiler aodhv2
[app:aodhv2]
paste.app_factory = aodh.api.app:app_factory
root = aodh.api.controllers.v2.root.V2Controller
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/aodh/api_audit_map.conf
service_name = aodh
driver = log
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
oslo_config_project = aodh
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = aodh
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
oslo_config_project = aodh
[filter:osprofiler]
paste.filter_factory = aodh.profiler:WsgiMiddleware.factory
oslo_config_project = aodh

View File

@@ -0,0 +1,18 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = alarming
[custom_actions]
[path_keywords]
alarms = alarm
state = alarm-state
capabilities = capability
quotas = quota
metrics = metric
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
alarming = service/alarming

View File

@@ -347,6 +347,26 @@ class BarbicanOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
)
return _cadapters
@property
def container_configs(self) -> list[sunbeam_core.ContainerConfigFile]:
"""Container configuration files for the service."""
_cconfigs = super().container_configs
_cconfigs.extend(
[
sunbeam_core.ContainerConfigFile(
"/etc/barbican/api_audit_map.conf",
self.service_user,
self.service_group,
),
sunbeam_core.ContainerConfigFile(
"/etc/barbican/barbican-api-paste.ini",
self.service_user,
self.service_group,
),
]
)
return _cconfigs
def disable_barbican_config(self):
"""Disable default barbican config."""
container = self.unit.get_container(BARBICAN_API_CONTAINER)

View File

@@ -0,0 +1,28 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = key-manager
# map urls ending with specific text to a unique action
# Don't need custom mapping for other resource operations
# Note: action should match action names defined in CADF taxonomy
[custom_actions]
acl/get = read
# path of api requests for CADF target typeURI
# Just need to include top resource path to identify class of resources
[path_keywords]
secrets=secret
metadata=secret-metadata
containers=container
consumers=consumer
orders=order
cas=None
quotas=quota
project-quotas=project-quota
secret-stores=secret-store
acl=acl
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
key-manager = service/security/keymanager

View File

@@ -0,0 +1,72 @@
[composite:main]
use = egg:Paste#urlmap
/: barbican_version
/healthcheck: healthcheck
/v1: barbican-api-keystone-audit
# Use this pipeline for Barbican API - versions no authentication
[pipeline:barbican_version]
pipeline = cors http_proxy_to_wsgi microversion versionapp
# Use this pipeline for Barbican API - DEFAULT no authentication
[pipeline:barbican_api]
pipeline = cors http_proxy_to_wsgi unauthenticated-context microversion apiapp
#Use this pipeline to activate a repoze.profile middleware and HTTP port,
# to provide profiling information for the REST API processing.
[pipeline:barbican-profile]
pipeline = cors http_proxy_to_wsgi unauthenticated-context microversion egg:Paste#cgitb egg:Paste#httpexceptions profile apiapp
#Use this pipeline for keystone auth
[pipeline:barbican-api-keystone]
pipeline = cors http_proxy_to_wsgi authtoken context microversion apiapp
#Use this pipeline for keystone auth with audit feature
[pipeline:barbican-api-keystone-audit]
pipeline = http_proxy_to_wsgi authtoken context microversion audit apiapp
[app:apiapp]
paste.app_factory = barbican.api.app:create_main_app
[app:versionapp]
paste.app_factory = barbican.api.app:create_version_app
[filter:simple]
paste.filter_factory = barbican.api.middleware.simple:SimpleFilter.factory
[filter:unauthenticated-context]
paste.filter_factory = barbican.api.middleware.context:UnauthenticatedContextMiddleware.factory
[filter:context]
paste.filter_factory = barbican.api.middleware.context:ContextMiddleware.factory
[filter:microversion]
paste.filter_factory = barbican.api.middleware.microversion:MicroversionMiddleware.factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/barbican/api_audit_map.conf
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:profile]
use = egg:repoze.profile
log_filename = myapp.profile
cachegrind_filename = cachegrind.out.myapp
discard_first_request = true
path = /__profile__
flush_at_shutdown = true
unwind = false
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = barbican
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware:HTTPProxyToWSGI.factory
[app:healthcheck]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
backends = disable_by_file
disable_by_file_path = /etc/barbican/healthcheck_disable

View File

@@ -2,6 +2,7 @@
debug = {{ options.debug }}
lock_path = /var/lock/barbican
state_path = /var/lib/barbican
api_paste_confg = /etc/barbican/barbican-api-paste.ini
host_href = ""
transport_url = {{ amqp.transport_url }}
@@ -39,3 +40,6 @@ ssl_ca_crt_file = {{ vault_kv.ca_crt_file }}
{% endif -%}
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -92,6 +92,9 @@ class CinderWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
sunbeam_core.ContainerConfigFile(
"/etc/cinder/cinder.conf", "root", "cinder", 0o640
),
sunbeam_core.ContainerConfigFile(
"/etc/cinder/api_audit_map.conf", "root", "cinder", 0o640
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",

View File

@@ -11,8 +11,13 @@ use = call:cinder.api:root_app_factory
[composite:openstack_volume_api_v3]
use = call:cinder.api.middleware.auth:pipeline_factory
noauth = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler noauth apiv3
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
keystone_nolimit = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext audit apiv3
keystone_nolimit = cors http_proxy_to_wsgi request_id faultwrap sizelimit osprofiler authtoken keystonecontext audit apiv3
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/cinder/api_audit_map.conf
service_name = cinder
[filter:request_id]
paste.filter_factory = oslo_middleware.request_id:RequestId.factory

View File

@@ -0,0 +1,55 @@
[DEFAULT]
# Default target endpoint type,
# should match the endpoint type defined in service catalog.
# Possible values: cinderv3, block-storage.
target_endpoint_type = block-storage
# map urls ending with specific text to a unique action
[custom_actions]
associate = update/associate
disassociate = update/disassociate
disassociate_all = update/disassociate_all
associations = read/list/associations
backups/import_record = import_backup
restore = restore
export_record = export
accept = accept-transfer
cleanup = cleanup
# possible end path of api requests
[path_keywords]
defaults = None
detail = None
limits = None
os-quota-specs = project
os-quota-sets = quota-set
os-quota-class-sets = quota-class
qos-specs = qos-spec
snapshots = snapshot
types = type
volumes = volume
default-types = default-type
manageable_volumes = manageable-volume
manageable_snapshots = manageable-snapshot
attachments = attachment
os-availability-zone = availability-zone
scheduler-stats = scheduler-stats
os-volume-transfers = volume-transfer
volume-transfers = volume-transfer
backups = backup
encryption = volume-encryption
os-services = service
consistencygroups = consistency-group
clusters = cluster
groups = group
group_snapshots = group_snapshot
group_specs = group_specs
messages = message
os-hosts = host
resource_filters = resource_filter
workers = worker
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
volume = service/storage/block
volumev2 = service/storage/block

View File

@@ -20,3 +20,6 @@ auth_strategy = keystone
lock_path = /var/lib/cinder/tmp
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -153,6 +153,16 @@ class DesignatePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
"designate",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/designate/api_audit_map.conf",
"designate",
"designate",
),
sunbeam_core.ContainerConfigFile(
"/etc/designate/api-paste.ini",
"designate",
"designate",
),
]
)
return _cconfig

View File

@@ -0,0 +1,77 @@
[composite:osapi_dns]
use = egg:Paste#urlmap
/: osapi_dns_versions
/healthcheck: healthcheck
/v2: osapi_dns_v2
/admin: osapi_dns_admin
[composite:osapi_dns_versions]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors maintenance faultwrapper osprofiler osapi_dns_app_versions
keystone = http_proxy_to_wsgi cors maintenance faultwrapper osprofiler osapi_dns_app_versions
[app:osapi_dns_app_versions]
paste.app_factory = designate.api.versions:factory
[composite:osapi_dns_v2]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 sizelimit osprofiler noauthcontext maintenance normalizeuri osapi_dns_app_v2
keystone = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 sizelimit osprofiler authtoken keystonecontext maintenance normalizeuri audit osapi_dns_app_v2
[app:osapi_dns_app_v2]
paste.app_factory = designate.api.v2:factory
[composite:osapi_dns_admin]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors request_id faultwrapper sizelimit osprofiler noauthcontext maintenance normalizeuri osapi_dns_app_admin
keystone = http_proxy_to_wsgi cors request_id faultwrapper sizelimit osprofiler authtoken keystonecontext maintenance normalizeuri audit osapi_dns_app_admin
[app:osapi_dns_app_admin]
paste.app_factory = designate.api.admin:factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/designate/api_audit_map.conf
service_name = designate
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = designate
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware:HTTPProxyToWSGI.factory
[filter:osprofiler]
paste.filter_factory = designate.common.profiler:WsgiMiddleware.factory
[filter:sizelimit]
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
[filter:noauthcontext]
paste.filter_factory = designate.api.middleware:NoAuthContextMiddleware.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:keystonecontext]
paste.filter_factory = designate.api.middleware:KeystoneContextMiddleware.factory
[filter:maintenance]
paste.filter_factory = designate.api.middleware:MaintenanceMiddleware.factory
[filter:normalizeuri]
paste.filter_factory = designate.api.middleware:NormalizeURIMiddleware.factory
[filter:faultwrapper]
paste.filter_factory = designate.api.middleware:FaultWrapperMiddleware.factory
[filter:validation_API_v2]
paste.filter_factory = designate.api.middleware:APIv2ValidationErrorMiddleware.factory
[app:healthcheck]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
backends = disable_by_file
disable_by_file_path = /etc/designate/healthcheck_disable

View File

@@ -0,0 +1,39 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = dns
[custom_actions]
export = export
abandon = abandon
xfr = xfr
pool_move = pool_move
[path_keywords]
zones = zone
tasks = task
imports = import
exports = export
nameservers = nameserver
shares = share
transfer_requests = transfer_request
transfer_accepts = transfer_accept
recordsets = recordset
pools = pool
limits = limit
tlds = tld
tsigkeys = tsigkey
blacklists = blacklist
quotas = quota
service_statuses = service_status
floatingips = floatingip
export = None
abandon = None
xfr = None
pool_move = None
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
dns = service/dns

View File

@@ -2,6 +2,7 @@
debug = {{ options.debug }}
lock_path = /var/lock/designate
state_path = /var/lib/designate
api_paste_confg = /etc/designate/api-paste.ini
transport_url = {{ amqp.transport_url }}
@@ -24,3 +25,6 @@ enabled = True
{% include "parts/database-connection-settings" %}
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -342,6 +342,18 @@ class GlanceOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/glance/api_audit_map.conf",
self.service_user,
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/glance/glance-api-paste.ini",
self.service_user,
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
self.service_user,

View File

@@ -0,0 +1,29 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = image
[custom_actions]
deactivate = deactivate
reactivate = reactivate
stage = stage
import = import
# possible end path of api requests
[path_keywords]
detail = None
file = None
images = image
members = member
tags = tag
tasks = task
locations = location
schemas = schema
stores = store
info = info
cache = cache
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
image = service/storage/image

View File

@@ -0,0 +1,98 @@
# Use this composite for no auth or image caching - DEFAULT
[composite:glance-api]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
# Use this composite for image caching and no auth
[composite:glance-api-caching]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
# Use this composite for caching w/ management interface but no auth
[composite:glance-api-cachemanagement]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
# Use this composite for keystone auth
[composite:glance-api-keystone]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
# Use this composite for keystone auth with image caching
[composite:glance-api-keystone+caching]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
# Use this composite for keystone auth with caching and cache management
[composite:glance-api-keystone+cachemanagement]
paste.composite_factory = glance.api:root_app_factory
/: api
/healthcheck: healthcheck
[composite:api]
paste.composite_factory = glance.api:pipeline_factory
default = cors http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context rootapp
caching = cors http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache rootapp
cachemanagement = cors http_proxy_to_wsgi versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp
keystone = cors http_proxy_to_wsgi versionnegotiation osprofiler authtoken context audit rootapp
keystone+caching = cors http_proxy_to_wsgi versionnegotiation osprofiler authtoken context audit cache rootapp
keystone+cachemanagement = cors http_proxy_to_wsgi versionnegotiation osprofiler authtoken context audit cache cachemanage rootapp
[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
[app:healthcheck]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
backends = disable_by_file
disable_by_file_path = /etc/glance/healthcheck_disable
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/glance/api_audit_map.conf
service_name = glance
[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

View File

@@ -49,3 +49,6 @@ rbd_store_ceph_conf = /etc/ceph/ceph.conf
flavor = keystone
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -111,6 +111,12 @@ class GnocchiWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
self.charm.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/gnocchi/api_audit_map.conf",
self.charm.service_user,
self.charm.service_group,
0o640,
),
]
)
_cconfigs.extend(self.charm.default_container_configs())

View File

@@ -34,8 +34,14 @@ use = egg:Paste#urlmap
[pipeline:gnocchiv1+noauth]
pipeline = http_proxy_to_wsgi gnocchiv1
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/gnocchi/api_audit_map.conf
service_name = gnocchi
driver = log
[pipeline:gnocchiv1+keystone]
pipeline = http_proxy_to_wsgi keystone_authtoken gnocchiv1
pipeline = http_proxy_to_wsgi keystone_authtoken audit gnocchiv1
[pipeline:gnocchiversions_pipeline]
pipeline = http_proxy_to_wsgi gnocchiversions

View File

@@ -0,0 +1,23 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = metric
# possible end path of api requests
[path_keywords]
metric = metric_id
measures = None
archive_policy = archive_policy_name
archive_policy_rule = archive_policy_rule_name
generic = generic_id
instance = instance_id
history = None
resource_type = resource_type_name
capabilities = None
status = None
resource = resource
aggregates = aggregate
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
metric=service/metric

View File

@@ -1,5 +1,6 @@
[DEFAULT]
debug = {{ options.debug }}
api_paste_confg = /etc/gnocchi/api-paste.ini
[api]
auth_mode = keystone
@@ -20,3 +21,6 @@ ceph_conffile = /etc/ceph/ceph.conf
workers = 4
{% include "parts/section-identity" %}
[audit_middleware_notifications]
driver = log

View File

@@ -616,6 +616,12 @@ class HeatOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/heat/api_audit_map.conf",
self.service_user,
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",
@@ -639,6 +645,12 @@ class HeatOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/heat/api_audit_map_cfn.conf",
self.service_user,
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",

View File

@@ -70,14 +70,14 @@ paste.composite_factory = heat.api:root_app_factory
[composite:api]
paste.composite_factory = heat.api:pipeline_factory
default = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authtoken context osprofiler apiv1app
default = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authtoken context osprofiler audit apiv1app
standalone = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authpassword context apiv1app
custombackend = cors request_id context faultwrap versionnegotiation custombackendauth apiv1app
noauth = cors request_id faultwrap noauth context http_proxy_to_wsgi versionnegotiation apiv1app
[composite:api-cfn]
paste.composite_factory = heat.api:pipeline_factory
default = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken authtoken context osprofiler apicfnv1app
default = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken authtoken context osprofiler audit apicfnv1app
standalone = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken context apicfnv1app
[app:apiv1app]
@@ -91,6 +91,11 @@ heat.app_factory = heat.api.cfn.v1:API
[app:healthcheck]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/heat/api_audit_map_cfn.conf
service_name = heat
[filter:versionnegotiation]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:version_negotiation_filter

View File

@@ -70,14 +70,14 @@ paste.composite_factory = heat.api:root_app_factory
[composite:api]
paste.composite_factory = heat.api:pipeline_factory
default = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authtoken context osprofiler apiv1app
default = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authtoken context osprofiler audit apiv1app
standalone = cors request_id faultwrap http_proxy_to_wsgi versionnegotiation authurl authpassword context apiv1app
custombackend = cors request_id context faultwrap versionnegotiation custombackendauth apiv1app
noauth = cors request_id faultwrap noauth context http_proxy_to_wsgi versionnegotiation apiv1app
[composite:api-cfn]
paste.composite_factory = heat.api:pipeline_factory
default = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken authtoken context osprofiler apicfnv1app
default = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken authtoken context osprofiler audit apicfnv1app
standalone = cors request_id http_proxy_to_wsgi cfnversionnegotiation ec2authtoken context apicfnv1app
[app:apiv1app]
@@ -91,6 +91,11 @@ heat.app_factory = heat.api.cfn.v1:API
[app:healthcheck]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/heat/api_audit_map.conf
service_name = heat
[filter:versionnegotiation]
paste.filter_factory = heat.common.wsgi:filter_factory
heat.filter_factory = heat.api.openstack:version_negotiation_filter

View File

@@ -0,0 +1,39 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = orchestration
[custom_actions]
abandon = abandon
export = export
signal = signal
validate = validate
# possible end path of api requests
[path_keywords]
abandon = None
export = None
signal = None
validate = None
stacks = stack
resources = resource
preview = None
detail = None
snapshots = snapshot
restore = None
outputs = output
metadata = server
events = event
template = None
template_versions = template_version
functions = None
resource_types = resource_type
build_info = None
actions = None
software_configs = software_config
software_deployments = software_deployment
services = None
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
orchestration = service/orchestration

View File

@@ -0,0 +1,32 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = cloudformation
# possible end path of api requests
[path_keywords]
stacks = stack
resources = resource
preview = None
detail = None
abandon = None
snapshots = snapshot
restore = None
outputs = output
metadata = server
signal = None
events = event
template = None
template_versions = template_version
functions = None
validate = None
resource_types = resource_type
build_info = None
actions = None
software_configs = software_config
software_deployments = software_deployment
services = None
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
orchestration = service/orchestration

View File

@@ -32,3 +32,5 @@ workers = 4
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -33,3 +33,5 @@ workers = 4
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -274,6 +274,36 @@ class IdentityResourceProvidesHandler(sunbeam_rhandlers.RelationHandler):
class WSGIKeystonePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
"""Keystone Pebble Handler."""
def get_layer(self) -> dict:
"""Keystone WSGI service layer.
:returns: pebble layer configuration
:rtype: dict
"""
layer = super().get_layer()
# Append the audit monitor service, which will receive audit
# messages over AMQP and log them.
layer["services"]["keystone-audit-monitor"] = {
"override": "replace",
"summary": "keystone audit monitor",
"command": "/usr/bin/python3 /usr/bin/keystone_audit_monitor.py",
"user": "keystone",
"group": "keystone",
}
return layer
def default_container_configs(
self,
) -> list[sunbeam_core.ContainerConfigFile]:
"""Container configs for WSGI service."""
configs = super().default_container_configs()
configs += [
sunbeam_core.ContainerConfigFile(
"/usr/bin/keystone_audit_monitor.py", "root", "keystone", 0o750
),
]
return configs
def init_service(self, context: sunbeam_core.OPSCharmContexts) -> None:
"""Enable and start WSGI service."""
container = self.charm.unit.get_container(self.container_name)
@@ -860,7 +890,7 @@ export OS_AUTH_VERSION=3
self.template_dir,
self.configure_charm,
f"wsgi-{self.service_name}",
)
),
]
def get_relation_handlers(

View File

@@ -0,0 +1,65 @@
#! /usr/bin/python3
import sys
import oslo_messaging
from oslo_config import cfg # noqa
from oslo_log import log as logging
LOG = logging.getLogger("keystone_audit_monitor")
CONF = cfg.CONF
NOTIFICATION_TOPIC = "notifications"
NOTIFICATION_EXCHANGE = "keystone"
NOTIFICATION_POOL = "keystone-charm"
class NotificationEndpoint(object):
filter_rule = oslo_messaging.NotificationFilter(
publisher_id='^identity.*')
def info(self, ctxt, publisher_id, event_type, payload, metadata):
self._log_notification(
ctxt, "info", publisher_id, event_type, payload, metadata)
def _log_notification(self, ctxt, priority, publisher_id,
event_type, payload, metadata):
LOG.info(
"Received %(priority)s notification, "
"publisher: %(publisher_id)s, "
"event: %(event_type)s, "
"payload: %(payload)s, "
"metadata: %(metadata)s.",
dict(priority=priority,
publisher_id=publisher_id,
event_type=event_type,
payload=payload,
metadata=metadata))
def run_monitor():
LOG.info("Starting Keystone audit monitor")
transport = oslo_messaging.get_notification_transport(CONF)
targets = [
oslo_messaging.Target(
topic=NOTIFICATION_TOPIC,
exchange=NOTIFICATION_EXCHANGE),
]
endpoints = [NotificationEndpoint()]
pool = NOTIFICATION_POOL
server = oslo_messaging.get_notification_listener(transport, targets,
endpoints, pool=pool)
server.start()
server.wait()
if __name__ == "__main__":
logging.register_options(CONF)
CONF(sys.argv[1:], project="keystone",
default_config_files=["/etc/keystone/keystone.conf"])
logging.setup(CONF, "keystone_audit_monitor")
run_monitor()

View File

@@ -1,17 +1,17 @@
[loggers]
keys=root
keys=root,keystone_audit_monitor
[formatters]
keys=normal,normal_with_name,debug,context
[handlers]
keys=production,devel
keys=production,devel,keystone_audit_monitor
[logger_root]
{% if ks_logging.root_level -%}
level={{ ks_logging.root_level }}
{% else -%}
level=WARNING
level=INFO
{% endif -%}
handlers=production
@@ -20,11 +20,22 @@ class=StreamHandler
{% if ks_logging.log_level -%}
level={{ ks_logging.log_level }}
{% else -%}
level=ERROR
level=INFO
{% endif -%}
formatter=context
args=(sys.stdout,)
[logger_keystone_audit_monitor]
level=INFO
handlers=keystone_audit_monitor
qualname=keystone_audit_monitor
[handler_keystone_audit_monitor]
class=StreamHandler
level=INFO
formatter=context
args=(sys.stdout,)
[handler_file]
class=FileHandler
level=DEBUG

View File

@@ -227,6 +227,11 @@ class MagnumOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
"magnum",
"magnum",
),
sunbeam_core.ContainerConfigFile(
"/etc/magnum/api_audit_map.conf",
"magnum",
"magnum",
),
sunbeam_core.ContainerConfigFile(
"/etc/magnum/keystone_auth_default_policy.json",
"magnum",

View File

@@ -7,11 +7,16 @@ paste.composite_factory = magnum.api:root_app_factory
{% endif %}
[pipeline:api]
pipeline = cors http_proxy_to_wsgi request_id osprofiler authtoken api_v1
pipeline = cors http_proxy_to_wsgi request_id osprofiler authtoken audit api_v1
[app:api_v1]
paste.app_factory = magnum.api.app:app_factory
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/magnum/api_audit_map.conf
service_name = magnum
[filter:authtoken]
acl_public_routes = /, /v1
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory

View File

@@ -0,0 +1,21 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = container-infra
[custom_actions]
resize = resize
upgrade = upgrade
[path_keywords]
clusters = cluster
clustertemplates = clustertemplate
certificates = certificate
mservices = mservice
stats = stats
quotas = quota
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
alarming = service/container-infra

View File

@@ -64,3 +64,6 @@ region_name = {{ options.region }}
{% if receive_ca_cert and receive_ca_cert.ca_bundle -%}
ca_file = /usr/local/share/ca-certificates/ca-bundle.pem
{% endif -%}
[audit_middleware_notifications]
driver = log

View File

@@ -297,6 +297,18 @@ class MasakariOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
group=self.service_group,
permissions=0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/masakari/api_audit_map.conf",
self.service_user,
self.service_group,
permissions=0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/masakari/api-paste.ini",
self.service_user,
self.service_group,
permissions=0o640,
),
]
)
return _cconfigs

View File

@@ -0,0 +1,51 @@
[composite:masakari_api]
use = call:masakari.api.urlmap:urlmap_factory
/: apiversions
/v1: masakari_api_v1
[composite:masakari_api_v1]
use = call:masakari.api.auth:pipeline_factory_v1
keystone = cors http_proxy_to_wsgi request_id faultwrap sizelimit authtoken keystonecontext audit osapi_masakari_app_v1
noauth2 = cors http_proxy_to_wsgi request_id faultwrap sizelimit noauth2 osapi_masakari_app_v1
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/masakari/api_audit_map.conf
service_name = masakari
# filters
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = masakari
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[filter:faultwrap]
paste.filter_factory = masakari.api.openstack:FaultWrapper.factory
[filter:sizelimit]
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:keystonecontext]
paste.filter_factory = masakari.api.auth:MasakariKeystoneContext.factory
[filter:noauth2]
paste.filter_factory = masakari.api.auth:NoAuthMiddleware.factory
# apps
[app:osapi_masakari_app_v1]
paste.app_factory = masakari.api.openstack.ha:APIRouterV1.factory
[pipeline:apiversions]
pipeline = faultwrap http_proxy_to_wsgi apiversionsapp
[app:apiversionsapp]
paste.app_factory = masakari.api.openstack.ha.versions:Versions.factory

View File

@@ -0,0 +1,16 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = instance-ha
[custom_actions]
[path_keywords]
segments = segment
hosts = host
notifications = notification
vmoves = vmove
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
alarming = service/instance-ha

View File

@@ -3,6 +3,8 @@
debug = {{ options.debug }}
auth_strategy = keystone
api_paste_confg = /etc/masakari/api-paste.ini
{% if amqp.transport_url -%}
transport_url = {{ amqp.transport_url }}
{% endif -%}
@@ -40,3 +42,6 @@ wait_period_after_service_update = {{ options.evacuation_delay }}
[host_failure]
evacuate_all_instances = {{ options.evacuate_all_instances }}
[audit_middleware_notifications]
driver = log

View File

@@ -16,8 +16,8 @@ Listen {{ wsgi_config.public_port }}
ErrorLogFormat "%{cu}t %M"
</IfVersion>
ErrorLog /var/log/apache2/masakari_error.log
CustomLog /var/log/apache2/masakari_access.log combined
ErrorLog {{ wsgi_config.error_log }}
CustomLog {{ wsgi_config.custom_log }} combined
<Directory /usr/bin>
<IfVersion >= 2.4>

View File

@@ -142,6 +142,9 @@ class NeutronServerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
sunbeam_core.ContainerConfigFile(
"/etc/neutron/api-paste.ini", "neutron", "neutron"
),
sunbeam_core.ContainerConfigFile(
"/etc/neutron/api_audit_map.conf", "root", "neutron"
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",
@@ -394,6 +397,9 @@ class NeutronServerOVNPebbleHandler(NeutronServerPebbleHandler):
sunbeam_core.ContainerConfigFile(
"/etc/neutron/api-paste.ini", "root", "neutron", 0o640
),
sunbeam_core.ContainerConfigFile(
"/etc/neutron/api_audit_map.conf", "root", "neutron", 0o640
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",

View File

@@ -11,13 +11,18 @@ use = egg:Paste#urlmap
[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = cors http_proxy_to_wsgi request_id fake_project_id catch_errors osprofiler extensions neutronapiapp_v2_0
keystone = cors http_proxy_to_wsgi request_id catch_errors osprofiler authtoken keystonecontext extensions neutronapiapp_v2_0
keystone = cors http_proxy_to_wsgi request_id catch_errors osprofiler authtoken audit keystonecontext extensions neutronapiapp_v2_0
[composite:neutronversions_composite]
use = call:neutron.auth:pipeline_factory
noauth = cors http_proxy_to_wsgi neutronversions
keystone = cors http_proxy_to_wsgi neutronversions
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/neutron/api_audit_map.conf
service_name = neutron
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory

View File

@@ -0,0 +1,108 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = network
[custom_actions]
add_router_interface = update/add
remove_router_interface = update/remove
add_allowed_address_pairs = update/add
remove_allowed_address_pairs = update/remove
activate = activate
add_subports = update/add
remove_subports = update/remove
get_subports = get/list
add_extraroutes = update/add
remove_extraroutes = update/remove
add_external_gateways = update/add
update_external_gateways = update
remove_external_gateways = update/remove
add_prefixes = update/add
remove_prefixes = update/remove
add_addresses = update/add
remove_addresses = update/remove
insert_rule = update/add
remove_rule = update/remove
add_bgp_peer = update/add
remove_bgp_peer = update/remove
add_gateway_network = update/add
remove_gateway_network = update/remove
get_advertised_routes = get/list
bgp-dragents = get/list
# possible end path of api requests
[path_keywords]
floatingips = ip
healthmonitors = healthmonitor
health_monitors = health_monitor
lb = None
members = member
metering-labels = label
metering-label-rules = rule
networks = network
pools = pool
ports = port
routers = router
quotas = quota
security-groups = security-group
security-group-rules = rule
subnets = subnet
vips = vip
extensions = extension
network_segment_ranges = network-segment-range
bindings = binding
segments = segment
trunks = trunk
address-scopes = address-scope
conntrack_helpers = conntrack_helper
port_forwardings = port-forwarding
ndp_proxies = ndp_proxy
subnetpools = subnetpool
local_ips = local_ip
port_associations = port-association
address-groups = address-group
firewall_groups = firewall-groups
firewall_policies = firewall-policy
firewall_rules = firewall-rule
rbac-policies = rbac-policy
default-security-group-rules = default-security-group-rule
ikepolicies = ikepolicy
ipsecpolicies = ipsecpolicy
ipsec-site-connections = ipsec-site-connection
endpoint-groups = endpoint-group
vpnservices = vpnservice
flavors = flavor
service_profiles = service-profile
network-ip-availabilities = network-ip-availability
service-providers = service-provider
tags = tag
qos/rule-types = qos/rule-type
qos/policies = qos/policy
bandwidth_limit_rules = bandwidth_limit_rule
dscp_marking_rules = dscp_marking_rule
minimum_bandwidth_rules = minimum_bandwidth_rule
minimum_packet_rate_rules = minimum_packet_rate_rule
packet_rate_limit_rules = packet_rate_limit_rule
alias_bandwidth_limit_rules = alias_bandwidth_limit_rule
alias_minimum_packet_rate_rules = alias_minimum_packet_rate_rule
logging_resources = logging_resource
firewall_logs = firewall_log
bgpvpns = bgpvpn
network_associations = network_association
router_associations = router_association
bgp-speakers = bgp-speaker
dhcp-agents = dhcp-agent
auto-allocated-topology = auto-allocated-topology
tap_services = tap-service
tap_flows = tap-flow
tap_mirrors = tap-mirror
port_chains = port-chain
port_pair_groups = port-pair-group
port_pairs = port-pairs
flow_classifiers = flow-classifier
service_graphs = service-graph
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
network = service/network

View File

@@ -92,3 +92,6 @@ ipv6_ptr_zone_prefix_size = {{ options.ipv6_ptr_zone_prefix_size }}
cafile = /usr/local/share/ca-certificates/ca-bundle.pem
{% endif -%}
{% endif -%}
[audit_middleware_notifications]
driver = log

View File

@@ -566,6 +566,18 @@ class NovaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
"nova",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/nova/api_audit_map.conf",
"root",
"nova",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/nova/api-paste.ini",
"root",
"nova",
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",

View File

@@ -0,0 +1,95 @@
############
# Metadata #
############
[composite:metadata]
use = egg:Paste#urlmap
/: meta
[pipeline:meta]
pipeline = cors http_proxy_to_wsgi metaapp
[app:metaapp]
paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory
#############
# OpenStack #
#############
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v2: oscomputeversion_legacy_v2
/v2.1: oscomputeversion_v2
# v21 is an exactly feature match for v2, except it has more stringent
# input validation on the wsgi surface (prevents fuzzing early on the
# API). It also provides new features via API microversions which are
# opt into for clients. Unaware clients will receive the same frozen
# v2 API feature set, but with some relaxed validation
/v2/+: openstack_compute_api_v21_legacy_v2_compatible
/v2.1/+: openstack_compute_api_v21
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext audit osapi_compute_app_v21
[composite:openstack_compute_api_v21_legacy_v2_compatible]
use = call:nova.api.auth:pipeline_factory_v21
keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext audit legacy_v2_compatible osapi_compute_app_v21
[filter:audit]
paste.filter_factory = keystonemiddleware.audit:filter_factory
audit_map_file = /etc/nova/api_audit_map.conf
service_name = nova
[filter:request_log]
paste.filter_factory = nova.api.openstack.requestlog:RequestLog.factory
[filter:compute_req_id]
paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory
[filter:faultwrap]
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
[filter:osprofiler]
paste.filter_factory = nova.profiler:WsgiMiddleware.factory
[filter:sizelimit]
paste.filter_factory = oslo_middleware:RequestBodySizeLimiter.factory
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
[filter:legacy_v2_compatible]
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory
[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory
[pipeline:oscomputeversions]
pipeline = cors faultwrap request_log http_proxy_to_wsgi oscomputeversionapp
[pipeline:oscomputeversion_v2]
pipeline = cors compute_req_id faultwrap request_log http_proxy_to_wsgi oscomputeversionapp_v2
[pipeline:oscomputeversion_legacy_v2]
pipeline = cors compute_req_id faultwrap request_log http_proxy_to_wsgi legacy_v2_compatible oscomputeversionapp_v2
[app:oscomputeversionapp]
paste.app_factory = nova.api.openstack.compute.versions:Versions.factory
[app:oscomputeversionapp_v2]
paste.app_factory = nova.api.openstack.compute.versions:VersionsV2.factory
##########
# Shared #
##########
[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = nova
[filter:keystonecontext]
paste.filter_factory = nova.api.auth:NovaKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

View File

@@ -0,0 +1,72 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = compute
[custom_actions]
enable = enable
disable = disable
delete = delete
startup = start/startup
shutdown = stop/shutdown
reboot = start/reboot
os-migrations/get = read
os-server-password/post = update
# possible end path of api requests
[path_keywords]
add = None
action = None
enable = None
disable = None
configure-project = None
defaults = None
delete = None
detail = None
diagnostics = None
entries = entry
extensions = alias
flavors = flavor
images = image
ips = label
limits = None
metadata = key
os-agents = os-agent
os-aggregates = os-aggregate
os-availability-zone = None
os-certificates = None
os-cloudpipe = None
os-fixed-ips = ip
os-extra_specs = key
os-flavor-access = None
os-floating-ip-dns = domain
os-floating-ips-bulk = host
os-floating-ip-pools = None
os-floating-ips = floating-ip
os-hosts = host
os-hypervisors = hypervisor
os-instance-actions = instance-action
os-keypairs = keypair
os-migrations = None
os-networks = network
os-quota-sets = tenant
os-security-groups = security_group
os-security-group-rules = rule
os-server-password = None
os-services = None
os-simple-tenant-usage = tenant
os-virtual-interfaces = None
os-volume_attachments = attachment
os-volumes_boot = None
os-volumes = volume
os-volume-types = volume-type
os-snapshots = snapshot
reboot = None
servers = server
shutdown = None
startup = None
statistics = None
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
compute = service/compute

View File

@@ -2,6 +2,7 @@
lock_path = /var/lock/nova
state_path = /var/lib/nova
debug = {{ options.debug }}
api_paste_confg = /etc/nova/api-paste.ini
transport_url = {{ amqp.transport_url }}
@@ -56,3 +57,6 @@ workers = 4
openstack =
{% include "parts/section-oslo-messaging-rabbit" %}
[audit_middleware_notifications]
driver = log

View File

@@ -215,6 +215,12 @@ class OctaviaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/etc/octavia/api_audit_map.conf",
self.service_user,
self.service_group,
0o640,
),
sunbeam_core.ContainerConfigFile(
"/usr/local/share/ca-certificates/ca-bundle.pem",
"root",

View File

@@ -0,0 +1,40 @@
[DEFAULT]
# default target endpoint type
# should match the endpoint type defined in service catalog
target_endpoint_type = load-balancer
[custom_actions]
failover = update/failover
# possible end path of API requests
# path of api requests for CADF target typeURI
# Just need to include top resource path to identify class
# of resources. Ex: Log audit event for API requests
# path containing "nodes" keyword and node uuid.
[path_keywords]
amphorae = amphora
availabilityzones = availabilityzone
availabilityzoneprofiles = availabilityzoneprofile
config = None
defaults = None
failover = None
flavors = flavor
flavorprofiles = flavorprofile
healthmonitors = healthmonitor
l7policies = l7policy
listeners = listener
loadbalancers = loadbalancer
members = member
pools = pool
providers = None
quotas = quota
rules = rule
stats = None
status = None
# map endpoint type defined in service catalog to CADF typeURI
[service_endpoints]
load-balancer = service/load-balancer
[audit_middleware_notifications]
driver = log

View File

@@ -31,3 +31,10 @@ ovn_sb_ca_cert = {{ ovn.ovn_ca_cert }}
{% include "parts/identity-data" %}
{% include "parts/section-identity" %}
[audit]
enabled = True
audit_map_file = /etc/octavia/api_audit_map.conf
[audit_middleware_notifications]
driver = log

View File

@@ -390,6 +390,8 @@ relations:
- keystone:database
- - traefik:ingress
- keystone:ingress-internal
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database
- glance:database

View File

@@ -11,6 +11,18 @@ configure:
tests:
- zaza.openstack.charm_tests.tempest.tests.TempestTestWithKeystoneMinimalNewRBAC
- zaza.sunbeam.charm_tests.openstack_images_sync_k8s.tests.OpenStackImagesSyncK8sTest
- zaza.sunbeam.charm_tests.api_audit.tests.AodhAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.BarbicanAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.CinderAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.DesignateAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.GlanceAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.HeatAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.KeystoneAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.MagnumAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.MasakariAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.NeutronAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.NovaAPIAuditTest
- zaza.sunbeam.charm_tests.api_audit.tests.OctaviaAPIAuditTest
tests_options:
trust:
- smoke

View File

@@ -145,6 +145,8 @@ relations:
- keystone:database
- - traefik:ingress
- keystone:ingress-internal
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database
- glance:database

View File

@@ -126,6 +126,8 @@ relations:
- keystone:database
- - traefik:ingress
- keystone:ingress-internal
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database
- cinder:database

View File

@@ -160,6 +160,8 @@ relations:
- keystone:database
- - traefik:ingress
- keystone:ingress-internal
- - rabbitmq:amqp
- keystone:amqp
- - mysql:database
- glance:database

View File

@@ -0,0 +1,204 @@
# Copyright (c) 2024 Canonical Ltd.
#
# 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.
import random
import subprocess
import barbicanclient.client as barbican_client
import zaza.openstack.charm_tests.test_utils as test_utils
from zaza.openstack.utilities import openstack as openstack_utils
class OpenStackAPIAuditTest(test_utils.BaseCharmTest):
"""Charm tests for API audit logging."""
application_name = None
@classmethod
def setUpClass(cls):
super().setUpClass(application_name=cls.application_name)
cls.keystone_session = openstack_utils.get_overcloud_keystone_session()
auth = openstack_utils.get_overcloud_auth()
cls.keystone_client = openstack_utils.get_keystone_client(auth)
@classmethod
def get_pod_logs(cls, pod_name, since="5m",
container=None, all_containers=True):
# We expect the k8s namespace name to match the model name.
namespace = cls.model_name
cmd = ["sudo", "k8s", "kubectl", "logs", "-n", namespace,
f"pod/{pod_name}"]
if all_containers:
cmd += ["--all-containers"]
if container:
cmd += ["--container", container]
result = subprocess.run(cmd, check=True, capture_output=True)
return result.stdout.decode()
def _trigger_audit_event(self):
# Perform any API action that is expected to trigger an audit event.
pass
def check_audit_logs(self, exp_msg):
self._trigger_audit_event()
# For simplicity we expect there to be just one pod.
# If needed, we can check multiple pods or aggregate the
# logs using COS.
pod_name = f"{self.application_name}-0"
pod_logs = self.get_pod_logs(pod_name)
assert exp_msg in pod_logs, (
f"{pod_name} logs do not contain the expected message: {exp_msg}")
class KeystoneAPIAuditTest(OpenStackAPIAuditTest):
application_name = "keystone"
def _trigger_audit_event(self):
# We'll update the user email to trigger an audit event.
auth = openstack_utils.get_overcloud_auth()
username = auth["OS_USERNAME"]
user = self.keystone_client.users.find(name=username)
rand_num = random.randint(0, 32768)
email = f"test{rand_num}.example.com"
self.keystone_client.users.update(user.id, email=email)
def test_audit(self):
# Expects the following relation: rabbitmq:amqp keystone:amqp
default_config = {'enable-telemetry-notifications': False}
alternate_config = {'enable-telemetry-notifications': True}
with self.config_change(
default_config=default_config,
alternate_config=alternate_config,
application_name="keystone"):
exp_msg = (
"keystone_audit_monitor Received info notification, "
"publisher: identity.keystone")
self.check_audit_logs(exp_msg)
class AuditMiddlewareTest(OpenStackAPIAuditTest):
"""Base class for services that use the audit paste middleware"""
def test_audit(self):
exp_msg = "oslo.messaging.notification.audit.http.request"
self.check_audit_logs(exp_msg)
class AodhAPIAuditTest(AuditMiddlewareTest):
application_name = "aodh"
def _trigger_audit_event(self):
client = openstack_utils.get_aodh_session_client(
self.keystone_session)
client.alarm.list()
class BarbicanAPIAuditTest(AuditMiddlewareTest):
application_name = "barbican"
def _trigger_audit_event(self):
barbican_endpoint = self.keystone_client.service_catalog.url_for(
service_type='key-manager', interface='publicURL')
client = barbican_client.Client(session=self.keystone_session,
endpoint=barbican_endpoint)
client.secrets.list()
class CinderAPIAuditTest(AuditMiddlewareTest):
application_name = "cinder"
def _trigger_audit_event(self):
client = openstack_utils.get_cinder_session_client(
self.keystone_session)
client.volumes.list()
class DesignateAPIAuditTest(AuditMiddlewareTest):
application_name = "designate"
def _trigger_audit_event(self):
client = openstack_utils.get_designate_session_client(
session=self.keystone_session)
client.zones.list()
class GlanceAPIAuditTest(AuditMiddlewareTest):
application_name = "glance"
def _trigger_audit_event(self):
client = openstack_utils.get_glance_session_client(
self.keystone_session)
client.images.list()
class HeatAPIAuditTest(AuditMiddlewareTest):
application_name = "heat"
def _trigger_audit_event(self):
client = openstack_utils.get_heat_session_client(
self.keystone_session)
client.stacks.list()
class MagnumAPIAuditTest(AuditMiddlewareTest):
application_name = "magnum"
def _trigger_audit_event(self):
client = openstack_utils.get_magnum_session_client(
self.keystone_session)
client.clusters.list()
class MasakariAPIAuditTest(AuditMiddlewareTest):
application_name = "masakari"
def _trigger_audit_event(self):
client = openstack_utils.get_masakari_session_client(
self.keystone_session)
for segment in client.segments():
pass
class NovaAPIAuditTest(AuditMiddlewareTest):
application_name = "nova"
def _trigger_audit_event(self):
client = openstack_utils.get_nova_session_client(
self.keystone_session)
client.servers.list()
class NeutronAPIAuditTest(AuditMiddlewareTest):
application_name = "neutron"
def _trigger_audit_event(self):
client = openstack_utils.get_neutron_session_client(
self.keystone_session)
client.list_networks()
class OctaviaAPIAuditTest(AuditMiddlewareTest):
application_name = "octavia"
def _trigger_audit_event(self):
client = openstack_utils.get_octavia_session_client(
self.keystone_session)
client.amphora_list()