<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pascal Reitermann</title>
    <description>The latest articles on DEV Community by Pascal Reitermann (@pascalre).</description>
    <link>https://dev.to/pascalre</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3654440%2F28f86863-a828-401d-97da-4ce10a244898.jpeg</url>
      <title>DEV Community: Pascal Reitermann</title>
      <link>https://dev.to/pascalre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pascalre"/>
    <language>en</language>
    <item>
      <title>Design-First or Infrastructure-as-Code? Choosing the Source of Truth for your Solace Broker</title>
      <dc:creator>Pascal Reitermann</dc:creator>
      <pubDate>Wed, 11 Mar 2026 19:51:58 +0000</pubDate>
      <link>https://dev.to/pascalre/design-first-or-infrastructure-as-code-choosing-the-source-of-truth-for-your-solace-broker-33di</link>
      <guid>https://dev.to/pascalre/design-first-or-infrastructure-as-code-choosing-the-source-of-truth-for-your-solace-broker-33di</guid>
      <description>&lt;p&gt;In a modern Event-Driven Architecture (EDA), the broker is no longer just "infrastructure". It is the heartbeat of the business. As enterprises scale their event meshes, the question isn't &lt;em&gt;if&lt;/em&gt; they should automate, but &lt;em&gt;where&lt;/em&gt; the "Source of Truth" should live.&lt;/p&gt;

&lt;p&gt;In the Solace ecosystem, you have two powerful paths: &lt;strong&gt;Terraform (Infrastructure-as-Code)&lt;/strong&gt; or &lt;strong&gt;Event Portal (Design-First)&lt;/strong&gt;. Both are excellent, but they represent fundamentally different philosophies. To avoid configuration drift and operational chaos, you must choose your "North Star."&lt;/p&gt;

&lt;h2&gt;
  
  
  The IaC Path: Terraform &amp;amp; Engineering Excellence
&lt;/h2&gt;

&lt;p&gt;This approach treats your Solace broker like any other cloud resource, such as an S3 bucket or an EC2 instance. You define your queues, RDPs, and ACLs as code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"solacebroker_msg_vpn_queue"&lt;/span&gt; &lt;span class="s2"&gt;"this"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;msg_vpn_name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
  &lt;span class="nx"&gt;queue_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"acme-queue"&lt;/span&gt;
  &lt;span class="nx"&gt;max_msg_spool_usage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70000&lt;/span&gt;
  &lt;span class="nx"&gt;ingress_enabled&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;egress_enabled&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unified Pipeline:&lt;/strong&gt; Seamless integration into existing CI/CD and GitOps workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control:&lt;/strong&gt; Every change is documented in Git, requiring Pull Requests and Peer Reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale:&lt;/strong&gt; Manage hundreds of Message VPNs across different regions with a single command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ideal for:&lt;/strong&gt; Platform teams who prioritize operational consistency, auditability, and infrastructure automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Design-First Path: Solace Event Portal
&lt;/h2&gt;

&lt;p&gt;The Design-First approach focuses on modeling your "Event API" before a single line of infrastructure is provisioned. It’s about the "What" and "Why" rather than just the "How".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxle5oild2o4zpvh3x9p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxle5oild2o4zpvh3x9p.jpg" alt="Event Portal" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual Governance:&lt;/strong&gt; Stakeholders and architects see the flow of data through visual graphs, not just lines of code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Registry:&lt;/strong&gt; Native management of payloads (Avro, JSON, etc.) and versioning of events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discovery:&lt;/strong&gt; Developers can easily find existing events to subscribe to, preventing the creation of redundant "event silos."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ideal for:&lt;/strong&gt; Organizations that require strong cross-team alignment on data contracts and focused on data contracts and architectural visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Can’t Have Two Masters
&lt;/h2&gt;

&lt;p&gt;Without a clear boundary, organizations may face &lt;strong&gt;Configuration Overlap&lt;/strong&gt;. If both Terraform and the Event Portal attempt to manage the same broker objects without a hierarchy, the system state can become inconsistent. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Establish a clear hierarchy. For example, use the Event Portal for high-level design and contract definition, while designating Terraform as the final authority for deploying those resources to the broker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Matrix: Which one is for you?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Terraform (IaC)&lt;/th&gt;
&lt;th&gt;Event Portal (Design-First)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary User&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DevOps / Platform Engineer&lt;/td&gt;
&lt;td&gt;Architect / Application Developer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (Code-centric)&lt;/td&gt;
&lt;td&gt;High (Visual/Graph-centric)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual / External&lt;/td&gt;
&lt;td&gt;Native / Integrated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infrastructure Automation&lt;/td&gt;
&lt;td&gt;Event Governance &amp;amp; Discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion: Picking Your North Star
&lt;/h2&gt;

