<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Igor Zhivilo</title>
    <description>The latest articles on DEV Community by Igor Zhivilo (@warolv).</description>
    <link>https://dev.to/warolv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F705860%2F1be21bc5-7605-4dc4-aad8-e52c413a5130.jpeg</url>
      <title>DEV Community: Igor Zhivilo</title>
      <link>https://dev.to/warolv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/warolv"/>
    <language>en</language>
    <item>
      <title>Scheduled backup of Vault secrets with Jenkins on Kubernetes</title>
      <dc:creator>Igor Zhivilo</dc:creator>
      <pubDate>Tue, 14 Sep 2021 12:11:32 +0000</pubDate>
      <link>https://dev.to/warolv/scheduled-backup-of-vault-secrets-with-jenkins-on-kubernetes-15h6</link>
      <guid>https://dev.to/warolv/scheduled-backup-of-vault-secrets-with-jenkins-on-kubernetes-15h6</guid>
      <description>&lt;p&gt;I am a DevOps engineer at Cloudify.co and I will share in this post my experience related to automation of Vault backup creation using Jenkins scheduled job and simple python script which I built to create dump of vault secrets.&lt;/p&gt;

&lt;p&gt;Let's start.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is HashiCorp's Vault?
&lt;/h2&gt;

&lt;p&gt;Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret while providing tight access control and recording a detailed audit log.&lt;br&gt;
&lt;a href="https://www.vaultproject.io/"&gt;https://www.vaultproject.io/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vault Installed&lt;/li&gt;
&lt;li&gt;Jenkins Installed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  My Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;EKS Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Vault runs on EKS cluster&lt;/li&gt;
&lt;li&gt;Jenkins runs on EKS cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read in this tutorial how to run Jenkins on EKS cluster:&lt;br&gt;
&lt;a href="https://igorzhivilo.com/jenkins/ci-cd-future-k8s-jenkins"&gt;https://igorzhivilo.com/jenkins/ci-cd-future-k8s-jenkins&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What you will learn from this post?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to use &lt;a href="https://github.com/hvac/hvac"&gt;python hvac&lt;/a&gt; library for authentication with Vault programmatically and backup vault secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What is AppRole authentication mechanism in Vault and how to enable/create it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to create scheduled backup for Vault secrets with Jenkins pipeline on k8s.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  AppRole authentication method in Vault
&lt;/h3&gt;

&lt;p&gt;How can an application programmatically request a token so that it can read secrets from Vault?&lt;/p&gt;

&lt;p&gt;Using the AppRole which is an authentication mechanism within Vault to allow machines or apps to acquire a token to interact with Vault and using the policies you can set access limitations for your app.&lt;/p&gt;

&lt;p&gt;It uses RoleID and SecretID for login.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DGnmC5X4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qojtqpq8fektj08fs9k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DGnmC5X4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qojtqpq8fektj08fs9k3.png" alt="role-secret"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Create AppRole and policy for Jenkins
&lt;/h3&gt;

&lt;p&gt;I explained how to do it in detail in my blog post: &lt;a href="https://igorzhivilo.com/jenkins/how-to-read-vault-secrets-from-declarative-pipeline"&gt;https://igorzhivilo.com/jenkins/how-to-read-vault-secrets-from-declarative-pipeline&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you applied everything I wrote in this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enabled approle in vault &lt;/li&gt;
&lt;li&gt;v2 kv secrets engine enabled&lt;/li&gt;
&lt;li&gt;applied all needed policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;eventually you will get &lt;strong&gt;role_id&lt;/strong&gt; and &lt;strong&gt;secret_id&lt;/strong&gt; which will be used programmatically with 'python hvac'.&lt;/p&gt;

&lt;p&gt;Another way for 3rd step (apply all needed policies) is to create policy using Vault's UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; vault svc/vault 8200
Forwarding from 127.0.0.1:8200 -&amp;gt; 8200
Forwarding from &lt;span class="o"&gt;[&lt;/span&gt;::1]:8200 -&amp;gt; 8200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Go to policy tab -&amp;gt; Create ACL Policy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2iP8hQN6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqb2j5cpc2f3b2tekbmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2iP8hQN6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqb2j5cpc2f3b2tekbmi.png" alt="acl_policy"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;path &lt;span class="s2"&gt;"sys/auth/approle"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"sudo"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
path &lt;span class="s2"&gt;"sys/auth/approle/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
path &lt;span class="s2"&gt;"auth/approle/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
path &lt;span class="s2"&gt;"sys/policies/acl/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
path &lt;span class="s2"&gt;"secret/data/jenkins/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
path &lt;span class="s2"&gt;"secret/metadata/jenkins/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then run via vault CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vault write auth/approle/role/jenkins &lt;span class="nv"&gt;token_policies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jenkins &lt;span class="se"&gt;\&lt;/span&gt;
 &lt;span class="nv"&gt;token_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1h &lt;span class="nv"&gt;token_max_ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4h

&lt;span class="c"&gt;# Get RoleID and SecretID&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;vault &lt;span class="nb"&gt;read &lt;/span&gt;auth/approle/role/jenkins/role-id
&lt;span class="nv"&gt;$ &lt;/span&gt;vault write &lt;span class="nt"&gt;-f&lt;/span&gt; auth/approle/role/jenkins/secret-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test that you created correctly role_id/secret_id
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vault write auth/approle/login &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;role_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ROLE_ID &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;secret_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SECRET_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing authentication with vault using python hvac and appRole
&lt;/h2&gt;

&lt;p&gt;Simple python script to test auth with vault:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hvac&lt;/span&gt;

&lt;span class="n"&gt;VAULT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'http://vault.vault.svc.cluster.local:8200'&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hvac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VAULT_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;approle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;role_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;secret_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secret_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_authenticated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;first run 'pip install hvac'.&lt;/p&gt;

&lt;p&gt;I am running this script from pod with python container inside of my Kubernetes cluster. &lt;/p&gt;

&lt;p&gt;URL of vault in k8s cluster: '&lt;a href="http://vault.vault.svc.cluster.local:8200"&gt;http://vault.vault.svc.cluster.local:8200&lt;/a&gt;'&lt;/p&gt;

&lt;p&gt;You will see authentication error if authentication is failed, if you do, make sure you applied all the needed policies, enabled applrole, and generated properly role_id / secret_id.&lt;/p&gt;

&lt;p&gt;Validate role_id/secret_id is correct using vault CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vault write auth/approle/login &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;role_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOU_ROLE_ID &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;secret_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOU_SECRET_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get the list of secrets under 'jenkins' vault_prefix (CLI)
&lt;/h3&gt;

&lt;p&gt;In my case, &lt;strong&gt;vault_prefix&lt;/strong&gt; looks like: 'secret/data/jenkins' and all secrets stored under 'jenkins' prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vault kv list secret/jenkins

Keys
&lt;span class="nt"&gt;----&lt;/span&gt;
aws
git
web-app1
web-app2
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each key in list has additional subset of keys, for example 'aws' has access_key_id/secret_access_keys&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting the secrets list (python)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;secrets_list_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_secrets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'jenkins'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'The following keys are available under "jenkins" prefix: {keys}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;','&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secrets_list_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'keys'&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a permissions error on the secrets list, check you have access to &lt;strong&gt;metadata&lt;/strong&gt;, that what you should see in UI for 'jenkins policy':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;path &lt;span class="s2"&gt;"secret/metadata/jenkins/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't, add using the UI or vault CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tee &lt;/span&gt;jenkins-policy-metadata.hcl &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;"
  path "secret/metadata/jenkins/*" {
    capabilities = [ "read" ]
  }
&lt;/span&gt;&lt;span class="no"&gt;  EOF

&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;vault policy write jenkins jenkins-policy-metadata.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get a specific secret (python)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;secret_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'jenkins/aws'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a permission error, check you have access to &lt;strong&gt;data&lt;/strong&gt; in UI of Vault:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;path &lt;span class="s2"&gt;"secret/data/jenkins/*"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  capabilities &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"create"&lt;/span&gt;, &lt;span class="s2"&gt;"read"&lt;/span&gt;, &lt;span class="s2"&gt;"update"&lt;/span&gt;, &lt;span class="s2"&gt;"delete"&lt;/span&gt;, &lt;span class="s2"&gt;"list"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  VaultHandler
&lt;/h2&gt;

