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-workload
For Puppet:
/opt/puppetlabs/puppet/bin/gem install spiffe-workload
Prerequisites
- 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/app
Quick 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.shutdown
Fetch 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.shutdown
mTLS 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.shutdown
Configuration
Socket Path
Auto-detected from:
socket_pathparameterSPIFFE_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
end
Use 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 binary
API Reference
Spiffe.workload_api_client
Creates a new Workload API client.
Parameters:
socket_path(String, optional): Path to SPIRE agent sockettimeout(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 stringleaf_certificate- OpenSSL::X509::Certificatecertificate_chain- Array of certificatesprivate_key- OpenSSL::PKey::RSAttl- 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 stringtoken- JWT token stringexpiration- Expiration timeclaims- 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 $USER
Architecture
This gem implements a client-side library for the SPIFFE Workload API:
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-*.gem
Run Tests
bundle exec rspec
Generate Protobuf
gem install grpc-tools
rake generate_protos
License
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.