security

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: Apache-2.0 Imports: 41 Imported by: 4

Documentation

Overview

Package security provides authentication and encryption protocols for CEDAR streams.

This package implements HTCondor's security methods including SSL, SCITOKENS, and IDTOKENS authentication.

Package security provides FS and CLAIMTOBE authentication implementation for CEDAR streams.

This file implements HTCondor's FS (filesystem) and CLAIMTOBE authentication methods as documented in condor_auth_fs.cpp and condor_auth_claim.cpp.

Package security provides SCITOKENS authentication implementation for CEDAR streams using SSL + SciToken exchange.

This file implements HTCondor's SCITOKENS authentication method based on SSL authentication followed by SciToken verification as documented in HTCondor's condor_auth_ssl.cpp.

Package security provides SSL authentication implementation for HTCondor CEDAR protocol

Package security provides TOKEN/IDTOKENS authentication implementation for CEDAR streams using the AKEP2 protocol.

This file implements HTCondor's TOKEN authentication method based on JWT tokens and the AKEP2 (Authenticated Key Exchange Protocol 2) as documented in HTCondor's condor_auth_passwd.cpp.

Index

Constants

View Source
const (
	AuthBitmaskNone      = 0    // CAUTH_NONE
	AuthBitmaskAny       = 1    // CAUTH_ANY
	AuthBitmaskClaimToBe = 2    // CAUTH_CLAIMTOBE
	AuthBitmaskFS        = 4    // CAUTH_FILESYSTEM
	AuthBitmaskFSRemote  = 8    // CAUTH_FILESYSTEM_REMOTE
	AuthBitmaskNTSSPI    = 16   // CAUTH_NTSSPI
	AuthBitmaskGSI       = 32   // CAUTH_GSI
	AuthBitmaskKerberos  = 64   // CAUTH_KERBEROS
	AuthBitmaskAnonymous = 128  // CAUTH_ANONYMOUS
	AuthBitmaskSSL       = 256  // CAUTH_SSL
	AuthBitmaskPassword  = 512  // CAUTH_PASSWORD
	AuthBitmaskMunge     = 1024 // CAUTH_MUNGE
	AuthBitmaskToken     = 2048 // CAUTH_TOKEN
	AuthBitmaskSciTokens = 4096 // CAUTH_SCITOKENS
)

Authentication method bitmasks for the authentication handshake These values must match HTCondor's condor_auth.h CAUTH_* constants

View Source
const (
	// SubmitSideMatchSessionFQU is the identity a startd attributes to the
	// schedd over a claim session.
	SubmitSideMatchSessionFQU = "submit-side@matchsession"
	// ExecuteSideMatchSessionFQU is the identity a schedd attributes to the
	// startd over a claim session.
	ExecuteSideMatchSessionFQU = "execute-side@matchsession"
	// NegotiatorSideMatchSessionFQU is the identity used for negotiator claim
	// sessions.
	NegotiatorSideMatchSessionFQU = "negotiator-side@matchsession"
	// AuthMethodMatch is the authentication method recorded on a claim session
	// (AUTH_METHOD_MATCH in C++).
	AuthMethodMatch = "MATCH"
)

Match-session identities and auth method, mirroring the constants in src/condor_io/authentication.cpp. When a startd hands out a claim it registers the session tagged with the SUBMIT side identity (the peer it expects to talk to is the schedd), and the schedd registers the mirror-image session tagged with the EXECUTE side identity (its peer is the startd). A Go schedd importing a startd claim therefore attributes the peer as execute-side@matchsession, exactly matching what the startd sees on its end.

View Source
const (
	// Maximum sizes for DoS protection
	MaxDirPathSize  = 4096 // 4KB max for directory paths
	MaxUsernameSize = 1024 // 1KB max for usernames

)
View Source
const (
	// ENV_CONDOR_INHERIT contains parent process information and inherited sockets
	EnvCondorInherit = "CONDOR_INHERIT"
	// ENV_CONDOR_PRIVATE_INHERIT contains security session keys
	EnvCondorPrivateInherit = "CONDOR_PRIVATE_INHERIT"
	// ENV_CONDOR_PARENT_ID contains the parent's unique ID
	EnvCondorParentID = "CONDOR_PARENT_ID"
)

Environment variable names used by HTCondor for passing session information

View Source
const (
	AuthSSLOK        = 0
	AuthSSLSending   = 1
	AuthSSLReceiving = 2
	AuthSSLQuitting  = 3
	AuthSSLHolding   = 4
	AuthSSLError     = -1

	// Session key length for symmetric encryption after SSL handshake
	AuthSSLSessionKeyLen = 256
)

SSL authentication state constants matching HTCondor's implementation

View Source
const (
	AUTH_PW_A_OK          = 0     // Authentication OK status
	AUTH_PW_ERROR         = -1    // Authentication error status
	AUTH_PW_ABORT         = 1     // Authentication abort status
	AUTH_PW_KEY_LEN       = 256   // Maximum key length in bytes
	AUTH_PW_MAX_NAME_LEN  = 1024  // Maximum length for client/server IDs
	AUTH_PW_MAX_TOKEN_LEN = 65536 // Maximum token length (64KB)
)

AUTH_PW protocol constants matching HTCondor

View Source
const NoCommand = -1

NoCommand marks a SecurityConfig that carries no command (an auth-only / session-establishment handshake). It is distinct from command 0 (UPDATE_STARTD_AD), which is a legitimate command; on the wire a NoCommand handshake advertises DC_AUTHENTICATE instead.

View Source
const (
	TokenKeyLength = 32 // 256-bit key length
)

AKEP2 protocol constants for TOKEN authentication

Variables

View Source
var ErrNetwork = errors.New("network communication error")

ErrNetwork is a singleton error used to wrap network/communication errors from Message Put/Get operations

Functions

func ClearSessionCache

func ClearSessionCache()

ClearSessionCache removes all sessions from the global cache

func ConvertJWKToPublicKey

func ConvertJWKToPublicKey(jwk *JWK) (interface{}, error)

ConvertJWKToPublicKey converts a JWK to a public key for verification

func ExportClaimID

func ExportClaimID(sessionID, sessionInfo, sessionKey string) string

ExportClaimID creates a claim ID string from session components This is the inverse of ParseClaimID

func ExportSecSessionInfo added in v0.5.0

func ExportSecSessionInfo(policy *classad.ClassAd) (string, error)

ExportSecSessionInfo renders a session policy into the bracketed session_info blob embedded in a claim id, mirroring SecMan::ExportSecSessionInfo. It is the exact inverse of ImportSecSessionInfo: only the trusted, claim-id-safe attributes are exported, and

  • CryptoMethods with more than one method (comma-separated) is split into a single preferred method (the first) plus a '.'-delimited CryptoMethodsList (',' is not a legal character inside a claim id). A single method is emitted verbatim as CryptoMethods.
  • RemoteVersion (a full "$CondorVersion$" string) is emitted as the compact ShortVersion (e.g. "25.4.0") so its spaces do not break claim-id parsing.

Attributes are emitted in sorted (alphabetical) order, matching the iteration order of a C++ ClassAd, so a minted blob is byte-identical to what a C++ startd of the same configuration would produce. String values are quoted and integer values (SessionExpires) are bare, matching ExprTreeToString. The result is always bracketed; passing a policy with none of the exported attributes yields "[]".

Round-trip guarantee: ImportSecSessionInfo(ExportSecSessionInfo(p)) reproduces the crypto/encryption/integrity/expiry/commands policy carried by p.

func GenerateJWT

func GenerateJWT(keyDir, keyID, subject, issuer string, issuedAt, expiration int64, authzLimits []string) (string, error)

GenerateJWT generates a JWT token signed with the specified key Parameters:

  • keyDir: Directory containing signing keys
  • keyID: Name of the key file (used as kid in JWT header)
  • subject: Subject claim (sub) - username
  • issuer: Issuer claim (iss) - trust domain
  • issuedAt: Issued at time (iat)
  • expiration: Expiration time (exp)
  • authzLimits: Optional list of authorization limits (e.g., ["READ", "WRITE"]) encoded as scopes

Returns the JWT token string in format: header.payload.signature

func GeneratePoolSigningKey

func GeneratePoolSigningKey(keyFile string) error

GeneratePoolSigningKey generates a pool signing key and writes it to the specified file

func GenerateSecuritySessionKey

func GenerateSecuritySessionKey() (string, error)

GenerateSecuritySessionKey generates a random session key suitable for use in a claim ID

func GenerateSessionID

func GenerateSessionID(counter int) string

GenerateSessionID generates a unique session ID Format: hostname:pid:timestamp:counter

func GenerateSigningKey

func GenerateSigningKey(keyFile string) error

GenerateSigningKey generates a signing key and writes it to the specified file The key is scrambled using HTCondor's simple_scramble (XOR with 0xdeadbeef)

func GenerateTestCA

func GenerateTestCA(certFile, keyFile string) error

GenerateTestCA generates a self-signed CA certificate and private key

func GenerateTestHostCert

func GenerateTestHostCert(certFile, keyFile, caCertFile, caKeyFile, hostname string) error

GenerateTestHostCert generates a host certificate signed by the CA

func GenerateTestJWT

func GenerateTestJWT(keyDir, keyID, subject, issuer string, validDuration time.Duration, authzLimits []string) (string, error)

GenerateTestJWT is a convenience function that generates a signing key and JWT for testing Parameters are simplified for common test scenarios

func GetFamilySessionID

func GetFamilySessionID() string

GetFamilySessionID returns the session ID of the family session, if one was inherited

func GetInheritedParentAddr

func GetInheritedParentAddr() string

GetInheritedParentAddr returns the parent daemon's address from CONDOR_INHERIT

func GetInheritedParentPID

func GetInheritedParentPID() int

GetInheritedParentPID returns the parent daemon's PID from CONDOR_INHERIT

func GetNextSessionCounter

func GetNextSessionCounter() int

GetNextSessionCounter returns the next session counter value

func GetParentSessionID

func GetParentSessionID() string

GetParentSessionID returns the session ID of the parent session, if one was inherited

func ImportClaimSession added in v0.5.0

func ImportClaimSession(cache *SessionCache, claimID string, opts ClaimSessionOptions) (string, error)

ImportClaimSession parses a claim id, derives the pre-shared security session it embeds exactly as HTCondor's SecMan::CreateNonNegotiatedSecuritySession does, and registers it in cache so it works in BOTH directions:

  • Outbound: an outbound connect with SecurityConfig.SessionID set to the returned id (or a command that ImportClaimSession mapped) resumes this session instead of authenticating.
  • Inbound: the server side resumes this session when the peer presents its id in a DC_AUTHENTICATE resumption request.

It returns the security session id. The key is derived from the claim secret with HKDF (salt "htcondor", info "keygen", 32 bytes) for AES-256-GCM, identical to Condor_Crypt_Base::hkdf for CONDOR_AESGCM. cedar implements only AES-GCM, so a claim keyed on any other cipher is rejected.

func ImportFileTransferSession added in v0.5.0

func ImportFileTransferSession(cache *SessionCache, claimID string, opts ClaimSessionOptions) (string, error)

ImportFileTransferSession registers the separate file-transfer session a shadow derives from a claim id (src/condor_shadow.V6.1/remoteresource.cpp). The session id is the claim's session id prefixed with "filetrans." and the key is the SAME claim secret, but the startd's exported policy is discarded: file transfer uses the importer's own WRITE-level policy (encryption and integrity on, AES-GCM), which is why it is a distinct session.

It returns the derived file-transfer session id.

func ImportSecSessionInfo added in v0.5.0

func ImportSecSessionInfo(sessionInfo string) (*classad.ClassAd, error)

