spiffe-workload 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'google/protobuf'
4
+ require 'google/protobuf/struct_pb'
5
+
6
+ # Protobuf message definitions for SPIFFE Workload API
7
+ # These mirror the definitions in proto/spiffe/workload.proto
8
+
9
+ Google::Protobuf::DescriptorPool.generated_pool.build do
10
+ add_file('spiffe/workload.proto', syntax: :proto3) do
11
+ # X509SVID message
12
+ add_message 'spiffe.workload.X509SVID' do
13
+ optional :spiffe_id, :string, 1
14
+ optional :x509_svid, :bytes, 2
15
+ optional :x509_svid_key, :bytes, 3
16
+ optional :bundle, :bytes, 4
17
+ optional :hint, :string, 5
18
+ end
19
+
20
+ # X509SVIDRequest message (empty)
21
+ add_message 'spiffe.workload.X509SVIDRequest' do
22
+ end
23
+
24
+ # X509SVIDResponse message
25
+ add_message 'spiffe.workload.X509SVIDResponse' do
26
+ repeated :svids, :message, 1, 'spiffe.workload.X509SVID'
27
+ repeated :crl, :bytes, 2
28
+ map :federated_bundles, :string, :bytes, 3
29
+ end
30
+
31
+ # X509BundlesRequest message (empty)
32
+ add_message 'spiffe.workload.X509BundlesRequest' do
33
+ end
34
+
35
+ # X509BundlesResponse message
36
+ add_message 'spiffe.workload.X509BundlesResponse' do
37
+ repeated :crl, :bytes, 1
38
+ map :bundles, :string, :bytes, 2
39
+ end
40
+
41
+ # JWTSVID message
42
+ add_message 'spiffe.workload.JWTSVID' do
43
+ optional :spiffe_id, :string, 1
44
+ optional :svid, :string, 2
45
+ optional :hint, :string, 3
46
+ end
47
+
48
+ # JWTSVIDRequest message
49
+ add_message 'spiffe.workload.JWTSVIDRequest' do
50
+ repeated :audience, :string, 1
51
+ optional :spiffe_id, :string, 2
52
+ end
53
+
54
+ # JWTSVIDResponse message
55
+ add_message 'spiffe.workload.JWTSVIDResponse' do
56
+ repeated :svids, :message, 1, 'spiffe.workload.JWTSVID'
57
+ end
58
+
59
+ # JWTBundlesRequest message (empty)
60
+ add_message 'spiffe.workload.JWTBundlesRequest' do
61
+ end
62
+
63
+ # JWTBundlesResponse message
64
+ add_message 'spiffe.workload.JWTBundlesResponse' do
65
+ map :bundles, :string, :bytes, 1
66
+ end
67
+
68
+ # ValidateJWTSVIDRequest message
69
+ add_message 'spiffe.workload.ValidateJWTSVIDRequest' do
70
+ optional :audience, :string, 1
71
+ optional :svid, :string, 2
72
+ end
73
+
74
+ # ValidateJWTSVIDResponse message
75
+ add_message 'spiffe.workload.ValidateJWTSVIDResponse' do
76
+ optional :spiffe_id, :string, 1
77
+ optional :claims, :message, 2, 'google.protobuf.Struct'
78
+ end
79
+ end
80
+ end
81
+
82
+ module Spiffe
83
+ module Workload
84
+ # Load message classes
85
+ X509SVIDProto = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.X509SVID').msgclass
86
+ X509SVIDRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.X509SVIDRequest').msgclass
87
+ X509SVIDResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.X509SVIDResponse').msgclass
88
+
89
+ X509BundlesRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.X509BundlesRequest').msgclass
90
+ X509BundlesResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.X509BundlesResponse').msgclass
91
+
92
+ JWTSVIDProto = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.JWTSVID').msgclass
93
+ JWTSVIDRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.JWTSVIDRequest').msgclass
94
+ JWTSVIDResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.JWTSVIDResponse').msgclass
95
+
96
+ JWTBundlesRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.JWTBundlesRequest').msgclass
97
+ JWTBundlesResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.JWTBundlesResponse').msgclass
98
+
99
+ ValidateJWTSVIDRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.ValidateJWTSVIDRequest').msgclass
100
+ ValidateJWTSVIDResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup('spiffe.workload.ValidateJWTSVIDResponse').msgclass
101
+ end
102
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file provides proto generation instructions
4
+ # Run `rake generate_protos` to generate proper gRPC stubs
5
+
6
+ # The generate_protos task will:
7
+ # 1. Use grpc-tools-ruby-protoc to compile proto/spiffe/workload.proto
8
+ # 2. Generate workload_pb.rb and workload_services_pb.rb
9
+ # 3. Place them in lib/spiffe/workload/
10
+ #
11
+ # After generation, update client.rb to use:
12
+ # require 'spiffe/workload/workload_pb'
13
+ # require 'spiffe/workload/workload_services_pb'
14
+ #
15
+ # And replace grpc_stub.rb with the generated service stub
16
+
17
+ module Spiffe
18
+ module Workload
19
+ module ProtoHelper
20
+ PROTO_PATH = File.expand_path('../../../proto/spiffe/workload.proto', __dir__)
21
+
22
+ def self.generate_instructions
23
+ <<~INSTRUCTIONS
24
+ To generate Ruby gRPC stubs from proto files:
25
+
26
+ 1. Install grpc-tools:
27
+ gem install grpc-tools
28
+
29
+ 2. Generate the stubs:
30
+ rake generate_protos
31
+
32
+ Or manually:
33
+ grpc_tools_ruby_protoc \\
34
+ -I proto \\
35
+ --ruby_out=lib \\
36
+ --grpc_out=lib \\
37
+ proto/spiffe/workload.proto
38
+
39
+ This will create:
40
+ - lib/spiffe/workload_pb.rb (message definitions)
41
+ - lib/spiffe/workload_services_pb.rb (gRPC service client)
42
+ INSTRUCTIONS
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grpc'
4
+ require_relative 'messages'
5
+
6
+ module Spiffe
7
+ module Workload
8
+ # gRPC service definition for SPIFFE Workload API
9
+ module SpiffeWorkloadAPI
10
+ class Service
11
+ include GRPC::GenericService
12
+
13
+ self.marshal_class_method = :encode
14
+ self.unmarshal_class_method = :decode
15
+ self.service_name = 'SpiffeWorkloadAPI'
16
+
17
+ # FetchX509SVID - streaming server RPC
18
+ rpc :FetchX509SVID, X509SVIDRequest, stream(X509SVIDResponse)
19
+
20
+ # FetchX509Bundles - streaming server RPC
21
+ rpc :FetchX509Bundles, X509BundlesRequest, stream(X509BundlesResponse)
22
+
23
+ # FetchJWTSVID - unary RPC
24
+ rpc :FetchJWTSVID, JWTSVIDRequest, JWTSVIDResponse
25
+
26
+ # FetchJWTBundles - streaming server RPC
27
+ rpc :FetchJWTBundles, JWTBundlesRequest, stream(JWTBundlesResponse)
28
+
29
+ # ValidateJWTSVID - unary RPC
30
+ rpc :ValidateJWTSVID, ValidateJWTSVIDRequest, ValidateJWTSVIDResponse
31
+ end
32
+
33
+ Stub = Service.rpc_stub_class
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Spiffe
6
+ module Workload
7
+ # TLS configuration helper for SPIFFE
8
+ class TLSConfig
9
+ # Create an SSL context from an X.509 SVID
10
+ # @param svid [X509SVID] The SVID to use
11
+ # @param verify_mode [Integer] OpenSSL verify mode
12
+ # @return [OpenSSL::SSL::SSLContext]
13
+ def self.create_context(svid, verify_mode: OpenSSL::SSL::VERIFY_PEER)
14
+ context = OpenSSL::SSL::SSLContext.new
15
+ context.cert = svid.leaf_certificate
16
+ context.key = svid.private_key
17
+ context.extra_chain_cert = svid.cert_chain[1..-1] if svid.cert_chain.length > 1
18
+ context.cert_store = svid.trust_bundle
19
+ context.verify_mode = verify_mode
20
+ context.min_version = OpenSSL::SSL::TLS1_2_VERSION
21
+ context
22
+ end
23
+
24
+ # Create an SSL context that automatically updates with SVID rotation
25
+ # @param client [Client] The workload API client
26
+ # @param verify_mode [Integer] OpenSSL verify mode
27
+ # @return [OpenSSL::SSL::SSLContext]
28
+ def self.create_auto_updating_context(client, verify_mode: OpenSSL::SSL::VERIFY_PEER)
29
+ context = create_context(client.x509_svid, verify_mode: verify_mode)
30
+
31
+ # Register callback to update context on rotation
32
+ client.on_x509_svid_update do |new_svid|
33
+ context.cert = new_svid.leaf_certificate
34
+ context.key = new_svid.private_key
35
+ context.extra_chain_cert = new_svid.cert_chain[1..-1] if new_svid.cert_chain.length > 1
36
+ context.cert_store = new_svid.trust_bundle
37
+ end
38
+
39
+ context
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Spiffe
6
+ module Workload
7
+ # Represents an X.509 SVID with certificate chain, private key, and trust bundle
8
+ class X509SVIDWrapper
9
+ attr_reader :spiffe_id, :cert_chain, :private_key, :trust_bundle, :hint
10
+
11
+ # @param spiffe_id [String] The SPIFFE ID of this SVID
12
+ # @param cert_chain [Array<OpenSSL::X509::Certificate>] Certificate chain
13
+ # @param private_key [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] Private key
14
+ # @param trust_bundle [OpenSSL::X509::Store] Trust bundle for the trust domain
15
+ # @param hint [String, nil] Optional hint for SVID usage
16
+ def initialize(spiffe_id:, cert_chain:, private_key:, trust_bundle:, hint: nil)
17
+ @spiffe_id = spiffe_id
18
+ @cert_chain = cert_chain
19
+ @private_key = private_key
20
+ @trust_bundle = trust_bundle
21
+ @hint = hint
22
+ end
23
+
24
+ # Get the leaf certificate (first in chain)
25
+ # @return [OpenSSL::X509::Certificate]
26
+ def leaf_certificate
27
+ @cert_chain.first
28
+ end
29
+
30
+ # Check if the SVID is expired
31
+ # @return [Boolean]
32
+ def expired?
33
+ leaf_certificate.not_after < Time.now
34
+ end
35
+
36
+ # Get time until expiration
37
+ # @return [Float] Seconds until expiration
38
+ def ttl
39
+ leaf_certificate.not_after - Time.now
40
+ end
41
+
42
+ # Parse X.509 SVID from proto response
43
+ # @param proto_svid [Spiffe::Workload::X509SVIDProto] Proto message
44
+ # @return [X509SVID]
45
+ def self.from_proto(proto_svid)
46
+ # Parse certificate chain
47
+ cert_chain = []
48
+ cert_data = proto_svid.x509_svid
49
+
50
+ # The cert data may contain multiple certificates
51
+ # We need to extract all of them
52
+ offset = 0
53
+ while offset < cert_data.bytesize
54
+ begin
55
+ cert = OpenSSL::X509::Certificate.new(cert_data[offset..-1])
56
+ cert_chain << cert
57
+ # Move offset forward by the size of the DER-encoded cert
58
+ offset += cert.to_der.bytesize
59
+ rescue OpenSSL::X509::CertificateError
60
+ break
61
+ end
62
+ end
63
+
64
+ # Parse private key (PKCS#8 unencrypted)
65
+ private_key = OpenSSL::PKey.read(proto_svid.x509_svid_key)
66
+
67
+ # Parse trust bundle
68
+ trust_bundle = OpenSSL::X509::Store.new
69
+ bundle_data = proto_svid.bundle
70
+
71
+ # Parse all certificates in the bundle
72
+ offset = 0
73
+ while offset < bundle_data.bytesize
74
+ begin
75
+ ca_cert = OpenSSL::X509::Certificate.new(bundle_data[offset..-1])
76
+ trust_bundle.add_cert(ca_cert)
77
+ offset += ca_cert.to_der.bytesize
78
+ rescue OpenSSL::X509::CertificateError
79
+ break
80
+ end
81
+ end
82
+
83
+ new(
84
+ spiffe_id: proto_svid.spiffe_id,
85
+ cert_chain: cert_chain,
86
+ private_key: private_key,
87
+ trust_bundle: trust_bundle,
88
+ hint: proto_svid.hint.empty? ? nil : proto_svid.hint
89
+ )
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: spiffe/workload.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'google/protobuf/struct_pb'
8
+
9
+
10
+ descriptor_data = "\n\x15spiffe/workload.proto\x12\x0fspiffe.workload\x1a\x1cgoogle/protobuf/struct.proto\"\x11\n\x0fX509SVIDRequest\"\xd6\x01\n\x10X509SVIDResponse\x12(\n\x05svids\x18\x01 \x03(\x0b\x32\x19.spiffe.workload.X509SVID\x12\x0b\n\x03\x63rl\x18\x02 \x03(\x0c\x12R\n\x11\x66\x65\x64\x65rated_bundles\x18\x03 \x03(\x0b\x32\x37.spiffe.workload.X509SVIDResponse.FederatedBundlesEntry\x1a\x37\n\x15\x46\x65\x64\x65ratedBundlesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"e\n\x08X509SVID\x12\x11\n\tspiffe_id\x18\x01 \x01(\t\x12\x11\n\tx509_svid\x18\x02 \x01(\x0c\x12\x15\n\rx509_svid_key\x18\x03 \x01(\x0c\x12\x0e\n\x06\x62undle\x18\x04 \x01(\x0c\x12\x0c\n\x04hint\x18\x05 \x01(\t\"\x14\n\x12X509BundlesRequest\"\x96\x01\n\x13X509BundlesResponse\x12\x0b\n\x03\x63rl\x18\x01 \x03(\x0c\x12\x42\n\x07\x62undles\x18\x02 \x03(\x0b\x32\x31.spiffe.workload.X509BundlesResponse.BundlesEntry\x1a.\n\x0c\x42undlesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"5\n\x0eJWTSVIDRequest\x12\x10\n\x08\x61udience\x18\x01 \x03(\t\x12\x11\n\tspiffe_id\x18\x02 \x01(\t\":\n\x0fJWTSVIDResponse\x12\'\n\x05svids\x18\x01 \x03(\x0b\x32\x18.spiffe.workload.JWTSVID\"8\n\x07JWTSVID\x12\x11\n\tspiffe_id\x18\x01 \x01(\t\x12\x0c\n\x04svid\x18\x02 \x01(\t\x12\x0c\n\x04hint\x18\x03 \x01(\t\"\x13\n\x11JWTBundlesRequest\"\x87\x01\n\x12JWTBundlesResponse\x12\x41\n\x07\x62undles\x18\x01 \x03(\x0b\x32\x30.spiffe.workload.JWTBundlesResponse.BundlesEntry\x1a.\n\x0c\x42undlesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"8\n\x16ValidateJWTSVIDRequest\x12\x10\n\x08\x61udience\x18\x01 \x01(\t\x12\x0c\n\x04svid\x18\x02 \x01(\t\"U\n\x17ValidateJWTSVIDResponse\x12\x11\n\tspiffe_id\x18\x01 \x01(\t\x12\'\n\x06\x63laims\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct2\xe3\x03\n\x11SpiffeWorkloadAPI\x12V\n\rFetchX509SVID\x12 .spiffe.workload.X509SVIDRequest\x1a!.spiffe.workload.X509SVIDResponse0\x01\x12_\n\x10\x46\x65tchX509Bundles\x12#.spiffe.workload.X509BundlesRequest\x1a$.spiffe.workload.X509BundlesResponse0\x01\x12Q\n\x0c\x46\x65tchJWTSVID\x12\x1f.spiffe.workload.JWTSVIDRequest\x1a .spiffe.workload.JWTSVIDResponse\x12\\\n\x0f\x46\x65tchJWTBundles\x12\".spiffe.workload.JWTBundlesRequest\x1a#.spiffe.workload.JWTBundlesResponse0\x01\x12\x64\n\x0fValidateJWTSVID\x12\'.spiffe.workload.ValidateJWTSVIDRequest\x1a(.spiffe.workload.ValidateJWTSVIDResponseB?Z=github.com/spiffe/go-spiffe/v2/proto/spiffe/workload;workloadb\x06proto3"
11
+
12
+ pool = ::Google::Protobuf::DescriptorPool.generated_pool
13
+ pool.add_serialized_file(descriptor_data)
14
+
15
+ module Spiffe
16
+ module Workload
17
+ X509SVIDRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.X509SVIDRequest").msgclass
18
+ X509SVIDResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.X509SVIDResponse").msgclass
19
+ X509SVID = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.X509SVID").msgclass
20
+ X509BundlesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.X509BundlesRequest").msgclass
21
+ X509BundlesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.X509BundlesResponse").msgclass
22
+ JWTSVIDRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.JWTSVIDRequest").msgclass
23
+ JWTSVIDResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.JWTSVIDResponse").msgclass
24
+ JWTSVID = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.JWTSVID").msgclass
25
+ JWTBundlesRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.JWTBundlesRequest").msgclass
26
+ JWTBundlesResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.JWTBundlesResponse").msgclass
27
+ ValidateJWTSVIDRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.ValidateJWTSVIDRequest").msgclass
28
+ ValidateJWTSVIDResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("spiffe.workload.ValidateJWTSVIDResponse").msgclass
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: spiffe/workload.proto for package 'spiffe.workload'
3
+
4
+ require 'grpc'
5
+ require 'spiffe/workload_pb'
6
+
7
+ module Spiffe
8
+ module Workload
9
+ module SpiffeWorkloadAPI
10
+ class Service
11
+
12
+ include ::GRPC::GenericService
13
+
14
+ self.marshal_class_method = :encode
15
+ self.unmarshal_class_method = :decode
16
+ self.service_name = 'SpiffeWorkloadAPI'
17
+
18
+ # Fetch X.509-SVIDs for all SPIFFE identities the workload is entitled to,
19
+ # as well as related information like trust bundles and CRLs. As this
20
+ # information changes, subsequent messages will be streamed from the
21
+ # server.
22
+ rpc :FetchX509SVID, ::Spiffe::Workload::X509SVIDRequest, stream(::Spiffe::Workload::X509SVIDResponse)
23
+ # Fetch trust bundles and CRLs. Useful for clients that only need to
24
+ # validate SVIDs without obtaining an SVID for themself. As this
25
+ # information changes, subsequent messages will be streamed from the
26
+ # server.
27
+ rpc :FetchX509Bundles, ::Spiffe::Workload::X509BundlesRequest, stream(::Spiffe::Workload::X509BundlesResponse)
28
+ # ///////////////////////////////////////////////////////////////////////
29
+ # JWT-SVID Profile
30
+ # ///////////////////////////////////////////////////////////////////////
31
+ #
32
+ # Fetch JWT-SVIDs for all SPIFFE identities the workload is entitled to,
33
+ # for the requested audience. If an optional SPIFFE ID is requested, only
34
+ # the JWT-SVID for that SPIFFE ID is returned.
35
+ rpc :FetchJWTSVID, ::Spiffe::Workload::JWTSVIDRequest, ::Spiffe::Workload::JWTSVIDResponse
36
+ # Fetches the JWT bundles, formatted as JWKS documents, keyed by the
37
+ # SPIFFE ID of the trust domain. As this information changes, subsequent
38
+ # messages will be streamed from the server.
39
+ rpc :FetchJWTBundles, ::Spiffe::Workload::JWTBundlesRequest, stream(::Spiffe::Workload::JWTBundlesResponse)
40
+ # Validates a JWT-SVID against the requested audience. Returns the SPIFFE
41
+ # ID of the JWT-SVID and JWT claims.
42
+ rpc :ValidateJWTSVID, ::Spiffe::Workload::ValidateJWTSVIDRequest, ::Spiffe::Workload::ValidateJWTSVIDResponse
43
+ end
44
+
45
+ Stub = Service.rpc_stub_class
46
+ end
47
+ # ///////////////////////////////////////////////////////////////////////
48
+ # X509-SVID Profile
49
+ # ///////////////////////////////////////////////////////////////////////
50
+ end
51
+ end
data/lib/spiffe.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'spiffe/version'
4
+ require_relative 'spiffe/workload/client'
5
+ require_relative 'spiffe/workload/x509_svid'
6
+ require_relative 'spiffe/workload/jwt_svid'
7
+ require_relative 'spiffe/workload/tls_config'
8
+ require_relative 'spiffe/workload/http_client'
9
+
10
+ module Spiffe
11
+ class Error < StandardError; end
12
+ class SocketError < Error; end
13
+ class AuthenticationError < Error; end
14
+ class RotationError < Error; end
15
+
16
+ # Convenience method to create a new Workload API client
17
+ # @param socket_path [String, nil] Optional socket path
18
+ # @return [Workload::Client]
19
+ def self.workload_api_client(socket_path: nil)
20
+ Workload::Client.new(socket_path: socket_path)
21
+ end
22
+ end
@@ -0,0 +1,168 @@
1
+ syntax = "proto3";
2
+
3
+ package spiffe.workload;
4
+
5
+ import "google/protobuf/struct.proto";
6
+
7
+ service SpiffeWorkloadAPI {
8
+ /////////////////////////////////////////////////////////////////////////
9
+ // X509-SVID Profile
10
+ /////////////////////////////////////////////////////////////////////////
11
+
12
+ // Fetch X.509-SVIDs for all SPIFFE identities the workload is entitled to,
13
+ // as well as related information like trust bundles and CRLs. As this
14
+ // information changes, subsequent messages will be streamed from the
15
+ // server.
16
+ rpc FetchX509SVID(X509SVIDRequest) returns (stream X509SVIDResponse);
17
+
18
+ // Fetch trust bundles and CRLs. Useful for clients that only need to
19
+ // validate SVIDs without obtaining an SVID for themself. As this
20
+ // information changes, subsequent messages will be streamed from the
21
+ // server.
22
+ rpc FetchX509Bundles(X509BundlesRequest) returns (stream X509BundlesResponse);
23
+
24
+ /////////////////////////////////////////////////////////////////////////
25
+ // JWT-SVID Profile
26
+ /////////////////////////////////////////////////////////////////////////
27
+
28
+ // Fetch JWT-SVIDs for all SPIFFE identities the workload is entitled to,
29
+ // for the requested audience. If an optional SPIFFE ID is requested, only
30
+ // the JWT-SVID for that SPIFFE ID is returned.
31
+ rpc FetchJWTSVID(JWTSVIDRequest) returns (JWTSVIDResponse);
32
+
33
+ // Fetches the JWT bundles, formatted as JWKS documents, keyed by the
34
+ // SPIFFE ID of the trust domain. As this information changes, subsequent
35
+ // messages will be streamed from the server.
36
+ rpc FetchJWTBundles(JWTBundlesRequest) returns (stream JWTBundlesResponse);
37
+
38
+ // Validates a JWT-SVID against the requested audience. Returns the SPIFFE
39
+ // ID of the JWT-SVID and JWT claims.
40
+ rpc ValidateJWTSVID(ValidateJWTSVIDRequest) returns (ValidateJWTSVIDResponse);
41
+ }
42
+
43
+ // The X509SVIDRequest message conveys parameters for requesting an X.509-SVID.
44
+ // There are currently no request parameters.
45
+ message X509SVIDRequest { }
46
+
47
+ // The X509SVIDResponse message carries X.509-SVIDs and related information,
48
+ // including a set of global CRLs and a list of bundles the workload may use
49
+ // for federating with foreign trust domains.
50
+ message X509SVIDResponse {
51
+ // Required. A list of X509SVID messages, each of which includes a single
52
+ // X.509-SVID, its private key, and the bundle for the trust domain.
53
+ repeated X509SVID svids = 1;
54
+
55
+ // Optional. ASN.1 DER encoded certificate revocation lists.
56
+ repeated bytes crl = 2;
57
+
58
+ // Optional. CA certificate bundles belonging to foreign trust domains that
59
+ // the workload should trust, keyed by the SPIFFE ID of the foreign trust
60
+ // domain. Bundles are ASN.1 DER encoded.
61
+ map<string, bytes> federated_bundles = 3;
62
+ }
63
+
64
+ // The X509SVID message carries a single SVID and all associated information,
65
+ // including the X.509 bundle for the trust domain.
66
+ message X509SVID {
67
+ // Required. The SPIFFE ID of the SVID in this entry
68
+ string spiffe_id = 1;
69
+
70
+ // Required. ASN.1 DER encoded certificate chain. MAY include
71
+ // intermediates, the leaf certificate (or SVID itself) MUST come first.
72
+ bytes x509_svid = 2;
73
+
74
+ // Required. ASN.1 DER encoded PKCS#8 private key. MUST be unencrypted.
75
+ bytes x509_svid_key = 3;
76
+
77
+ // Required. ASN.1 DER encoded X.509 bundle for the trust domain.
78
+ bytes bundle = 4;
79
+
80
+ // Optional. An operator-specified string used to provide guidance on how this
81
+ // identity should be used by a workload when more than one SVID is returned.
82
+ // For example, `internal` and `external` to indicate an SVID for internal or
83
+ // external use, respectively.
84
+ string hint = 5;
85
+ }
86
+
87
+ // The X509BundlesRequest message conveys parameters for requesting X.509
88
+ // bundles. There are currently no such parameters.
89
+ message X509BundlesRequest {
90
+ }
91
+
92
+ // The X509BundlesResponse message carries a set of global CRLs and a map of
93
+ // trust bundles the workload should trust.
94
+ message X509BundlesResponse {
95
+ // Optional. ASN.1 DER encoded certificate revocation lists.
96
+ repeated bytes crl = 1;
97
+
98
+ // Required. CA certificate bundles belonging to trust domains that the
99
+ // workload should trust, keyed by the SPIFFE ID of the trust domain.
100
+ // Bundles are ASN.1 DER encoded.
101
+ map<string, bytes> bundles = 2;
102
+ }
103
+
104
+ message JWTSVIDRequest {
105
+ // Required. The audience(s) the workload intends to authenticate against.
106
+ repeated string audience = 1;
107
+
108
+ // Optional. The requested SPIFFE ID for the JWT-SVID. If unset, all
109
+ // JWT-SVIDs to which the workload is entitled are requested.
110
+ string spiffe_id = 2;
111
+ }
112
+
113
+ // The JWTSVIDResponse message conveys JWT-SVIDs.
114
+ message JWTSVIDResponse {
115
+ // Required. The list of returned JWT-SVIDs.
116
+ repeated JWTSVID svids = 1;
117
+ }
118
+
119
+ // The JWTSVID message carries the JWT-SVID token and associated metadata.
120
+ message JWTSVID {
121
+ // Required. The SPIFFE ID of the JWT-SVID.
122
+ string spiffe_id = 1;
123
+
124
+ // Required. Encoded JWT using JWS Compact Serialization.
125
+ string svid = 2;
126
+
127
+ // Optional. An operator-specified string used to provide guidance on how this
128
+ // identity should be used by a workload when more than one SVID is returned.
129
+ // For example, `internal` and `external` to indicate an SVID for internal or
130
+ // external use, respectively.
131
+ string hint = 3;
132
+ }
133
+
134
+ // The JWTBundlesRequest message conveys parameters for requesting JWT bundles.
135
+ // There are currently no such parameters.
136
+ message JWTBundlesRequest { }
137
+
138
+ // The JWTBundlesReponse conveys JWT bundles.
139
+ message JWTBundlesResponse {
140
+ // Required. JWK encoded JWT bundles, keyed by the SPIFFE ID of the trust
141
+ // domain.
142
+ map<string, bytes> bundles = 1;
143
+ }
144
+
145
+ // The ValidateJWTSVIDRequest message conveys request parameters for
146
+ // JWT-SVID validation.
147
+ message ValidateJWTSVIDRequest {
148
+ // Required. The audience of the validating party. The JWT-SVID must
149
+ // contain an audience claim which contains this value in order to
150
+ // succesfully validate.
151
+ string audience = 1;
152
+
153
+ // Required. The JWT-SVID to validate, encoded using JWS Compact
154
+ // Serialization.
155
+ string svid = 2;
156
+ }
157
+
158
+ // The ValidateJWTSVIDReponse message conveys the JWT-SVID validation results.
159
+ message ValidateJWTSVIDResponse {
160
+ // Required. The SPIFFE ID of the validated JWT-SVID.
161
+ string spiffe_id = 1;
162
+
163
+ // Optional. Arbitrary claims contained within the payload of the validated
164
+ // JWT-SVID.
165
+ google.protobuf.Struct claims = 2;
166
+ }
167
+
168
+ option go_package = "github.com/spiffe/go-spiffe/v2/proto/spiffe/workload;workload";