SPIFFE Workload API - Ruby Client
A Ruby client library for the SPIFFE Workload API, enabling Ruby applications to obtain and use SPIFFE identities from a SPIRE agent.
This project aims to be part of the SPIFFE ecosystem. See the SPIFFE GitHub for more information about the SPIFFE project.
What is this?
This gem allows Ruby applications (including Puppet) to:
- Fetch X.509 SVIDs (certificates) from SPIRE for mTLS
- Generate JWT SVIDs for API authentication
- Access trust bundles for verification
- Handle automatic credential rotation
It communicates directly with the SPIRE agent over Unix domain sockets, enabling proper process-based attestation (unlike subprocess-based CLI tools).
Installation
gem install spiffe-workloadFor Puppet:
/opt/puppetlabs/puppet/bin/gem install spiffe-workloadPrerequisites
- SPIRE Agent running with workload API socket accessible
- Workload entry registered for your application
# Verify SPIRE agent is running
ls -la /run/spire/sockets/agent.sock
# Register your application
spire-server entry create \
-parentID spiffe://example.org/agent/myhost \
-spiffeID spiffe://example.org/myapp \
-selector unix:uid:$(id -u) \
-selector unix:path:/path/to/your/appQuick Start
Fetch X.509 Certificate
require 'spiffe'
client = Spiffe.workload_api_client
svid = client.x509_svid
puts "My identity: #{svid.spiffe_id}"
puts "Expires: #{svid.leaf_certificate.not_after}"
client.shutdownFetch JWT Token
require 'spiffe'
client = Spiffe.workload_api_client
jwt = client.jwt_svid(audience: 'my-service')
puts "Token: #{jwt.token}"
puts "Expires: #{jwt.expiration}"
client.shutdownmTLS HTTP Request
require 'spiffe'
require 'net/http'
client = Spiffe.workload_api_client
uri = URI('https://api.example.com/data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.ssl_context = client.tls_context
response = http.get(uri.path)
puts response.body
client.shutdownConfiguration
Socket Path
Auto-detected from:
-
socket_pathparameter -
SPIFFE_ENDPOINT_SOCKETenvironment variable - Default:
/run/spire/sockets/agent.sock
client = Spiffe.workload_api_client(
socket_path: '/var/run/spire/agent.sock'
)Timeout
client = Spiffe.workload_api_client(timeout: 10)Puppet Integration
Custom Function Example
# modules/spiffe/lib/puppet/functions/spiffe_jwt.rb
Puppet::Functions.create_function(:spiffe_jwt) do
def spiffe_jwt(audience)
require 'spiffe'
client = Spiffe.workload_api_client
jwt = client.jwt_svid(audience: audience)
jwt.token
ensure
client&.shutdown
end
endUse in Puppet manifest:
$token = Deferred('spiffe_jwt', ['vault.example.com'])
file { '/etc/app/token':
content => $token,
mode => '0600',
}SPIRE Configuration for Puppet
# Puppet agent workload entry
selectors:
- "unix:uid:0" # Root user
- "unix:path:/opt/puppetlabs/puppet/bin/ruby" # Puppet Ruby binaryAPI Reference
Spiffe.workload_api_client
Creates a new Workload API client.
Parameters:
-
socket_path(String, optional): Path to SPIRE agent socket -
timeout(Integer, optional): Request timeout in seconds
Returns: Spiffe::Workload::Client
Client#x509_svid
Fetches X.509 SVID.
Returns: Spiffe::Workload::X509SVIDWrapper
Properties:
-
spiffe_id- SPIFFE ID string -
leaf_certificate- OpenSSL::X509::Certificate -
certificate_chain- Array of certificates -
private_key- OpenSSL::PKey::RSA -
ttl- Time to live in seconds
Client#jwt_svid(audience:, spiffe_id: nil)
Fetches JWT SVID.
Parameters:
-
audience(String|Array): Target audience(s) -
spiffe_id(String, optional): Specific SPIFFE ID
Returns: Spiffe::Workload::JWTSVIDWrapper
Properties:
-
spiffe_id- SPIFFE ID string -
token- JWT token string -
expiration- Expiration time -
claims- Decoded JWT claims
Client#tls_context
Creates OpenSSL context with current SVID.
Returns: OpenSSL::SSL::SSLContext
Client#on_x509_svid_update { |svid| ... }
Registers callback for SVID rotation.
Client#shutdown
Cleanly shuts down the client.
Troubleshooting
"Socket does not exist"
# Check SPIRE agent status
systemctl status spire-agent
# Verify socket location
sudo find /run /var/run -name "agent.sock" 2>/dev/null"No identity issued"
# Check workload registration
spire-server entry show
# Verify selectors match your process
ps aux | grep your-app"Permission denied"
# Check socket permissions
ls -la /run/spire/sockets/agent.sock
# Add user to spire group
sudo usermod -a -G spire $USERArchitecture
This gem implements a client-side library for the SPIFFE Workload API:
┌────────────────┐
│ Ruby App │
│ (this gem) │
└───────┬────────┘
│ gRPC over Unix socket
▼
┌────────────────┐
│ SPIRE Agent │
│ (attests app) │
└───────┬────────┘
│ mTLS
▼
┌────────────────┐
│ SPIRE Server │
└────────────────┘
Key points:
- No subprocess attestation issues (unlike CLI tools)
- Direct process attestation by SPIRE
- Thread-safe credential caching
- Automatic rotation support
Contributing
We welcome contributions! This project follows the SPIFFE contribution guidelines.
Getting Started
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Ensure tests pass (
bundle exec rspec) - Ensure code style passes (
bundle exec rubocop) - Commit with DCO sign-off (
git commit -s -m 'Add amazing feature') - Push to your fork (
git push origin feature/amazing-feature) - Open a Pull Request
DCO Sign-off
All commits must include a Signed-off-by line to certify the Developer Certificate of Origin (DCO):
git commit -s -m "Your commit message"Similar SPIFFE Projects
- go-spiffe - Go implementation
- java-spiffe - Java implementation
- py-spiffe - Python implementation
See SPIFFE CONTRIBUTING.md for more details on contributing to SPIFFE projects.
Development
Build from Source
git clone https://github.com/halradaideh/spiffe-rubygem.git
cd spiffe-rubygem
bundle install
gem build spiffe-workload.gemspec
gem install spiffe-workload-*.gemRun Tests
bundle exec rspecGenerate Protobuf
gem install grpc-tools
rake generate_protosLicense
Apache License 2.0 - see LICENSE file
Support
- Issues: GitHub Issues
- SPIFFE Slack: slack.spiffe.io
- SPIFFE Docs: spiffe.io/docs
Version
Current version: 1.0.0
See CHANGELOG.md for version history.