Skip to content

feat: add SASL/GSSAPI auth for kafka clusters - #3

Open
armitageee wants to merge 3 commits into
Ender-Wang:mainfrom
armitageee:feat/gssapi
Open

feat: add SASL/GSSAPI auth for kafka clusters#3
armitageee wants to merge 3 commits into
Ender-Wang:mainfrom
armitageee:feat/gssapi

Conversation

@armitageee

@armitageee armitageee commented Jul 16, 2026

Copy link
Copy Markdown

Summary

This PR adds SASL/GSSAPI (Kerberos) as a cluster authentication option in Swifka, alongside the existing PLAIN and SCRAM mechanisms.

Kerberos-protected clusters can be configured with:

  • Kerberos service name (e.g. kafka)
  • Client principal (e.g. client@REALM)
  • Keytab path
  • krb5.conf path (exported as KRB5_CONFIG for kinit / GSSAPI)

Works with both SASL_PLAINTEXT and SASL_SSL (via the existing Use TLS toggle).

Motivation

I work at a large company where virtually all internal Kafka clusters are protected with Kerberos (GSSAPI over SASL). Keytab-based authentication and a custom krb5.conf (with realm/KDC settings) are the standard way to connect from developer tooling.

Swifka already covers PLAIN/SCRAM well, but there was no path to connect to Kerberos-enabled environments without falling back to Java clients or CLI tools. I use Swifka daily for read-only monitoring and wanted the same workflow for our production-like and staging clusters.

This change is based on a setup that already works in our internal Rust Kafka explorer (keytab + krb5.conf + SASL_PLAINTEXT).

What changed

Core

  • New SASL mechanism: GSSAPI
  • ClusterConfig fields: saslKerberosPrincipal, saslKerberosServiceName, saslKerberosKeytabPath, saslKerberosKrb5ConfPath
  • KafkaService: shared applySaslConfig(); KerberosEnvironment sets KRB5_CONFIG before connect / test / browse
  • Passwords stay in Keychain for PLAIN/SCRAM only; Kerberos credentials are paths stored in cluster config
  • Cluster clone, export, and import preserve Kerberos fields

UI

  • GSSAPI-specific fields in Add/Edit Cluster when SASL → GSSAPI is selected
  • Single file picker for keytab and krb5.conf (fixes macOS issue with two .fileImporter modifiers)
  • Localized strings: English + Simplified Chinese

Other

  • README: mention GSSAPI in features; document release build command
  • .gitignore: ignore Xcode build/ derived data directory
  • Bug report template: SASL/GSSAPI in auth examples

Screenshots

Cluster form — GSSAPI settings

![GSSAPI cluster configuration]
Снимок экрана — 2026-07-16 в 16 49 08

Successful connection test

![Connection test success]
Снимок экрана — 2026-07-16 в 16 49 54

Connected cluster (topics / dashboard)

![Connected to Kerberos cluster]
Снимок экрана — 2026-07-16 в 16 50 20

Test plan

  • Build succeeds locally
  • SwiftFormat passes on changed Swift files
  • Connect to a real Kerberos Kafka cluster (SASL_PLAINTEXT, keytab + custom krb5.conf)
  • Test connection from cluster form
  • Browse topics and messages after connect
  • Keytab and krb5.conf Browse buttons open Finder
  • PLAIN/SCRAM clusters still work (Keychain password unchanged)
  • Clone / export / import cluster with GSSAPI settings

Notes for reviewers

  1. krb5.conf is optional in the model but required in many enterprise setups where /etc/krb5.conf does not define the corporate realm/KDC. Swifka sets KRB5_CONFIG to the user-provided path before librdkafka runs kinit.

  2. Bootstrap host should be a DNS name, not an IP — Kerberos service principals are hostname-based (kafka/broker.example.com@REALM).

  3. No client.dns.lookup=resolve_canonical_bootstrap_servers_only — that setting caused librdkafka to authenticate against IP-based principals (kafka/10.x.x.x@…), which failed against our KDC. Default DNS lookup works with our clusters.

  4. Backward compatible: existing cluster JSON without Kerberos fields decodes normally; new fields are optional.

Example configuration

Field Example
Host kafkab01.example.corp.cloud
Port 9093
Auth SASL → GSSAPI
Service name kafka
Principal my_client@CORP.REALM
Keytab /path/to/client.keytab
krb5.conf /path/to/krb5.conf
Use TLS off (SASL_PLAINTEXT)

@Ender-Wang

Copy link
Copy Markdown
Owner

Good job, will review it carefully when I got time after work.

@Ender-Wang

Copy link
Copy Markdown
Owner

A general comment on the plaintext principle. This project's rule is "passwords never in config files; Keychain only" (see KeychainManager / v1.0.0 release notes). Storing the keytab path and principal in plaintext clusters.json is a judgment call. It's defensible — paths aren't keytab contents, and the principal is analogous to the existing saslUsername (already in config) — but please make a conscious decision and note it: is the principal + keytab path acceptable in plaintext, or should the principal at least go to Keychain (like saslPassword)? If keeping in config, add a doc comment clarifying these are non-secret paths/identity, not credentials.

Overall, the PR is solid. Good job.

@armitageee

Copy link
Copy Markdown
Author

Thanks for the thoughtful note — agreed this deserves an explicit decision.

Decision: keep principal + keytab/krb5 paths in clusters.json.

Rationale, aligned with the existing v1.0.0 rule:

• Secrets stay out of config — same as today: only SASL passwords (PLAIN/SCRAM) go to
Keychain via KeychainManager.
• Identity and file references in config — saslKerberosPrincipal is analogous to
saslUsername (public client identity, not a credential). saslKerberosKeytabPath and
saslKerberosKrb5ConfPath are filesystem paths, not keytab contents.
• Actual credential = keytab file on disk — authentication fails without a readable
keytab at that path; storing the path in JSON does not grant access by itself.

I don’t think moving the principal to Keychain adds meaningful security (it’s not
secret), while it would complicate clone/export/import UX. Storing keytab bytes in
Keychain would be a larger scope change and a different threat model.

I’ll add doc comments on the Kerberos fields clarifying they are non-secret
identity/paths, not credentials — happy to adjust wording if you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants