Class: Legion::Crypt::Spiffe::SvidRotation

Inherits:
Object
  • Object
show all
Includes:
Logging::Helper
Defined in:
lib/legion/crypt/spiffe/svid_rotation.rb

Overview

Background thread that keeps the current X.509 SVID fresh.

Mirrors the pattern used by CertRotation (mTLS) but targets the SPIFFE Workload API instead of Vault PKI. The check interval defaults to 60 seconds; renewal fires when the SVID is past 50% of its lifetime (configurable via security.spiffe.renewal_window).

Constant Summary collapse

DEFAULT_CHECK_INTERVAL =
60

Constants included from Logging::Helper

Logging::Helper::CompatLogger

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging::Helper

#handle_exception, #log

Constructor Details

#initialize(check_interval: DEFAULT_CHECK_INTERVAL, client: nil) ⇒ SvidRotation

Returns a new instance of SvidRotation.



21
22
23
24
25
26
27
28
29
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 21

def initialize(check_interval: DEFAULT_CHECK_INTERVAL, client: nil)
  @check_interval = check_interval
  @client         = client || WorkloadApiClient.new
  @current_svid   = nil
  @issued_at      = nil
  @running        = false
  @thread         = nil
  @mutex          = Mutex.new
end

Instance Attribute Details

#check_intervalObject (readonly)

Returns the value of attribute check_interval.



19
20
21
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 19

def check_interval
  @check_interval
end

#current_svidObject (readonly)

Returns the value of attribute current_svid.



19
20
21
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 19

def current_svid
  @current_svid
end

Instance Method Details

#needs_renewal?Boolean

Returns:

  • (Boolean)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 72

def needs_renewal?
  svid      = nil
  issued_at = nil
  @mutex.synchronize do
    svid      = @current_svid
    issued_at = @issued_at
  end

  return true if svid.nil? || issued_at.nil?
  return true if svid.expired?

  total = svid.expiry - issued_at
  return true if total <= 0

  remaining = svid.expiry - Time.now
  fraction  = remaining / total
  fraction < renewal_window
end

#rotate!Object



62
63
64
65
66
67
68
69
70
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 62

def rotate!
  svid = @client.fetch_x509_svid
  @mutex.synchronize do
    @current_svid = svid
    @issued_at    = Time.now
  end
  log.info("[SPIFFE] SVID rotated: id=#{svid.spiffe_id} expiry=#{svid.expiry}")
  svid
end

#running?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 58

def running?
  (@running && @thread&.alive?) || false
end

#startObject



31
32
33
34
35
36
37
38
39
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 31

def start
  return unless Legion::Crypt::Spiffe.enabled?
  return if running?

  @running = true
  @thread  = Thread.new { rotation_loop }
  @thread.name = 'spiffe-svid-rotation'
  log.info '[SPIFFE] SvidRotation started'
end

#stopObject



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/legion/crypt/spiffe/svid_rotation.rb', line 41

def stop
  @running = false
  begin
    @thread&.wakeup
  rescue ThreadError => e
    handle_exception(e, level: :debug, operation: 'crypt.spiffe.svid_rotation.stop')
    nil
  end
  @thread&.join(3)
  if @thread&.alive?
    log.warn '[SPIFFE] SvidRotation thread did not stop within timeout'
  else
    @thread = nil
  end
  log.info '[SPIFFE] SvidRotation stopped'
end