Introduction
Until recently, it wasn't straightforward for applications running on OpenShift to authenticate with Vault in order to retrieve secrets. It involved a complex orchestration workflow, involving multiple actors (including a Vault Controller and init container as outlined in this post) in order to retrieve secrets stored in Vault.
In this post, we demonstrate a simpler approach for applications to authenticate with Vault, in a way more native to Kubernetes. This is made possible using by using the Kubernetes authentication method that has been added (since Vault 0.8.3), to integrate Vault directly with Kubernetes.
Additionally, in this blog post, we demonstrate how to run Vault on OpenShift. We then configure Vault to use the Kubernetes Auth Method that can be used by client applications to authenticate with Vault. Finally, we demonstrate running a Spring Boot application client on OpenShift that connects to Vault using the Kubernetes Auth Method to retrieve secrets. It makes use of Spring Cloud Vault Config that supports this method out of the box.
Why Vault
Vault is a secret store software created by HashiCorp. With Vault you have a central place to manage external secret properties for your applications across all environments. Vault can manage static and dynamic secrets such as username/password and manage credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS and more. Vault can run in the cloud and also encrypts credentials at rest. Additionally, no OpenShift cluster admin can see the credentials, plus in Vault you can create sharded master keys so that no Vault admin can, by themselves, un-encrypt the credentials.
Kubernetes Auth Method
The Kubernetes authentication method can be used to authenticate with Vault using a Kubernetes Service Account Token. The token for a pod’s service account is automatically mounted within a pod at /var/run/secrets/kubernetes.io/serviceaccount/token
and is sent to Vault for authentication. Vault is configured with a service account that has permissions to access the TokenReview API. This service account can then be used to make authenticated calls to Kubernetes to verify tokens of the service accounts of pods that want to connect to Vault to get secrets.
This is depicted in the diagram below.
- Use the JWT token for a pod’s service account to authenticate with Vault. A file containing the token is automatically mounted at
/var/run/secrets/kubernetes.io/serviceaccount/token
. - Vault sends the service account token of a pod that wants to access it to the OpenShift master API for authentication. During this call, Vault uses the token of the service account with token reviewer permissions to authenticate with the master API. If the service account token of the pod is successfully authenticated, then a Vault token correctly scoped is returned to the pod.
- The Vault token is subsequently used to retrieve the secrets from Vault. The Vault token is also short-lived: It's the application’s responsibility to renew it if new secrets need to be retrieved later.
Below are the steps to install Vault, enable the Kubernetes authentication method, and configure a Spring Boot application to authenticate with Vault using this method and retrieve secrets.
A. Install Vault
1. Requirements
You need the Vault CLI installed on your machine.<
2. Clone the repository below containing a Vault aware Spring Boot application that is using the Vault Kubernetes authentication method.
git clone https://github.com/raffaelespazzoli/credscontroller
3. Create a new project
oc new-project vault-controller
4. Install Vault.
Note: You need a cluster administrator role to execute the first step below. As you can see, we are adding the default serviceaccount to the anyuid SCC as the application needs to run as user root in the container. We then create a configmap that contains configuration data for Vault, and create the OpenShift service, deployment config, persistent volume claim that are defined in vault.yaml, and finally expose a reencrypt route on port 8200.
oc adm policy add-scc-to-user anyuid -z default
oc create configmap vault-config --from-file=vault-config=./openshift/vault-config.json
oc create -f ./openshift/vault.yaml
oc create route reencrypt vault --port=8200 --service=vault
5. Initialize Vault
export VAULT_ADDR=https://`oc get route | grep -m1 vault | awk '{print $2}'`
vault init -tls-skip-verify -key-shares=1 -key-threshold=1
Save the generated key and token.
6. Unseal Vault
You have to repeat this step every time you start Vault. Don't try to automate this step, this is manual by design. You can make the initial seal stronger by increasing the number of keys.
We will assume that the KEYS environment variable contains the key necessary to unseal Vault and that ROOT_TOKEN
contains the root token.
For example:
export KEYS=tjgv5s7M4CtMeUz92dU9jV3EudPawgNz6euEnciZoFs=
export ROOT_TOKEN=1487cceb-f05d-63be-3e24-d08e429c760c
vault unseal -tls-skip-verify $KEYS
B. Setup Kubernetes Vault auth backend
1. Create a token reviewer service account called vault-auth
in the vault-controller project
oc create sa vault-auth
2. Give the vault-auth service account permissions to create tokenreviews.authentication.k8s.io
at the cluster scope
oc adm policy add-cluster-role-to-user system:auth-delegator system:serviceaccount:vault-controller:vault-auth
3. Get the token for the vault-auth
service account
reviewer_service_account_jwt=$(oc serviceaccounts get-token vault-auth)
4. Get the internal OpenShift Certificate Authority from the pod running Vault.
pod=`oc get pods -n $(oc project -q) | grep vault | awk '{print $1}'`
oc exec $pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt >> ca.crt
5. Setup the Kubernetes auth backend
You need to have a Kubernetes mount in Vault per Kubernetes/OpenShift cluster you want it to connect to. Enable the Kubernetes authentication backend. If you don't use the -path
variable, then it defaults to the path /auth/kubernetes
. If you want to mount other Kubernetes backends, use the-path=
variable... i.e. a -path=kubernetes/cluster1
will create a path /auth/kubernetes/cluster1
export VAULT_TOKEN=$ROOT_TOKEN
vault auth-enable -tls-skip-verify kubernetes
6. Configure the Kubernetes authentication method to use the vault-auth
service account token to authenticate with the OpenShift master API, in order to verify other service account tokens of pods that want to access Vault.
vault write -tls-skip-verify auth/kubernetes/config token_reviewer_jwt=$reviewer_service_account_jwt kubernetes_host=<server-url> kubernetes_ca_cert=@ca.crt
rm ca.crt
7. A sample policy file called spring-native-example.hcl
has already been provided to you that allows a token to get a secret from the generic secret backend for the client role.
path "secret/application" {
capabilities = ["read", "list"]
}
path "secret/spring-native-example" {
capabilities = ["read", "list"]
}
8. Create the policy
vault policy-write -tls-skip-verify spring-native-example ./examples/spring-native-example/spring-native-example.hcl
9. Authorization with this backend is role based. Before a token can be used to login, it must be configured in a role.
vault write -tls-skip-verify auth/kubernetes/role/spring-native-example bound_service_account_names=default bound_service_account_namespaces='*' policies=spring-native-example ttl=2h
This authorizes all default service accounts (and pods running with the default service accounts) in all namespaces and gives them the spring-native-example policy, which allows them to read secrets from Vault.
10. Confirm that the policy enables secret creation only under the path secret/spring-native-example
export VAULT_TOKEN=$ROOT_TOKEN
vault write -tls-skip-verify secret/spring-native-example password=pwd
11. Verify that the backend can now be used to authenticate Vault requests using the default service account.
a. Get the default service account token from the default namespace
default_account_token=$(oc serviceaccounts get-token default -n default)
b. Log in to the Kubernetes auth backend using the service account token. The output returns a Vault token that can be subsequently used to read secrets.
vault write -tls-skip-verify auth/kubernetes/login role=spring-native-example jwt=${default_account_token}
Key Value
--- -----
token 74603479-607d-4ab8-a406-d0456d9f3d65
token_accessor 4893b0a1-f42a-bfd8-cd9c-c14b9bdb6095
token_duration 1h0m0s
token_renewable true
token_policies [default spring-native-example]
token_meta_role "spring-native-example"
token_meta_service_account_name "default"
token_meta_service_account_namespace "default"
token_meta_service_account_secret_name "default-token-fndln"
token_meta_service_account_uid "aaf6c23c-b04a-11e7-9aea-0245c85cf1cc"
C. Configure Spring Boot Client application to Use the Kubernetes Auth Method to Authenticate with Vault
In this example, we are using Spring Cloud Vault in order to bind properties based on secrets. You need to have the following two prerequisites in your project.
1. Add the dependency on Spring Cloud Vault
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-vault-dependencies</artifactId>
<version>1.1.0.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
</dependencies>
2. Configure Vault information in the bootstrap.yml
file of the project
spring.application.name: spring-native-example
spring.cloud.vault:
host: ${vault.host:localhost}
port: ${vault.port:8200}
scheme: https
authentication: KUBERNETES
kubernetes:
role: spring-native-example
service-account-token-file: /var/run/secrets/kubernetes.io/serviceaccount/token
The Kubernetes authentication mechanism is role-based and the role is bound to a service account name and namespace.
- role sets the Role.
- service-account-token-file sets the location of the file containing the Kubernetes Service Account Token. Defaults to
/var/run/secrets/kubernetes.io/serviceaccount/token
.
Deploy the Spring Boot application on OpenShift
1. Create the project
oc new-project spring-native-example
oc new-build registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift~https://github.com/raffaelespazzoli/credscontroller --context-dir=examples/spring-native-example --name spring-native-example
2. Join the project with vault-controller
to be able to call the Vault service
Note: This is only required when using the multitenant SDN plugin
oc adm pod-network join-projects --to vault-controller spring-native-example
3. Deploy the spring-native-example
application
oc create -f ./examples/spring-native-example/spring-native-example.yaml
oc expose svc spring-native-example
4. Now you should be able to call a service that returns the secret
export SPRING_EXAMPLE_ADDR=http://`oc get route | grep -m1 spring | awk '{print $2}'`
curl $SPRING_EXAMPLE_ADDR/secret
Summary
In this post, we saw how to run Vault on OpenShift and configure it to use the Kubernetes authentication method. We also showed how to deploy a reference Spring Boot application that makes use of this authentication method to authenticate with Vault and bind application properties to secrets stored in Vault.
Sugli autori
Raffaele is a full-stack enterprise architect with 20+ years of experience. Raffaele started his career in Italy as a Java Architect then gradually moved to Integration Architect and then Enterprise Architect. Later he moved to the United States to eventually become an OpenShift Architect for Red Hat consulting services, acquiring, in the process, knowledge of the infrastructure side of IT.
Currently Raffaele covers a consulting position of cross-portfolio application architect with a focus on OpenShift. Most of his career Raffaele worked with large financial institutions allowing him to acquire an understanding of enterprise processes and security and compliance requirements of large enterprise customers.
Raffaele has become part of the CNCF TAG Storage and contributed to the Cloud Native Disaster Recovery whitepaper.
Recently Raffaele has been focusing on how to improve the developer experience by implementing internal development platforms (IDP).
Altri risultati simili a questo
Ricerca per canale
Automazione
Novità sull'automazione IT di tecnologie, team e ambienti
Intelligenza artificiale
Aggiornamenti sulle piattaforme che consentono alle aziende di eseguire carichi di lavoro IA ovunque
Hybrid cloud open source
Scopri come affrontare il futuro in modo più agile grazie al cloud ibrido
Sicurezza
Le ultime novità sulle nostre soluzioni per ridurre i rischi nelle tecnologie e negli ambienti
Edge computing
Aggiornamenti sulle piattaforme che semplificano l'operatività edge
Infrastruttura
Le ultime novità sulla piattaforma Linux aziendale leader a livello mondiale
Applicazioni
Approfondimenti sulle nostre soluzioni alle sfide applicative più difficili
Serie originali
Raccontiamo le interessanti storie di leader e creatori di tecnologie pensate per le aziende
Prodotti
- Red Hat Enterprise Linux
- Red Hat OpenShift
- Red Hat Ansible Automation Platform
- Servizi cloud
- Scopri tutti i prodotti
Strumenti
- Formazione e certificazioni
- Il mio account
- Supporto clienti
- Risorse per sviluppatori
- Trova un partner
- Red Hat Ecosystem Catalog
- Calcola il valore delle soluzioni Red Hat
- Documentazione
Prova, acquista, vendi
Comunica
- Contatta l'ufficio vendite
- Contatta l'assistenza clienti
- Contatta un esperto della formazione
- Social media
Informazioni su Red Hat
Red Hat è leader mondiale nella fornitura di soluzioni open source per le aziende, tra cui Linux, Kubernetes, container e soluzioni cloud. Le nostre soluzioni open source, rese sicure per un uso aziendale, consentono di operare su più piattaforme e ambienti, dal datacenter centrale all'edge della rete.
Seleziona la tua lingua
Red Hat legal and privacy links
- Informazioni su Red Hat
- Opportunità di lavoro
- Eventi
- Sedi
- Contattaci
- Blog di Red Hat
- Diversità, equità e inclusione
- Cool Stuff Store
- Red Hat Summit