ImportSecSessionInfo parses the bracketed session_info string carried in a claim id (e.g. `[Encryption="YES";Integrity="YES";CryptoMethods="AES"; SessionExpires=1700000000;ValidCommands="443,444";]`) into a policy ClassAd.

It mirrors SecMan::ImportSecSessionInfo: only a specific, trusted set of attributes is copied over (Integrity, Encryption, CryptoMethods, SessionExpires, ValidCommands), a present CryptoMethodsList overrides CryptoMethods, the '.'-delimited crypto list is converted back to ',' (because ',' is not permitted inside a claim id), and ShortVersion is mapped to RemoteVersion.

An empty string yields an empty policy (no session info was exported); a non-empty string that is not bracketed is an error.

func ImportSessionInfoAttributes

func ImportSessionInfoAttributes(sessionInfo string) (map[string]string, error)

ImportSessionInfoAttributes parses session info string and extracts attributes Session info format: [Attr1="value1";Attr2="value2";...]

func InvalidateExpiredSessions

func InvalidateExpiredSessions() int

InvalidateExpiredSessions removes all expired sessions from the global cache

func InvalidateSession

func InvalidateSession(sessionID string) bool

InvalidateSession removes a session from the global cache

func IsSciToken

func IsSciToken(tokenStr string) bool

IsSciToken determines if a JWT token is a SciToken by checking its signature algorithm SciTokens use asymmetric signatures (RS*, ES*, PS*), not HMAC (HS*)

func IsSessionResumptionError

func IsSessionResumptionError(err error) bool

IsSessionResumptionError checks if an error is a SessionResumptionError

func ParseCondorInherit

func ParseCondorInherit(inherit string) (ppid int, parentAddr string, remaining []string)

ParseCondorInherit parses the CONDOR_INHERIT environment variable Format: ppid psinful [socket_info...] [remaining_items...]

Types

type AuthMethod

type AuthMethod string

AuthMethod represents different authentication methods supported by HTCondor

const (
	AuthSSL       AuthMethod = "SSL"
	AuthSciTokens AuthMethod = "SCITOKENS"
	AuthIDTokens  AuthMethod = "IDTOKENS"
	AuthToken     AuthMethod = "TOKEN"
	AuthFS        AuthMethod = "FS"
	AuthClaimToBe AuthMethod = "CLAIMTOBE"
	AuthPassword  AuthMethod = "PASSWORD"
	AuthKerberos  AuthMethod = "KERBEROS"
	AuthNone      AuthMethod = "NONE"
)

type AuthMethodAttempt added in v0.0.24

type AuthMethodAttempt struct {
	Method AuthMethod
	Err    error
}

AuthMethodAttempt records one auth method tried during negotiation and the error that caused it to fail. Stored in the order attempts happened so the operator can see which method was preferred.

type AuthMethodsExhaustedError added in v0.0.24

type AuthMethodsExhaustedError struct {
	// Attempts is in negotiation order: the first entry is the
	// method the server preferred first, etc.
	Attempts []AuthMethodAttempt
}

AuthMethodsExhaustedError is returned by ClientHandshake (and the underlying handleClientAuthentication) when negotiation runs out of methods to try. It carries every method that was attempted along with the specific error that caused it to be removed from the candidate set, so callers can render an actionable failure message without having to enable cedar DEBUG logging and replay the connection.

The wrapped errors are also reachable via errors.Is / errors.As — a caller checking for SessionResumptionError, x509 verification errors, or net.OpError (e.g. handshake timeouts) will match against the per-method error that produced them.

func (*AuthMethodsExhaustedError) Error added in v0.0.24

func (e *AuthMethodsExhaustedError) Error() string

Error renders all attempts in a single line so the failure shows up usefully even from a non-structured logger. Format:

all authentication methods failed: SSL: <err>; TOKEN: <err>

When Attempts is empty (no method even started — e.g., the server rejected the initial bitmask outright), the message degrades gracefully to the historical "all authentication methods failed".

func (*AuthMethodsExhaustedError) Unwrap added in v0.0.24

func (e *AuthMethodsExhaustedError) Unwrap() []error

Unwrap returns the per-attempt errors so errors.Is / errors.As can match against any of them. A caller probing for an x509 cert mismatch on a multi-method handshake will get a hit even when SSL was just one of the methods that failed.

type Authenticator

type Authenticator struct {
	// contains filtered or unexported fields
}

Authenticator handles the security handshake for a stream

func NewAuthenticator

func NewAuthenticator(config *SecurityConfig, s *stream.Stream) *Authenticator

NewAuthenticator creates a new authenticator with the given config and stream

func (*Authenticator) ClientHandshake

func (a *Authenticator) ClientHandshake(ctx context.Context) (*SecurityNegotiation, error)

ClientHandshake performs the client-side security handshake This sends a single message with DC_AUTHENTICATE command followed by client security ClassAd

func (*Authenticator) PerformTokenAuthenticationDemo

func (a *Authenticator) PerformTokenAuthenticationDemo(method AuthMethod, negotiation *SecurityNegotiation) error

PerformTokenAuthenticationDemo is a simple wrapper for demonstration purposes

func (*Authenticator) ServerHandshake

func (a *Authenticator) ServerHandshake(ctx context.Context) (*SecurityNegotiation, error)

ServerHandshake performs the server-side security handshake This receives a single message with DC_AUTHENTICATE command and client ClassAd, then responds

func (*Authenticator) ServerHandshakeWithMessage added in v0.1.0

func (a *Authenticator) ServerHandshakeWithMessage(ctx context.Context, msg *message.Message, command int) (*SecurityNegotiation, error)

ServerHandshakeWithMessage performs the server-side security handshake when the caller has already created the inbound Message and consumed the leading command integer. This lets a dispatching server peek the command to distinguish an authenticated command (DC_AUTHENTICATE) from a "raw" command (e.g. CCB_REVERSE_CONNECT) before deciding to authenticate. The provided message MUST be the one the command int was read from, so the client security ClassAd is read from the same message.