&lt;p&gt;Choosing between Terraform and Event Portal isn't about which tool is "better". It's about who owns the &lt;strong&gt;Source of Truth&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your team lives in the terminal and manages 50+ production brokers with a focus on uptime and reliability, &lt;strong&gt;Terraform&lt;/strong&gt; is your best friend.&lt;/li&gt;
&lt;li&gt;If your team is building a complex ecosystem of microservices and needs to govern data contracts and event discovery, &lt;strong&gt;Event Portal&lt;/strong&gt; is your home.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The "Hybrid" Bridge:&lt;/strong&gt; Keep in mind that Solace Event Portal and Terraform can co-exist. You can, for example, deploy your entire infrastructure with Terraform and use the Discovery functionality in Event Portal to scan, load, and visualize your runtime environment. This gives you the best of both worlds: automated, code-based deployments and a clear, graphical map of your ecosystem. However, even in this setup, the hierarchy must be clear: &lt;strong&gt;One tool configures, the other visualizes.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>architecture</category>
      <category>eventdriven</category>
      <category>terraform</category>
    </item>
    <item>
      <title>🚀 Unlocking the Future: My AI Agent Mesh Portfolio Backend for the New Year, New You Challenge</title>
      <dc:creator>Pascal Reitermann</dc:creator>
      <pubDate>Fri, 09 Jan 2026 21:35:27 +0000</pubDate>
      <link>https://dev.to/pascalre/unlocking-the-future-my-ai-agent-mesh-portfolio-backend-for-the-new-year-new-you-challenge-3cam</link>
      <guid>https://dev.to/pascalre/unlocking-the-future-my-ai-agent-mesh-portfolio-backend-for-the-new-year-new-you-challenge-3cam</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;Hello DEV.to community! I'm Pascal, a Senior Engineer with a passion for building robust, event-driven systems. My career has spanned significant challenges, including managing and scaling over 14,000 endpoints at dmTECH. Starting from march, I'm leveraging this extensive enterprise background as a Customer Technical Trainer at Solace, where I empower developers to master Event-Driven Architecture (EDA) and messaging technologies.&lt;/p&gt;

&lt;p&gt;This portfolio represents my next step: merging my deep understanding of distributed systems with the transformative power of Google AI. My goal is to showcase not just what I've done, but how I build scalable, intelligent, and cloud-native applications — and to connect with the incredible community driving innovation in AI and cloud engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;Get a live look at my &lt;a href="https://portfolio-backend-964537274400.europe-west3.run.app" rel="noopener noreferrer"&gt;AI Agent Mesh&lt;/a&gt; in action! This interactive component demonstrates real-time insights and asynchronous processing.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://portfolio-backend-964537274400.europe-west3.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;&lt;em&gt;Note: As a serverless application, the first request might experience a brief "cold start" delay.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;My journey in this challenge was about pushing the boundaries of what a portfolio can be, leveraging &lt;strong&gt;Google Cloud Platform (GCP)&lt;/strong&gt; and &lt;strong&gt;Google AI&lt;/strong&gt; to create an &lt;strong&gt;AI Agent Mesh&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: Go (Golang) - Chosen for its efficiency, concurrency, and robust standard library (net/http) for a lean, high-performance backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Model&lt;/strong&gt;: Google Gemini 2.0 Flash - The core intelligence powering dynamic interactions and context-aware responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute&lt;/strong&gt;: Google Cloud Run - Fully serverless, offering "scale-to-zero" for cost efficiency and automatic scaling under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messaging&lt;/strong&gt;: Google Pub/Sub - The backbone of my Event-Driven Architecture, enabling asynchronous communication and decoupling services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD&lt;/strong&gt;: GitHub Actions - Automating Docker builds, image pushes to Google Artifact Registry, and deployments to Cloud Run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IaC&lt;/strong&gt;: Terraform - Managing IAM roles and cloud infrastructure with code for consistency and repeatability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Design Decisions &amp;amp; Development Process&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;From the outset, I aimed for an "AI-native" and "cloud-native" design. My backend, built with the Go standard library, emphasizes minimal overhead, crucial for serverless environments. The "Agent Mesh" concept means that user interactions don't block the UI; instead, they trigger a cascade of lightweight, asynchronous events handled by Pub/Sub, allowing for parallel processing, logging, and interaction with Gemini.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google AI Tools in Action – My DevOps Partner&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;A pivotal aspect of this project was my &lt;strong&gt;Human-AI Collaboration with Google Gemini&lt;/strong&gt;. Gemini wasn't just a chatbot; it was an integral partner throughout the development and deployment cycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architecture &amp;amp; Best Practices&lt;/strong&gt;: Gemini helped refine my event-driven architecture, advising on optimal Pub/Sub topic structures and Go concurrency patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Engineering &amp;amp; Debugging&lt;/strong&gt;: When faced with platform challenges - such as multi-platform Docker builds (&lt;code&gt;linux/amd64&lt;/code&gt; for Cloud Run from my ARM64 local machine), intricate IAM permissions (&lt;code&gt;artifactregistry.writer&lt;/code&gt;, &lt;code&gt;run.developer&lt;/code&gt;), or &lt;code&gt;exec format error&lt;/code&gt; - Gemini acted as a Senior SRE, providing precise debugging steps and &lt;code&gt;gcloud&lt;/code&gt; commands. This significantly accelerated problem-solving and deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Optimization&lt;/strong&gt;: Gemini assisted in ensuring my Go code adhered to idiomatic patterns, making it clean, maintainable, and efficient for serverless execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Agent Mesh" in Action&lt;/strong&gt;: I'm particularly proud of demonstrating how an event-driven approach, powered by Pub/Sub, allows for a truly decoupled and scalable AI interaction. It showcases how to move beyond simple request-response to a more robust, extensible system where user input can trigger multiple parallel intelligent agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lean &amp;amp; Performant Go Backend&lt;/strong&gt;: Building with just the Go standard library for the backend was a deliberate choice. It proves that powerful, efficient applications don't always need heavy frameworks, especially in serverless contexts where binary size and cold start times are critical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mastering the DevOps Struggles with AI&lt;/strong&gt;: Overcoming deployment hurdles - from Docker architecture mismatches to intricate Google Cloud IAM policies - with Gemini as my guide was incredibly rewarding. It highlights how modern engineers can leverage AI not just for code, but for navigating the complexities of cloud infrastructure, making the entire CI/CD pipeline robust and efficient.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
    <item>
      <title>PEM vs. PKCS#12 (P12/PFX): Understanding the Difference Between Certificate Formats</title>
      <dc:creator>Pascal Reitermann</dc:creator>
      <pubDate>Fri, 26 Dec 2025 22:46:47 +0000</pubDate>
      <link>https://dev.to/pascalre/pem-vs-pkcs12-p12pfx-understanding-the-difference-between-certificate-formats-1b9d</link>
      <guid>https://dev.to/pascalre/pem-vs-pkcs12-p12pfx-understanding-the-difference-between-certificate-formats-1b9d</guid>
      <description>&lt;p&gt;When working with TLS, HTTPS, OAuth, API gateways, or modern messaging platforms, you’ll eventually encounter two very common certificate formats: &lt;strong&gt;PEM&lt;/strong&gt; and &lt;strong&gt;PKCS#12 (P12/PFX)&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
