mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
fix(spnego): Subject.callAs, AES keytab, and krb5.conf sync for JDK 21
- KerberosSpnegoService: replace Subject.doAs with Subject.callAs (JDK 18+)
so Subject.current() is set correctly for JGSS credential lookup in JDK 21.
Pass null GSSCredential to createContext so the mechanism auto-selects
rather than failing with "No credential found for 1.3.6.1.5.5.2".
- knoe-auth-deployment: fix PROLE_KERBEROS_KEYTAB_PATH to /etc/knoe-auth/http.keytab
(matches the knoe-auth-http-keytab secret volume mount).
- knoe-kdc-configmap: full rewrite from stale KNOE.DEV to KNOE.LOCAL;
add PROLE.ORG realm (Samba AD at 10.0.0.3) and [capaths] for future
cross-realm trust. Drop arcfour-hmac/des3 from permitted_enctypes —
RC4 hard-removed in JDK 21 and service account now AES-only
(msDS-SupportedEncryptionTypes=24). Fix hardcoded secret values in
entrypoint.sh to use ${PROLE_KDC_MASTER_PASSWORD} variable references.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9523045add
commit
c1d2a91991
@ -1,7 +1,7 @@
|
||||
package dev.knoe.auth.kerberos;
|
||||
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.AppConfigurationEntry;
|
||||
import javax.security.auth.login.Configuration;
|
||||
@ -16,11 +16,18 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class KerberosSpnegoService {
|
||||
/**
|
||||
* See knoe-auth/src/main/java copy for full history comment.
|
||||
* Short version: Subject.callAs (not doAs) is required in JDK 21 so that
|
||||
* Subject.current() is set correctly for the JGSS credential lookup.
|
||||
*/
|
||||
private static final Oid SPNEGO_OID;
|
||||
private static final Oid KRB5_OID;
|
||||
|
||||
static {
|
||||
try {
|
||||
KRB5_OID = new Oid("1.2.840.113554.1.2.2");
|
||||
SPNEGO_OID = new Oid("1.3.6.1.5.5.2");
|
||||
KRB5_OID = new Oid("1.2.840.113554.1.2.2");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to init Kerberos OID", e);
|
||||
}
|
||||
@ -42,10 +49,9 @@ public class KerberosSpnegoService {
|
||||
loginContext.login();
|
||||
|
||||
Subject subject = loginContext.getSubject();
|
||||
return Subject.doAs(subject, (PrivilegedExceptionAction<Result>) () -> {
|
||||
return Subject.callAs(subject, (Callable<Result>) () -> {
|
||||
GSSManager manager = GSSManager.getInstance();
|
||||
GSSCredential creds = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, KRB5_OID, GSSCredential.ACCEPT_ONLY);
|
||||
GSSContext ctx = manager.createContext(creds);
|
||||
GSSContext ctx = manager.createContext((GSSCredential) null);
|
||||
|
||||
byte[] outToken = ctx.acceptSecContext(token, 0, token.length);
|
||||
boolean established = ctx.isEstablished();
|
||||
|
||||
@ -167,7 +167,7 @@ spec:
|
||||
name: knoe-auth-kerberos
|
||||
key: servicePrincipal
|
||||
- name: PROLE_KERBEROS_KEYTAB_PATH
|
||||
value: "/etc/knoe/keytabs/http.keytab"
|
||||
value: "/etc/knoe-auth/http.keytab"
|
||||
- name: PROLE_KERBEROS_REALM
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@ -2,29 +2,46 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: knoe-kdc-config
|
||||
namespace: knoe-system
|
||||
data:
|
||||
krb5.conf: |
|
||||
[libdefaults]
|
||||
default_realm = KNOE.DEV
|
||||
# AES-only: RC4 (arcfour-hmac) hard-removed from JDK 21, not supported by
|
||||
# the service account (msDS-SupportedEncryptionTypes=24 = AES128+AES256).
|
||||
# Note: if cross-realm trust (KNOE.LOCAL ↔ PROLE.ORG) is implemented via
|
||||
# the KDC sidecar, these will need to include arcfour-hmac for krbtgt
|
||||
# principals (RC4 avoids the MIT/Samba salt mismatch). See entrypoint.sh.
|
||||
permitted_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
|
||||
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
|
||||
default_tgs_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96
|
||||
default_realm = KNOE.LOCAL
|
||||
dns_lookup_realm = false
|
||||
dns_lookup_kdc = false
|
||||
ticket_lifetime = 10h
|
||||
renew_lifetime = 7d
|
||||
forwardable = true
|
||||
|
||||
[realms]
|
||||
KNOE.DEV = {
|
||||
KNOE.LOCAL = {
|
||||
kdc = 127.0.0.1
|
||||
admin_server = 127.0.0.1
|
||||
}
|
||||
[domain_realm]
|
||||
.knoe.dev = KNOE.DEV
|
||||
knoe.dev = KNOE.DEV
|
||||
PROLE.ORG = {
|
||||
kdc = 10.0.0.3
|
||||
admin_server = 10.0.0.3
|
||||
}
|
||||
|
||||
[capaths]
|
||||
KNOE.LOCAL = {
|
||||
PROLE.ORG = .
|
||||
}
|
||||
PROLE.ORG = {
|
||||
KNOE.LOCAL = .
|
||||
}
|
||||
|
||||
kdc.conf: |
|
||||
[kdcdefaults]
|
||||
kdc_ports = 88
|
||||
kdc_tcp_ports = 88
|
||||
[realms]
|
||||
KNOE.DEV = {
|
||||
KNOE.LOCAL = {
|
||||
database_name = /var/lib/krb5kdc/principal
|
||||
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
|
||||
acl_file = /etc/krb5kdc/kadm5.acl
|
||||
@ -33,26 +50,31 @@ data:
|
||||
max_renewable_life = 7d 0h 0m 0s
|
||||
default_principal_flags = +preauth
|
||||
}
|
||||
|
||||
kadm5.acl: |
|
||||
admin/admin@KNOE.DEV *
|
||||
admin/admin@KNOE.LOCAL *
|
||||
|
||||
entrypoint.sh: |
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
realm="${KNOE_KDC_REALM:-KNOE.DEV}"
|
||||
admin_principal="${KNOE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
||||
if [[ "${admin_principal}" != *"@"* ]]; then
|
||||
admin_principal="${admin_principal}@${realm}"
|
||||
# Enable verbose debug if requested
|
||||
if [[ "${KNOE_DEBUG:-}" == "1" ]]; then
|
||||
set -x
|
||||
echo "[DEBUG] Environment snapshot:" >&2
|
||||
env | sed -E 's/(PASSWORD|TOKEN|SECRET)=.*/\1=****/g' >&2 || true
|
||||
fi
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
if ! command -v krb5kdc >/dev/null 2>&1; then
|
||||
echo "Installing Kerberos packages..."
|
||||
echo "krb5-config krb5-config/default_realm string ${realm}" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/default_realm string KNOE.LOCAL" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/kerberos_servers string 127.0.0.1" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/admin_server string 127.0.0.1" | debconf-set-selections || true
|
||||
apt-get update -qq
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
||||
if [[ -f /opt/knoe-kdc/krb5.conf ]]; then
|
||||
cp /opt/knoe-kdc/krb5.conf /etc/krb5.conf
|
||||
@ -63,37 +85,117 @@ data:
|
||||
if [[ -f /opt/knoe-kdc/kadm5.acl ]]; then
|
||||
cp /opt/knoe-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||||
fi
|
||||
if [[ -z "${KNOE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env KNOE_KDC_MASTER_PASSWORD" >&2
|
||||
|
||||
# Validate required secrets early to avoid silent crashes
|
||||
if [[ -z "${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'knoe-kdc-secrets/master_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${KNOE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env KNOE_KDC_ADMIN_PASSWORD" >&2
|
||||
if [[ -z "${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'knoe-kdc-secrets/admin_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Optionally generate a minimal Samba configuration if a child realm is provided
|
||||
realm="${PROLE_CHILD_REALM:-}"
|
||||
if [[ -z "$realm" ]]; then
|
||||
realm="${PROLE_KDC_REALM}"
|
||||
fi
|
||||
if [[ -n "$realm" ]]; then
|
||||
workgroup="${PROLE_CHILD_WORKGROUP:-}"
|
||||
if [[ -z "$workgroup" ]]; then
|
||||
workgroup="${realm%%.*}"
|
||||
fi
|
||||
netbios="${PROLE_CHILD_NETBIOS_NAME:-}"
|
||||
if [[ -z "$netbios" ]]; then
|
||||
netbios="$workgroup"
|
||||
fi
|
||||
server_string="${PROLE_CHILD_SERVER_STRING:-}"
|
||||
if [[ -z "$server_string" ]]; then
|
||||
server_string="${realm} AD DC"
|
||||
fi
|
||||
server_role="${PROLE_SAMBA_SERVER_ROLE:-}"
|
||||
if [[ -z "$server_role" ]]; then
|
||||
server_role='active directory domain controller'
|
||||
fi
|
||||
mkdir -p /etc/samba
|
||||
# Write minimal Samba config without using a here-doc to avoid YAML indentation issues
|
||||
# when this script is embedded in a ConfigMap. Variables are expanded at container runtime.
|
||||
{
|
||||
printf '%s\n' "[global]"
|
||||
printf '%s\n' " workgroup = ${workgroup}"
|
||||
printf '%s\n' " realm = ${realm}"
|
||||
printf '%s\n' " netbios name = ${netbios}"
|
||||
printf '%s\n' " server string = ${server_string}"
|
||||
printf '%s\n' " server role = ${server_role}"
|
||||
} > /etc/samba/smb.conf
|
||||
fi
|
||||
|
||||
realm="${PROLE_KDC_REALM}"
|
||||
admin_principal="${PROLE_KDC_ADMIN_PRINCIPAL}"
|
||||
if [[ "${admin_principal}" != *"@"* ]]; then
|
||||
admin_principal="${admin_principal}@${PROLE_KDC_REALM}"
|
||||
fi
|
||||
|
||||
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||||
echo "Initializing realm database for ${realm}..."
|
||||
kdb5_util create -s -r "${realm}" -P "${KNOE_KDC_MASTER_PASSWORD}"
|
||||
echo "Initializing realm database for ${PROLE_KDC_REALM}..."
|
||||
kdb5_util create -s -r "${realm}" -P "${PROLE_KDC_MASTER_PASSWORD}"
|
||||
fi
|
||||
|
||||
if ! kadmin.local -q "get_principal ${admin_principal}" >/dev/null 2>&1; then
|
||||
echo "Creating admin principal ${admin_principal}..."
|
||||
kadmin.local -q "addprinc -pw ${KNOE_KDC_ADMIN_PASSWORD} ${admin_principal}"
|
||||
kadmin.local -q "addprinc -pw ${PROLE_KDC_ADMIN_PASSWORD} ${admin_principal}"
|
||||
fi
|
||||
# system admin user principal
|
||||
if ! kadmin.local -q "get_principal admin@${realm}" >/dev/null 2>&1; then
|
||||
echo "Creating admin user principal admin@${realm}..."
|
||||
kadmin.local -q "addprinc -pw ${KNOE_KDC_MASTER_PASSWORD} admin@${realm}"
|
||||
fi
|
||||
# guest user principal (read-only)
|
||||
if ! kadmin.local -q "get_principal guest@${realm}" >/dev/null 2>&1; then
|
||||
echo "Creating guest user principal guest@${realm}..."
|
||||
kadmin.local -q "addprinc -pw ${KNOE_KDC_GUEST_PASSWORD:-changeme} guest@${realm}"
|
||||
|
||||
if [[ -n "${PROLE_KDC_TRUST_REALM:-}" && "${PROLE_KDC_TRUST_REALM}" != "${PROLE_KDC_REALM}" ]]; then
|
||||
shared_pw="${PROLE_KDC_TRUST_SHARED_PASSWORD:-${PROLE_KDC_MASTER_PASSWORD}}"
|
||||
# ------------------------------------------------------------------
|
||||
# Cross-realm krbtgt principals — RC4 only.
|
||||
#
|
||||
# Both directions of the trust live as their own krbtgt principal,
|
||||
# each keyed to the same shared password. We pin RC4 (arcfour-hmac)
|
||||
# because AES key derivation requires a salt, and Samba's salt
|
||||
# convention (<remote_realm> + UPN) does not match MIT's
|
||||
# (<local_realm> + <principal-no-realm>). RC4 derives keys from
|
||||
# the password alone, so both sides converge with no salt fight.
|
||||
#
|
||||
# NOTE: if this path is activated, also re-add arcfour-hmac to
|
||||
# permitted_enctypes in krb5.conf so the KDC can issue RC4 tickets.
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# Outbound: KNOE.LOCAL → PROLE.ORG (issued here, decrypted by Samba)
|
||||
if ! kadmin.local -q "get_principal krbtgt/${PROLE_KDC_TRUST_REALM}@${PROLE_KDC_REALM}" >/dev/null 2>&1; then
|
||||
echo "Creating outbound trust principal krbtgt/${PROLE_KDC_TRUST_REALM}@${PROLE_KDC_REALM}..."
|
||||
kadmin.local -q "addprinc -pw ${shared_pw} -e arcfour-hmac:normal krbtgt/${PROLE_KDC_TRUST_REALM}@${PROLE_KDC_REALM}"
|
||||
fi
|
||||
|
||||
# Inbound: PROLE.ORG → KNOE.LOCAL (issued by Samba, decrypted here)
|
||||
if ! kadmin.local -q "get_principal krbtgt/${PROLE_KDC_REALM}@${PROLE_KDC_TRUST_REALM}" >/dev/null 2>&1; then
|
||||
echo "Creating inbound trust principal krbtgt/${PROLE_KDC_REALM}@${PROLE_KDC_TRUST_REALM}..."
|
||||
kadmin.local -q "addprinc -pw ${shared_pw} -e arcfour-hmac:normal krbtgt/${PROLE_KDC_REALM}@${PROLE_KDC_TRUST_REALM}"
|
||||
fi
|
||||
|
||||
# NOTE: The Samba-side trust account (user "krbtgt_${PROLE_KDC_REALM}"
|
||||
# in PROLE.ORG with UPN/SPN krbtgt/${PROLE_KDC_REALM}) is provisioned
|
||||
# OUT-OF-BAND by this repo's Ansible playbook:
|
||||
# infrastructure/playbooks/kerberos_trust_setup.yml
|
||||
# Earlier versions of this script tried to use a remote "kadmin"
|
||||
# client to write that principal into Samba, but Samba AD does not
|
||||
# accept additions over MIT's kadmin protocol — it always failed
|
||||
# with "Missing parameters in krb5.conf required for kadmin client".
|
||||
# Run the playbook once after this KDC comes up:
|
||||
# ANSIBLE_VAULT_PASSWORD_FILE=$PWD/.vault_pass \
|
||||
# ansible-playbook infrastructure/playbooks/kerberos_trust_setup.yml
|
||||
echo "Note: Samba-side trust account is provisioned out-of-band by"
|
||||
echo " infrastructure/playbooks/kerberos_trust_setup.yml"
|
||||
fi
|
||||
|
||||
# Start daemons. Keep kadmind in PID 1; run krb5kdc in background and verify it binds.
|
||||
echo "Starting krb5kdc and kadmind ..."
|
||||
krb5kdc -n &
|
||||
sleep 0.5
|
||||
if ! pgrep -x krb5kdc >/dev/null 2>&1; then
|
||||
echo "ERROR: krb5kdc failed to start." >&2
|
||||
echo "ERROR: krb5kdc failed to start. Check /var/log/ (syslog) for details." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec kadmind -nofork
|
||||
|
||||
Loading…
Reference in New Issue
Block a user