mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 16:24:32 +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;
|
package dev.knoe.auth.kerberos;
|
||||||
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import javax.security.auth.login.AppConfigurationEntry;
|
import javax.security.auth.login.AppConfigurationEntry;
|
||||||
import javax.security.auth.login.Configuration;
|
import javax.security.auth.login.Configuration;
|
||||||
@ -16,11 +16,18 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class KerberosSpnegoService {
|
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;
|
private static final Oid KRB5_OID;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to init Kerberos OID", e);
|
throw new RuntimeException("Failed to init Kerberos OID", e);
|
||||||
}
|
}
|
||||||
@ -42,10 +49,9 @@ public class KerberosSpnegoService {
|
|||||||
loginContext.login();
|
loginContext.login();
|
||||||
|
|
||||||
Subject subject = loginContext.getSubject();
|
Subject subject = loginContext.getSubject();
|
||||||
return Subject.doAs(subject, (PrivilegedExceptionAction<Result>) () -> {
|
return Subject.callAs(subject, (Callable<Result>) () -> {
|
||||||
GSSManager manager = GSSManager.getInstance();
|
GSSManager manager = GSSManager.getInstance();
|
||||||
GSSCredential creds = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, KRB5_OID, GSSCredential.ACCEPT_ONLY);
|
GSSContext ctx = manager.createContext((GSSCredential) null);
|
||||||
GSSContext ctx = manager.createContext(creds);
|
|
||||||
|
|
||||||
byte[] outToken = ctx.acceptSecContext(token, 0, token.length);
|
byte[] outToken = ctx.acceptSecContext(token, 0, token.length);
|
||||||
boolean established = ctx.isEstablished();
|
boolean established = ctx.isEstablished();
|
||||||
|
|||||||
@ -167,7 +167,7 @@ spec:
|
|||||||
name: knoe-auth-kerberos
|
name: knoe-auth-kerberos
|
||||||
key: servicePrincipal
|
key: servicePrincipal
|
||||||
- name: PROLE_KERBEROS_KEYTAB_PATH
|
- name: PROLE_KERBEROS_KEYTAB_PATH
|
||||||
value: "/etc/knoe/keytabs/http.keytab"
|
value: "/etc/knoe-auth/http.keytab"
|
||||||
- name: PROLE_KERBEROS_REALM
|
- name: PROLE_KERBEROS_REALM
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|||||||
@ -2,29 +2,46 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: knoe-kdc-config
|
name: knoe-kdc-config
|
||||||
|
namespace: knoe-system
|
||||||
data:
|
data:
|
||||||
krb5.conf: |
|
krb5.conf: |
|
||||||
[libdefaults]
|
[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_realm = false
|
||||||
dns_lookup_kdc = false
|
dns_lookup_kdc = false
|
||||||
ticket_lifetime = 10h
|
|
||||||
renew_lifetime = 7d
|
|
||||||
forwardable = true
|
|
||||||
[realms]
|
[realms]
|
||||||
KNOE.DEV = {
|
KNOE.LOCAL = {
|
||||||
kdc = 127.0.0.1
|
kdc = 127.0.0.1
|
||||||
admin_server = 127.0.0.1
|
admin_server = 127.0.0.1
|
||||||
}
|
}
|
||||||
[domain_realm]
|
PROLE.ORG = {
|
||||||
.knoe.dev = KNOE.DEV
|
kdc = 10.0.0.3
|
||||||
knoe.dev = KNOE.DEV
|
admin_server = 10.0.0.3
|
||||||
|
}
|
||||||
|
|
||||||
|
[capaths]
|
||||||
|
KNOE.LOCAL = {
|
||||||
|
PROLE.ORG = .
|
||||||
|
}
|
||||||
|
PROLE.ORG = {
|
||||||
|
KNOE.LOCAL = .
|
||||||
|
}
|
||||||
|
|
||||||
kdc.conf: |
|
kdc.conf: |
|
||||||
[kdcdefaults]
|
[kdcdefaults]
|
||||||
kdc_ports = 88
|
kdc_ports = 88
|
||||||
kdc_tcp_ports = 88
|
kdc_tcp_ports = 88
|
||||||
[realms]
|
[realms]
|
||||||
KNOE.DEV = {
|
KNOE.LOCAL = {
|
||||||
database_name = /var/lib/krb5kdc/principal
|
database_name = /var/lib/krb5kdc/principal
|
||||||
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
|
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
|
||||||
acl_file = /etc/krb5kdc/kadm5.acl
|
acl_file = /etc/krb5kdc/kadm5.acl
|
||||||
@ -33,26 +50,31 @@ data:
|
|||||||
max_renewable_life = 7d 0h 0m 0s
|
max_renewable_life = 7d 0h 0m 0s
|
||||||
default_principal_flags = +preauth
|
default_principal_flags = +preauth
|
||||||
}
|
}
|
||||||
|
|
||||||
kadm5.acl: |
|
kadm5.acl: |
|
||||||
admin/admin@KNOE.DEV *
|
admin/admin@KNOE.LOCAL *
|
||||||
|
|
||||||
entrypoint.sh: |
|
entrypoint.sh: |
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
# Enable verbose debug if requested
|
||||||
realm="${KNOE_KDC_REALM:-KNOE.DEV}"
|
if [[ "${KNOE_DEBUG:-}" == "1" ]]; then
|
||||||
admin_principal="${KNOE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
set -x
|
||||||
if [[ "${admin_principal}" != *"@"* ]]; then
|
echo "[DEBUG] Environment snapshot:" >&2
|
||||||
admin_principal="${admin_principal}@${realm}"
|
env | sed -E 's/(PASSWORD|TOKEN|SECRET)=.*/\1=****/g' >&2 || true
|
||||||
fi
|
fi
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
if ! command -v krb5kdc >/dev/null 2>&1; then
|
if ! command -v krb5kdc >/dev/null 2>&1; then
|
||||||
echo "Installing Kerberos packages..."
|
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/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
|
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
|
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
||||||
if [[ -f /opt/knoe-kdc/krb5.conf ]]; then
|
if [[ -f /opt/knoe-kdc/krb5.conf ]]; then
|
||||||
cp /opt/knoe-kdc/krb5.conf /etc/krb5.conf
|
cp /opt/knoe-kdc/krb5.conf /etc/krb5.conf
|
||||||
@ -63,37 +85,117 @@ data:
|
|||||||
if [[ -f /opt/knoe-kdc/kadm5.acl ]]; then
|
if [[ -f /opt/knoe-kdc/kadm5.acl ]]; then
|
||||||
cp /opt/knoe-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
cp /opt/knoe-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||||||
fi
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [[ -z "${KNOE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
if [[ -z "${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||||
echo "ERROR: Missing required env KNOE_KDC_ADMIN_PASSWORD" >&2
|
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'knoe-kdc-secrets/admin_password')." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||||||
echo "Initializing realm database for ${realm}..."
|
echo "Initializing realm database for ${PROLE_KDC_REALM}..."
|
||||||
kdb5_util create -s -r "${realm}" -P "${KNOE_KDC_MASTER_PASSWORD}"
|
kdb5_util create -s -r "${realm}" -P "${PROLE_KDC_MASTER_PASSWORD}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! kadmin.local -q "get_principal ${admin_principal}" >/dev/null 2>&1; then
|
if ! kadmin.local -q "get_principal ${admin_principal}" >/dev/null 2>&1; then
|
||||||
echo "Creating admin principal ${admin_principal}..."
|
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
|
fi
|
||||||
# system admin user principal
|
|
||||||
if ! kadmin.local -q "get_principal admin@${realm}" >/dev/null 2>&1; then
|
if [[ -n "${PROLE_KDC_TRUST_REALM:-}" && "${PROLE_KDC_TRUST_REALM}" != "${PROLE_KDC_REALM}" ]]; then
|
||||||
echo "Creating admin user principal admin@${realm}..."
|
shared_pw="${PROLE_KDC_TRUST_SHARED_PASSWORD:-${PROLE_KDC_MASTER_PASSWORD}}"
|
||||||
kadmin.local -q "addprinc -pw ${KNOE_KDC_MASTER_PASSWORD} admin@${realm}"
|
# ------------------------------------------------------------------
|
||||||
fi
|
# Cross-realm krbtgt principals — RC4 only.
|
||||||
# guest user principal (read-only)
|
#
|
||||||
if ! kadmin.local -q "get_principal guest@${realm}" >/dev/null 2>&1; then
|
# Both directions of the trust live as their own krbtgt principal,
|
||||||
echo "Creating guest user principal guest@${realm}..."
|
# each keyed to the same shared password. We pin RC4 (arcfour-hmac)
|
||||||
kadmin.local -q "addprinc -pw ${KNOE_KDC_GUEST_PASSWORD:-changeme} guest@${realm}"
|
# 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
|
fi
|
||||||
|
|
||||||
|
# Start daemons. Keep kadmind in PID 1; run krb5kdc in background and verify it binds.
|
||||||
echo "Starting krb5kdc and kadmind ..."
|
echo "Starting krb5kdc and kadmind ..."
|
||||||
krb5kdc -n &
|
krb5kdc -n &
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
if ! pgrep -x krb5kdc >/dev/null 2>&1; then
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
exec kadmind -nofork
|
exec kadmind -nofork
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user