SlideShare une entreprise Scribd logo
1  sur  36
Télécharger pour lire hors ligne
A tale of application
development
Nicolas Corrarello - HashiConf 2018
Disclaimer
• Argentinian by birth, Italian by blood

• English is not my first language

• I’m not a developer

• Don’t criticise my programming language choices, I
basically had Christmas week last year to write this :D.
Client libraries available in lots of languages (https://
www.vaultproject.io/api/libraries.html / https://
www.consul.io/api/libraries-and-sdks.html)
(What’s the story) Morning
glory?
• Spending lots of time authoring documents

• Write an app to automate the process

• Don’t have time to maintain it so make it as readable as
possible, zero touch maintenance as possible
1. IMPORTANT - READ BEFORE SIGNING. Are there Consul / Vault Tokens readily
available on the system (see Nomad or the myriad of Vault Authentication Methods
available, then use Vault to get a Consul token :D)

Are you provisioning this yourself or someone else doing it for you?

J. Developer
AbstractionsNot HashiCorp specific (duh)
“The best thing to come out of Java” - Variety
Abstract your code from lock-in

Minimal effort, greater portability

Using existing libraries (datamapper, dal.py,
hibernate) may help. Also may make things way
more complicated (in certain cases, see Rails)
require 'json'
require './models/vault'
##
# Persistence layer. Implemented in S3, but the interface is generic enough so
it can be migrated to other platforms.
class Persistence
##
# Create a persistence layer. Initialises a HashiCorp Vault session and obtains
credentials from it using the existing abstraction.
def initialize
@vault = LocalVault.new
@awscreds = self.awsClient
@data = {}
end
##
# Store something in the persistence layer. Uses namespace (which translates to
an s3 bucket), and a simple key/value. Object is stored as JSON.
def Store(key,value,namespace)
s3 = @awscreds
begin
obj = s3.bucket(namespace).object(key)
obj.put(body: value.to_json)
return true, nil
rescue
return false, "Error persisting object"
end
end
##
# Retrieve something from the persistence layer. Uses namespace (which
translates to an s3 bucket), and a key. Returns the full object in it's native
format.
def Retrieve(key,namespace)
[…]
end
##
# List objects by "glob". Returns a list of objects available. In this case as
S3 is somewhat hierarchical, glob would be 'customers' to to retrieve customer
objets, or 'pov' to return POV documents.
def List(glob,namespace)
[…]
end
end
Initialize the object
Store something
Let’s talk secrets… lots…
• AWS API Credentials for Deployment

• AWS Credentials for the application to read/write the
bucket

• JWT Issuer / Secret

• Google API Keys for logins

• Crypto
Crypto?
Not the kind that your
coworker keeps talking
about, you know who,
the self named Bitcoin
expert….
##
# Creates a customer in the object, requires an sfdcAccountId (unique)
and a customer name. Information is encrypted and persisted, but kept in
plaintext in memory.
def Create(sfdcAccountId,name,domain)
unless sfdcAccountId.nil? || name.nil?
begin
cyphertext = @vault.logical.write("transit/encrypt/#{key}",
plaintext: Base64.encode64(value).gsub('n',''), context:
Base64.encode64(context).gsub('n',''))
cyphername = cyphertext.data[:ciphertext]
rescue
return false, "Error encrypting data"
end
customer = {
:id => sfdcAccountId,
:name => cyphername,
:contacts => nil,
}
customerm = {
:id => sfdcAccountId,
:name => name,
:contacts => nil,
}
key = 'customers/' + sfdcAccountId + '/customer.json'
begin
@persistence.Store(key,customer,"jamo-#{domain}")
if @data[domain] == nil
@data[domain] = {}
end
@data[domain][sfdcAccountId] = customerm
return true, nil
rescue
return false, "error persisting customer"
end
end
end
Encrypt a string
Persist it
##
# Creates a customer in the object, requires an sfdcAccountId (unique)
and a customer name. Information is encrypted and persisted, but kept
in plaintext in memory.
def Create(sfdcAccountId,name,domain)
unless sfdcAccountId.nil? || name.nil?
begin
cyphername = @vault.encrypt(name,'foo', @jwt_secret)
rescue
return false, "Error encrypting data"
end
customer = {
:id => sfdcAccountId,
:name => cyphername,
:contacts => nil,
}
customerm = {
:id => sfdcAccountId,
:name => name,
:contacts => nil,
}
key = 'customers/' + sfdcAccountId + '/customer.json'
begin
@persistence.Store(key,customer,"jamo-#{domain}")
if @data[domain] == nil
@data[domain] = {}
end
@data[domain][sfdcAccountId] = customerm
return true, nil
rescue
return false, "error persisting customer"
end
end
end
##
# Encrypt a value with a defined key and
context for symmetric encryption.
def encrypt(value,key,context)
cyphertext =
@vault.logical.write("transit/encrypt/#{key}",
plaintext:
Base64.encode64(value).gsub('n',''), context:
Base64.encode64(context).gsub('n',''))
return cyphertext.data[:ciphertext]
end
require 'vault'
require 'base64'
##
# This object abstract the crypto / secrets management functions of HashiCorp Vault
class LocalVault
def initialize
@vault = Vault::Client.new
end
##
# Obtain a set of credentials to access the S3 Bucket used by the persistence layer
def getAwsCreds
credentials = @vault.logical.read("aws/creds/s3-bucket")
return credentials
end
def getGoogleCredentials
credentials = @vault.logical.read("secret/gsuite")
return credentials
end
def getJWTsecret
jwtsecret = @vault.logical.read("secret/JWT")
return jwtsecret
end
##
# Encrypt a value with a defined key and context for asymmetric encryption.
def encrypt(value,key,context)
cyphertext = @vault.logical.write("transit/encrypt/#{key}", plaintext:
Base64.encode64(value).gsub('n',''), context:
Base64.encode64(context).gsub('n',''))
return cyphertext.data[:ciphertext]
end
##
# Decrypt a value with a defined key and context.
def decrypt(value,key,context)
plaintext = @vault.logical.write("transit/decrypt/#{key}", ciphertext: value,
context: Base64.encode64(context).gsub('n',''))
return Base64.decode64(plaintext.data[:plaintext]).gsub('n','')
end
end
Initialize the object
Get dynamically generated AWS Tokens
Get static GSuite API Keys
Get info to sign JWTs
Encrypt a string
Decrypt a string
2. How is this application getting its Vault / Nomad / Consul token?

How is this application supposed to get it’s runtime configuration?

J. Developer
group "webs" {
count = 1
task "jamo" {
vault {
policies = ["jamo"]
change_mode = "signal"
change_signal = "SIGHUP"
}
driver = "docker"
env {
VAULT_ADDR = “https://vault.service.consul:8200"
MEMCACHE_ADDR = "memcache.service.consul:11211"
}
[...]
}
Let Nomad get me a Token
What to do if that token is rotated
Environment variables.
DNS resolved by Consul
What if I’m using K8s?
• The good option, is using the K8s authentication method
and doing a login in the Dockerfile so the pod gets it
VAULT_TOKEN and then consume secrets

• The not bad option, is using consul-template to deploy a
helm chart with the secrets, doing authentication in the
pipeline that deploys it (see https://www.hashicorp.com/
resources/how-to-share-secrets-pipeline or http://
nicolas.corrarello.com/general/vault/security/ci/
2017/04/23/Reading-Vault-Secrets-in-your-Jenkins-
pipeline.html)
Perfect > Good Enough
• Getting credentials out of Vault is super easy, so
it’s easy to fall on the trap of having very short TTL
on secrets.

• Don’t underestimate complexity in recovering from
failures in running state

• Focus on business logic! (Always with reasonable
abstractions)
get '/' do
begin
client = Mysql2::Client.new(:host =>
mysqladdr, :username =>
mysqlvars.data[:username], :password =>
mysqlvars.data[:password])
mysqlstatus = client.query("SHOW STATUS")
rescue
puts "Asking for new credentials"
mysqlvars = getmysqlcreds(localvault)
client = Mysql2::Client.new(:host =>
mysqladdr, :username =>
mysqlvars.data[:username], :password =>
mysqlvars.data[:password])
mysqlstatus = client.query("SHOW STATUS")
end
##
# Returns an S3 client
protected
def awsClient
awscreds = self.awsCreds
begin
s3 =
Aws::S3::Resource.new(access_key_id:
awscreds[:access_key], secret_access_key:
awscreds[:secret_key], region: 'us-east-1')
return s3
rescue
awscreds = self.awsCreds
s3 =
Aws::S3::Resource.new(access_key_id:
awscreds[:access_key], secret_access_key:
awscreds[:secret_key], region: 'us-east-1')
return s3
end
end
3. Are you supposed to provide credentials / configuration to the application owner? Do
application teams chuck releases over the fence to you?

J. Developer
Friction-less alternatives
• Consul-template

• Great for PKI & Existing servers (see https://www.yet.org/
2018/10/vault-pki/ by Sebastién Braun)

• Envconsul

• Similar pattern, but with environment variables
##
# Wait for lock to be acquired before returning form
data
sessionid = Diplomat::Session.create({:hostname =>
"server1", :ipaddress => "4.4.4.4"})
lock_acquired = Diplomat::Lock.wait_to_acquire("/
key/to/lock", sessionid)
##
# Set a lock for the record that is about to
start being modified
sessionid = Diplomat::Session.create({:Node
=> "consul.service.consul", :Name =>
"locking session"})
lock_acquired = Diplomat::Lock.acquire("/
jamo/locks/#{docid}", sessionid)
# Do stuff
[…]
# Release lock
Diplomat::Lock.release("/jamo/locks/
#{docid}", sessionid )
Load balancing
• Fabio is awesome! I don’t even consider it’s there, it just
works!

• Not the only great pattern, see https://www.hashicorp.com/
resources/favorite-consul-customer-success-story
Not used in this project but
really cool….
• K/V Store

• Blocking queries

• Prepared queries

• Leader election
Don’t do this at home…
##
# Get Docker Hub Webhook
post '/dockerhubwebhook' do
callback = request.body.read
docker = JSON.parse(callback)
puts docker['push_data']['tag']
if docker['push_data']['tag'] != 'latest'
puts 'Deploying new Jamo Version' + docker['push_data']['tag']
nomadjob = erb :jsonnomad, :locals => { :tag =>
docker['push_data']['tag'] }
Nomad.job.create(nomadjob)
end
end
So what was
accomplished?
• 100% of the code is business code (alright, minus the
HTTP interface). Decoupled logical objects, from HTTP API,
from presentation layer that can be maintained individually

• Data encrypted at rest and in transit

• All credentials are ephemeral / short lived

• Microservices loosely coupled, discovered via Consul

• Cloud agnostic
What’s next?
• Already have a scheduler, stop running long task on
runtime and just schedule things! (I can get Nomad tokens
from the Vault API)

• Stop TLS Nightmare / port forwarding. Have Consul
Connect handle that
Stop TLS nightmare
▪ Service Access Graph
▪ Certificate Authority (built-in, Vault, Others)
▪ Application Integration
Thank you

Contenu connexe

Tendances

Tendances (20)

Managing secrets at scale
Managing secrets at scaleManaging secrets at scale
Managing secrets at scale
 
Secret Management with Hashicorp Vault and Consul on Kubernetes
Secret Management with Hashicorp Vault and Consul on KubernetesSecret Management with Hashicorp Vault and Consul on Kubernetes
Secret Management with Hashicorp Vault and Consul on Kubernetes
 
Secret Management with Hashicorp’s Vault
Secret Management with Hashicorp’s VaultSecret Management with Hashicorp’s Vault
Secret Management with Hashicorp’s Vault
 
Vault
VaultVault
Vault
 
Vault - Secret and Key Management
Vault - Secret and Key ManagementVault - Secret and Key Management
Vault - Secret and Key Management
 
HashiCorp's Vault - The Examples
HashiCorp's Vault - The ExamplesHashiCorp's Vault - The Examples
HashiCorp's Vault - The Examples
 
Keeping a Secret with HashiCorp Vault
Keeping a Secret with HashiCorp VaultKeeping a Secret with HashiCorp Vault
Keeping a Secret with HashiCorp Vault
 
Using Vault to decouple MySQL Secrets
Using Vault to decouple MySQL SecretsUsing Vault to decouple MySQL Secrets
Using Vault to decouple MySQL Secrets
 
Vault 101
Vault 101Vault 101
Vault 101
 
Introduction to vault
Introduction to vaultIntroduction to vault
Introduction to vault
 
Various Types of OpenSSL Commands and Keytool
Various Types of OpenSSL Commands and KeytoolVarious Types of OpenSSL Commands and Keytool
Various Types of OpenSSL Commands and Keytool
 
Vault
VaultVault
Vault
 
Types of ssl commands and keytool
Types of ssl commands and keytoolTypes of ssl commands and keytool
Types of ssl commands and keytool
 
Dynamic Database Credentials: Security Contingency Planning
Dynamic Database Credentials: Security Contingency PlanningDynamic Database Credentials: Security Contingency Planning
Dynamic Database Credentials: Security Contingency Planning
 
Using Vault for your Nodejs Secrets
Using Vault for your Nodejs SecretsUsing Vault for your Nodejs Secrets
Using Vault for your Nodejs Secrets
 
Hashicorp Vault: Open Source Secrets Management at #OPEN18
Hashicorp Vault: Open Source Secrets Management at #OPEN18Hashicorp Vault: Open Source Secrets Management at #OPEN18
Hashicorp Vault: Open Source Secrets Management at #OPEN18
 
6 Months Sailing with Docker in Production
6 Months Sailing with Docker in Production 6 Months Sailing with Docker in Production
6 Months Sailing with Docker in Production
 
DevOpsDays - DevOps: Security 干我何事?
DevOpsDays - DevOps: Security 干我何事?DevOpsDays - DevOps: Security 干我何事?
DevOpsDays - DevOps: Security 干我何事?
 
Vault 1.1: Secret Caching with Vault Agent and Other New Features
Vault 1.1: Secret Caching with Vault Agent and Other New FeaturesVault 1.1: Secret Caching with Vault Agent and Other New Features
Vault 1.1: Secret Caching with Vault Agent and Other New Features
 
Vault 1.0: How to Auto-Unseal and Other New Features
Vault 1.0: How to Auto-Unseal and Other New FeaturesVault 1.0: How to Auto-Unseal and Other New Features
Vault 1.0: How to Auto-Unseal and Other New Features
 

Similaire à A tale of application development

Getting Started with Couchbase Ruby
Getting Started with Couchbase RubyGetting Started with Couchbase Ruby
Getting Started with Couchbase Ruby
Sergey Avseyev
 
Amazon Cloud Services and Zend Framework
Amazon Cloud Services and Zend FrameworkAmazon Cloud Services and Zend Framework
Amazon Cloud Services and Zend Framework
Shahar Evron
 

Similaire à A tale of application development (20)

AWS Java SDK @ scale
AWS Java SDK @ scaleAWS Java SDK @ scale
AWS Java SDK @ scale
 
Immutable Deployments with AWS CloudFormation and AWS Lambda
Immutable Deployments with AWS CloudFormation and AWS LambdaImmutable Deployments with AWS CloudFormation and AWS Lambda
Immutable Deployments with AWS CloudFormation and AWS Lambda
 
Codetainer: a Docker-based browser code 'sandbox'
Codetainer: a Docker-based browser code 'sandbox'Codetainer: a Docker-based browser code 'sandbox'
Codetainer: a Docker-based browser code 'sandbox'
 
Pdf tech deep dive 42 paris
Pdf tech deep dive 42 parisPdf tech deep dive 42 paris
Pdf tech deep dive 42 paris
 
Information security programming in ruby
Information security programming in rubyInformation security programming in ruby
Information security programming in ruby
 
Hadoop Security Now and Future
Hadoop Security Now and FutureHadoop Security Now and Future
Hadoop Security Now and Future
 
Introduction to InSpec and 1.0 release update
Introduction to InSpec and 1.0 release updateIntroduction to InSpec and 1.0 release update
Introduction to InSpec and 1.0 release update
 
Static Typing in Vault
Static Typing in VaultStatic Typing in Vault
Static Typing in Vault
 
Symfony finally swiped right on envvars
Symfony finally swiped right on envvarsSymfony finally swiped right on envvars
Symfony finally swiped right on envvars
 
Working in the multi-cloud with libcloud
Working in the multi-cloud with libcloudWorking in the multi-cloud with libcloud
Working in the multi-cloud with libcloud
 
Amazon Web Services for PHP Developers
Amazon Web Services for PHP DevelopersAmazon Web Services for PHP Developers
Amazon Web Services for PHP Developers
 
The Dynamic Duo of Puppet and Vault tame SSL Certificates - Puppet Camps Cent...
The Dynamic Duo of Puppet and Vault tame SSL Certificates - Puppet Camps Cent...The Dynamic Duo of Puppet and Vault tame SSL Certificates - Puppet Camps Cent...
The Dynamic Duo of Puppet and Vault tame SSL Certificates - Puppet Camps Cent...
 
The Dynamic Duo of Puppet and Vault tame SSL Certificates, Nick Maludy
The Dynamic Duo of Puppet and Vault tame SSL Certificates, Nick MaludyThe Dynamic Duo of Puppet and Vault tame SSL Certificates, Nick Maludy
The Dynamic Duo of Puppet and Vault tame SSL Certificates, Nick Maludy
 
Alex Casalboni - Configuration management and service discovery - Codemotion ...
Alex Casalboni - Configuration management and service discovery - Codemotion ...Alex Casalboni - Configuration management and service discovery - Codemotion ...
Alex Casalboni - Configuration management and service discovery - Codemotion ...
 
Getting Started with Couchbase Ruby
Getting Started with Couchbase RubyGetting Started with Couchbase Ruby
Getting Started with Couchbase Ruby
 
Cloud Security At Netflix, October 2013
Cloud Security At Netflix, October 2013Cloud Security At Netflix, October 2013
Cloud Security At Netflix, October 2013
 
Amazon Cloud Services and Zend Framework
Amazon Cloud Services and Zend FrameworkAmazon Cloud Services and Zend Framework
Amazon Cloud Services and Zend Framework
 
Security and Encryption on iOS
Security and Encryption on iOSSecurity and Encryption on iOS
Security and Encryption on iOS
 
HashiConf Digital 2020: HashiCorp Vault configuration as code via HashiCorp T...
HashiConf Digital 2020: HashiCorp Vault configuration as code via HashiCorp T...HashiConf Digital 2020: HashiCorp Vault configuration as code via HashiCorp T...
HashiConf Digital 2020: HashiCorp Vault configuration as code via HashiCorp T...
 
Node.js Patterns for Discerning Developers
Node.js Patterns for Discerning DevelopersNode.js Patterns for Discerning Developers
Node.js Patterns for Discerning Developers
 

Dernier

The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 

Dernier (20)

Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 

A tale of application development

  • 1. A tale of application development Nicolas Corrarello - HashiConf 2018
  • 2.
  • 3.
  • 4. Disclaimer • Argentinian by birth, Italian by blood • English is not my first language • I’m not a developer • Don’t criticise my programming language choices, I basically had Christmas week last year to write this :D. Client libraries available in lots of languages (https:// www.vaultproject.io/api/libraries.html / https:// www.consul.io/api/libraries-and-sdks.html)
  • 5. (What’s the story) Morning glory? • Spending lots of time authoring documents • Write an app to automate the process • Don’t have time to maintain it so make it as readable as possible, zero touch maintenance as possible
  • 6.
  • 7. 1. IMPORTANT - READ BEFORE SIGNING. Are there Consul / Vault Tokens readily available on the system (see Nomad or the myriad of Vault Authentication Methods available, then use Vault to get a Consul token :D) Are you provisioning this yourself or someone else doing it for you? J. Developer
  • 8. AbstractionsNot HashiCorp specific (duh) “The best thing to come out of Java” - Variety
  • 9. Abstract your code from lock-in Minimal effort, greater portability Using existing libraries (datamapper, dal.py, hibernate) may help. Also may make things way more complicated (in certain cases, see Rails)
  • 10. require 'json' require './models/vault' ## # Persistence layer. Implemented in S3, but the interface is generic enough so it can be migrated to other platforms. class Persistence ## # Create a persistence layer. Initialises a HashiCorp Vault session and obtains credentials from it using the existing abstraction. def initialize @vault = LocalVault.new @awscreds = self.awsClient @data = {} end ## # Store something in the persistence layer. Uses namespace (which translates to an s3 bucket), and a simple key/value. Object is stored as JSON. def Store(key,value,namespace) s3 = @awscreds begin obj = s3.bucket(namespace).object(key) obj.put(body: value.to_json) return true, nil rescue return false, "Error persisting object" end end ## # Retrieve something from the persistence layer. Uses namespace (which translates to an s3 bucket), and a key. Returns the full object in it's native format. def Retrieve(key,namespace) […] end ## # List objects by "glob". Returns a list of objects available. In this case as S3 is somewhat hierarchical, glob would be 'customers' to to retrieve customer objets, or 'pov' to return POV documents. def List(glob,namespace) […] end end Initialize the object Store something
  • 11.
  • 12. Let’s talk secrets… lots… • AWS API Credentials for Deployment • AWS Credentials for the application to read/write the bucket • JWT Issuer / Secret • Google API Keys for logins • Crypto
  • 13. Crypto? Not the kind that your coworker keeps talking about, you know who, the self named Bitcoin expert….
  • 14.
  • 15.
  • 16.
  • 17.
  • 18. ## # Creates a customer in the object, requires an sfdcAccountId (unique) and a customer name. Information is encrypted and persisted, but kept in plaintext in memory. def Create(sfdcAccountId,name,domain) unless sfdcAccountId.nil? || name.nil? begin cyphertext = @vault.logical.write("transit/encrypt/#{key}", plaintext: Base64.encode64(value).gsub('n',''), context: Base64.encode64(context).gsub('n','')) cyphername = cyphertext.data[:ciphertext] rescue return false, "Error encrypting data" end customer = { :id => sfdcAccountId, :name => cyphername, :contacts => nil, } customerm = { :id => sfdcAccountId, :name => name, :contacts => nil, } key = 'customers/' + sfdcAccountId + '/customer.json' begin @persistence.Store(key,customer,"jamo-#{domain}") if @data[domain] == nil @data[domain] = {} end @data[domain][sfdcAccountId] = customerm return true, nil rescue return false, "error persisting customer" end end end Encrypt a string Persist it
  • 19. ## # Creates a customer in the object, requires an sfdcAccountId (unique) and a customer name. Information is encrypted and persisted, but kept in plaintext in memory. def Create(sfdcAccountId,name,domain) unless sfdcAccountId.nil? || name.nil? begin cyphername = @vault.encrypt(name,'foo', @jwt_secret) rescue return false, "Error encrypting data" end customer = { :id => sfdcAccountId, :name => cyphername, :contacts => nil, } customerm = { :id => sfdcAccountId, :name => name, :contacts => nil, } key = 'customers/' + sfdcAccountId + '/customer.json' begin @persistence.Store(key,customer,"jamo-#{domain}") if @data[domain] == nil @data[domain] = {} end @data[domain][sfdcAccountId] = customerm return true, nil rescue return false, "error persisting customer" end end end ## # Encrypt a value with a defined key and context for symmetric encryption. def encrypt(value,key,context) cyphertext = @vault.logical.write("transit/encrypt/#{key}", plaintext: Base64.encode64(value).gsub('n',''), context: Base64.encode64(context).gsub('n','')) return cyphertext.data[:ciphertext] end
  • 20. require 'vault' require 'base64' ## # This object abstract the crypto / secrets management functions of HashiCorp Vault class LocalVault def initialize @vault = Vault::Client.new end ## # Obtain a set of credentials to access the S3 Bucket used by the persistence layer def getAwsCreds credentials = @vault.logical.read("aws/creds/s3-bucket") return credentials end def getGoogleCredentials credentials = @vault.logical.read("secret/gsuite") return credentials end def getJWTsecret jwtsecret = @vault.logical.read("secret/JWT") return jwtsecret end ## # Encrypt a value with a defined key and context for asymmetric encryption. def encrypt(value,key,context) cyphertext = @vault.logical.write("transit/encrypt/#{key}", plaintext: Base64.encode64(value).gsub('n',''), context: Base64.encode64(context).gsub('n','')) return cyphertext.data[:ciphertext] end ## # Decrypt a value with a defined key and context. def decrypt(value,key,context) plaintext = @vault.logical.write("transit/decrypt/#{key}", ciphertext: value, context: Base64.encode64(context).gsub('n','')) return Base64.decode64(plaintext.data[:plaintext]).gsub('n','') end end Initialize the object Get dynamically generated AWS Tokens Get static GSuite API Keys Get info to sign JWTs Encrypt a string Decrypt a string
  • 21. 2. How is this application getting its Vault / Nomad / Consul token? How is this application supposed to get it’s runtime configuration? J. Developer
  • 22. group "webs" { count = 1 task "jamo" { vault { policies = ["jamo"] change_mode = "signal" change_signal = "SIGHUP" } driver = "docker" env { VAULT_ADDR = “https://vault.service.consul:8200" MEMCACHE_ADDR = "memcache.service.consul:11211" } [...] } Let Nomad get me a Token What to do if that token is rotated Environment variables. DNS resolved by Consul
  • 23. What if I’m using K8s? • The good option, is using the K8s authentication method and doing a login in the Dockerfile so the pod gets it VAULT_TOKEN and then consume secrets • The not bad option, is using consul-template to deploy a helm chart with the secrets, doing authentication in the pipeline that deploys it (see https://www.hashicorp.com/ resources/how-to-share-secrets-pipeline or http:// nicolas.corrarello.com/general/vault/security/ci/ 2017/04/23/Reading-Vault-Secrets-in-your-Jenkins- pipeline.html)
  • 24. Perfect > Good Enough • Getting credentials out of Vault is super easy, so it’s easy to fall on the trap of having very short TTL on secrets. • Don’t underestimate complexity in recovering from failures in running state • Focus on business logic! (Always with reasonable abstractions)
  • 25. get '/' do begin client = Mysql2::Client.new(:host => mysqladdr, :username => mysqlvars.data[:username], :password => mysqlvars.data[:password]) mysqlstatus = client.query("SHOW STATUS") rescue puts "Asking for new credentials" mysqlvars = getmysqlcreds(localvault) client = Mysql2::Client.new(:host => mysqladdr, :username => mysqlvars.data[:username], :password => mysqlvars.data[:password]) mysqlstatus = client.query("SHOW STATUS") end ## # Returns an S3 client protected def awsClient awscreds = self.awsCreds begin s3 = Aws::S3::Resource.new(access_key_id: awscreds[:access_key], secret_access_key: awscreds[:secret_key], region: 'us-east-1') return s3 rescue awscreds = self.awsCreds s3 = Aws::S3::Resource.new(access_key_id: awscreds[:access_key], secret_access_key: awscreds[:secret_key], region: 'us-east-1') return s3 end end
  • 26. 3. Are you supposed to provide credentials / configuration to the application owner? Do application teams chuck releases over the fence to you? J. Developer
  • 27. Friction-less alternatives • Consul-template • Great for PKI & Existing servers (see https://www.yet.org/ 2018/10/vault-pki/ by Sebastién Braun) • Envconsul • Similar pattern, but with environment variables
  • 28.
  • 29. ## # Wait for lock to be acquired before returning form data sessionid = Diplomat::Session.create({:hostname => "server1", :ipaddress => "4.4.4.4"}) lock_acquired = Diplomat::Lock.wait_to_acquire("/ key/to/lock", sessionid) ## # Set a lock for the record that is about to start being modified sessionid = Diplomat::Session.create({:Node => "consul.service.consul", :Name => "locking session"}) lock_acquired = Diplomat::Lock.acquire("/ jamo/locks/#{docid}", sessionid) # Do stuff […] # Release lock Diplomat::Lock.release("/jamo/locks/ #{docid}", sessionid )
  • 30. Load balancing • Fabio is awesome! I don’t even consider it’s there, it just works! • Not the only great pattern, see https://www.hashicorp.com/ resources/favorite-consul-customer-success-story
  • 31. Not used in this project but really cool…. • K/V Store • Blocking queries • Prepared queries • Leader election
  • 32. Don’t do this at home… ## # Get Docker Hub Webhook post '/dockerhubwebhook' do callback = request.body.read docker = JSON.parse(callback) puts docker['push_data']['tag'] if docker['push_data']['tag'] != 'latest' puts 'Deploying new Jamo Version' + docker['push_data']['tag'] nomadjob = erb :jsonnomad, :locals => { :tag => docker['push_data']['tag'] } Nomad.job.create(nomadjob) end end
  • 33. So what was accomplished? • 100% of the code is business code (alright, minus the HTTP interface). Decoupled logical objects, from HTTP API, from presentation layer that can be maintained individually • Data encrypted at rest and in transit • All credentials are ephemeral / short lived • Microservices loosely coupled, discovered via Consul • Cloud agnostic
  • 34. What’s next? • Already have a scheduler, stop running long task on runtime and just schedule things! (I can get Nomad tokens from the Vault API) • Stop TLS Nightmare / port forwarding. Have Consul Connect handle that
  • 35. Stop TLS nightmare ▪ Service Access Graph ▪ Certificate Authority (built-in, Vault, Others) ▪ Application Integration