They both deal with keys and certificates – but they are &lt;em&gt;not&lt;/em&gt; the same thing, and using the wrong one can cause frustrating errors.&lt;/p&gt;

&lt;p&gt;This post explains what each format is, what problems they solve, how they differ, and when to use which.&lt;/p&gt;
&lt;h2&gt;
  
  
  What problem do certificate files solve?
&lt;/h2&gt;

&lt;p&gt;Digital certificates are used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prove identity (who am I?)&lt;/li&gt;
&lt;li&gt;Enable encryption&lt;/li&gt;
&lt;li&gt;Establish trust between systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do that, we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;private key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;public certificate&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sometimes: intermediate + root certificates&lt;/li&gt;
&lt;li&gt;Sometimes: credentials to protect them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different tools and ecosystems expect these packaged in different ways — that’s where PEM and P12 come in.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is a PEM Certificate?
&lt;/h2&gt;

&lt;p&gt;PEM stands for &lt;strong&gt;Privacy Enhanced Mail&lt;/strong&gt;, but today it’s a general-purpose certificate format used almost everywhere.&lt;/p&gt;

&lt;p&gt;PEM files are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Text-based (Base64 encoded)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Human-readable&lt;/li&gt;
&lt;li&gt;Easy to copy, paste, and version&lt;/li&gt;
&lt;li&gt;Very common on Linux, cloud environments, and DevOps tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----
MIID2zCCAsOgAwIBAgIUWlK...
-----END CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PEM files can contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public certificates (&lt;code&gt;.crt\&lt;/code&gt;, &lt;code&gt;.cer\&lt;/code&gt;, &lt;code&gt;.pem\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Private keys (&lt;code&gt;.key\&lt;/code&gt;, sometimes &lt;code&gt;.pem\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Certificate chains (multiple certs in one file)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Advantages of PEM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Easy to inspect&lt;/li&gt;
&lt;li&gt;Git- and diff-friendly&lt;/li&gt;
&lt;li&gt;Supported by OpenSSL, Kubernetes, NGINX, Apache, Keycloak, Solace, Prometheus, etc.&lt;/li&gt;
&lt;li&gt;Flexible: multiple certificates can be stored together&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Private keys may be unencrypted&lt;/li&gt;
&lt;li&gt;Not ideal for securely bundling multiple credentials&lt;/li&gt;
&lt;li&gt;Lacks built-in password protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a PKCS#12 / P12 / PFX File?
&lt;/h2&gt;

&lt;p&gt;PKCS#12 (also known as &lt;strong&gt;P12&lt;/strong&gt; or &lt;strong&gt;PFX&lt;/strong&gt;) is a &lt;strong&gt;binary&lt;/strong&gt;, standardized container format defined in Public Key Cryptography Standards.&lt;/p&gt;

&lt;p&gt;A P12 file is basically a &lt;strong&gt;secure bundle&lt;/strong&gt; that can contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private key&lt;/li&gt;
&lt;li&gt;Public certificate&lt;/li&gt;
&lt;li&gt;Certificate chain (intermediate + root)&lt;/li&gt;
&lt;li&gt;Optional trust store&lt;/li&gt;
&lt;li&gt;Protected with a password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why Windows, macOS Keychain, browsers, enterprise IAM tools, and hardware security devices love it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of P12
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Designed for &lt;em&gt;secure transport&lt;/em&gt; of identities&lt;/li&gt;
&lt;li&gt;Password protected&lt;/li&gt;
&lt;li&gt;One file contains everything&lt;/li&gt;
&lt;li&gt;Great for importing identities into:

&lt;ul&gt;
&lt;li&gt;Browsers&lt;/li&gt;
&lt;li&gt;Windows Certificate Store&lt;/li&gt;
&lt;li&gt;Java KeyStores&lt;/li&gt;
&lt;li&gt;Enterprise systems&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Binary → harder to inspect&lt;/li&gt;
&lt;li&gt;Not Git-friendly&lt;/li&gt;
&lt;li&gt;Requires tooling to open&lt;/li&gt;
&lt;li&gt;Slightly heavier operationally&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  PEM vs P12 — Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PEM&lt;/th&gt;
&lt;th&gt;PKCS#12 (P12/PFX)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encoding&lt;/td&gt;
&lt;td&gt;Text (Base64)&lt;/td&gt;
&lt;td&gt;Binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical OS&lt;/td&gt;
&lt;td&gt;Linux / Unix&lt;/td&gt;
&lt;td&gt;Windows + enterprise tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can store private key&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can store cert chain&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stores multiple items&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;td&gt;Yes (by design)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password protection&lt;/td&gt;
&lt;td&gt;No (unless manually encrypted)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human readable&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical Extensions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.pem\&lt;/code&gt;, &lt;code&gt;.crt\&lt;/code&gt;, &lt;code&gt;.cer\&lt;/code&gt;, &lt;code&gt;.key\&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.p12\&lt;/code&gt;, &lt;code&gt;.pfx\&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Converting Between PEM and P12
&lt;/h2&gt;

&lt;p&gt;Since tools often disagree on formats, conversion is very common.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert PEM → P12
&lt;/h3&gt;

&lt;p&gt;Combine certificate + private key into a secure bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkcs12 -export \
  -in cert.pem \
  -inkey private.key \
  -certfile chain.pem \
  -out identity.p12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll be asked for a password — this protects the .p12 file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convert P12 → PEM
&lt;/h3&gt;

&lt;p&gt;Extract the private key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkcs12 \
  -in identity.p12 \
  -nocerts \
  -out private.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkcs12 \
  -in identity.p12 \
  -clcerts \
  -nokeys \
  -out cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extract the full chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl pkcs12 \
  -in identity.p12 \
  -nodes \
  -out fullchain.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When Should You Use Which?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use &lt;strong&gt;PEM&lt;/strong&gt; when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;configuring servers or infrastructure&lt;/li&gt;
&lt;li&gt;using Linux / DevOps tooling&lt;/li&gt;
&lt;li&gt;working with Kubernetes, NGINX, Apache, Envoy&lt;/li&gt;
&lt;li&gt;certificates are long-lived and versioned&lt;/li&gt;
&lt;li&gt;human readability is useful&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use &lt;strong&gt;P12&lt;/strong&gt; when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;you need to securely &lt;em&gt;transport&lt;/em&gt; an identity&lt;/li&gt;
&lt;li&gt;importing into Windows, browsers, Java, Keycloak, etc.&lt;/li&gt;
&lt;li&gt;bundling key + cert + chain in one file&lt;/li&gt;
&lt;li&gt;password protection is required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;PEM and PKCS#12 aren’t competitors — they solve different problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PEM&lt;/strong&gt; = simple, readable, DevOps &amp;amp; Linux friendly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P12&lt;/strong&gt; = secure, portable identity container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll encounter both, and understanding their roles saves a lot of confusion when systems refuse to accept your certificates.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
      <category>java</category>
    </item>
    <item>
      <title>Securing Solace Metrics: How to Use OAuth with solace-prometheus-exporter</title>
      <dc:creator>Pascal Reitermann</dc:creator>
      <pubDate>Fri, 19 Dec 2025 00:16:44 +0000</pubDate>
      <link>https://dev.to/pascalre/securing-solace-metrics-how-to-use-oauth-with-solace-prometheus-exporter-2i6l</link>
      <guid>https://dev.to/pascalre/securing-solace-metrics-how-to-use-oauth-with-solace-prometheus-exporter-2i6l</guid>
      <description>&lt;p&gt;Monitoring often gets less security attention than it deserves. While applications and APIs are protected with modern identity standards, metrics endpoints frequently rely on static credentials. For Solace environments exposed to Prometheus, this can turn monitoring into a weak spot within an otherwise well-secured system.&lt;/p&gt;

&lt;p&gt;To address this, the &lt;em&gt;community-driven&lt;/em&gt; &lt;a href="https://github.com/solacecommunity/solace-prometheus-exporter" rel="noopener noreferrer"&gt;solace-prometheus-exporter&lt;/a&gt; recently gained OAuth 2.0 support, enabling secure, token-based access to the Solace monitoring endpoints.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain why this matters, how it works, and how you can configure it in your own environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Authentication Matters for Metrics
&lt;/h2&gt;

&lt;p&gt;Metrics contain sensitive operational data: queue backlogs, client sessions, system load, and detailed message flow patterns. Exposing these without secure authentication can reveal internal system behavior or traffic patterns to unauthorized parties.&lt;/p&gt;

&lt;p&gt;OAuth 2.0 addresses these risks by providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use of short-lived tokens instead of long-lived static secrets&lt;/li&gt;
&lt;li&gt;Centralized identity control across environments&lt;/li&gt;
&lt;li&gt;Automatic token rotation and refresh without requiring service restarts or redeployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes OAuth 2.0 a natural fit for production monitoring, particularly in Zero Trust or multi-cluster environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing OAuth 2.0 Support in solace-prometheus-exporter
&lt;/h2&gt;

&lt;p&gt;Starting from version &lt;a href="https://github.com/solacecommunity/solace-prometheus-exporter/releases/tag/v1.13.0" rel="noopener noreferrer"&gt;1.13.0&lt;/a&gt;, the solace-prometheus-exporter supports OAuth 2.0 using the Client Credentials Flow, when scraping metrics from the Solace SEMP API.&lt;/p&gt;

&lt;p&gt;The exporter is now capable of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requesting access tokens from an Identity Provider (IdP) using a Client ID and Secret.&lt;/li&gt;
&lt;li&gt;Automatically refreshing the tokens before expiration to maintain continuous scraping.&lt;/li&gt;
&lt;li&gt;Using the returned Bearer tokens in the HTTP Authorization header when calling the Solace SEMP API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview of the OAuth-Based Setup
&lt;/h2&gt;

&lt;p&gt;This setup consists of three main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity Provider&lt;/strong&gt;: Keycloak (issues the tokens)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Server&lt;/strong&gt;: Solace Broker (validates tokens and provides metrics)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;: solace-prometheus-exporter (requests tokens and scrapes metrics)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqe02sgq5n6j4lqohq1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqe02sgq5n6j4lqohq1q.png" alt="Architecture Overview" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step: Configuring OAuth for Solace Metrics
&lt;/h2&gt;

&lt;p&gt;In this lab, we will generate our own CA and TLS certificates to keep everything self-contained. In a production environment, these certificates would typically be issued by an internal PKI or a trusted corporate CA.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before we start, ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker is installed and running.&lt;/li&gt;
&lt;li&gt;openssl is installed and available to generate the required TLS certificates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Prepare the Demo Infrastructure
&lt;/h3&gt;

&lt;p&gt;OAuth in Solace requires TLS. To enable secure communication between containers using hostnames, we create a dedicated Docker bridge network and a local Certificate Authority (CA).&lt;/p&gt;

&lt;p&gt;Certificates must include both the container hostname and localhost as Subject Alternative Names (SANs), allowing access from inside Docker and from the host.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a Docker Bridge Network
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker network create solace-net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create a Root CA
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate private key for CA&lt;/span&gt;
openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/rootCA.key 4096

&lt;span class="c"&gt;# Create a self-signed Root CA&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; /tmp/rootCA.key &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/rootCA.crt &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=MyLocalCA"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Identity Provider Setup (Keycloak)
&lt;/h3&gt;

&lt;p&gt;In this example, we use Keycloak as our IdP, as it is open source and easily runnable via Docker.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generate TLS Certificates for Keycloak
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OpenSSL config with SANs for Keycloak&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[req]
default_bits       = 4096
prompt             = no
default_md         = sha256
req_extensions     = req_ext
distinguished_name = dn

[dn]
CN = keycloak

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = keycloak
DNS.2 = localhost'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/keycloak-cert.conf

&lt;span class="c"&gt;# Create CSR&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/keycloak.csr &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="nt"&gt;-keyout&lt;/span&gt; /tmp/keycloak.key &lt;span class="nt"&gt;-config&lt;/span&gt; /tmp/keycloak-cert.conf

&lt;span class="c"&gt;# Sign CSR with the Root CA&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; /tmp/keycloak.csr &lt;span class="nt"&gt;-CA&lt;/span&gt; /tmp/rootCA.crt &lt;span class="nt"&gt;-CAkey&lt;/span&gt; /tmp/rootCA.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/keycloak.crt &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-extfile&lt;/span&gt; /tmp/keycloak-cert.conf &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configure the Realm
&lt;/h4&gt;

&lt;p&gt;We set up a Realm (acme-org) to represent our identity domain. Within this realm, we define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Client &lt;code&gt;prometheus-exporter&lt;/code&gt; - &lt;em&gt;The Token Requester&lt;/em&gt;&lt;br&gt;
This is the client application requesting the token. It uses the Client Secret (&lt;code&gt;my-secret&lt;/code&gt;) to authenticate itself with Keycloak.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client &lt;code&gt;solace&lt;/code&gt; - &lt;em&gt;The Audience&lt;/em&gt;&lt;br&gt;
This represents the Resource Server (our Solace Broker) and is the expected Audience (&lt;code&gt;aud&lt;/code&gt;) of the token. It tells Keycloak that the issued token is intended for use with Solace.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We rely on specific Mappers within Keycloak to inject the necessary claims into the JWT:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Audience (&lt;code&gt;aud&lt;/code&gt;) - &lt;em&gt;Where is this token valid?&lt;/em&gt;&lt;br&gt;
The oidc-audience-mapper ensures the issued JWT contains the claim &lt;code&gt;"aud": "solace"&lt;/code&gt;. This identifies the Solace Broker as the only intended recipient. If a token without this claim is presented, the broker will reject it, preventing tokens meant for other services from being misused here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scope (&lt;code&gt;scope&lt;/code&gt;) - &lt;em&gt;What permissions does the Client want?&lt;/em&gt;&lt;br&gt;
The scope specifies what actions the client is requesting, regardless of the client's assigned roles. In this example, we define a custom client scope called &lt;code&gt;solace.admin&lt;/code&gt;. It tells the Solace broker that the exporter is requesting admin access for Solace. Also we assign this scope as a default scope for the client &lt;code&gt;prometheus.exporter&lt;/code&gt;. Scopes are usually coarse-grained and represent what the client wants to do, not who the client is.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Roles/Groups (&lt;code&gt;groups&lt;/code&gt; Claim) - &lt;em&gt;Who is the client and what privileges does it have?&lt;/em&gt;&lt;br&gt;
The &lt;code&gt;oidc-usermodel-realm-role-mapper&lt;/code&gt; maps the client's assigned roles (in this case, the default role &lt;code&gt;default-roles-acme-org&lt;/code&gt;) into a claim named &lt;code&gt;"groups"&lt;/code&gt;. Solace uses this claim to map the identity to an internal access level (e.g., &lt;code&gt;read-only&lt;/code&gt;). Usually you want to create a custom role for the Client to map the permissions on Solace. But for the blog, we stick to the default roles.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{
  "realm": "acme-org",
  "enabled": true,
  "accessTokenLifespan": 3600,
  "clients": [
    {
      "clientId": "solace",
      "protocol": "openid-connect",
      "publicClient": false,
      "secret": "my-secret",
      "serviceAccountsEnabled": true,
      "directAccessGrantsEnabled": false,
      "standardFlowEnabled": false
    },
    {
      "clientId": "prometheus-exporter",
      "protocol": "openid-connect",
      "publicClient": false,
      "secret": "my-secret",
      "serviceAccountsEnabled": true,
      "directAccessGrantsEnabled": false,
      "standardFlowEnabled": false,
      "defaultClientScopes": ["solace.admin"],
      "protocolMappers": [
        {
          "name": "audience-mapper",
          "protocol": "openid-connect",
          "protocolMapper": "oidc-audience-mapper",
          "consentRequired": false,
          "config": {
            "claim.name": "aud",
            "jsonType.label": "String",
            "access.token.claim": "true",
            "id.token.claim": "false",
            "included.client.audience": "solace"
          }
        },
        {
          "name": "roles-mapper",
          "protocol": "openid-connect",
          "protocolMapper": "oidc-usermodel-realm-role-mapper",
          "consentRequired": false,
          "config": {
            "claim.name": "groups",
            "jsonType.label": "String",
            "access.token.claim": "true",
            "id.token.claim": "false",
            "multivalued": "true"
          }
        }
      ]
    }
  ],
  "clientScopes": [
    {
      "name": "solace.admin",
      "description": "Scope for Prometheus Exporter to read Solace metrics",
      "protocol": "openid-connect",
      "attributes": {
        "display.on.consent.screen": "false"
      }
    }
  ]
}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/acme-org.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Run Keycloak
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; solace-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8443:8443 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/acme-org.json:/opt/keycloak/data/import/realm.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/keycloak.key:/opt/keycloak/data/keycloak.key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/keycloak.crt:/opt/keycloak/data/keycloak.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_HOSTNAME_STRICT_HTTPS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_HOSTNAME_STRICT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_HTTPS_CERTIFICATE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/keycloak/data/keycloak.crt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KC_HTTPS_CERTIFICATE_KEY_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/keycloak/data/keycloak.key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; keycloak &lt;span class="se"&gt;\&lt;/span&gt;
  quay.io/keycloak/keycloak:26.4.7 &lt;span class="se"&gt;\&lt;/span&gt;
  start-dev &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--import-realm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--https-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8443 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keycloak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you see the log entry &lt;code&gt;[...] Listening on: http://0.0.0.0:8080 and https://0.0.0.0:8443&lt;/code&gt;, Keycloak is up and running and you can proceed with the next steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  Validate Issued OAuth Tokens
&lt;/h4&gt;

&lt;p&gt;We can now test the setup by fetching a token for the &lt;code&gt;prometheus-exporter&lt;/code&gt; client with the required scope &lt;code&gt;solace.admin&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--insecure&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://localhost:8443/realms/acme-org/protocol/openid-connect/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=client_credentials"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=prometheus-exporter"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_secret=my-secret"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"scope=solace.admin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains a JSON Web Token (JWT) in the &lt;code&gt;access_token&lt;/code&gt; field. When &lt;a href="https://www.jwt.io" rel="noopener noreferrer"&gt;decoded&lt;/a&gt;, the payload confirms the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1766104964&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1766101364&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jti&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;trrtcc:c50ac927-4c81-e22a-83b7-f36fad114c89&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://keycloak:8443/realms/acme-org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sub&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;85ba915f-6c6f-4109-9704-dca2ff1057cd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bearer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;azp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prometheus-exporter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;solace.admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offline_access&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uma_authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default-roles-acme-org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Configure Solace as the OAuth Resource Server
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Generate TLS Certificates for Solace
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# openssl config with san&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[req]
default_bits       = 4096
prompt             = no
default_md         = sha256
req_extensions     = req_ext
distinguished_name = dn

[dn]
CN = solace

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = solace
DNS.2 = localhost'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/solace-cert.conf

&lt;span class="c"&gt;# create csr&lt;/span&gt;
openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/solace.csr &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:4096 &lt;span class="nt"&gt;-keyout&lt;/span&gt; /tmp/solace.key &lt;span class="nt"&gt;-config&lt;/span&gt; /tmp/solace-cert.conf

&lt;span class="c"&gt;# sign csr with ca:&lt;/span&gt;
openssl x509 &lt;span class="nt"&gt;-req&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; /tmp/solace.csr &lt;span class="nt"&gt;-CA&lt;/span&gt; /tmp/rootCA.crt &lt;span class="nt"&gt;-CAkey&lt;/span&gt; /tmp/rootCA.key &lt;span class="nt"&gt;-CAcreateserial&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-out&lt;/span&gt; /tmp/solace.crt &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-sha256&lt;/span&gt; &lt;span class="nt"&gt;-extfile&lt;/span&gt; /tmp/solace-cert.conf &lt;span class="nt"&gt;-extensions&lt;/span&gt; req_ext

&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/solace.key /tmp/solace.crt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/solace.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Start the Solace Broker
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; solace-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 1943:1943 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/solace.pem:/etc/solace_tls/solace.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--shm-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2g &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;tls_servercertificate_filepath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/solace_tls/solace.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;username_admin_globalaccesslevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;username_admin_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; solace &lt;span class="se"&gt;\&lt;/span&gt;
  solace/solace-pubsub-standard:10.25.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solace distinguishes between client users and management users.&lt;br&gt;
For interacting with the SEMP API - which the Prometheus exporter uses to retrieve metrics - we need a management user, not a client user.&lt;/p&gt;

&lt;p&gt;To enable OAuth for management access, we have to configure an OAuth profile at the broker level (outside of any Message VPN). This configuration is currently not available via the web UI, so it must be done via the SEMP API, either through the CLI or a direct REST call. Solace has a &lt;a href="https://docs.solace.com/Admin/Configuring-OAuth-for-Management-Access.htm" rel="noopener noreferrer"&gt;good documentation&lt;/a&gt; for this.&lt;/p&gt;

&lt;p&gt;Since Solace needs to validate the signature of the JWT access tokens issued by Keycloak, we must upload the Root CA certificate which signed the Identity Provider's certificate (Keycloak). This establishes a trust relationship, allowing the broker to securely verify that the token truly came from our Keycloak server.&lt;/p&gt;
&lt;h4&gt;
  
  
  Establish Trust: Uploading the Root CA to Solace
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# store CA in variable and replace newlines&lt;/span&gt;
&lt;span class="nv"&gt;ROOT_CA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{printf "%s\\n", $0}'&lt;/span&gt; /tmp/rootCA.crt&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# upload domain CA&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:8081/SEMP/v2/config/domainCertAuthorities"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"admin:admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;certAuthorityName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;root_ca&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;certContent&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$ROOT_CA&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
  }"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  OAuth Profiles: Trusting the Identity Provider
&lt;/h4&gt;

&lt;p&gt;An OAuth Profile represents the trust relationship between the Solace broker and an OAuth 2.0 / OpenID Connect IdP.&lt;/p&gt;

&lt;p&gt;In simple terms, the OAuth profile answers the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I verify that an incoming access token is valid, and what do I expect it to look like?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An OAuth profile configures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who issued the token: The issuer (iss claim) ensures the token comes from the expected IdP.&lt;/li&gt;
&lt;li&gt;How tokens are validated

&lt;ul&gt;
&lt;li&gt;Verifying the JWT signature using the IdP's JWKS endpoint&lt;/li&gt;
&lt;li&gt;Optionally calling the token introspection endpoint&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Which token properties are required, For example:

&lt;ul&gt;
&lt;li&gt;Token type (JWT)&lt;/li&gt;
&lt;li&gt;Audience (aud)&lt;/li&gt;
&lt;li&gt;Required scopes&lt;/li&gt;
&lt;li&gt;Required claims&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Which OAuth endpoints Solace can use

&lt;ul&gt;
&lt;li&gt;Authorization endpoint&lt;/li&gt;
&lt;li&gt;Token endpoint&lt;/li&gt;
&lt;li&gt;Introspection endpoint&lt;/li&gt;
&lt;li&gt;JWKS endpoint&lt;/li&gt;
&lt;li&gt;Userinfo endpoint&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What happens if no fine-grained authorization matches

&lt;ul&gt;
&lt;li&gt;Default global access level&lt;/li&gt;
&lt;li&gt;Default Message VPN access level&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Which claim value grants access (&lt;code&gt;accessLevelGroupsClaimName&lt;/code&gt; claim)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From Solace's perspective, the OAuth profile defines authentication rules:&lt;br&gt;
If a token does not match these rules, access is denied before any permissions are evaluated.&lt;/p&gt;

&lt;p&gt;Create the OAuth Profile with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:8081/SEMP/v2/config/oauthProfiles"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"admin:admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "accessLevelGroupsClaimName": "groups",
    "clientId": "solace",
    "clientSecret": "my-secret",
    "displayName": "keycloak_profile",
    "enabled": true,
    "endpointDiscovery": "https://keycloak:8443/realms/acme-org/.well-known/openid-configuration",
    "interactiveEnabled": false,
    "issuer": "https://keycloak:8443/realms/acme-org",
    "oauthProfileName": "keycloak_profile",
    "oauthRole": "resource-server",
    "resourceServerRequiredAudience": "solace",
    "resourceServerRequiredIssuer": "https://keycloak:8443/realms/acme-org",
    "resourceServerRequiredScope": "solace.admin",
    "resourceServerRequiredType": "JWT",
    "sempEnabled": true,
    "usernameClaimName": "azp"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  OAuth Access Level Groups: Mapping Identity to Permissions