&lt;p&gt;I created VaultHandler which you can find &lt;a href="https://github.com/warolv/vault-backup"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/warolv/vault-backup"&gt;https://github.com/warolv/vault-backup&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  You can use it to:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Dump all your secrets as encrypted yaml/json files, or you can store it without encryption.&lt;/li&gt;
&lt;li&gt;Get a list of your secrets.&lt;/li&gt;
&lt;li&gt;Print all secrets nicely.&lt;/li&gt;
&lt;li&gt;Populate Vault from yaml/json dumps to a specific 'vault_prefix'.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, I think to extend it to use different auth methods, besides appRole, create CLI, to run it in the command line and much more :-)&lt;/p&gt;

&lt;p&gt;If the idea sounds interesting, &lt;strong&gt;add stars to the repo or clone it&lt;/strong&gt;, I will know this way you like the idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Jenkins scheduled job for daily vault backup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I am using &lt;em&gt;Vault Plugin&lt;/em&gt; in Jenkins &lt;a href="https://plugins.jenkins.io/hashicorp-vault-plugin"&gt;https://plugins.jenkins.io/hashicorp-vault-plugin&lt;/a&gt; to add secrets as env variables during job execution. Read more about how to integrate this plugin into jenkins here: &lt;a href="https://igorzhivilo.com/jenkins/how-to-read-vault-secrets-from-declarative-pipeline"&gt;https://igorzhivilo.com/jenkins/how-to-read-vault-secrets-from-declarative-pipeline&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;During job execution POD will be created with 2 contaienrs: awscli to use aws s3 utility, and push created encrypted dump to private s3 bucket (vault-backups), python to run VaultHandler.*&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;vaultUrl:&lt;/span&gt; &lt;span class="s2"&gt;"${VAULT_URL}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  &lt;span class="nl"&gt;vaultCredentialId:&lt;/span&gt; &lt;span class="s2"&gt;"vault-role-app"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;engineVersion:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s1"&gt;'secret/jenkins/aws'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;engineVersion:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'AWS_ACCESS_KEY_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'aws_access_key_id'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'AWS_SECRET_ACCESS_KEY'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'aws_secret_access_key'&lt;/span&gt;&lt;span class="o"&gt;]]],&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s1"&gt;'secret/jenkins/vault-backup'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;engineVersion:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;secretValues:&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'VAULT_ADDR'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'vault_url'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'ROLE_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'role_id'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'SECRET_ID'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'secret_id'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'VAULT_PREFIX'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'vault_prefix'&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;envVar:&lt;/span&gt; &lt;span class="s1"&gt;'DUMP_ENCRYPTION_PASSWORD'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultKey:&lt;/span&gt; &lt;span class="s1"&gt;'encryption_password'&lt;/span&gt;&lt;span class="o"&gt;]]],&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;podTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""
                apiVersion: v1
                kind: Pod
                spec:
                  containers:
                    - name: awscli
                      image: amazon/aws-cli
                      command:
                      - cat
                      tty: true
                    - name: python
                      image: python:3.6
                      command:
                      - cat
                      tty: true
                """&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stripIndent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;kubernetes&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;defaultContainer&lt;/span&gt; &lt;span class="s1"&gt;'jnlp'&lt;/span&gt;
      &lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="s2"&gt;"${podTemplate}"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AWS_DEFAULT_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Backup Jenkins'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'python'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${env.WORKSPACE}/pipelines-k8s/vault-backup/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;configuration:&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;]){&lt;/span&gt;
              &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"""#!/bin/bash
                pip install -r requirements.txt
                python -u vault_handler.py
                tar -zcvf vault_secrets.json.enc.tar.gz vault_secrets.json.enc
              """&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'awscli'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
          &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${env.WORKSPACE}/pipelines-k8s/vault-backup/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;withVault&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nl"&gt;configuration:&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;vaultSecrets:&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;]){&lt;/span&gt;
              &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
                aws s3 cp vault_secrets.json.enc.tar.gz s3://vault-backups/$(date +%Y%m%d%H%M)/vault_secrets.json.enc.tar.gz
              '''&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a new pipeline in jenkins: newitem -&amp;gt; pipeline and make it periodic (daily).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--allTzEyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in64vew83obvrdg1d6fo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--allTzEyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/in64vew83obvrdg1d6fo.png" alt="new_pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I described how to automate Vault backup creation using Jenkins scheduled job and simple python script which I built to create dump of vault secrets.&lt;/p&gt;

&lt;p&gt;Thank you for reading, I hope you enjoyed it, see you in the next post.&lt;/p&gt;

&lt;p&gt;If you want to be notified when the next post of this tutorial is published, please follow me on Twitter &lt;a href="https://twitter.com/warolv"&gt;@warolv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Original story on my blog: &lt;a href="https://igorzhivilo.com/vault/scheduled-backup-vault-secrets/"&gt;https://igorzhivilo.com/vault/scheduled-backup-vault-secrets/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vault</category>
      <category>jenkins</category>
      <category>kubernetes</category>
      <category>hashicorp</category>
    </item>
    <item>
      <title>Scheduled backup of Vault secrets with CronJob of Kubernetes</title>
      <dc:creator>Igor Zhivilo</dc:creator>
      <pubDate>Tue, 14 Sep 2021 12:01:21 +0000</pubDate>
      <link>https://dev.to/warolv/scheduled-backup-of-vault-secrets-with-cronjob-of-kubernetes-54io</link>
      <guid>https://dev.to/warolv/scheduled-backup-of-vault-secrets-with-cronjob-of-kubernetes-54io</guid>
      <description>&lt;p&gt;I am a DevOps engineer at Cloudify.co and I will share in this post my experience related to automation of Vault backup creation using Kubernetes CronJob. &lt;/p&gt;

&lt;p&gt;This post is a continuation of the previous post: &lt;a href="https://igorzhivilo.com/vault/scheduled-backup-vault-secrets"&gt;https://igorzhivilo.com/vault/scheduled-backup-vault-secrets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository with all the code: &lt;a href="https://github.com/warolv/vault-backup"&gt;https://github.com/warolv/vault-backup&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is HashiCorp's Vault?
&lt;/h2&gt;

&lt;p&gt;Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret while providing tight access control and recording a detailed audit log.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.vaultproject.io/"&gt;https://www.vaultproject.io/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;EKS Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Vault runs on EKS cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you will learn from this post?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to create a scheduled backup for Vault secrets with CronJob of Kubernetes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to add Prometheus alerts for failed jobs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the code presented in my repository: &lt;a href="https://github.com/warolv/vault-backup"&gt;https://github.com/warolv/vault-backup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the docker container
&lt;/h2&gt;

&lt;p&gt;First, need to build a docker container based on python3 and include the code of vault_handler.py&lt;/p&gt;

&lt;p&gt;Need clone the &lt;a href="https://github.com/warolv/vault-backup"&gt;repo&lt;/a&gt; first with Docker file: 'git clone &lt;a href="https://github.com/warolv/vault-backup"&gt;https://github.com/warolv/vault-backup&lt;/a&gt;'&lt;/p&gt;

&lt;p&gt;Docker file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;FROM python:3&lt;/span&gt;

&lt;span class="s"&gt;COPY requirements.txt /&lt;/span&gt;

&lt;span class="s"&gt;RUN pip install -r requirements.txt&lt;/span&gt;

&lt;span class="s"&gt;COPY vault_handler.py /&lt;/span&gt;

&lt;span class="s"&gt;CMD [ "python", "./vault_handler.py" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# login to dockerhub&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker login &lt;span class="nt"&gt;-u&lt;/span&gt; YOUR_USERNAME &lt;span class="nt"&gt;-p&lt;/span&gt; YOUR_PASSWORD

&lt;span class="c"&gt;# Build Docker&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; vault-backup &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validate docker container working properly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; test-vault-backup &lt;span class="nt"&gt;--rm&lt;/span&gt; vault-backup
Specify one of the commands below
print
print-dump
dump
populate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's working, we got a list of commands from vault-backup:-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushing vault-backup docker container to docker hub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag vault-backup &amp;lt;Your Docker ID&amp;gt;/vault-backup:latest
&lt;span class="nv"&gt;$ &lt;/span&gt;docker push &amp;lt;Your Docker ID&amp;gt;/vault-backup:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, it's 'warolv/vault-backup:latest', you can find an already built image &lt;a href="https://hub.docker.com/r/warolv/vault-backup"&gt;there&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CronJob to run vault-backup on a daily basis
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/warolv/vault-backup/blob/main/examples/cronjob/cronjob.yaml"&gt;https://github.com/warolv/vault-backup/blob/main/examples/cronjob/cronjob.yaml&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CronJob&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-backup&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;jobTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
          &lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;instance-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spot&lt;/span&gt;
          &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awscli&lt;/span&gt;
              &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/aws-cli:latest&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cp"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/data/vault_secrets.enc"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3://jenkins-backups/vault_secrets.enc"&lt;/span&gt;
              &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
              &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-creds-secret&lt;/span&gt;
              &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup-dir&lt;/span&gt;
                &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data&lt;/span&gt;
          &lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-backup&lt;/span&gt;
              &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warolv/vault-backup&lt;/span&gt;
              &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python3"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vault_handler.py"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dump"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-dp"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/data/vault_secrets.enc"&lt;/span&gt;
              &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
              &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault-backup-secret&lt;/span&gt;
              &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup-dir&lt;/span&gt;
                &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data&lt;/span&gt;
          &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup-dir&lt;/span&gt;
            &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First 'vault_backup.py' script will run from InitContainer and secrets dump will be created (vault_secrets.enc) and saved to &lt;em&gt;/data&lt;/em&gt; folder which is a shared folder for both containers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second will run 'awscli' container which will be used to push the secrets dump to a private S3 bucket (AWS CLI is used to copy the secrets dump to the privare S3 bucket). Of course, S3 private bucket must exist.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credentials for AWS CLI (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) and for vault_backup script exported to the environment as k8s secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In this example, I am copying the dump to 's3://jenkins-backups/vault_secrets.enc', in the production use case I suggest adding a timestamp to dump of secrets to be something like vault_secrets_${timestamp}.enc&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating secrets for CronJob
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create k8s secret for AWS&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create secret generic aws-creds-secret &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_AWS_ACCESS_KEY_ID &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_AWS_SECRET_ACCESS_KEY

&lt;span class="c"&gt;# create k8s secret with all needed data for vault-backup&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create secret generic vault-backup-secret &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;VAULT_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://vault.vault.svc.cluster.local:8200 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;ROLE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_ROLE_ID &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;SECRET_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_SECRET_ID &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;ENCRYPTION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ENCRYPTION_KEY &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;VAULT_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's only an example, you need to put &lt;em&gt;real values&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy vault-backup cronjob
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; examples/cronjob/cronjob.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to trigger a Job from CronJob?
&lt;/h3&gt;

&lt;p&gt;In case you want to test your job is working properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create job &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cronjob/vault-backup vault-backup-001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding alerts to Prometheus
&lt;/h2&gt;

&lt;p&gt;I am using &lt;a href="https://github.com/kubernetes/kube-state-metrics"&gt;kube-state-metrics&lt;/a&gt; with Prometheus and we have these metrics available: &lt;a href="https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cronjob-metrics.md"&gt;https://github.com/kubernetes/kube-state-metrics/blob/master/docs/cronjob-metrics.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add an alert for &lt;em&gt;'failed job'&lt;/em&gt; and for cronjob which &lt;em&gt;'takes too much time'&lt;/em&gt;, of course, it's only an example to give you an idea.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cronjob.rules&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SlowCronJob&lt;/span&gt;
    &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;time()-kube_cronjob_next_schedule_time &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;1800&lt;/span&gt;
    &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30m&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
    &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CronJob {{$labels.namespaces}}/{{$labels.cronjob}} is taking more than 30m to complete&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CronJob taking more than 30m&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FailedJob&lt;/span&gt;
    &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube_job_status_failed  &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30m&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
    &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job {{$labels.namespaces}}/{{$labels.job}} failed&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job failure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this post, I described how to automate Vault backup creation using Kubernetes CronJob and a simple python script that I built.&lt;/p&gt;

&lt;p&gt;Thank you for reading, I hope you enjoyed it, see you in the next post.&lt;/p&gt;

&lt;p&gt;Original story on my blog: &lt;a href="https://igorzhivilo.com/vault/scheduled-backup-vault-cronjob/"&gt;https://igorzhivilo.com/vault/scheduled-backup-vault-cronjob/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to be notified when the next post of this tutorial is published, please follow me on Twitter &lt;a href="https://twitter.com/warolv"&gt;@warolv&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instagram: &lt;a href="https://www.instagram.com/warolv"&gt;@warolv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>vault</category>
      <category>hashicorp</category>
      <category>k8s</category>
    </item>
  </channel>
</rss>
