├── .editorconfig
├── .gitignore
├── README.md
├── example
└── main.tf
├── helpers
└── scripts
│ ├── command_exists
│ ├── config_write
│ ├── consul
│ ├── consul_configure
│ ├── consul_env
│ ├── docker
│ ├── download
│ ├── is_empty_dir
│ ├── json_merge
│ ├── json_read
│ ├── local_begin
│ ├── local_end
│ ├── local_prepare
│ ├── nomad
│ ├── nomad_configure
│ ├── nomad_env
│ ├── remote_begin
│ ├── remote_end
│ ├── service_configure
│ ├── service_download
│ ├── service_install
│ ├── service_latest
│ ├── service_uninstall
│ ├── shred
│ ├── sigsum
│ ├── tls_cloudflared_enable
│ ├── tls_local_add
│ ├── tls_local_enable
│ ├── vault
│ ├── vault_configure
│ ├── vault_env
│ ├── vault_init
│ └── vault_unseal
├── main.tf
├── outputs.tf
└── variables.tf
/.editorconfig:
--------------------------------------------------------------------------------
1 | # 2018 January 24
2 | # https://github.com/bevry/base
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = false
11 | indent_style = tab
12 |
13 | [{*.mk,*.py}]
14 | indent_style = tab
15 | indent_size = 4
16 |
17 | [*.md]
18 | indent_style = space
19 | indent_size = 4
20 |
21 | [{*.json,*.yml,*.bowerrc,*.babelrc}]
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.json]
26 | insert_final_newline = true
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 2017 April 12
2 | # https://github.com/bevry/base
3 |
4 | # System Files
5 | **/.DS_Store
6 |
7 | # Temp Files
8 | yarn.lock
9 | **/.docpad.db
10 | **/out.*
11 | **/*.log
12 | **/*.cpuprofile
13 | **/*.heapsnapshot
14 |
15 | # Build Files
16 | build/
17 | components/
18 | bower_components/
19 | node_modules/
20 | out/
21 | *output/
22 | coffeejs/
23 | coffee/
24 | es5/
25 | es2015/
26 | esnext/
27 | docs/
28 |
29 | # Editor Caches
30 | .c9/
31 | .vscode/
32 |
33 | # Private Files
34 | .env
35 | .idea
36 | .cake_task_cache
37 |
38 |
39 | # =====================================
40 | # CUSTOM
41 |
42 | helpers/data
43 | helpers/files
44 | helpers/outputs
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hashistack on Scaleway
2 |
3 | [Terraform](https://www.terraform.io) module to deploy [Consul](https://www.consul.io), [Nomad](https://www.nomadproject.io), [Vault](https://www.vaultproject.io) onto [Scaleway](https://www.scaleway.com)
4 |
5 | This module is currently under construction. I would love assistance. [Please reach out.](https://balupton.com/meet)
6 |
7 | ## Features
8 |
9 | - [x] deploys a consul, vault, nomad, docker cluster to scaleway
10 | - [x] configures firewalls correctly
11 | - [x] uses local TLS via `tls_mode=local`
12 | - [x] uses mutual TLS for consul and vault
13 | - [ ] uses mutual TLS for nomad
14 | - [ ] uses [Cloudflare's Argo Tunnel](https://www.cloudflare.com/products/argo-tunnel/) via `tls_mode=cloudflared`
15 | - [ ] uses [fabio](https://github.com/fabiolb/fabio) or [traefik](https://github.com/containous/traefik)
16 |
17 | ## Preparation
18 |
19 | If you are using MacOS, you will need to do the following:
20 |
21 | ``` bash
22 | brew install coreutils
23 | npm i -g json
24 | ```
25 |
26 | ## Servers
27 |
28 | Origin Server:
29 |
30 | - Creates consul server + vault server
31 | - Initialises consul
32 | - Initialises vault
33 | - Generates nomad vault configuration
34 | - Generates TLS certificates via vault pki
35 | - Restarts consul and vault with TLS
36 |
37 | Master Server:
38 |
39 | - Creates consul server + nomad server
40 |
41 | Slave Server:
42 |
43 | - Creates consul agent + docker + nomad agent
44 |
45 |
46 | ## Usage
47 |
48 | Refer to [`./example/main.tf`](https://github.com/bevry/terraform-scaleway-hashistack/blob/master/example/main.tf)
49 |
50 |
51 | ## Debugging
52 |
53 | If you need to debug DNS:
54 |
55 | ``` bash
56 | sudo yum install -y net-tools # ifconfig
57 | sudo yum install -y bind-utils # dig
58 | netstat -lnp
59 | netstat -rn
60 | route -n
61 | dig consul.service.consul
62 | dig @127.0.0.1 -p 8600 consul.service.consul SRV
63 | ```
64 |
65 |
66 |
67 | ## License
68 |
69 | Unless stated otherwise all works are:
70 |
71 | - Copyright © 2018+ [Benjamin Lupton](https://balupton.com)
72 |
73 | and licensed under:
74 |
75 | - [MIT License](http://spdx.org/licenses/MIT.html)
76 |
77 |
78 |
--------------------------------------------------------------------------------
/example/main.tf:
--------------------------------------------------------------------------------
1 | # =====================================
2 | # Variables
3 |
4 | variable "hostname" {
5 | type = "string"
6 | default = "bevry.me"
7 | }
8 |
9 | variable "region" {
10 | type = "string"
11 | default = "par1"
12 | }
13 |
14 | variable "image" {
15 | type = "string"
16 | default = ""
17 | }
18 |
19 | variable "bootscript" {
20 | type = "string"
21 | default = ""
22 | }
23 |
24 | variable "base_server_status" {
25 | type = "string"
26 | default = "stopped"
27 | }
28 |
29 | # =====================================
30 | # Locals
31 |
32 | provider "scaleway" {
33 | region = "${var.region}"
34 | }
35 |
36 | data "scaleway_bootscript" "centos" {
37 | architecture = "arm64"
38 | name_filter = "mainline 4.14"
39 | }
40 |
41 | data "scaleway_image" "centos" {
42 | architecture = "arm64"
43 | name = "CentOS 7.3"
44 | }
45 |
46 | locals {
47 | image = "${var.image != "" ? "${var.image}" : "${data.scaleway_image.centos.id}"}"
48 | bootscript = "${var.bootscript != "" ? "${var.bootscript}" : "${data.scaleway_bootscript.centos.id}"}"
49 | data_path = "${path.root}/data"
50 | private_key_path = "${path.root}/.ssh/scaleway"
51 | }
52 |
53 | # =====================================
54 | # Server: Origin
55 |
56 | module "cluster_origin" {
57 | source = "bevry/hashistack/scaleway"
58 |
59 | providers = {
60 | scaleway = "scaleway"
61 | }
62 |
63 | image = "${local.image}"
64 | bootscript = "${local.bootscript}"
65 | data_path = "${local.data_path}"
66 | private_key_path = "${local.private_key_path}"
67 | region = "${var.region}"
68 | hostname = "${var.hostname}"
69 | type = "origin"
70 | count = 1
71 | consul_expect = 1
72 | }
73 |
74 | # =====================================
75 | # Server: Agents
76 |
77 | module "par1_cluster_master" {
78 | source = "bevry/hashistack/scaleway"
79 |
80 | providers = {
81 | scaleway = "scaleway"
82 | }
83 |
84 | image = "${local.image}"
85 | bootscript = "${local.bootscript}"
86 | data_path = "${local.data_path}"
87 | private_key_path = "${local.private_key_path}"
88 | region = "${var.region}"
89 | hostname = "${var.hostname}"
90 | type = "master"
91 | count = 2
92 | join = "${module.cluster_origin.private_ip}"
93 | consul_expect = 3
94 | nomad_expect = 2
95 | }
96 |
97 | module "par1_cluster_slave" {
98 | source = "bevry/hashistack/scaleway"
99 |
100 | providers = {
101 | scaleway = "scaleway"
102 | }
103 |
104 | image = "${local.image}"
105 | bootscript = "${local.bootscript}"
106 | data_path = "${local.data_path}"
107 | private_key_path = "${local.private_key_path}"
108 | region = "${var.region}"
109 | hostname = "${var.hostname}"
110 | type = "slave"
111 | count = 2
112 | join = "${module.cluster_origin.private_ip}"
113 | }
114 |
--------------------------------------------------------------------------------
/helpers/scripts/command_exists:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | type "$1" &> /dev/null
--------------------------------------------------------------------------------
/helpers/scripts/config_write:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | dir="$1"
8 |
9 | for arg in "${@:2}"; do
10 | value="${arg#*=}"
11 | key="${arg%=*}"
12 | echo -n "$value" > "$dir/$key"
13 | done
14 |
15 | echo ''
16 |
--------------------------------------------------------------------------------
/helpers/scripts/consul:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Install
8 | ./service_install consul
9 | ./service_configure consul
10 |
11 | # Gossip Encryption
12 | consul_type="$(cat ../data/input/consul_type)"
13 | if test "$consul_type" != "slave"; then
14 | # keyring interaction is not available for clients/slaves, will produce:
15 | # error: Unexpected response code: 500 (keyring operations must run against a server node)
16 | echo 'Configuring Gossip Encryption for consul...'
17 | source ./consul_env
18 | consul_gossip_key="$(cat ../data/shared/auth/consul_gossip_key)"
19 | consul keyring -install="$consul_gossip_key"
20 | consul keyring -use="$consul_gossip_key"
21 | consul keyring -list
22 | fi
23 |
24 | echo ''
25 |
--------------------------------------------------------------------------------
/helpers/scripts/consul_configure:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Variables
8 | user="$(cat ../data/input/consul_user)"
9 | group="$(cat ../data/input/consul_group)"
10 | consul_type="$(cat ../data/input/consul_type)"
11 | private_ip="$(cat ../data/input/private_ip)"
12 | loopback_ip="$(cat ../data/input/loopback_ip)"
13 | join="$(cat ../data/input/join)"
14 |
15 | # Shared
16 | consul_gossip_key="$(cat ../data/shared/auth/consul_gossip_key)"
17 |
18 | # Write configuration files
19 | echo "Writing consul configuration..."
20 | cat > ../data/local/conf/consul.conf < ../data/local/conf/consul_base.json < ../data/local/conf/consul_tls.json < ../data/local/conf/consul_origin.json < ../data/local/conf/consul_master.json < ../data/local/conf/consul_slave.json < ../data/local/conf/consul.json
111 | else
112 | echo 'Configuring Consul without TLS...'
113 | ./json_merge \
114 | ../data/local/conf/consul_base.json \
115 | "../data/local/conf/consul_${consul_type}.json" > ../data/local/conf/consul.json
116 | fi
117 |
118 | # DNS
119 | echo "Configuring Consul DNS access..."
120 | if grep "1.1.1.1" < /etc/resolv.conf; then
121 | echo 'nameservers already adjusted'
122 | else
123 | echo 'adjusting nameservers...'
124 | resolv_contents="$(cat /etc/resolv.conf)"
125 | echo -e "nameserver ${loopback_ip}\\nnameserver 1.1.1.1\\nnameserver 1.0.0.1\\n${resolv_contents}" > /etc/resolv.conf
126 | fi
127 | # sudo service networking restart
128 | # systemctl disable NetworkManager
129 | # systemctl enable network
130 | # we can't just do
131 | # sudo echo "nameserver $LOOPBACK_IP" > /etc/resolv.conf
132 | # becasuse /etc/resolv.conf also contains these, which is necessary for scaleway networking perhaps... we could try add them to the consul resolver, but not sure that works
133 | # nameserver 10.1.94.8
134 | # domain cloud.online.net
135 | # search cloud.online.net
136 |
137 | echo ''
138 |
--------------------------------------------------------------------------------
/helpers/scripts/consul_env:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo ''
4 |
5 | if test "${BASH_SOURCE[0]}" = "${0}"; then
6 | echo "${BASH_SOURCE[0]} must be sourced"
7 | exit 1
8 | fi
9 |
10 | # https://www.consul.io/docs/commands/index.html#environment-variables
11 | if test -f /etc/certs/consul.key; then
12 | export CONSUL_HTTP_SSL=true
13 | export CONSUL_HTTP_SSL_VERIFY=true
14 | export CONSUL_CACERT=/etc/certs/user.ca
15 | export CONSUL_CLIENT_CERT=/etc/certs/user.crt
16 | export CONSUL_CLIENT_KEY=/etc/certs/user.key
17 | # CONSUL_TLS_SERVER_NAME
18 | fi
19 |
20 | echo ''
21 |
--------------------------------------------------------------------------------
/helpers/scripts/docker:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | echo "Installing Docker..."
8 | sudo yum install -y docker
9 | sudo systemctl enable docker
10 | sudo systemctl start docker
11 |
12 | echo ''
13 |
--------------------------------------------------------------------------------
/helpers/scripts/download:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | # follow redirects (-L), be quiet (-s), but show errors (-S)
6 | curl -sS -L "$1" -o "$2"
--------------------------------------------------------------------------------
/helpers/scripts/is_empty_dir:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | # include hidden files
6 | shopt -s nullglob dotglob
7 | if test -d "$1"; then
8 | files=("$1"/*)
9 | count="${#files[@]}"
10 | test "$count" -eq 0
11 | else
12 | exit 0
13 | fi
--------------------------------------------------------------------------------
/helpers/scripts/json_merge:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | // jq -s '.[0] * .[1]' one.json two.json > out.json
3 |
4 | const fs = require('fs')
5 |
6 | const results = process.argv.slice(2).map(function (value) {
7 | return JSON.parse(fs.readFileSync(value).toString())
8 | })
9 |
10 | const result = results.reduce(function (previousValue, currentValue) {
11 | return Object.assign({}, previousValue, currentValue)
12 | })
13 |
14 | process.stdout.write(JSON.stringify(result, null, ' '))
--------------------------------------------------------------------------------
/helpers/scripts/json_read:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | json "$1" | tr -d '\n'
6 |
--------------------------------------------------------------------------------
/helpers/scripts/local_begin:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Inputs
8 | data_path="$1"
9 |
10 | # Notify
11 | if ./command_exists say; then
12 | say "server allocated, starting setup"
13 | fi
14 |
15 | # Variables
16 | public_ip="$(cat "$data_path/input/public_ip")"
17 |
18 | # Check latest version
19 | ./service_latest "consul"
20 | ./service_latest "vault"
21 | ./service_latest "nomad"
22 |
23 | # Known Hosts
24 | echo "removing old keys of the host"
25 | ssh-keygen -R "$public_ip" || echo 'no old keys for the host'
26 | echo "adding new keys for the host"
27 | until ssh-keyscan "$public_ip" >> "$HOME/.ssh/known_hosts"; do
28 | sleep 10
29 | done
30 | echo "cleaning up"
31 | ./shred "$HOME/.ssh/known_hosts.old"
32 | echo 'testing the new connection'
33 | echo 'exit' | ssh -T "root@$public_ip"
34 |
35 | echo ''
36 |
--------------------------------------------------------------------------------
/helpers/scripts/local_end:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Inputs
8 | data_path="$1"
9 |
10 | # Variables
11 | private_key_path="$(cat "$data_path/input/private_key_path")"
12 | private_ip="$(cat "$data_path/input/private_ip")"
13 | public_ip="$(cat "$data_path/input/public_ip")"
14 | hostname="$(cat "$data_path/input/hostname")"
15 | type="$(cat "$data_path/input/type")"
16 | nomad_type="$(cat "$data_path/input/nomad_type")"
17 | vault_type="$(cat "$data_path/input/vault_type")"
18 | tls_mode="$(cat "$data_path/input/tls_mode")"
19 |
20 | # Copy remote files
21 | if test "$vault_type" = "origin"; then
22 | echo "copying outputs and extracting values"
23 | scp -i "$private_key_path" -r "root@$public_ip:/root/cluster/data/output/*" "$data_path/output"
24 | fi
25 |
26 | # Copy outputs to shared
27 | if test -f "$data_path/output/auth/consul_gossip_key"; then
28 | cp -v "$data_path/output/auth/consul_gossip_key" "$data_path/shared/auth/"
29 | fi
30 | if test -f "$data_path/output/auth/nomad_gossip_key"; then
31 | cp -v "$data_path/output/auth/nomad_gossip_key" "$data_path/shared/auth/"
32 | fi
33 | if test "$type" = "origin"; then
34 | cp -v "$data_path/output/auth/cluster_token" "$data_path/shared/auth/"
35 | cp -v "$data_path/output/auth/nomad_token" "$data_path/shared/auth/"
36 | if test "$tls_mode" = "local"; then
37 | if ! ./is_empty_dir "$data_path/output/cert"; then
38 | cp -v "$data_path/output/cert/"* "$data_path/shared/cert/"
39 | fi
40 | fi
41 | fi
42 |
43 | # Add certificates to system
44 | if test "$tls_mode" = "local"; then
45 | if test "$type" = "origin"; then
46 | ./tls_local_add "$data_path/output/cert/consul"
47 | ./tls_local_add "$data_path/output/cert/vault"
48 | fi
49 | if test -n "$nomad_type"; then
50 | ./tls_local_add "$data_path/output/cert/nomad"
51 | fi
52 | fi
53 |
54 | # IPs
55 | echo ''
56 | echo "Cluster Type: $type"
57 | echo "Public IP: $public_ip"
58 | echo "Private IP: $private_ip"
59 |
60 | # SSH
61 | echo ''
62 | echo 'SSH:'
63 | echo "ssh root@$public_ip"
64 | echo "export private_ip=$private_ip"
65 |
66 | # Consul
67 | echo ''
68 | echo 'Consul Web UI:'
69 | echo "ssh -L 127.0.0.1:8500:127.0.0.1:8500 root@$public_ip"
70 | echo "open http://127.0.0.1:8500"
71 | echo "open https://consul.$hostname:8500"
72 |
73 | # Vault
74 | if test -n "$vault_type"; then
75 | echo ''
76 | echo 'Vault Web UI:'
77 | echo "ssh -L 127.0.0.1:8200:${private_ip}:8200 root@$public_ip"
78 | echo "open http://127.0.0.1:8200/ui"
79 | echo "open https://vault.$hostname:8200/ui"
80 | fi
81 |
82 | # Nomad
83 | if test -n "$nomad_type"; then
84 | echo ''
85 | echo 'Nomad Web UI:'
86 | echo "ssh -L 127.0.0.1:4646:${private_ip}:4646 root@$public_ip"
87 | echo "open http://127.0.0.1:4646"
88 | echo "open https://server.nomad.$hostname:4646"
89 | fi
90 |
91 | # Notify
92 | if ./command_exists say; then
93 | say "server setup, all done"
94 | fi
95 |
96 |
97 | echo "...ALL DONE..."
98 |
99 | echo ''
100 |
101 |
--------------------------------------------------------------------------------
/helpers/scripts/local_prepare:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Inputs
8 | root_data_path="$1"
9 | data_path="$1/$2"
10 | type="$3"
11 |
12 | # Link
13 | rm -f "$data_path/shared"
14 | ./shred "$data_path"
15 | if test "$type" = "origin"; then
16 | # @todo detect if certs exist already on origin deploy
17 | # if so ask the user if they would like to reuse them
18 | # and add support for it
19 | # it would require a nomad cluster still persisting, as vault stores its stuff there
20 | # or a restore of a consul dump
21 | # https://www.consul.io/docs/commands/kv/export.html
22 | # https://www.consul.io/docs/commands/kv/import.html
23 | ./shred "$root_data_path/shared"
24 | fi
25 | mkdir -p "$data_path" "$root_data_path/shared/auth" "$root_data_path/shared/cert" "$data_path/input" "$data_path/output"
26 |
27 | # Link shared inside the server's data path
28 | if test "$3" != "base"; then
29 | ln -Fs "$root_data_path/shared" "$data_path/shared"
30 | fi
31 |
32 | # Create Gossip Encryption keys if they don't exist
33 | if ! test -f "$data_path/shared/auth/consul_gossip_key"; then
34 | openssl rand -base64 16 > "$data_path/shared/auth/consul_gossip_key"
35 | fi
36 | if ! test -f "$data_path/shared/auth/nomad_gossip_key"; then
37 | openssl rand -base64 16 > "$data_path/shared/auth/nomad_gossip_key"
38 | fi
39 |
40 | echo ''
41 |
42 |
--------------------------------------------------------------------------------
/helpers/scripts/nomad:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Install
8 | ./service_install nomad
9 | ./service_configure nomad
10 |
11 | # Gossip Encryption
12 | nomad_type="$(cat ../data/input/nomad_type)"
13 | if test "$nomad_type" != "slave"; then
14 | # keyring interaction is not available for clients/slaves, will produce:
15 | # error: Unexpected response code: 501 (Invalid method)
16 | echo 'Configuring Gossip Encryption for nomad...'
17 | source ./nomad_env
18 | nomad_gossip_key="$(cat ../data/shared/auth/nomad_gossip_key)"
19 | nomad operator keyring -install="$nomad_gossip_key"
20 | nomad operator keyring -use="$nomad_gossip_key"
21 | nomad operator keyring -list
22 | fi
23 |
24 | echo ''
25 |
26 |
--------------------------------------------------------------------------------
/helpers/scripts/nomad_configure:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Variables
8 | user="$(cat ../data/input/nomad_user)"
9 | group="$(cat ../data/input/nomad_group)"
10 | count="$(cat ../data/input/count)"
11 | nomad_type="$(cat ../data/input/nomad_type)"
12 | private_ip="$(cat ../data/input/private_ip)"
13 | region="$(cat ../data/input/region)"
14 |
15 | # Shared
16 | nomad_token="$(cat ../data/shared/auth/nomad_token)"
17 | nomad_gossip_key="$(cat ../data/shared/auth/nomad_gossip_key)"
18 |
19 | # https://www.nomadproject.io/docs/agent/configuration/index.html
20 | # https://www.nomadproject.io/guides/cluster/automatic.html
21 | # https://www.nomadproject.io/docs/agent/configuration/consul.html
22 | # https://www.nomadproject.io/docs/vault-integration/index.html
23 | # "bootstrap_expect": $nomad_expect,
24 | echo "Writing nomad configuration..."
25 | cat > ../data/local/conf/nomad.conf < ../data/local/conf/nomad_base.json < ../data/local/conf/nomad_tls.json < ../data/local/conf/nomad_master.json < ../data/local/conf/nomad_slave.json < ../data/local/conf/nomad.json
130 | else
131 | echo 'Configuring Nomad without TLS...'
132 | ./json_merge \
133 | ../data/local/conf/nomad_base.json \
134 | "../data/local/conf/nomad_${nomad_type}.json" > ../data/local/conf/nomad.json
135 | fi
136 |
137 | echo ''
138 |
139 |
--------------------------------------------------------------------------------
/helpers/scripts/nomad_env:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo ''
4 |
5 | if test "${BASH_SOURCE[0]}" = "${0}"; then
6 | echo "${BASH_SOURCE[0]} must be sourced"
7 | exit 1
8 | fi
9 |
10 | # Variables
11 | private_ip="$(cat ../data/input/private_ip)"
12 |
13 | # TLS
14 | if test -f /etc/certs/nomad.key; then
15 | # https://www.nomadproject.io/docs/commands/index.html
16 | export NOMAD_ADDR="https://${private_ip}:4646"
17 | # https://www.nomadproject.io/guides/securing-nomad.html#node-certificates
18 | # client.global.nomad for a client node in the global region
19 | # server.global.nomad for a server node in the us-west region
20 | export NOMAD_CACERT=/etc/certs/nomad.ca
21 | export NOMAD_CLIENT_CERT=/etc/certs/nomad.crt
22 | export NOMAD_CLIENT_KEY=/etc/certs/nomad.key
23 | # export NOMAD_TLS_SERVER_NAME
24 | else
25 | export NOMAD_ADDR="http://${private_ip}:4646"
26 | fi
27 |
28 | echo ''
--------------------------------------------------------------------------------
/helpers/scripts/remote_begin:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Variables
8 | type="$(cat ../data/input/type)"
9 | consul_type="$(cat ../data/input/consul_type)"
10 | vault_type="$(cat ../data/input/vault_type)"
11 | docker_type="$(cat ../data/input/docker_type)"
12 | nomad_type="$(cat ../data/input/nomad_type)"
13 | tls_mode="$(cat ../data/input/tls_mode)"
14 |
15 | # Prepare
16 | mkdir -p \
17 | ../data/input \
18 | ../data/local/conf \
19 | ../data/output/cert \
20 | ../data/output/vault \
21 | ../data/output/auth \
22 | /etc/certs
23 |
24 | echo "Installing dependencies..."
25 | sudo yum update -y
26 | if ! ./command_exists unzip; then
27 | sudo yum install -y unzip
28 | fi
29 | # sudo yum install -y net-tools # ifconfig
30 | # sudo yum install -y bind-tools # dig
31 |
32 | # As JQ does not exist for arm64, use node and json
33 | # https://github.com/stedolan/jq/issues/1655
34 | # https://github.com/trentm/json
35 | if ! ./command_exists node; then
36 | sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
37 | sudo yum install -y nodejs --enablerepo=epel
38 | fi
39 | if ! ./command_exists json; then
40 | sudo npm install -g json
41 | fi
42 |
43 | if test "$tls_mode" = "cloudflared"; then
44 | # this may or may not work due to
45 | # https://github.com/cloudflare/cloudflared/issues/22
46 | # https://github.com/cloudflare/cloudflared/issues/25
47 | sudo yum install -y git
48 |
49 | # install go manually, as yum has older version
50 | # https://golang.org/doc/install
51 | go_version='1.10.1'
52 | go_arch='arm64'
53 | go_url="https://dl.google.com/go/go${go_version}.linux-${go_arch}.tar.gz"
54 | go_file="$(mktemp -d)/go$VERSION.$OS-$ARCH.tar.gz"
55 | ./download "$go_url" "$go_file"
56 | tar -C /usr/local -xzf "$go_file"
57 | export PATH=$PATH:/usr/local/go/bin
58 | shred "$go_file"
59 |
60 | # install cloudflared
61 | sudo go install -v github.com/cloudflare/cloudflared/cmd/cloudflared
62 | fi
63 |
64 | # End of base
65 | if test "$type" = "base"; then
66 | exit 0
67 | fi
68 |
69 | if test "$tls_mode" = "local"; then
70 | if ! ./is_empty_dir ../data/shared/cert; then
71 | cp -v ../data/shared/cert/* /etc/certs
72 | fi
73 | fi
74 |
75 | echo "Configuring ports..."
76 | # https://github.com/bevry/terraform-scaleway-hashistack/issues/11
77 | # @todo not sure if this is necessary when using cloudflared
78 | tr ',' '\n' < ../data/input/ports_local_tcp | while read -r port || [ -n "$port" ]; do
79 | echo "configuring local $port/tcp"
80 | sudo firewall-cmd --zone=public --add-port="$port/tcp" --permanent
81 | done
82 | tr ',' '\n' < ../data/input/ports_local_udp | while read -r port || [ -n "$port" ]; do
83 | echo "configuring local $port/udp"
84 | sudo firewall-cmd --zone=public --add-port="$port/udp" --permanent
85 | done
86 | sudo firewall-cmd --reload
87 |
88 | echo "Configuring services as $type..."
89 |
90 | if test -n "$consul_type"; then
91 | echo "Configuring consul as $consul_type..."
92 | ./consul
93 | fi
94 |
95 | if test -n "$vault_type"; then
96 | echo "Configuring vault as $vault_type..."
97 | ./vault
98 | fi
99 |
100 | if test -n "$docker_type"; then
101 | echo "Configuring docker as $docker_type..."
102 | ./docker
103 | fi
104 |
105 | if test -n "$nomad_type"; then
106 | echo "Configuring nomad as $nomad_type..."
107 | ./nomad
108 | fi
109 |
110 | if test "$tls_mode" = "local"; then
111 | echo "Enabling Local TLS via Vault PKI..."
112 | ./tls_local_enable
113 | elif test "$tls_mode" = "cloudflared"; then
114 | echo 'Enabling TLS via Cloudflare Argo Tunnel (cloudflared)...'
115 | ./tls_cloudflared_enable
116 | fi
117 |
118 | echo ''
119 |
--------------------------------------------------------------------------------
/helpers/scripts/remote_end:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | ./shred /root/cluster
8 |
9 | echo ''
10 |
--------------------------------------------------------------------------------
/helpers/scripts/service_configure:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Arguments
8 | service="$1"
9 |
10 | # Variables
11 | user="$(cat "../data/input/${service}_user")"
12 | group="$(cat "../data/input/${service}_group")"
13 |
14 | echo "Running custom configuration script for $service..."
15 | "./${service}_configure"
16 |
17 | echo "Configuring service configuration for $service..."
18 | # service configuration
19 | sudo rm -Rf "/etc/systemd/system/$service.d"
20 | sudo mkdir -p "/etc/systemd/system/$service.d"
21 | sudo mv "../data/local/conf/$service.json" "/etc/systemd/system/$service.d/$service.json"
22 | sudo chmod 0640 "/etc/systemd/system/$service.d/$service.json"
23 | sudo chown "$user:$group" "/etc/systemd/system/$service.d/$service.json"
24 |
25 | echo "Injecting service for $service..."
26 | # service specification
27 | sudo rm -Rf "/etc/systemd/system/$service.service"
28 | sudo mv "../data/local/conf/$service.conf" "/etc/systemd/system/$service.service"
29 | sudo chmod 0640 "/etc/systemd/system/$service.service"
30 | sudo chown "$user:$group" "/etc/systemd/system/$service.service"
31 |
32 | echo "Reload service daeomon $service..."
33 | sudo systemctl daemon-reload
34 |
35 | echo "Enabling service for $service..."
36 | sudo systemctl enable "$service.service"
37 |
38 | echo "Starting service for $service..."
39 | if ! sudo systemctl restart "$service"; then
40 | echo 'failed to start...'
41 | echo 'here is the service status:'
42 | sudo systemctl status "$service" -l
43 | echo 'here are the process logs:'
44 | sudo journalctl -u "$service"
45 | echo 'and here is the manual execution:'
46 | service_exec="$(grep ExecStart < "/etc/systemd/system/$service.service" | sed 's/ExecStart=//')"
47 | $service_exec
48 | fi
49 | sleep 2
50 |
51 | echo ''
52 |
--------------------------------------------------------------------------------
/helpers/scripts/service_download:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 |
6 | echo ""
7 |
8 |
9 | # Files
10 | temp="$(mktemp -d)"
11 |
12 | # Clean
13 | function finish {
14 | ./shred "$temp"
15 | }
16 | trap finish EXIT
17 |
18 | # Locals
19 | service="$1"
20 | version="$2"
21 | out_file="$3"
22 | platform="linux_arm64"
23 |
24 | # Download ZIP
25 | zip_filename="${service}_${version}_${platform}.zip"
26 | zip_url="https://releases.hashicorp.com/${service}/${version}/${service}_${version}_${platform}.zip"
27 | zip_file="$temp/$service.zip"
28 | ./download "$zip_url" "$zip_file"
29 |
30 | # Download Signature
31 | sig_url="https://releases.hashicorp.com/${service}/${version}/${service}_${version}_SHA256SUMS.sig"
32 | sig_file="$temp/$service.sig"
33 | ./download "$sig_url" "$sig_file"
34 |
35 | # Download Hashes
36 | sha_url="https://releases.hashicorp.com/${service}/${version}/${service}_${version}_SHA256SUMS"
37 | sha_file="$temp/$service.sha"
38 | ./download "$sha_url" "$sha_file"
39 |
40 | # Verify Signature with GPG
41 | key_url="https://keybase.io/hashicorp/pgp_keys.asc"
42 | key_file="$temp/hashicorp.asc"
43 | ./download "$key_url" "$key_file"
44 | gpg --import "$key_file"
45 | gpg --verify "$sig_file" "$sha_file"
46 |
47 | # Verify Signature with Keybase
48 | if ./command_exists keybase; then
49 | keybase pgp verify -d "$sig_file" -S hashicorp -i "$sha_file"
50 | fi
51 |
52 | # Verify Zip
53 | sha_expected="$(grep "$zip_filename" < "$sha_file" | sed 's/ .*//')"
54 | sha_actual="$(./sigsum "$zip_file" | sed 's/ .*//')"
55 | if test "$sha_expected" = "$sha_actual"; then
56 | echo "zip file hash matched"
57 | else
58 | echo "zip file hash did not match!"
59 | echo "expected hash: $sha_expected"
60 | echo "actual hash: $sha_actual"
61 | exit 1
62 | fi
63 |
64 | # Move zip to files
65 | mv "$zip_file" "$out_file"
66 |
67 |
68 | echo ''
69 |
70 |
--------------------------------------------------------------------------------
/helpers/scripts/service_install:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Locals
8 | temp="$(mktemp -d)"
9 |
10 | # Clean
11 | function finish {
12 | ./shred "$temp"
13 | }
14 | trap finish EXIT
15 |
16 | # Functions
17 | function get_version {
18 | set +o pipefail
19 | "$1" -v | grep --color=never -Eo '[0-9]+\.[0-9]+\.[0-9]+'
20 | set -o pipefail
21 | }
22 | function user_exists {
23 | local user="$1"
24 | id "$user" >/dev/null 2>&1
25 | }
26 |
27 | # Arguments
28 | service="$1"
29 | user="$(cat "../data/input/${service}_user")"
30 | group="$(cat "../data/input/${service}_group")"
31 | expected_version="$(cat "../data/input/${service}_version")"
32 |
33 | echo "Downloading $service..."
34 | ./service_download "$service" "$expected_version" "$temp/$service.zip"
35 |
36 | echo "Unzipping $service..."
37 | unzip -d "$temp" "$temp/$service.zip"
38 | chmod +x "$temp/$service"
39 |
40 | echo "Checking $service version..."
41 | version="$(get_version "$temp/$service")"
42 | if test "$version" != "$expected_version"; then
43 | echo "version was not as expected"
44 | echo "actual version: $version"
45 | echo "desired version: $expected_version"
46 | exit 1
47 | fi
48 |
49 | echo "Moving $service into path..."
50 | sudo mv "$temp/$service" "/usr/local/bin/$service"
51 |
52 | echo "Detecting $service user $user..."
53 | if ! user_exists "$user"; then
54 | echo "Adding $service user $user..."
55 | sudo useradd "$user"
56 | fi
57 |
58 | echo "Setting $service executable permissions..."
59 | # make the executable only runnable by the user
60 | sudo chown "$user:$group" "/usr/local/bin/$service"
61 | sudo chmod 0740 "/usr/local/bin/$service"
62 |
63 | echo "Adjusting $service executable..."
64 | # ensure sensitive data is written by the system
65 | if test "$service" = "vault"; then
66 | echo "Adjusting $service executable for vault..."
67 | sudo setcap cap_ipc_lock=+ep /usr/local/bin/vault
68 | fi
69 |
70 | echo "Initialising data for $service..."
71 | # data configuration
72 | rm -Rf "/opt/$service"
73 | mkdir -p "/opt/$service/data"
74 | sudo chmod -R 0740 "/opt/$service"
75 | sudo chown -R "$user:$group" "/opt/$service"
76 |
77 | echo ''
78 |
--------------------------------------------------------------------------------
/helpers/scripts/service_latest:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | # Locals
8 | service="$1"
9 | if test "$#" -eq 2; then
10 | version="$2"
11 | else
12 | version=false
13 | fi
14 |
15 | # Functions
16 | function fetch_latest_version {
17 | curl -s "https://releases.hashicorp.com/$1/index.json" | json versions | json -ka | grep -v "beta" | grep -v "rc" | grep -v "alpha" | sort -V | tail -n 1
18 | }
19 |
20 | # Fetch
21 | latest_version="$(fetch_latest_version "$service")"
22 |
23 | # Check latest version
24 | if test "$version" = false; then
25 | echo "$service [latest $latest_version] https://github.com/hashicorp/$service/blob/master/CHANGELOG.md"
26 | elif test "$latest_version" = "$version"; then
27 | echo "$service [current $version] is [latest $latest_version] https://github.com/hashicorp/$service/blob/master/CHANGELOG.md"
28 | elif test "$latest_version" != "$version"; then
29 | echo "$service [current $version] isnt [latest $latest_version] https://github.com/hashicorp/$service/blob/master/CHANGELOG.md"
30 | fi
31 |
32 | echo ''
33 |
--------------------------------------------------------------------------------
/helpers/scripts/service_uninstall:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ""
6 |
7 | service="$1"
8 |
9 | if test "$service" = "vault"; then
10 | vault_type="$(cat ../data/input/vault_type)"
11 | if test "$vault_type" = "origin"; then
12 | echo "erasing previous origin vault"
13 | systemctl stop vault
14 | consul kv delete --recurse vault
15 | fi
16 | ./shred ../data/output/vault/*
17 | ./shred ../data/output/auth/*_token
18 | ./shred ../data/output/auth/unseal_key_*
19 | ./shred "/etc/certs/${service}.key"
20 | ./shred "/etc/certs/${service}.crt"
21 | ./shred "/etc/certs/${service}.ca"
22 | else
23 | echo "cannot yet uninstall the service $1"
24 | exit 1
25 | fi
26 |
27 | echo ""
28 |
--------------------------------------------------------------------------------
/helpers/scripts/shred:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 |
4 | if ./command_exists shred; then
5 | exec="shred"
6 | args="-u"
7 | elif ./command_exists gshred; then
8 | exec="gshred"
9 | args="-u"
10 | else
11 | exec="rm"
12 | args="-Rf"
13 | fi
14 |
15 | for p in "$@"; do
16 | if test -d "$p"; then
17 | find "$p" -type f -exec "$exec" "$args" {} \;
18 | rm -Rfv "$p"
19 | elif test -f "$p"; then
20 | echo "$exec" "$args" "$p"
21 | "$exec" "$args" "$p"
22 | else
23 | echo "no need to shred the path, as it does not seem to exist: $1"
24 | fi
25 | done
26 |
27 |
--------------------------------------------------------------------------------
/helpers/scripts/sigsum:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 |
4 | if ./command_exists sha256sum; then
5 | sha256sum "$1"
6 | elif ./command_exists shasum; then
7 | shasum -a 256 "$1"
8 | else
9 | echo "missing sha256sum/shasum"
10 | exit 1
11 | fi
--------------------------------------------------------------------------------
/helpers/scripts/tls_cloudflared_enable:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 |
4 | echo ''
5 |
6 | # Variables
7 | hostname="$(cat ../data/input/hostname)"
8 |
9 | echo 'verify cloudflared works'
10 | cloudflared --version
11 |
12 | # echo 'login to cloudflared'
13 | # cloudflared login
14 |
15 | echo 'create the service'
16 | # cloudflared/config.yml
17 | sudo cloudflared service install
18 |
19 | # echo 'create a tunnel'
20 | # cloudflared --hostname "$hostname" --hello-world
21 |
22 | # /Users/balupton/.cloudflared/cert.pem
23 | echo ''
24 |
--------------------------------------------------------------------------------
/helpers/scripts/tls_local_add:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 |
4 | echo ""
5 |
6 | # https://github.com/balupton/dotfiles/blob/master/.scripts/commands/cert
7 |
8 | # Inputs
9 | base_path="$1"
10 | name="$(basename "$base_path")"
11 |
12 | # Locals
13 | pass="$(openssl rand -base64 16)"
14 | cert_file="$base_path.crt"
15 | ca_file="$base_path.ca"
16 | key_file="$base_path.key"
17 | bundle_file="$base_path.p12"
18 |
19 | # Actions
20 | echo "creating $name pki bundle"
21 | openssl pkcs12 -export -CAfile "$ca_file" -inkey "$key_file" -in "$cert_file" -password "pass:$pass" -out "$bundle_file"
22 | echo "removing old $name pki bundle from system"
23 | echo '!!! ENTER YOUR OPERATING SYSTEM PASSWORD IN THE PROMPT BELOW TO CONTINUE !!!'
24 | sudo echo 'ok'
25 | subject="$(openssl x509 -subject -in "$cert_file" -noout | sed 's/.*CN=//')"
26 | sudo security delete-identity -c "$subject" || echo "old bundle doesn't exist in system - ok"
27 | echo "adding $name pki bundle to keychain"
28 | sudo security import "$bundle_file" -P "$pass"
29 | echo "trusting $name cert"
30 | sudo security add-trusted-cert -d -r trustAsRoot "$cert_file"
31 | echo "trusting $name cert authority"
32 | sudo security add-trusted-cert -d -r trustAsRoot "$ca_file"
33 | echo "setting $name usage preference"
34 | sudo security set-identity-preference -c "$subject" -s "https://$subject"
35 |
36 | echo ''
37 |
--------------------------------------------------------------------------------
/helpers/scripts/tls_local_enable:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Variables
8 | consul_type="$(cat ../data/input/consul_type)"
9 | vault_type="$(cat ../data/input/vault_type)"
10 | loopback_ip="$(cat ../data/input/loopback_ip)"
11 | private_ip="$(cat ../data/input/private_ip)"
12 | public_ip="$(cat ../data/input/public_ip)"
13 | hostname="$(cat ../data/input/hostname)"
14 |
15 | # -------------------------------------
16 | # Vault Access
17 |
18 | if ! ./command_exists vault; then
19 | ./service_install vault
20 | fi
21 | source ./vault_env
22 |
23 |
24 | # -------------------------------------
25 | # Consul
26 |
27 | if test "$consul_type" = "origin"; then
28 | echo 'Generating TLS for consul...'
29 | vault write -format=json pki_int/issue/host common_name="consul.$hostname" alt_names="consul.service.consul,server.global.consul" ip_sans="${private_ip},${loopback_ip},${public_ip}" > ../data/output/vault/pki_int_consul.json
30 | json data.private_key < ../data/output/vault/pki_int_consul.json > ../data/output/cert/consul.key
31 | json data.certificate < ../data/output/vault/pki_int_consul.json > ../data/output/cert/consul.crt
32 | json data.issuing_ca < ../data/output/vault/pki_int_consul.json > ../data/output/cert/consul.ca
33 | cp -v ../data/output/cert/vault* /etc/certs
34 |
35 | echo 'Configuring TLS for consul...'
36 | ./service_configure consul
37 |
38 | echo 'Checking consul...'
39 | source ./consul_env
40 | consul info
41 | fi
42 |
43 |
44 | # -------------------------------------
45 | # Vault
46 |
47 | if test "$vault_type" = "origin"; then
48 | echo 'Generating TLS for vault...'
49 | vault write -format=json pki_int/issue/host common_name="vault.$hostname" alt_names="vault.service.consul,server.global.vault" ip_sans="${private_ip},${loopback_ip},${public_ip}" > ../data/output/vault/pki_int_vault.json
50 | json data.private_key < ../data/output/vault/pki_int_vault.json > ../data/output/cert/vault.key
51 | json data.certificate < ../data/output/vault/pki_int_vault.json > ../data/output/cert/vault.crt
52 | json data.issuing_ca < ../data/output/vault/pki_int_vault.json > ../data/output/cert/vault.ca
53 | cp -v ../data/output/cert/vault* /etc/certs
54 |
55 | echo 'Configuring TLS for vault...'
56 | ./service_configure vault
57 |
58 | echo 'Reunsealing Vault...'
59 | ./vault_unseal
60 |
61 | echo 'Checking vault...'
62 | source ./vault_env
63 | vault status
64 | fi
65 |
66 |
67 | # -------------------------------------
68 | # Nomad
69 |
70 | # not yet supported
71 | # https://github.com/bevry/terraform-scaleway-hashistack/issues/12
72 |
73 | # if test "$nomad_type" = "master"; then
74 | # vault write -format=json pki_int/issue/host common_name="server.nomad.$hostname" alt_names="server.global.nomad" ip_sans="${private_ip},${loopback_ip},${public_ip}" > ../data/output/vault/pki_int_nomad.json
75 | # json data.private_key < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.key
76 | # json data.certificate < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.crt
77 | # json data.issuing_ca < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.ca
78 |
79 | # echo 'Configuring TLS for nomad...'
80 | # ./service_configure nomad
81 |
82 | # echo 'Checking nomad...'
83 | # source ./nomad_env
84 | # nomad status
85 |
86 | # elif test "$nomad_type" = "slave"; then
87 | # vault write -format=json pki_int/issue/host common_name="client.nomad.$hostname" alt_names="client.global.nomad" ip_sans="${private_ip},${loopback_ip},${public_ip}" > ../data/output/vault/pki_int_nomad.json
88 | # json data.private_key < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.key
89 | # json data.certificate < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.crt
90 | # json data.issuing_ca < ../data/output/vault/pki_int_nomad.json > ../data/output/cert/nomad.ca
91 |
92 | # echo 'Configuring TLS for nomad...'
93 | # ./service_configure nomad
94 |
95 | # echo 'Checking nomad...'
96 | # source ./nomad_env
97 | # nomad status
98 | # fi
99 |
100 | echo ''
--------------------------------------------------------------------------------
/helpers/scripts/vault:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Install vault
8 | ./service_install vault
9 | ./service_configure vault
10 |
11 | # Initialise vault
12 | ./vault_init
13 | # curl --cacert /etc/certs/vault.ca --cert /etc/certs/vault.crt --key /etc/certs/vault.key "https://${private_ip}:8200"
14 |
15 | echo ''
16 |
--------------------------------------------------------------------------------
/helpers/scripts/vault_configure:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Variables
8 | user="$(cat ../data/input/vault_user)"
9 | group="$(cat ../data/input/vault_group)"
10 | vault_type="$(cat ../data/input/vault_type)"
11 | private_ip="$(cat ../data/input/private_ip)"
12 |
13 | # Check vault server type
14 | if test "$vault_type" != "origin"; then
15 | echo "Currently only support origin vault servers"
16 | exit 1
17 | fi
18 |
19 | echo "Writing vault configuration..."
20 | cat > ../data/local/conf/vault.conf < ../data/local/conf/vault_base.json < ../data/local/conf/vault_tls.json < ../data/local/conf/vault.json
91 | else
92 | echo 'Configuring Vault without TLS...'
93 | cp ../data/local/conf/vault_base.json ../data/local/conf/vault.json
94 | fi
95 |
96 | echo ''
97 |
--------------------------------------------------------------------------------
/helpers/scripts/vault_env:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo ''
4 |
5 | if test "${BASH_SOURCE[0]}" = "${0}"; then
6 | echo "${BASH_SOURCE[0]} must be sourced"
7 | exit 1
8 | fi
9 |
10 | # Variables
11 | private_ip="$(cat ../data/input/private_ip)"
12 | vault_type="$(cat ../data/input/vault_type)"
13 | if test "$vault_type" = "origin"; then
14 | vault_hostname="$private_ip"
15 | else
16 | vault_hostname='vault.service.consul'
17 | fi
18 |
19 | # Exports
20 | export VAULT_CLI_NO_COLOR=true
21 |
22 | # Configure vault CLI to access the vault server
23 | if test -f /etc/certs/vault.key; then
24 | # https://www.vaultproject.io/docs/commands/index.html#environment-variables
25 | export VAULT_ADDR="https://${vault_hostname}:8200"
26 | export VAULT_CACERT=/etc/certs/vault.ca
27 | export VAULT_CLIENT_CERT=/etc/certs/vault.crt
28 | export VAULT_CLIENT_KEY=/etc/certs/vault.key
29 | # VAULT_TLS_SERVER_NAME
30 | else
31 | export VAULT_ADDR="http://${vault_hostname}:8200"
32 | fi
33 |
34 | # Update the vault token
35 | if test -f ../data/shared/auth/cluster_token; then
36 | echo 'using shared cluster_token'
37 | cluster_token="$(cat ../data/shared/auth/cluster_token)"
38 | export VAULT_TOKEN="$cluster_token"
39 | elif test -f ../data/output/auth/cluster_token; then
40 | echo 'using cluster_token'
41 | cluster_token="$(cat ../data/output/auth/cluster_token)"
42 | export VAULT_TOKEN="$cluster_token"
43 | elif test -f ../data/output/auth/root_token; then
44 | echo 'using root_token'
45 | root_token="$(cat ../data/output/auth/root_token)"
46 | export VAULT_TOKEN="$root_token"
47 | fi
48 |
49 | echo ''
50 |
--------------------------------------------------------------------------------
/helpers/scripts/vault_init:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Local
8 | five_years=43800h
9 | short_ttl=72h
10 | temp="$(mktemp -d)"
11 | policy_file="$temp/nomad-server-policy.hcl"
12 | role_file="$temp/nomad-cluster-role.json"
13 |
14 | # Clean
15 | function finish {
16 | ./shred "$temp"
17 | }
18 | trap finish EXIT
19 |
20 | # trim colours from vault output
21 | # | sed 's/\x1b\[[0-9;]*m//g'
22 |
23 | # Variables
24 | hostname="$(cat ../data/input/hostname)"
25 |
26 | # Prepare
27 | source ./vault_env
28 |
29 | # Initialise the vault
30 | echo "Initialising the vault..."
31 | vault operator init -format=json > ../data/output/vault/init.json
32 | ./json_read unseal_keys_b64[0] < ../data/output/vault/init.json > ../data/output/auth/unseal_key_1
33 | ./json_read unseal_keys_b64[1] < ../data/output/vault/init.json > ../data/output/auth/unseal_key_2
34 | ./json_read unseal_keys_b64[2] < ../data/output/vault/init.json > ../data/output/auth/unseal_key_3
35 | ./json_read root_token < ../data/output/vault/init.json > ../data/output/auth/root_token
36 |
37 | # Unseal the vault
38 | sleep 2
39 | ./vault_unseal
40 | sleep 2
41 | source ./vault_env
42 | sleep 2
43 |
44 | # Create the cluster token
45 | echo 'Creating the cluster token...'
46 | vault token create -format=json -display-name=cluster > ../data/output/vault/cluster.json
47 | ./json_read auth.client_token < ../data/output/vault/cluster.json > ../data/output/auth/cluster_token
48 | source ./vault_env
49 |
50 | # Enable PKI
51 | # https://www.vaultproject.io/docs/secrets/pki/index.html
52 |
53 | echo 'Creating PKI certificate authorities...'
54 | # root
55 | vault secrets enable pki
56 | vault secrets tune -max-lease-ttl="$five_years" pki
57 | vault write -format=json pki/root/generate/internal common_name="${hostname} Root Authority" ttl="$five_years" > ../data/output/vault/pki.json
58 | vault write -format=json pki/config/urls issuing_certificates="$VAULT_ADDR/v1/pki/ca" crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
59 | vault write -format=json pki/roles/host allowed_domains="$hostname" allow_subdomains=true max_ttl=72h
60 | # intermediate
61 | vault secrets enable -path=pki_int pki
62 | vault secrets tune -max-lease-ttl="$five_years" pki_int
63 | # create certificate signing request
64 | vault write -format=json pki_int/intermediate/generate/internal common_name="${hostname} Intermediate Authority" ttl="$five_years" > ../data/output/vault/pki_int_csr.json
65 | json data.csr < ../data/output/vault/pki_int_csr.json > ../data/output/vault/int.csr
66 | # have the root ca sign the int ca's signing request to generate the certs for the int ca
67 | vault write -format=json pki/root/sign-intermediate csr=@../data/output/vault/int.csr format=pem_bundle > ../data/output/vault/pki_int.json
68 | json data.certificate < ../data/output/vault/pki_int.json > ../data/output/vault/pki_int.cert
69 | # upload the signed certificate to vault
70 | vault write -format=json pki_int/intermediate/set-signed certificate=@../data/output/vault/pki_int.cert
71 | vault write -format=json pki_int/config/urls issuing_certificates="$VAULT_ADDR/v1/pki_int/ca" crl_distribution_points="$VAULT_ADDR/v1/pki_int/crl"
72 | # Allow the creation of certificates
73 | vault write -format=json pki_int/roles/host allowed_domains="$hostname,consul,vault,nomad" allow_subdomains=true max_ttl="$short_ttl"
74 |
75 | # https://www.nomadproject.io/docs/vault-integration/index.html
76 | # @todo perhaps can be replaced with: https://www.vaultproject.io/docs/secrets/nomad/index.html
77 |
78 | echo "Setup vault permissions for nomad..."
79 | ./download "https://nomadproject.io/data/vault/nomad-server-policy.hcl" "$policy_file"
80 | ./download "https://nomadproject.io/data/vault/nomad-cluster-role.json" "$role_file"
81 | vault policy write nomad-server "$policy_file"
82 | vault write /auth/token/roles/nomad-cluster @"$role_file"
83 | vault token create -format=json -policy nomad-server -period "$short_ttl" -orphan > ../data/output/vault/nomad.json
84 | ./json_read auth.client_token < ../data/output/vault/nomad.json > ../data/output/auth/nomad_token
85 |
86 | echo ''
87 |
--------------------------------------------------------------------------------
/helpers/scripts/vault_unseal:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -ueE -o pipefail
3 | cd "$(dirname "$0")"
4 |
5 | echo ''
6 |
7 | # Reload the vault env
8 | source ./vault_env
9 |
10 | # Unseal the vault
11 | echo "Unsealing the vault..."
12 | unseal_key_1="$(cat ../data/output/auth/unseal_key_1)"
13 | unseal_key_2="$(cat ../data/output/auth/unseal_key_2)"
14 | unseal_key_3="$(cat ../data/output/auth/unseal_key_3)"
15 | vault operator unseal "$unseal_key_1"
16 | vault operator unseal "$unseal_key_2"
17 | vault operator unseal "$unseal_key_3"
18 |
19 | echo ''
20 |
--------------------------------------------------------------------------------
/main.tf:
--------------------------------------------------------------------------------
1 | # Web Ports:
2 | # [80, 443]
3 | # Consul Ports:
4 | # https://www.consul.io/docs/agent/options.html#ports-used
5 | # Docker Ports:
6 | # https://stackoverflow.com/a/43404044/130638
7 | # => [2375, 2376, 2377, 5000, 4789, 7946]
8 | # https://forums.docker.com/t/docker-ports-in-aws-ec2/15799
9 | # => however, they should not be opened as they are a security risk
10 | locals {
11 | loopback_ip = "127.0.0.1"
12 | docker_types = "${map("slave", "present")}"
13 | docker_type = "${lookup(local.docker_types, var.type, "")}"
14 | consul_user = "root"
15 | consul_group = "root"
16 | consul_version = "1.0.7"
17 | consul_types = "${map("origin", "origin", "master", "master", "slave", "slave")}"
18 | consul_type = "${lookup(local.consul_types, var.type, "")}"
19 | consul_ports_local = [8301, 8302, 8600]
20 | consul_ports_local_tcp = [8300, 8500]
21 | consul_ports_local_udp = []
22 | nomad_user = "nomad_user"
23 | nomad_group = "nomad_user"
24 | nomad_version = "0.8.1"
25 | nomad_types = "${map("master", "master", "slave", "slave")}"
26 | nomad_type = "${lookup(local.nomad_types, var.type, "")}"
27 | nomad_ports_local = []
28 | nomad_ports_local_tcp = "${compact(split(" ", local.nomad_type == "" ? "" : "4646 4647 4648"))}"
29 | nomad_ports_local_udp = []
30 | vault_user = "vault_user"
31 | vault_group = "vault_user"
32 | vault_version = "0.10.1"
33 | vault_types = "${map("origin", "origin")}"
34 | vault_type = "${lookup(local.vault_types, var.type, "")}"
35 | vault_ports_local = []
36 | vault_ports_local_tcp = "${compact(split(" ", local.vault_type == "" ? "" : "8200 8201"))}"
37 | vault_ports_local_udp = []
38 | ports_local = "${concat(local.consul_ports_local, local.nomad_ports_local, local.vault_ports_local)}"
39 | ports_local_tcp = "${distinct(concat(local.ports_local, local.consul_ports_local_tcp, local.nomad_ports_local_tcp, local.vault_ports_local_tcp))}"
40 | ports_local_udp = "${distinct(concat(local.ports_local, local.consul_ports_local_udp, local.nomad_ports_local_udp, local.vault_ports_local_udp))}"
41 |
42 | tags_string = "cluster cluster_${var.type} ${local.vault_type != "" ? "vault vault_${local.vault_type}" : ""} ${local.consul_type != "" ? "consul consul_${local.consul_type}" : ""} ${local.nomad_type != "" ? "nomad nomad_${local.nomad_type}" : ""} ${local.docker_type != "" ? "docker docker_${local.docker_type}" : ""}"
43 | tags_array = "${split(" ", local.tags_string)}"
44 | tags = "${compact(local.tags_array)}"
45 | }
46 |
47 | # =====================================
48 | # Security Groups
49 |
50 | resource "scaleway_security_group" "cluster" {
51 | name = "${var.region}_${var.type}"
52 | description = "${var.type} cluster security group"
53 | enable_default_security = false
54 | }
55 |
56 | # Provision Server
57 | # public_ip = "${element(scaleway_ip.public_ip.*.ip, count.index)}"
58 | resource "scaleway_server" "server" {
59 | count = "${var.count}"
60 | name = "${var.region}_${var.type}_${count.index}"
61 | image = "${var.image}"
62 | bootscript = "${var.bootscript}"
63 | security_group = "${scaleway_security_group.cluster.id}"
64 | type = "ARM64-2GB"
65 | state = "${var.state}"
66 | enable_ipv6 = false
67 | dynamic_ip_required = true
68 | tags = "${local.tags}"
69 |
70 | provisioner "local-exec" {
71 | command = "chmod +x ${path.module}/helpers/scripts/*"
72 | }
73 |
74 | provisioner "local-exec" {
75 | command = "${path.module}/helpers/scripts/local_prepare ${var.data_path} ${var.region}_${var.type}_${count.index} ${var.type}"
76 | }
77 |
78 | provisioner "local-exec" {
79 | command = "${path.module}/helpers/scripts/config_write ${var.data_path}/${var.region}_${var.type}_${count.index}/input tls_mode=${var.tls_mode} consul_user=${local.consul_user} consul_group=${local.consul_group} vault_user=${local.vault_user} vault_group=${local.vault_group} nomad_user=${local.nomad_user} nomad_group=${local.nomad_group} hostname=${var.hostname} private_key_path=${var.private_key_path} ports_local_tcp=${join(",", local.ports_local_tcp)} ports_local_udp=${join(",", local.ports_local_udp)} consul_version=${local.consul_version} consul_type=${local.consul_type} nomad_version=${local.nomad_version} nomad_type=${local.nomad_type} vault_version=${local.vault_version} vault_type=${local.vault_type} docker_type=${local.docker_type} name=${var.region}_${var.type}_${count.index} count=${var.count} join=${var.join} loopback_ip=${local.loopback_ip} type=${var.type} region=${var.region} private_ip=${self.private_ip} public_ip=${self.public_ip} "
80 | }
81 |
82 | provisioner "local-exec" {
83 | command = "${path.module}/helpers/scripts/local_begin ${var.data_path}/${var.region}_${var.type}_${count.index}"
84 | }
85 |
86 | connection {
87 | type = "ssh"
88 | user = "root"
89 | timeout = "180s"
90 | private_key = "${file("${var.private_key_path}")}"
91 | agent = false
92 | }
93 |
94 | provisioner "remote-exec" {
95 | inline = [
96 | "sysctl kernel.hostname=${var.region}_${var.type}_${count.index}",
97 | "rm -Rf /root/cluster",
98 | "mkdir -p /root/cluster /root/cluster/scripts /root/cluster/data",
99 | ]
100 | }
101 |
102 | provisioner "file" {
103 | source = "${path.module}/helpers/scripts/"
104 | destination = "/root/cluster/scripts"
105 | }
106 |
107 | provisioner "file" {
108 | source = "${var.data_path}/${var.region}_${var.type}_${count.index}/"
109 | destination = "/root/cluster/data"
110 | }
111 |
112 | provisioner "remote-exec" {
113 | inline = [
114 | "chmod +x /root/cluster/scripts/*",
115 | "/root/cluster/scripts/remote_begin",
116 | ]
117 | }
118 |
119 | provisioner "local-exec" {
120 | command = "${path.module}/helpers/scripts/local_end ${var.data_path}/${var.region}_${var.type}_${count.index}"
121 | }
122 |
123 | provisioner "remote-exec" {
124 | inline = [
125 | "/root/cluster/scripts/remote_end",
126 | ]
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | output "private_ip" {
2 | value = "${scaleway_server.server.0.private_ip}"
3 | }
4 |
5 | output "public_ip" {
6 | value = "${scaleway_server.server.0.public_ip}"
7 | }
8 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 | variable "tls_mode" {
2 | type = "string"
3 | default = "none"
4 |
5 | # options:
6 | # cloudflared: cloudflare's argo tunnel
7 | # local: generate via vault, not yet functional for nomad
8 | }
9 |
10 | variable "data_path" {
11 | type = "string"
12 | }
13 |
14 | variable "private_key_path" {
15 | type = "string"
16 | }
17 |
18 | variable "type" {
19 | type = "string" # origin, master, slave
20 | }
21 |
22 | variable "state" {
23 | type = "string" # running, stopped
24 | default = "running"
25 | }
26 |
27 | variable "hostname" {
28 | type = "string"
29 | }
30 |
31 | variable "count" {
32 | type = "string"
33 | default = 1
34 | }
35 |
36 | variable "join" {
37 | type = "string"
38 | default = ""
39 | }
40 |
41 | variable "region" {
42 | type = "string"
43 | }
44 |
45 | variable "image" {
46 | type = "string"
47 | }
48 |
49 | variable "bootscript" {
50 | type = "string"
51 | default = ""
52 | }
53 |
--------------------------------------------------------------------------------