func (*Authenticator) WasSessionResumed

func (a *Authenticator) WasSessionResumed() bool

WasSessionResumed returns true if the session was resumed from cache

type CEDARTLSConnection

type CEDARTLSConnection struct {
	// contains filtered or unexported fields
}

CEDARTLSConnection implements net.Conn for TLS over CEDAR messages

func (*CEDARTLSConnection) Close

func (c *CEDARTLSConnection) Close() error

func (*CEDARTLSConnection) LocalAddr

func (c *CEDARTLSConnection) LocalAddr() net.Addr

func (*CEDARTLSConnection) Read

func (c *CEDARTLSConnection) Read(b []byte) (int, error)

func (*CEDARTLSConnection) RemoteAddr

func (c *CEDARTLSConnection) RemoteAddr() net.Addr

func (*CEDARTLSConnection) SetDeadline

func (c *CEDARTLSConnection) SetDeadline(t time.Time) error

func (*CEDARTLSConnection) SetReadDeadline

func (c *CEDARTLSConnection) SetReadDeadline(t time.Time) error

func (*CEDARTLSConnection) SetWriteDeadline

func (c *CEDARTLSConnection) SetWriteDeadline(t time.Time) error

func (*CEDARTLSConnection) Write

func (c *CEDARTLSConnection) Write(b []byte) (int, error)

type ClaimID

type ClaimID struct {
	// contains filtered or unexported fields
}

ClaimID represents a parsed HTCondor claim ID Format: session_id#session_info#session_key

func ParseClaimID

func ParseClaimID(claimID string) *ClaimID

ParseClaimID parses an HTCondor claim ID string Format: session_id#session_info#session_key

func ParseClaimIDStrict added in v0.5.0

func ParseClaimIDStrict(claimID string) *ClaimID

ParseClaimIDStrict parses a claim id using the exact semantics of HTCondor's ClaimIdParser (src/condor_utils/condor_claimid_parser.h), which differ from the more permissive ParseClaimID used by the CONDOR_INHERIT path.

A startd claim id has the form

<sinful>#startd_bday#sequence_num#[session_info]session_key

where the session id itself contains '#'. The C++ parser therefore splits on the LAST '#':

  • secSessionInfo() is the "[...]" block, present only when the character immediately after the last '#' is '['.
  • secSessionId() is everything before the last '#' (but only when session info is present; otherwise there is no security session).
  • secSessionKey() is everything after the trailing ']' (or after the last '#' when there is no session info).

ParseClaimID's SplitN-on-first-'#' approach cannot represent a session id containing '#', so it is unsuitable for real startd claim ids; hence this dedicated, C++-faithful parser.

func (*ClaimID) PublicClaimID

func (c *ClaimID) PublicClaimID() string

PublicClaimID returns a version of the claim ID safe for logging (without the key)

func (*ClaimID) Raw

func (c *ClaimID) Raw() string

Raw returns the original claim ID string

func (*ClaimID) SecSessionID

func (c *ClaimID) SecSessionID() string

SecSessionID returns the session ID This matches ClaimIdParser::secSessionId() in HTCondor

func (*ClaimID) SecSessionInfo

func (c *ClaimID) SecSessionInfo() string

SecSessionInfo returns the session info (exported attributes)

func (*ClaimID) SecSessionKey

func (c *ClaimID) SecSessionKey() string

SecSessionKey returns the session key

type ClaimSessionOptions added in v0.5.0

type ClaimSessionOptions struct {
	// PeerAddr is the sinful/address string of the peer this session talks to
	// (for a schedd importing a startd claim, the startd's sinful).  It is
	// stored on the session entry and used as the key for command_map entries
	// so the outbound client can find the session by command.  It should match
	// the address the client will dial.  When empty, no command mappings are
	// created (the session can still be used via SecurityConfig.SessionID).
	PeerAddr string

	// PeerFQU is the authenticated identity attributed to the peer over this
	// session.  Defaults to ExecuteSideMatchSessionFQU (the schedd's view of a
	// startd) when empty.
	PeerFQU string

	// Duration is a fallback session lifetime, used only when the claim's
	// session_info does not carry SessionExpires.  Zero means the session does
	// not auto-expire.
	Duration time.Duration

	// Tag is the security context tag used for command_map lookups (usually "").
	Tag string

	// ExtraValidCommands are command integers to map to this session in
	// addition to any ValidCommands carried in the session_info.  A stock
	// startd claim's exported session_info typically omits ValidCommands (the
	// startd instead names the session explicitly via setSecSessionId when it
	// sends a command), so a caller that wants command_map-based resumption
	// should list the claim commands it will send (REQUEST_CLAIM, ALIVE, ...).
	ExtraValidCommands []int
}

ClaimSessionOptions configures how a claim-derived session is registered.

type CredentialReader added in v0.1.1

type CredentialReader interface {
	ReadCredential(path string) ([]byte, error)
}

CredentialReader reads the bytes of a credential file referenced by a SecurityConfig — the SSL server key/cert, a token signing key, or a token file. It exists so a daemon that has dropped privileges to a service account (e.g. condor) can still read root-owned 0600 credentials the way HTCondor's C++ daemons do: by momentarily re-elevating to root (set_priv(PRIV_ROOT)) for the read. Supply such a reader via SecurityConfig.Credentials.

Reload: cedar calls ReadCredential every time it needs a credential (e.g. per SSL handshake), so a reader is free to cache for speed and support reload-on-reconfig by invalidating its cache on SIGHUP — the next handshake then re-reads the fresh bytes. cedar holds no credential state of its own.

When SecurityConfig.Credentials is nil, credentials are read with a plain os.ReadFile under the process's current identity.

type CryptoMethod

type CryptoMethod string

CryptoMethod represents different encryption methods supported by HTCondor