&lt;/h4&gt;

&lt;p&gt;While the OAuth profile validates &lt;em&gt;who&lt;/em&gt; you are, OAuth Access Level Groups determine &lt;em&gt;what&lt;/em&gt; you are allowed to do.&lt;/p&gt;

&lt;p&gt;This answers the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Given this token, what permissions should this identity have inside Solace?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Solace extracts the value from the configured claim (in our case, &lt;code&gt;groups&lt;/code&gt;) and matches it against these groups.&lt;/p&gt;

&lt;p&gt;Create the authorization group mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"http://localhost:8081/SEMP/v2/config/oauthProfiles/keycloak_profile/accessLevelGroups"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"admin:admin"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "description": "Global read-only group",
    "globalAccessLevel": "read-only",
    "groupName": "default-roles-acme-org",
    "oauthProfileName": "keycloak_profile"
   }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Configure the Client (solace-prometheus-exporter)
&lt;/h3&gt;

&lt;p&gt;To use OAuth in the exporter, set the following parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Since this is a demo environment using self-signed certs, &lt;code&gt;SOLACE_SSL_VERIFY&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;. In production, you would mount the Root CA and set this to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The exporter can be run using these parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'SOLACE_LISTEN_ADDR=0.0.0.0:9628
SOLACE_LISTEN_TLS=false
SOLACE_SCRAPE_URI=https://solace:1943
SOLACE_OAUTH_TOKEN_URL=https://keycloak:8443/realms/acme-org/protocol/openid-connect/token
SOLACE_OAUTH_CLIENT_ID=prometheus-exporter
SOLACE_OAUTH_CLIENT_SECRET=my-secret
SOLACE_OAUTH_CLIENT_SCOPE=solace.admin
SOLACE_DEFAULT_VPN=default
SOLACE_TIMEOUT=5s
SOLACE_SSL_VERIFY=false
PREFETCH_INTERVAL=0s
SOLACE_PARALLEL_SEMP_CONNECTIONS=1
'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/env

docker run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; solace-net &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 9628:9628 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-file&lt;/span&gt; /tmp/env &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; exporter &lt;span class="se"&gt;\&lt;/span&gt;
  solacecommunity/solace-prometheus-exporter:v1.13.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Access to Secured Metrics
&lt;/h3&gt;

&lt;p&gt;Accessing a &lt;a href="http://localhost:9628/solace?m.ClientStats=*|*" rel="noopener noreferrer"&gt;metrics endpoints&lt;/a&gt; in your browser should now return metrics. These are being securely fetched from Solace using a short-lived OAuth token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Considerations &amp;amp; Best Practices
&lt;/h2&gt;

&lt;p&gt;Securing metrics endpoints goes beyond enabling OAuth. Keep these best practices in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforce TLS&lt;/strong&gt;: Always use trusted certificates. Self-signed certs are okay for demos but not for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least Privilege&lt;/strong&gt;: Grant the exporter only the necessary scope and map identity groups carefully (read-only permissions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short-Lived Tokens&lt;/strong&gt;: Use short-lived access tokens and automatic refresh to minimize risk and operational overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Token Claims&lt;/strong&gt;: Ensure the broker validates the audience (aud), issuer (iss), and token type (JWT) for all requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;And as always&lt;/strong&gt;: Observe access events and separate testing and production environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following these practices ensures your metrics infrastructure is secure, reliable, and production-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Monitoring endpoints are production APIs and should be treated as such.&lt;/p&gt;

&lt;p&gt;This setup eliminates static secrets from monitoring, closing a commonly overlooked security gap. With OAuth in place, your monitoring endpoints are no longer a weak link, bringing you one step closer to a truly secure and scalable messaging environment.&lt;/p&gt;

&lt;p&gt;This article focuses on securing communication between the exporter and the broker. Securing Prometheus scrape endpoints is a separate topic, but applying the same principles can further strengthen your overall monitoring infrastructure.&lt;/p&gt;

</description>
      <category>solace</category>
      <category>prometheus</category>
      <category>observability</category>
      <category>security</category>
    </item>
  </channel>
</rss>
