Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions deployment/nomad/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Nomad Deployment

This directory contains an example of deploying Boundary using Nomad

## Deploy

1. Create the nomad data directory

```bash
$ sudo mkdir /opt/nomad
$ sudo chown $USER /opt/nomad
```

2. Start the nomad agent

```bash
$ nomad agent -config nomad-config.hcl
```

3. Initialize Terraform:

```bash
$ terraform init
```

4. Run terraform apply against the nomad terraform module:

```bash
$ terraform apply -target module.nomad
```

Once boundary is running and the `boundary-init` job has run.
The controller service may restart a couple of times until the database initialization is finished.

5. Run terraform apply against the boundary terraform module:
```
$ terraform apply -target module.boundary \
-var "boundary_address=http://<public-ipv4-dns>:9200"
-var "target_ips=10.0.0.1"
```


## Verify

- You can launch the admin console at `http://<public-ipv4-dns>:9200`.
You can verify what address it is by visiting the nomad admin console.
![nomad console](img/nomad.png)
- Select the `primary` org and then login with the user `admin` / `foofoofoo`.


## Connect

```bash
export BOUNDARY_ADDR="http://<public-ipv4-dns>:9200"
export BOUNDARY_AUTH_METHOD_ID="ampw_<some ID>"

boundary authenticate password \
-login-name=admin \
-password foofoofoo

boundary connect ssh \
-target-scope-name="core_infra" \
-target-name="backend_servers_ssh"
```


## Improvements

### Using Vault for Secrets

Since nomad has integration with vault, you can use it to store the database credentials.

```hcl
vault {
policies = ["boundary"]
}
```

```hcl

task "boundary-controller" {
# ...
template {
data = <<EOF
controller {
{{with secret "<kv-mount-point>/data/boundary/db"}}
database {
url = "postgresql://{{.Data.data.user}}:{{.Data.data.pass}}@127.0.0.1:5432/boundary?sslmode=disable"
}
{{end}}
}
}
}
task "postgres" {
# ...
template {
data = <<EOT
{{ with secret "<kv-mount-point>/data/boundary/db" }}
POSTGRES_DB = "boundary"
POSTGRES_USER = "{{.Data.data.user}}"
POSTGRES_PASSWORD = "{{.Data.data.pass}}"
{{ end }}
EOT
destination = "config.env"
env = true
}
}
```

### Using Vault for KMS

Replace `kms "aead"` with `kms "vault"`.
Create the transit engine at the `boundary_kms` mount point.

```hcl
kms "transit" {
purpose = "root"
address = "https://vault:8200"
disable_renewal = "true"
key_name = "boundary_root_key"
mount_path = "boundary_kms/"
}
```

Make sure the Vault role that the nomad job assumes has the following policy:

```hcl
path "boundary_kms/encrypt/*" {
capabilities = ["update"]
}
path "boundary_kms/decrypt/*" {
capabilities = ["update"]
}
```

### Persistent data

Use host volumes to store the database in persistent storage.

In the `nomad-config.hcl` file, add:

```hcl
client {
host_volume "boundary_db" {
path = "/opt/boundary/db"
read_only = false
}
}
```

In the `boundary.hcl` job file, add:

```hcl
group "boundary-db" {
# ...
volume "boundary_db" {
type = "host"
source = "boundary_db"
}
# ...
task "postgres" {
# ...
volume_mount {
volume = "boundary_db"
destination = "/var/lib/postgresql/data"
}
# ...
}
}
```

### Other recommendations

- Redundant controllers
- Increase the `count` property in the `boundary-controller` group.
- Use Consul connect (mTLS) for communication between the database and the controller
6 changes: 6 additions & 0 deletions deployment/nomad/boundary/auth.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "boundary_auth_method" "password" {
name = "corp_password_auth_method"
description = "Password auth method for Corp org"
type = "password"
scope_id = boundary_scope.org.id
}
20 changes: 20 additions & 0 deletions deployment/nomad/boundary/boundary.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
required_providers {
boundary = {
source = "hashicorp/boundary"
version = "1.0.7"
}
}
}

provider "boundary" {
addr = var.boundary_address
recovery_kms_hcl = <<EOT
kms "aead" {
purpose = "recovery"
aead_type = "aes-gcm"
key = "8fZBjCUfN0TzjEGLQldGY4+iE9AkOvCfjh7+p0GtRBQ="
key_id = "global_recovery"
}
EOT
}
47 changes: 47 additions & 0 deletions deployment/nomad/boundary/roles.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Allows anonymous (un-authenticated) users to list and authenticate against any
# auth method, list the global scope, and read and change password on their account ID
# at the global and org level scope
resource "boundary_role" "global_anon_listing" {
scope_id = boundary_scope.global.id
grant_strings = [
"id=*;type=auth-method;actions=list,authenticate",
"type=scope;actions=list",
"id={{account.id}};actions=read,change-password"
]
principal_ids = ["u_anon"]
}
resource "boundary_role" "org_anon_listing" {
scope_id = boundary_scope.org.id
grant_strings = [
"id=*;type=auth-method;actions=list,authenticate",
"type=scope;actions=list",
"id={{account.id}};actions=read,change-password"
]
principal_ids = ["u_anon"]
}