const (
	CryptoAES      CryptoMethod = "AES"
	CryptoBlowfish CryptoMethod = "BLOWFISH"
	Crypto3DES     CryptoMethod = "3DES"
)

type IDTokenClaims added in v0.4.0

type IDTokenClaims struct {
	Subject  string                 // "sub"
	Issuer   string                 // "iss"
	Scope    string                 // "scope"
	Expiry   int64                  // "exp"
	IssuedAt int64                  // "iat"
	Raw      map[string]interface{} // all claims, for callers that need more
}

IDTokenClaims holds the validated claims of an HTCondor IDTOKEN.

func VerifyIDToken added in v0.4.0

func VerifyIDToken(tokenStr string, cfg *SecurityConfig) (*IDTokenClaims, error)

VerifyIDToken verifies a complete HTCondor IDTOKEN -- a 3-part JWT whose signature is HMAC-SHA256 over "header.payload" using a key derived from the pool/named signing key via HKDF (salt "htcondor", info "master jwt"), matching condor_auth_passwd and GenerateJWT -- and returns its claims.

The signing key is located from cfg (TokenPoolSigningKeyFile / TokenSigningKeyDir, or the SEC_TOKEN_POOL_SIGNING_KEY_FILE / SEC_PASSWORD_DIRECTORY env fallbacks); the key ID is the JWT "kid" header ("" => "POOL"). Expiration and issued-at max-age (SEC_TOKEN_MAX_AGE / cfg.TokenMaxAge, default 1h) are enforced.

This is the standalone equivalent of the server side of the IDTOKENS CEDAR handshake, for validating an IDTOKEN presented as a bearer token OUTSIDE a CEDAR session -- e.g. the WebSocket CCB carrier. It does NOT perform the AKEP2 key agreement; it is a pure token check.

type InheritedSession

type InheritedSession struct {
	// Type indicates whether this is a normal or family session
	Type InheritedSessionType

	// SessionID is the unique identifier for this session
	SessionID string

	// SessionInfo contains exported session attributes (ClassAd format)
	SessionInfo string

	// SessionKey is the raw key material for this session
	SessionKey string

	// ParentAddr is the sinful string of the parent daemon (from CONDOR_INHERIT)
	ParentAddr string

	// ParentPID is the process ID of the parent daemon
	ParentPID int
}

InheritedSession represents a security session passed from a parent daemon

func GetInheritedSessions

func GetInheritedSessions() []*InheritedSession

GetInheritedSessions returns the list of imported inherited sessions

func ImportInheritedSessions

func ImportInheritedSessions() ([]*InheritedSession, error)

ImportInheritedSessions imports security sessions from environment variables This should be called early in daemon initialization

func LookupInheritedSession

func LookupInheritedSession(sessionID string) *InheritedSession

LookupInheritedSession looks up an inherited session by ID

func ParseCondorPrivateInherit

func ParseCondorPrivateInherit(privateInherit string) (sessions []*InheritedSession)

ParseCondorPrivateInherit parses the CONDOR_PRIVATE_INHERIT environment variable Format: space-separated items like "SessionKey:<claim_id>" and "FamilySessionKey:<claim_id>"

type InheritedSessionType

type InheritedSessionType int

InheritedSessionType indicates the type of inherited session

const (
	// SessionTypeNormal is a regular session for parent-child communication
	SessionTypeNormal InheritedSessionType = iota
	// SessionTypeFamily is a "family" session for sibling daemon communication
	SessionTypeFamily
)

type JWK

type JWK struct {
	Kty string `json:"kty"`           // Key type (RSA, EC, etc.)
	Kid string `json:"kid"`           // Key ID
	Use string `json:"use"`           // Key use (sig, enc)
	Alg string `json:"alg"`           // Algorithm
	N   string `json:"n,omitempty"`   // RSA modulus
	E   string `json:"e,omitempty"`   // RSA exponent
	X   string `json:"x,omitempty"`   // EC X coordinate
	Y   string `json:"y,omitempty"`   // EC Y coordinate
	Crv string `json:"crv,omitempty"` // EC curve name
}

JWK represents a JSON Web Key

type JWKS

type JWKS struct {
	Keys []JWK `json:"keys"`
}

JWKS represents a JSON Web Key Set

func FetchJWKS

func FetchJWKS(jwksURI string) (*JWKS, error)

FetchJWKS fetches the JSON Web Key Set from the JWKS URI

type KeyInfo

type KeyInfo struct {
	Data     []byte
	Protocol string // "AESGCM", "BLOWFISH", "3DES", etc.
}

KeyInfo represents a cryptographic key with metadata

type MintClaimOptions added in v0.5.0

