diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7be3c6d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.kitchen diff --git a/.kitchen.yml b/.kitchen.yml new file mode 100644 index 0000000..b9272f2 --- /dev/null +++ b/.kitchen.yml @@ -0,0 +1,94 @@ +--- +driver: + name: docker + use_sudo: false + privileged: true + +verifier: + name: inspec + format: doc + +provisioner: + name: salt_solo + log_level: debug + require_chef: false + formula: vault + +platforms: + - name: ubuntu-16.04 + driver_config: + provision_command: + - apt-get update && apt-get install -y locales && locale-gen en_US.UTF-8 + run_command: /sbin/init + privileged: true + pid_one_command: /usr/lib/systemd/systemd + - name: amazonlinux + driver_config: + image: amazonlinux:latest + platform: rhel + run_command: /sbin/init + +suites: + - name: default + provisioner: + state_top: + base: + '*': + - vault + - name: dev_server_systemd + excludes: + - amazonlinux + provisioner: + state_top: + base: + '*': + - vault + - vault.server + pillars: + top.sls: + base: + '*': + - vault + vault.sls: + vault: + service: + type: systemd + - name: dev_server_upstart + includes: + - amazonlinux + provisioner: + state_top: + base: + '*': + - vault + - vault.server + pillars: + top.sls: + base: + '*': + - vault + vault.sls: + vault: + service: + type: upstart + - name: server_backend_s3 + includes: + - amazonlinux + provisioner: + state_top: + base: + '*': + - vault + - vault.server + pillars: + top.sls: + base: + '*': + - vault + vault.sls: + vault: + backend: + type: s3 + bucket: com-saltstack-vault + service: + type: upstart diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3706915 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +sudo: required + +language: ruby + +services: + - docker + +before_install: + - bundle install + +script: bundle exec kitchen verify diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..b4bbecb --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "test-kitchen" +gem "kitchen-docker" +gem "kitchen-salt" +gem 'kitchen-inspec' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..d4b5acc --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,149 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.5.1) + public_suffix (~> 2.0, >= 2.0.2) + artifactory (2.8.1) + blankslate (2.1.2.4) + builder (3.2.3) + coderay (1.1.1) + diff-lcs (1.3) + docker-api (1.33.4) + excon (>= 0.38.0) + json + erubis (2.7.0) + excon (0.55.0) + faraday (0.12.1) + multipart-post (>= 1.2, < 3) + ffi (1.9.18) + gssapi (1.2.0) + ffi (>= 1.0.1) + gyoku (1.3.1) + builder (>= 2.1.2) + hashie (3.5.5) + httpclient (2.8.3) + inspec (1.20.0) + addressable (~> 2.4) + faraday (>= 0.9.0) + hashie (~> 3.4) + json (>= 1.8, < 3.0) + method_source (~> 0.8) + mixlib-log + parallel (~> 1.9) + parslet (~> 1.5) + pry (~> 0) + rainbow (~> 2) + rspec (~> 3) + rspec-its (~> 1.2) + rubyzip (~> 1.1) + sslshake (~> 1.1) + thor (~> 0.19) + toml (~> 0.1) + train (>= 0.22.0, < 1.0) + json (2.1.0) + kitchen-docker (2.6.0) + test-kitchen (>= 1.0.0) + kitchen-inspec (0.18.0) + hashie (~> 3.4) + inspec (>= 0.34.0, < 2.0.0) + test-kitchen (~> 1.6) + kitchen-salt (0.0.24) + test-kitchen (~> 1.4) + little-plugger (1.1.4) + logging (2.2.2) + little-plugger (~> 1.1) + multi_json (~> 1.10) + method_source (0.8.2) + mixlib-install (2.1.12) + artifactory + mixlib-shellout + mixlib-versioning + thor + mixlib-log (1.7.1) + mixlib-shellout (2.2.7) + mixlib-versioning (1.1.0) + multi_json (1.12.1) + multipart-post (2.0.0) + net-scp (1.2.1) + net-ssh (>= 2.6.5) + net-ssh (4.1.0) + net-ssh-gateway (1.3.0) + net-ssh (>= 2.6.5) + nori (2.6.0) + parallel (1.11.1) + parslet (1.5.0) + blankslate (~> 2.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + public_suffix (2.0.5) + rainbow (2.2.2) + rake + rake (12.0.0) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + rubyntlm (0.6.1) + rubyzip (1.2.1) + safe_yaml (1.0.4) + slop (3.6.0) + sslshake (1.2.0) + test-kitchen (1.16.0) + mixlib-install (>= 1.2, < 3.0) + mixlib-shellout (>= 1.2, < 3.0) + net-scp (~> 1.1) + net-ssh (>= 2.9, < 5.0) + net-ssh-gateway (~> 1.2) + safe_yaml (~> 1.0) + thor (~> 0.19, < 0.19.2) + thor (0.19.1) + toml (0.1.2) + parslet (~> 1.5.0) + train (0.23.0) + docker-api (~> 1.26) + json (>= 1.8, < 3.0) + mixlib-shellout (~> 2.0) + net-scp (~> 1.2) + net-ssh (>= 2.9, < 5.0) + winrm (~> 2.0) + winrm-fs (~> 1.0) + winrm (2.2.2) + builder (>= 2.1.2) + erubis (~> 2.7) + gssapi (~> 1.2) + gyoku (~> 1.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (>= 1.6.1, < 3.0) + nori (~> 2.0) + rubyntlm (~> 0.6.0, >= 0.6.1) + winrm-fs (1.0.1) + erubis (~> 2.7) + logging (>= 1.6.1, < 3.0) + rubyzip (~> 1.1) + winrm (~> 2.0) + +PLATFORMS + ruby + +DEPENDENCIES + kitchen-docker + kitchen-inspec + kitchen-salt + test-kitchen + +BUNDLED WITH + 1.14.6 diff --git a/README.md b/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..ea17c31 --- /dev/null +++ b/README.rst @@ -0,0 +1,62 @@ +====== +Vault +====== + +.. image:: https://travis-ci.org/saltstack-formulas/vault-formula.svg?branch=master + +Formulas for working with `Vault `_ + +Available states +================ + +.. contents:: + :local: + +``vault`` +---------- + +Install the vault binary + + +``vault.server`` +--------------------- + +Install and configure the vault server + +To use it, just include *vault.server* in your *top.sls*, and configure it using pillars: + +:: + + vault: + vault_version: 0.7.0 + listen_protocol: tcp + listen_port: 8200 + listen_address: 0.0.0.0 + strict_tls: 0 + default_lease_ttl: 24h + max_lease_ttl: 24h + self_signed_cert: + enabled: false + backend: {} + dev_mode: true + service: + type: systemd + +Testing +======= + +Testing is done with `Test Kitchen `_ +for machine setup and `inspec `_ +for integration tests. + +Requirements +------------ + +* Ruby +* Docker + +:: + + gem install bundler + bundle install + bundle exec kitchen test all diff --git a/pillar.example b/pillar.example index eaca2c7..4e67e56 100644 --- a/pillar.example +++ b/pillar.example @@ -1,11 +1,16 @@ vault: - vault_version: 0.7.0 - source_hash: c6d97220e75335f75bd6f603bb23f1f16fe8e2a9d850ba59599b1a0e4d067aaa + version: 0.7.0 listen_protocol: tcp listen_port: 8200 listen_address: 0.0.0.0 strict_tls: 0 - default_lease_ttl: '4380h' - max_lease_ttl: '43800h' - s3_backend: - bucket: 'com-foo-vault' + tls_cert_file: {} + tls_key_file: {} + default_lease_ttl: 4380h + max_lease_ttl: 43800h + self_signed_cert: + enabled: false + backend: {} + dev_mode: true + service: + type: upstart diff --git a/test/integration/default/vault_spec.rb b/test/integration/default/vault_spec.rb new file mode 100644 index 0000000..b27fa42 --- /dev/null +++ b/test/integration/default/vault_spec.rb @@ -0,0 +1,6 @@ +describe command('/usr/local/bin/vault -version') do + its(:exit_status) { should eq 0 } + its(:stderr) { should be_empty } + its(:stdout) { should match(/^Vault v[0-9\.]+ \('[0-9a-f]+'\)/) } +end + diff --git a/test/integration/dev_server_systemd/vault_spec.rb b/test/integration/dev_server_systemd/vault_spec.rb new file mode 100644 index 0000000..bdef182 --- /dev/null +++ b/test/integration/dev_server_systemd/vault_spec.rb @@ -0,0 +1,35 @@ +describe file('/etc/vault/config/server.hcl') do + it { should be_a_file } + expected =<<-EOF +listener "tcp" { + address = "0.0.0.0:8200" + tls_disable = 0 + +} + +default_lease_ttl="24h" +max_lease_ttl="24h" +EOF + its(:content) { should eq(expected) } +end + +describe file('/etc/systemd/system/vault.service') do + it { should be_a_file } + its(:content) { should_not match /syslog/ } +end + +describe file('/etc/init/vault.conf') do + it { should_not be_a_file } +end + +describe service('vault') do + it { should be_enabled } + it { should be_running } +end + +describe command('journalctl -u vault') do + its(:exit_status) { should eq 0 } + its(:stderr) { should be_empty } + its(:stdout) { should match(/WARNING: Dev mode is enabled!/) } +end + diff --git a/test/integration/dev_server_upstart/vault_spec.rb b/test/integration/dev_server_upstart/vault_spec.rb new file mode 100644 index 0000000..1e8db72 --- /dev/null +++ b/test/integration/dev_server_upstart/vault_spec.rb @@ -0,0 +1,50 @@ +describe file('/etc/vault/config/server.hcl') do + it { should be_a_file } + expected = <<-EOF +listener "tcp" { + address = "0.0.0.0:8200" + tls_disable = 0 + +} + +default_lease_ttl="24h" +max_lease_ttl="24h" +EOF + its(:content) { should eq(expected) } +end + +describe file('/etc/systemd/system/vault.service') do + it { should_not be_a_file } +end + +describe file('/etc/init/vault.conf') do + it { should be_a_file } + its(:content) { should_not match /syslog/ } +end + +if os[:family] == 'amazon' + # serverspec assumes 'service' resource to be + # init.d for rhel-based os. have to just check + # that it is running, that means that it started + # with the instance + describe command('sudo initctl list | grep vault | grep -v grep') do + its(:stdout) { should match(/vault start\/running/) } + its(:stderr) { should be_empty } + end + + describe processes("vault") do + its('users') { should eq ['root'] } + end + +else + describe service('vault') do + it { should be_enabled } + it { should be_running } + end +end + +describe file('/var/log/vault.log') do + it { should be_a_file } + its(:content) { should match(/WARNING: Dev mode is enabled!/) } +end + diff --git a/test/integration/server_backend_s3/vault_spec.rb b/test/integration/server_backend_s3/vault_spec.rb new file mode 100644 index 0000000..af2a05a --- /dev/null +++ b/test/integration/server_backend_s3/vault_spec.rb @@ -0,0 +1,36 @@ +describe file('/etc/vault/config/server.hcl') do + it { should be_a_file } + its(:content) { should match /bucket = "com-saltstack-vault"/ } +end + +describe file('/etc/init/vault.conf') do + it { should be_a_file } + its(:content) { should_not match /syslog/ } +end + +if os[:family] == 'amazon' + # serverspec assumes 'service' resource to be + # init.d for rhel-based os. have to just check + # that it is running, that means that it started + # with the instance + describe command('sudo initctl list | grep vault | grep -v grep') do + its(:stdout) { should match(/vault start\/running/) } + its(:stderr) { should be_empty } + end + + describe processes("vault") do + its('users') { should eq ['root'] } + end + +else + describe service('vault') do + it { should be_enabled } + it { should be_running } + end +end + +describe file('/var/log/vault.log') do + it { should be_a_file } + its(:content) { should match(/WARNING: Dev mode is enabled!/) } +end + diff --git a/vault/defaults.yaml b/vault/defaults.yaml new file mode 100644 index 0000000..5dc73dc --- /dev/null +++ b/vault/defaults.yaml @@ -0,0 +1,17 @@ +vault: + version: 0.7.0 + listen_protocol: tcp + listen_port: 8200 + listen_address: 0.0.0.0 + strict_tls: 0 + service: upstart + tls_cert_file: {} + tls_key_file: {} + default_lease_ttl: 24h + max_lease_ttl: 24h + self_signed_cert: + enabled: false + backend: {} + dev_mode: true + service: + type: systemd diff --git a/vault/files/server.hcl.jinja b/vault/files/server.hcl.jinja index c927079..41355f5 100644 --- a/vault/files/server.hcl.jinja +++ b/vault/files/server.hcl.jinja @@ -1,27 +1,25 @@ -{% from "vault/map.jinja" import vault with context %} - -{% if vault.s3_backend %} +{%- from "vault/map.jinja" import vault with context -%} +{%- if vault.backend and vault.backend.type == "s3" %} backend "s3" { - bucket = "{{ vault.s3_backend.bucket }}" + bucket = "{{ vault.backend.bucket }}" } -{% endif %} +{% endif -%} listener "{{ vault.listen_protocol }}" { address = "{{ vault.listen_address }}:{{ vault.listen_port }}" - tls_disable = {{ vault.strict_tls }} - {% if vault.self_signed_cert.enabled %} + tls_disable = {{ vault.strict_tls }} +{% if vault.self_signed_cert.enabled %} tls_cert_file = "/etc/vault/{{ vault.self_signed_cert.hostname }}.pem" tls_key_file = "/etc/vault/{{ vault.self_signed_cert.hostname }}-nopass.key" - {% else %} - {% if vault.tls_cert_file %} +{% else %} +{%- if vault.tls_cert_file %} tls_cert_file = "{{ vault.tls_cert_file }}" - {% endif %} - {% if vault.tls_key_file %} - tls_key_file = "{{ vault.tls_cert_file }}" - {% endif %} - {% endif %} +{% endif -%} +{%- if vault.tls_key_file %} + tls_key_file = "{{ vault.tls_key_file }}" +{% endif -%} +{% endif %} } -#todo parameterize default_lease_ttl="{{ vault.default_lease_ttl }}" max_lease_ttl="{{ vault.max_lease_ttl }}" diff --git a/vault/files/vault_systemd.service.jinja b/vault/files/vault_systemd.service.jinja new file mode 100644 index 0000000..7042a30 --- /dev/null +++ b/vault/files/vault_systemd.service.jinja @@ -0,0 +1,10 @@ +{%- from "vault/map.jinja" import vault with context -%} +[Unit] +Description=vault server +Requires=network-online.target +After=network-online.target consul.service + +[Service] +EnvironmentFile=-/etc/sysconfig/vault +Restart=on-failure +ExecStart=/usr/local/bin/vault server {% if vault.dev_mode %}-dev{% else %} -config="/etc/vault/config/server.hcl"{% endif %} diff --git a/vault/files/vault.conf.jinja b/vault/files/vault_upstart.conf.jinja similarity index 78% rename from vault/files/vault.conf.jinja rename to vault/files/vault_upstart.conf.jinja index 174db28..0feb2f5 100644 --- a/vault/files/vault.conf.jinja +++ b/vault/files/vault_upstart.conf.jinja @@ -1,3 +1,4 @@ +{%- from "vault/map.jinja" import vault with context -%} description "Vault server" start on (runlevel [345] and started network) @@ -14,6 +15,10 @@ script export GOMAXPROCS=`nproc` exec /usr/local/bin/vault server \ +{%- if vault.dev_mode %} + -dev \ +{% else %} -config="/etc/vault/config/server.hcl" \ +{% endif -%} >>/var/log/vault.log 2>&1 end script diff --git a/vault/init.sls b/vault/init.sls index 2349f0e..045d200 100644 --- a/vault/init.sls +++ b/vault/init.sls @@ -1,8 +1,20 @@ -{%- set version = salt.pillar.get('vault:vault_version', '0.7.0') %} -{%- set source_hash = salt.pillar.get('vault:source_hash', 'c6d97220e75335f75bd6f603bb23f1f16fe8e2a9d850ba59599b1a0e4d067aaa') %} -install Vault: - archive.extracted: - - name: /usr/local/bin - - source: https://releases.hashicorp.com/vault/{{ version }}/vault_{{ version }}_linux_amd64.zip - - source_hash: {{ source_hash }} - - enforce_toplevel: False +{% from "vault/map.jinja" import vault with context %} +# using archive.extracted causes: 'Comment: Failed to cache https://releases.hashicorp.com/vault/0.7.0/vault_0.7.0_linux_amd64.zip: [Errno 1] _ssl.c:493: error:1409442E:SSL routines:SSL3_READ_BYTES:tlsv1 alert protocol version' +vault packages: + pkg.installed: + - names: + - unzip + - curl + +download vault: + cmd.run: + - name: curl --silent -L https://releases.hashicorp.com/vault/{{ vault.version }}/vault_{{ vault.version }}_linux_amd64.zip -o /tmp/vault.zip + - unless: test -e /tmp/vault.zip + +install vault: + cmd.run: + - name: unzip /tmp/vault.zip -d /usr/local/bin && chmod 0755 /usr/local/bin/vault && chown root:root /usr/local/bin/vault + - require: + - cmd: download vault + - pkg: unzip + - unless: test -e /usr/local/bin/vault diff --git a/vault/map.jinja b/vault/map.jinja index 5d5200c..3c3da4f 100644 --- a/vault/map.jinja +++ b/vault/map.jinja @@ -1,14 +1,2 @@ -{% set vault = salt['grains.filter_by']({ - 'default': { - listen_protocol: 'tcp', - listen_address: 0.0.0.0, - listen_port: 8200, - strict_tls: 1, - default_lease_ttl: '72h', - max_lease_ttl: '72h', - vault_version: '0.7.0', - self_signed_cert: { - enabled: false, - } - }, -}, merge=salt['pillar.get']('vault:lookup')) %} +{% import_yaml "vault/defaults.yaml" as defaults %} +{% set vault = salt['pillar.get']('vault', default=defaults['vault'], merge=True) %} diff --git a/vault/server.sls b/vault/server.sls index 33d2c26..15dc4ba 100644 --- a/vault/server.sls +++ b/vault/server.sls @@ -1,5 +1,5 @@ {% from "vault/map.jinja" import vault with context %} -{% if vault.self_signed_cert.enabled %} +{%- if vault.self_signed_cert.enabled %} /usr/local/bin/self-cert-gen.sh: file.managed: - source: salt://vault/files/cert-gen.sh.jinja @@ -14,7 +14,22 @@ generate self signed SSL certs: - cwd: /etc/vault - require: - file: /usr/local/bin/self-cert-gen.sh -{% endif %} +{% endif -%} + +/etc/vault: + file.directory: + - user: root + - group: root + - mode: 755 + +{%- if vault.dev_mode %} +/etc/vault/config: + file.directory: + - user: root + - group: root + - mode: 755 + - require: + - file: /etc/vault /etc/vault/config/server.hcl: file.managed: @@ -23,21 +38,39 @@ generate self signed SSL certs: - user: root - group: root - mode: 644 + - require: + - file: /etc/vault/config +{% endif -%} -/etc/init/vault.conf: +{%- if vault.service.type == 'systemd' %} +/etc/systemd/system/vault.service: file.managed: - - source: salt://vault/files/vault.conf.jinja + - source: salt://vault/files/vault_systemd.service.jinja - template: jinja - user: root - group: root - mode: 644 + - require_in: + - service: vault + +{% elif vault.service.type == 'upstart' %} +/etc/init/vault.conf: + file.managed: + - source: salt://vault/files/vault_upstart.conf.jinja + - template: jinja + - user: root + - group: root + - require_in: + - service: vault +{% endif -%} vault: service.running: - enable: True - require: - {% if vault.self_signed_cert.enabled %} + {%- if vault.self_signed_cert.enabled %} - cmd: generate self signed SSL certs - {% endif %} + {% endif -%} + {%- if vault.dev_mode %} - file: /etc/vault/config/server.hcl - - file: /etc/init/vault.conf + {% endif -%}