1"use strict";(globalThis.webpackChunkmikes_dev_notebook=globalThis.webpackChunkmikes_dev_notebook||[]).push([[6508],{895240(e,n,t){t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3/index","title":"Hashicorp Consul - Vault Cert Management Part 3","description":"Set-up Vault to be the Certificate Authority","source":"@site/docs/DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3/index.md","sourceDirName":"DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3","slug":"/DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3/2022-12-05","permalink":"/docs/DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3/2022-12-05","draft":false,"unlisted":false,"editUrl":"https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/docs/DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part3/index.md","tags":[{"inline":true,"label":"Consul","permalink":"/docs/tags/consul"},{"inline":true,"label":"Vault","permalink":"/docs/tags/vault"},{"inline":true,"label":"LINUX","permalink":"/docs/tags/linux"}],"version":"current","sidebarPosition":3900,"frontMatter":{"sidebar_position":3900,"slug":"2022-12-05","title":"Hashicorp Consul - Vault Cert Management Part 3","authors":"mpolinowski","tags":["Consul","Vault","LINUX"],"description":"Set-up Vault to be the Certificate Authority"},"sidebar":"tutorialSidebar","previous":{"title":"Hashicorp Vault - Certificate Authority","permalink":"/docs/DevOps/Hashicorp/2022-12-07-hashicorp-vault-ca-certificates/2022-12-07"},"next":{"title":"Hashicorp Consul - Vault Cert Management Part 2","permalink":"/docs/DevOps/Hashicorp/2022-12-05-hashicorp-consul-vault-certificates-part2/2022-12-05"}}');var a=t(474848),i=t(28453);const o={sidebar_position:3900,slug:"2022-12-05",title:"Hashicorp Consul - Vault Cert Management Part 3",authors:"mpolinowski",tags:["Consul","Vault","LINUX"],description:"Set-up Vault to be the Certificate Authority"},l=void 0,r={},c=[{value:"Configure consul-template",id:"configure-consul-template",level:2},{value:"Create Template Files",id:"create-template-files",level:3},{value:"Create consul-template Configuration",id:"create-consul-template-configuration",level:3},{value:"Server Configuration",id:"server-configuration",level:4},{value:"Client Configuration",id:"client-configuration",level:4},{value:"Start consul-template",id:"start-consul-template",level:2},{value:"Nomad Automation",id:"nomad-automation",level:3}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"Shen Zhen, China",src:t(564486).A+"",width:"2230",height:"839"})}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"#configure-consul-template",children:"Configure consul-template"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"#create-template-files",children:"Create Template Files"})}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"#create-consul-template-configuration",children:"Create consul-template Configuration"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"#server-configuration",children:"Server Configuration"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"#client-configuration",children:"Client Configuration"})}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"#start-consul-template",children:"Start consul-template"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"#nomad-automation",children:"Nomad Automation"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"configure-consul-template",children:"Configure consul-template"}),"\n",(0,a.jsx)(n.h3,{id:"create-template-files",children:"Create Template Files"}),"\n",(0,a.jsxs)(n.p,{children:["We can now use ",(0,a.jsx)(n.strong,{children:"Consul-Template"})," to automate certificate renewal for Consul when the TTL is expired. We can instruct consul-template to generate and retrieve those files from Vault using the following templates:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"mkdir /opt/consul/templates\n"})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.em,{children:"/opt/consul/templates/agent.crt.tpl"})," (",(0,a.jsx)(n.strong,{children:"Master only"}),")"]}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Careful"}),": The official documentation uses the ",(0,a.jsx)(n.code,{children:"common_name"})," for the default datacenter ",(0,a.jsx)(n.code,{children:"consul.dc1"}),". I use the name ",(0,a.jsx)(n.code,{children:"consul"})," here and have to change the variable ",(0,a.jsx)(n.code,{children:"consul.consul"})," accordingly. Otherwise the cert verification will fail. This
1name will be used a couple of times in the following commands - you need to change all of them according to your setup."]}),"\n"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'{{ with secret "pki_int/issue/consul-consul" "common_name=server.consul.consul" "ttl=24h" "alt_names=localhost" "ip_sans=127.0.0.1"}}\n{{ .Data.certificate }}\n{{ end }}\n'})}),"\n",(0,a.jsx)(n.p,{children:"The template will use the pki_int/issue/consul-consul endpoint that Vault exposes to generate new certificates."}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.em,{children:"/opt/consul/templates/agent.key.tpl"})," (",(0,a.jsx)(n.strong,{children:"Master only"}),")"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'{{ with secret "pki_int/issue/consul-consul" "common_name=server.consul.consul" "ttl=24h" "alt_names=localhost" "ip_sans=127.0.0.1"}}\n{{ .Data.private_key }}\n{{ end }}\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.em,{children:"/opt/consul/templates/ca.crt.tpl"})," (",(0,a.jsx)(n.strong,{children:"All Nodes"}),")"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'{{ with secret "pki_int/issue/consul-consul" "common_name=server.consul.consul" "ttl=24h"}}\n{{ .Data.issuing_ca }}\n{{ end }}\n'})}),"\n",(0,a.jsxs)(n.p,{children:["Not sure if this is necessary - but since the Consul agent is running with the ",(0,a.jsx)(n.code,{children:"consul"}
1)," user (Master node only) I will change the user rights:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"chown -R consul:consul /opt/consul/templates\n"})}),"\n",(0,a.jsx)(n.h3,{id:"create-consul-template-configuration",children:"Create consul-template Configuration"}),"\n",(0,a.jsxs)(n.p,{children:["Now we need to create a configuration file ",(0,a.jsx)(n.code,{children:"consul_template.hcl"})," that will instruct ",(0,a.jsx)(n.strong,{children:"Consul-Template"})," to retrieve the files needed for the agent, client and server, to configure TLS encryption:"]}),"\n",(0,a.jsx)(n.h4,{id:"server-configuration",children:"Server Configuration"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.em,{children:"/opt/consul/templates/consul_template.hcl"})," (",(0,a.jsx)(n.strong,{children:"Master only"}),")"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'# This denotes the start of the configuration section for Vault. All values\n# contained in this section pertain to Vault.\nvault {\n # This is the address of the Vault leader. The protocol (http(s)) portion\n # of the address is required.\n address = "http://localhost:8200"\n\n # This value can also be specified via the environment variable VAULT_TOKEN.\n # I am using the admin token created earlier\n token = "s.MdNlboI0nff3Xpo97d1TfIxd"\n\n unwrap_token = false\n\n renew_token = false\n}\n\n# This block defines the configuration for a template. Unlike other blocks,\n# this block may be specified multiple times to configure multiple templates.\ntemplate {\n # This is the source file on disk to use as the input template. This is often\n # called the "consul-template template".\n source = "/opt/consul/templates/agent.crt.tpl"\n\n # This is the destination path on disk where the source template will render.\n # If the parent directories do not exist, consul-template will attempt to\n # create them, unless create_dest_dirs is false.\n destination = "/opt/consul/agent-certs/agent.crt"\n\n # This is the permission to render the file. If this option is left\n # unspecified, consul-template will attempt to match the permissions of the\n # file that already exists at the destination path. If no file exists at that\n # path, the permissions are 0644.\n perms = 0700\n\n # This is the optional command to run when the template is rendered. The\n # command will only run if the resulting template changes.\n command = "sh -c \'date && consul reload\'"\n}\n\ntemplate {\n source = "/opt/consul/templates/agent.key.tpl"\n destination = "/opt/consul/agent-certs/agent.key"\n perms = 0700\n command = "sh -c \'date && consul reload\'"\n}\n\ntemplate {\n source = "/opt/consul/templates/ca.crt.tpl"\n destination = "/opt/consul/agent-certs/ca.crt"\n command = "sh -c \'date && consul reload\'"\n}\n'})}),"\n",(0,a.jsx)(n.p,{children:"The configuration file for the server contains the information to retrieve the CA certificate as well as the certificate/key pair for the server agent:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"address"})," : The address of your Vault server. If Vault runs on the same node as Consul you can use ",(0,a.jsx)(n.code,{children:"http://localhost:8200"})]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"token"})," : A valid Vault ACL token with appropriate permissions"]}),"\n"]}),"\n",(0,a.jsx)(n.h4,{id:"client-configuration",children:"Client Configuration"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.em,{children:"/opt/consul/templates/consul_template.hcl"})," (",(0,a.jsx)(n.strong,{children:"Minions only"}),")"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'# This denotes the start of the configuration section for Vault. All values\n# contained in this section pertain to Vault.\nvault {\n # This is the address of the Vault leader. The protocol (http(s)) portion\n # of the address is required.\n address = "https://server.consul.consul:8201"\n\n # This value can also be specified via the environment variable VAULT_TOKEN.\n # I am using the admin token created earlier\n token = "s.MdNlboI0nff3Xpo97d1TfIxd"\n\n unwrap_token = false\n\n renew_token = false\n}\n\ntemplate {\n source = "/opt/consul/templates/ca.crt.tpl"\n destination = "/opt/consul/agent-certs/ca.crt"\n command = "sh -c \'date && consul reload\'"\n}\n'})}),"\n",(0,a.jsx)(n.p,{children:"The configuration file for the server contains the information to retrieve the CA certificate only, the certificates for client agents are automatically generated from Consul when using the auto_encrypt setting:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"address"})," : The address of your Vault server. Here you need to use the IP / domain + opened port of your Vault master"]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"token"})," : A valid Vault ACL token with appropriate permissions"]}),"\n"]}),"\n",(0,a.jsxs)(n.p,{children:["To be able to access Vault from an external service I am using a Vault-generated certificate. On my test system, that runs both Consul and Vault on a single server, I can use the Consul cert itself. For this to work I needed to add the C
1onsul server address - which is in my case ",(0,a.jsx)(n.code,{children:"server.consul.consul"})," - to ",(0,a.jsx)(n.code,{children:"/etc/hosts"})," to resolve the WAN IP of my Vault server. Now I am still running into issues since the Certificate Authority is not being recognized by my Linux machine. So I have to add it to the trusted-store:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"ln -s /opt/consul/agent-certs/ca.crt /usr/local/share/ca-certificates/ca.crt\n"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"update-ca-certificates\nUpdating certificates in /etc/ssl/certs...\n1 added, 0 removed; done.\nRunning hooks in /etc/ca-certificates/update.d...\ndone.\n"})}),"\n",(0,a.jsx)(n.h2,{id:"start-consul-template",children:"Start consul-template"}),"\n",(0,a.jsxs)(n.p,{children:["After configuration is completed, you can start ",(0,a.jsx)(n.code,{children:"consul-template"}),". You must provide the file with the ",(0,a.jsx)(n.code,{children:"-config"})," parameter:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'consul-template -config "/opt/consul/templates/consul_template.hcl"\n'})}),"\n",(0,a.jsx)(n.p,{children:"THis service will output a `` every 24h - according to our Cert TTL. Meaning that the Consul service has been restarted after the certificate was updated. You can verify the cert validity by running:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:"openssl x509 -text -noout -in /opt/consul/agent-certs/agent.crt \n\nCertificate:\n Data:\n Version: 3 (0x2)\n Serial Number:\n 55:...\n Signature Algorithm: sha256WithRSAEncryption\n Issuer: CN = consul.consul Intermediate Authority\n Validity\n Not Before: Dec 5 06:34:13 2022 GMT\n Not After : Dec 6 06:34:43 2022 GMT\n Subject: CN = server.consul.consul\n\n ...\n"})}),"\n",(0,a.jsx)(n.h3,{id:"nomad-automation",children:"Nomad Automation"}),"\n",(0,a.jsxs)(n.p,{children:["We can run this job with Nomad on all clients (except the master) using the ",(0,a.jsx)(n.a,{href:"https://developer.hashicorp.com/nomad/docs/drivers/raw_exec",children:"raw_exec driver"}),". To enable raw exec, the Nomad client configuration must explicitly enable the raw_exec driver in the plugin's options:"]}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.em,{children:"/etc/nomad.d/client.hcl"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'plugin "raw_exec" {\n config {\n enabled = true\n }\n}\n'})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'job "consul-cert-renewal" {\n \n type = "system"\n\n datacenters = ["All","your","datacenters"]\n \n group "cert-renewal" {\n\n task "consul-template" {\n driver = "raw_exec"\n\n config {\n # When running a binary that exists on the host, the path must be absolute/\n command = "/usr/bin/consul-template"\n args = ["-config", "/opt/consul/templates/consul_template.hcl"]\n }\n }\n }\n}\n'})})]})}function u(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},564486(e,n,t){const s=t.p+"assets/images/photo-kt443t6d_64hdh43hfh6dgjdfhg4_d-6c1edb088dfea3a7d39f8eebb8e9dc23.jpg";t.d(n,["A",0,s])},28453(e,n,t){t.d(n,{R:()=>o,x:()=>l});var s=t(296540);const a={},i=s.createContext(a);function o(e){const n=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.