type MintClaimOptions struct {
	// Sinful is the startd's advertised address, the full bracketed sinful
	// string (e.g. "<127.0.0.1:9618?addrs=...&noUDP&sock=startd_1234_abcd>").
	// It may itself contain '#'; parsers scan to the first '>' to recover it.
	// It becomes the leading component of the claim id.  Required.
	Sinful string

	// Birthdate is the startd's startup time (unix seconds), the second
	// component of the claim id (startd_bday).
	Birthdate int64

	// SequenceNum is the per-startd monotonically increasing claim counter, the
	// third component of the claim id.
	SequenceNum int

	// PeerFQU is the identity the startd attributes to whoever presents this
	// claim (the schedd).  Defaults to SubmitSideMatchSessionFQU when empty,
	// matching SUBMIT_SIDE_MATCHSESSION_FQU in claim.cpp.
	PeerFQU string

	// PeerAddr, when non-empty, installs command_map entries so an OUTBOUND dial
	// from the startd to that address (e.g. SendAlive to the schedd) resumes this
	// session by command.  A stock startd leaves this empty at mint time (C++
	// passes peer_sinful=NULL): the schedd resumes INBOUND by naming the session
	// id explicitly, and command mappings are added later once the peer address
	// is known.
	PeerAddr string

	// Encryption toggles on-the-wire encryption in the exported policy.  Nil
	// means the default (on).  Point it at a false value to mint an
	// integrity-only or plaintext session.
	Encryption *bool

	// Integrity toggles on-the-wire integrity (MAC) in the exported policy.  Nil
	// means the default (on).
	Integrity *bool

	// CryptoMethods is the comma-separated cipher preference list recorded in the
	// session_info (e.g. "AES" or "AES,BLOWFISH").  Defaults to "AES".  cedar can
	// only key sessions on AES/AES-GCM, so the first method must be AES.
	CryptoMethods string

	// RemoteVersion, when set, is exported as ShortVersion so the peer learns the
	// minting daemon's version.  A full "$CondorVersion$" string or a compact
	// "25.4.0" are both accepted.
	RemoteVersion string

	// Lifetime bounds the session's validity.  When > 0 the absolute expiry
	// (now+Lifetime) is both embedded in the session_info as SessionExpires (so
	// the importing peer expires it in lockstep) and applied to the local cache
	// entry.  Zero means the session does not auto-expire.
	Lifetime time.Duration

	// ExtraValidCommands are command integers mapped to this session (in addition
	// to any ValidCommands carried in the policy), used only when PeerAddr is set.
	ExtraValidCommands []int

	// ValidCommands, when non-empty, is exported in the session_info so the
	// importing peer can command-map it.  A stock startd omits this.
	ValidCommands []int

	// Tag is the security context tag used for command_map lookups (usually "").
	Tag string
}

MintClaimOptions configures how a startd mints a claim id and registers the pre-shared security session it embeds. The zero value (apart from the required Sinful field) produces a stock modern-HTCondor claim: AES-GCM, encryption and integrity on, DAEMON auth level.

type MintedClaim added in v0.5.0

type MintedClaim struct {
	// contains filtered or unexported fields
}

MintedClaim is the result of MintClaimSession: the freshly generated claim id (which carries the secret) plus its derived identifiers.

func MintClaimSession added in v0.5.0

func MintClaimSession(cache *SessionCache, opts MintClaimOptions) (*MintedClaim, error)

MintClaimSession mints a claim id the way a startd does and registers the pre-shared security session it embeds in cache, so the session works in BOTH directions without a fresh handshake:

  • Inbound: when the schedd presents this claim id in a DC_AUTHENTICATE resumption request, the startd's server side resumes the session by id.
  • Outbound: an outbound connect from the startd whose SecurityConfig.SessionID is this session id (or a command MintClaimSession mapped via PeerAddr) resumes the session instead of authenticating (e.g. SendAlive).

It is the mint-side mirror of ImportClaimSession and derives identical key material: a random hex secret (SEC_SESSION_KEY_LENGTH_V9 bytes) is embedded in the claim id and run through the same HKDF (salt "htcondor", info "keygen", 32 bytes for AES-256-GCM) that the importing peer applies, so both ends hold the same AES-GCM key. cedar implements only AES-GCM.

The returned MintedClaim's ClaimID() is the secret capability; the session is registered under SessionID().

func (*MintedClaim) ClaimID added in v0.5.0

func (m *MintedClaim) ClaimID() string

ClaimID returns the full, SECRET claim id, of the form

<sinful>#startd_bday#sequence_num#[session_info]session_key

This is the capability the startd hands to the negotiator/schedd; anyone who holds it can resume the session, so it must be transmitted only over an already-secured channel and never logged.

func (*MintedClaim) PublicClaimID added in v0.5.0

func (m *MintedClaim) PublicClaimID() string

PublicClaimID returns the claim id with the secret elided, safe for logging (ClaimIdParser::publicClaimId): everything up to the last '#', plus "#...".

func (*MintedClaim) SessionID added in v0.5.0

func (m *MintedClaim) SessionID() string

SessionID returns the security session id embedded in the claim id (ClaimIdParser::secSessionId): everything before the last '#'.

type OIDCConfiguration

type OIDCConfiguration struct {
	Issuer  string `json:"issuer"`
	JWKSURI string `json:"jwks_uri"`
}

OIDCConfiguration represents the OIDC discovery document

func DiscoverOIDCConfiguration

func DiscoverOIDCConfiguration(issuer string) (*OIDCConfiguration, error)

DiscoverOIDCConfiguration fetches the OIDC configuration from the issuer

type SSLAuthenticator

type SSLAuthenticator struct {
	// contains filtered or unexported fields
}

SSLAuthenticator handles SSL certificate-based authentication following HTCondor's protocol

func NewSSLAuthenticator

func NewSSLAuthenticator(auth *Authenticator) *SSLAuthenticator

NewSSLAuthenticator creates a new SSL authenticator following HTCondor's implementation

func (*SSLAuthenticator) GetSessionKey

func (ssl *SSLAuthenticator) GetSessionKey() []byte

GetSessionKey returns the SSL session key for stream encryption

func (*SSLAuthenticator) PerformSSLHandshake

func (ssl *SSLAuthenticator) PerformSSLHandshake(ctx context.Context, negotiation *SecurityNegotiation) error

PerformSSLHandshake performs the complete SSL authentication handshake following HTCondor's protocol

type SciTokenClaims

type SciTokenClaims struct {
	Subject   string   `json:"sub"`
	Issuer    string   `json:"iss"`
	Scope     string   `json:"scope,omitempty"`
	Audience  []string `json:"aud,omitempty"`
	ExpiresAt int64    `json:"exp"`
	IssuedAt  int64    `json:"iat"`
	NotBefore int64    `json:"nbf,omitempty"`
	JWTID     string   `json:"jti,omitempty"`
	jwt.RegisteredClaims
}

SciTokenClaims represents the claims in a SciToken JWT

func VerifySciToken

func VerifySciToken(tokenStr string) (*SciTokenClaims, error)

VerifySciToken verifies a SciToken's signature using OIDC discovery Returns the validated claims if successful

type SecurityConfig