# Creates a role in the global scope that's granting administrative access to
# resources in the org scope for the admin user
resource "boundary_role" "org_admin" {
scope_id = boundary_scope.global.id
grant_scope_id = boundary_scope.org.id
grant_strings = [
"id=*;type=*;actions=*"
]
principal_ids = [
boundary_user.admin_user.id
]
}

resource "boundary_role" "project_admin" {
name = "core_infra_admin"
description = "Administrator role for core infra"
scope_id = boundary_scope.org.id
grant_scope_id = boundary_scope.core_infra.id
grant_strings = [
"id=*;type=*;actions=*"
]
principal_ids = [
boundary_user.admin_user.id
]
}
19 changes: 19 additions & 0 deletions deployment/nomad/boundary/scope.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "boundary_scope" "global" {
global_scope = true
name = "global"
scope_id = "global"
}

resource "boundary_scope" "org" {
scope_id = boundary_scope.global.id
name = "primary"
description = "Primary organization scope"
}

resource "boundary_scope" "core_infra" {
name = "core_infra"
description = "Backend infrastructure project"
scope_id = boundary_scope.org.id
auto_create_admin_role = true
auto_create_default_role = true
}
35 changes: 35 additions & 0 deletions deployment/nomad/boundary/targets.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
resource "boundary_target" "backend_servers_ssh" {
type = "tcp"
name = "backend_servers_ssh"
description = "Backend SSH target"
scope_id = boundary_scope.core_infra.id
session_connection_limit = -1
default_port = 22
host_source_ids = [
boundary_host_set.backend_servers.id
]
}

resource "boundary_host_catalog" "backend_servers" {
name = "backend_servers"
description = "Web servers for backend team"
type = "static"
scope_id = boundary_scope.core_infra.id
}

resource "boundary_host" "backend_servers" {
for_each = var.target_ips
type = "static"
name = "backend_server_${each.value}"
description = "Backend server #${each.value}"
address = each.key
host_catalog_id = boundary_host_catalog.backend_servers.id
}

resource "boundary_host_set" "backend_servers" {
type = "static"
name = "backend_servers"
description = "Host set for backend servers"
host_catalog_id = boundary_host_catalog.backend_servers.id
host_ids = [for host in boundary_host.backend_servers : host.id]
}
20 changes: 20 additions & 0 deletions deployment/nomad/boundary/users.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "boundary_user" "admin_user" {
name = "admin"
account_ids = [boundary_account_password.admin_acct.id]
scope_id = boundary_scope.org.id
}

resource "boundary_account_password" "admin_acct" {
name = "admin"
type = "password"
login_name = "admin"
password = "foofoofoo"
auth_method_id = boundary_auth_method.password.id
}

resource "boundary_group" "backend_core_infra" {
name = "backend"
description = "Backend team group"
member_ids = [boundary_user.admin_user.id]
scope_id = boundary_scope.core_infra.id
}
8 changes: 8 additions & 0 deletions deployment/nomad/boundary/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
variable "boundary_address" {
default = "http://localhost:9200"
}

variable "target_ips" {
type = set(string)
default = ["127.0.0.1"]
}
Binary file added deployment/nomad/img/nomad.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions deployment/nomad/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
variable "boundary_address" {
default = "http://localhost:9200"
}

variable "nomad_address" {
default = "http://localhost:4646"
}

variable "target_ips" {
type = set(string)
description = "SSH Targets for Boundary to proxy"
default = ["127.0.0.1"]
}

module "nomad" {
source = "./nomad"
nomad_address = var.nomad_address
}

module "boundary" {
source = "./boundary"
boundary_address = var.boundary_address
target_ips = var.target_ips
}
34 changes: 34 additions & 0 deletions deployment/nomad/nomad-config.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
client {
enabled = true
}

server {
enabled = true
bootstrap_expect = 1
}

data_dir = "/opt/nomad"
bind_addr = "0.0.0.0"

plugin "docker" {
config {
allow_caps = [
# Default caps
"audit_write",
"chown",
"dac_override",
"fowner",
"fsetid",
"kill",
"mknod",
"net_bind_service",
"setfcap",
"setgid",
"setpcap",
"setuid",
"sys_chroot",
# Needed for mlock
"ipc_lock"
]
}
}
Loading