type SecurityConfig struct {
	// Peer name; used by client to recall the server name
	PeerName string

	// Authentication settings
	AuthMethods    []AuthMethod
	Authentication SecurityLevel

	// Encryption settings
	CryptoMethods []CryptoMethod
	Encryption    SecurityLevel
	Integrity     SecurityLevel

	// PostAuthPolicy, if set, is invoked by the server side after a successful
	// authentication to supply the authorization result the security layer does
	// not itself own. authUser is the authenticated identity and peerAddr is the
	// peer's "host:port". It returns the identity to advertise to the peer (the
	// mapped FQU; empty keeps authUser) and the command integers this session is
	// authorized for. Those commands are reported in the post-auth ValidCommands
	// so an HTCondor peer can reuse the session for any of them.
	PostAuthPolicy func(authUser, peerAddr string) (fqu string, validCommands []int)

	// Certificate/Key files for SSL
	CertFile string
	KeyFile  string
	CAFile   string
	// Server name for SSL certificate verification (optional, defaults to hostname)
	ServerName string

	// Credentials, if set, reads credential files (SSL key/cert, token signing
	// keys, token files) on cedar's behalf. A daemon running as an unprivileged
	// service account supplies a reader that re-elevates to root for root-owned
	// 0600 credentials and supports reload-on-reconfig. Nil means plain
	// a plain read. See CredentialReader.
	Credentials CredentialReader

	// Token content for TOKEN authentication (JWT string)
	Token string
	// Token file for TOKEN authentication
	TokenFile string
	// Token directory for discovering multiple tokens (default: ~/.condor/tokens.d)
	TokenDir string

	// Token signing key configuration (server-side)
	// Path to pool signing key file (SEC_TOKEN_POOL_SIGNING_KEY_FILE)
	TokenPoolSigningKeyFile string
	// Directory containing named signing keys (SEC_PASSWORD_DIRECTORY)
	TokenSigningKeyDir string
	// Maximum token age in seconds (SEC_TOKEN_MAX_AGE)
	TokenMaxAge int
	// List of issuer key names accepted by server (from IssuerKeys ClassAd attribute)
	IssuerKeys []string

	// Other settings
	RemoteVersion   string
	TrustDomain     string
	Subsystem       string
	ServerPid       int
	SessionDuration int
	SessionLease    int

	// Command for this session (what the client intends to do). This is a real
	// HTCondor command int, which may legitimately be 0 (UPDATE_STARTD_AD). Use
	// NoCommand (-1) for an auth-only handshake that carries no command.
	Command int

	// AuthCommand specifies a sub-command for the security handshake (optional)
	// For example, when Command is DC_SEC_QUERY (60040), AuthCommand might be
	// DC_NOP_WRITE (60021) to specify the actual operation being authorized.
	// If not set (0), only Command will be sent in the handshake.
	AuthCommand int

	// ECDH key exchange
	ECDHPublicKey string

	// Security tag; used to select specific credentials from the
	// session cache
	SecurityTag string

	// Session cache (optional, if provided will be used instead of global cache)
	SessionCache *SessionCache

	// SessionID, if set, forces the client handshake to resume this exact
	// pre-registered session (e.g. one installed by ImportClaimSession),
	// bypassing both negotiation and the command_map lookup. This mirrors
	// CEDAR's setSecSessionId() for non-negotiated / claim sessions, where the
	// caller explicitly names the session to ride rather than relying on a
	// command-to-session mapping. It is an error if the id is not in the cache.
	SessionID string
}

SecurityConfig holds configuration for stream security

type SecurityLevel

type SecurityLevel string

SecurityLevel represents security requirement levels

const (
	SecurityRequired  SecurityLevel = "REQUIRED"
	SecurityPreferred SecurityLevel = "PREFERRED"
	SecurityOptional  SecurityLevel = "OPTIONAL"
	SecurityNever     SecurityLevel = "NEVER"
)

type SecurityManager

type SecurityManager struct {
	// contains filtered or unexported fields
}

SecurityManager provides a high-level interface for security operations

func NewSecurityManager

func NewSecurityManager() *SecurityManager

NewSecurityManager creates a new security manager with default configuration

func (*SecurityManager) ClientHandshake

func (sm *SecurityManager) ClientHandshake(ctx context.Context, s *stream.Stream) error

ClientHandshake performs a client-side security handshake on the given stream

func (*SecurityManager) ServerHandshake

func (sm *SecurityManager) ServerHandshake(ctx context.Context, s *stream.Stream) error

ServerHandshake performs a server-side security handshake on the given stream

type SecurityNegotiation

type SecurityNegotiation struct {
	Command          int
	ClientConfig     *SecurityConfig
	ServerConfig     *SecurityConfig
	NegotiatedAuth   AuthMethod
	NegotiatedCrypto CryptoMethod

	Enact          bool
	Authentication bool
	Encryption     bool
	IsClient       bool
	SessionResumed bool // Indicates if this session was resumed from cache
	// Session information from post-auth ClassAd
	SessionId     string
	User          string
	ValidCommands string
	// contains filtered or unexported fields
}

SecurityNegotiation represents the security negotiation state

func (*SecurityNegotiation) GetSharedSecret

func (sn *SecurityNegotiation) GetSharedSecret() []byte

GetSharedSecret returns the shared secret (read-only access)

type SessionCache

type SessionCache struct {
	// contains filtered or unexported fields
}

SessionCache manages cached security sessions

func GetSessionCache

func GetSessionCache() *SessionCache

GetSessionCache returns the global session cache, initializing it if necessary. On first access, this also imports any inherited sessions from the parent daemon (via CONDOR_PRIVATE_INHERIT environment variable).

func NewSessionCache

func NewSessionCache() *SessionCache

NewSessionCache creates a new session cache

func (*SessionCache) Clear

func (c *SessionCache) Clear()

Clear removes all sessions from the cache

func (*SessionCache) DebugDump

func (c *SessionCache) DebugDump() string

DebugDump returns a human-readable snapshot of the session cache for troubleshooting.

func (*SessionCache) Invalidate

func (c *SessionCache) Invalidate(id string) bool

Invalidate removes a session from the cache

func (*SessionCache) InvalidateExpired

func (c *SessionCache) InvalidateExpired() int

InvalidateExpired removes all expired sessions from the cache

func (*SessionCache) Lookup

func (c *SessionCache) Lookup(id string) (*SessionEntry, bool)

Lookup retrieves a session by ID

func (*SessionCache) LookupByCommand

func (c *SessionCache) LookupByCommand(tag, addr, command string) (*SessionEntry, bool)

LookupByCommand finds a session for a specific command to an address

func (*SessionCache) LookupNonExpired

func (c *SessionCache) LookupNonExpired(id string) (*SessionEntry, bool)

LookupNonExpired retrieves a non-expired session by ID

func (*SessionCache) MapCommand

func (c *SessionCache) MapCommand(tag, addr, command, sessionID string)

MapCommand maps a command to a session ID

func (*SessionCache) Size

func (c *SessionCache) Size() int

Size returns the number of sessions in the cache

func (*SessionCache) Snapshot added in v0.1.0

func (c *SessionCache) Snapshot() []*SessionEntry

Snapshot returns a copy of the current session entries. The returned slice is independent of the cache's internal map (callers may iterate it without holding the lock), though the *SessionEntry pointers are shared. It is used by persistence layers to enumerate sessions; callers typically skip expired and inherited entries (see IsExpired / IsInherited).

func (*SessionCache) Store

func (c *SessionCache) Store(entry *SessionEntry)

Store adds or updates a session in the cache

type SessionEntry

type SessionEntry struct {
	// contains filtered or unexported fields
}

SessionEntry represents a cached security session. One entry is shared by every connection that resumes the session, so its mutable fields (expiration, lastPeerVersion, inherited) are guarded by mu; the rest are immutable after construction. A concurrent collector/CCB resuming one session from many connections would otherwise race on these (caught by TestConcurrentServerHandshakes).

func CreateNonNegotiatedSession

func CreateNonNegotiatedSession(session *InheritedSession, peerAddr string) (*SessionEntry, error)

CreateNonNegotiatedSession creates a session entry from inherited session data This is equivalent to SecMan::CreateNonNegotiatedSecuritySession in HTCondor

func NewSessionEntry

func NewSessionEntry(id, addr string, keyInfo *KeyInfo, policy *classad.ClassAd, expiration time.Time, lease time.Duration, tag string) *SessionEntry

NewSessionEntry creates a new session cache entry

func (*SessionEntry) Addr

func (s *SessionEntry) Addr() string

Addr returns the remote address

func (*SessionEntry) Expiration

func (s *SessionEntry) Expiration() time.Time

Expiration returns the expiration time

func (*SessionEntry) ID

func (s *SessionEntry) ID() string

ID returns the session ID

func (*SessionEntry) IsExpired

func (s *SessionEntry) IsExpired() bool

IsExpired checks if the session has expired

func (*SessionEntry) IsInherited added in v0.1.0

func (s *SessionEntry) IsInherited() bool

IsInherited reports whether this session was imported from the parent daemon (via CONDOR_PRIVATE_INHERIT). Inherited sessions are re-imported from the environment on every start and must not be persisted to disk.

func (*SessionEntry) KeyInfo

func (s *SessionEntry) KeyInfo() *KeyInfo

KeyInfo returns the session key

func (*SessionEntry) LastPeerVersion

func (s *SessionEntry) LastPeerVersion() string

LastPeerVersion returns the last known peer version

func (*SessionEntry) Lease

func (s *SessionEntry) Lease() time.Duration

Lease returns the lease duration

func (*SessionEntry) Policy

func (s *SessionEntry) Policy() *classad.ClassAd

Policy returns the security policy

func (*SessionEntry) RenewLease

func (s *SessionEntry) RenewLease()

RenewLease renews the session lease

func (*SessionEntry) SetInherited added in v0.1.0

func (s *SessionEntry) SetInherited(v bool)

SetInherited marks this session as imported from the parent daemon.

func (*SessionEntry) SetLastPeerVersion

func (s *SessionEntry) SetLastPeerVersion(version string)

SetLastPeerVersion sets the last peer version

func (*SessionEntry) Tag

func (s *SessionEntry) Tag() string

Tag returns the security context tag

type SessionResumptionError

type SessionResumptionError struct {
	SessionID string
	Reason    string
}

SessionResumptionError represents an error that occurs when attempting to resume a session This error type can be used with errors.Is and errors.As to detect when a session resumption fails and a new connection should be established

func (*SessionResumptionError) Error

func (e *SessionResumptionError) Error() string

type TokenAuthData

type TokenAuthData struct {
	ClientID    string // Client identity (username@domain or token subject)
	ServerID    string // Server identity
	RA          []byte // Client random nonce
	RB          []byte // Server random nonce
	Token       string // JWT token (header.payload)
	Signature   []byte // JWT signature (used as shared key)
	SharedKey   []byte // Derived shared key K
	SharedKeyK  []byte // HMAC key K
	SharedKeyKP []byte // Key derivation key K'
	SessionKey  []byte // Final session key W = h'K'(RB)
	State       TokenAuthState
	// Error handling for graceful handshake completion
	AuthError   error // Stored authentication error
	ErrorStatus int   // AUTH_PW status to send (AUTH_PW_A_OK or AUTH_PW_ERROR)
}

TokenAuthData holds data for AKEP2 protocol

type TokenAuthState

type TokenAuthState int

TokenAuthState represents the current state in TOKEN authentication protocol

const (
	TokenStateInit             TokenAuthState = iota
	TokenStateSentRA                          // Client has sent RA
	TokenStateReceivedResponse                // Client has received server response
	TokenStateAuthComplete                    // Authentication complete
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL