mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
A few small updates to the recent "Federate access to AWS with Nomad Workload Identity" documentation, most notably that restart isn't needed because AWS SDKs handle OIDC reauth gracefully (unlike any other type of auth - for all others it's cached statically on startup, so nothing but a full restart works in case your credentials expire).
407 lines
14 KiB
Plaintext
407 lines
14 KiB
Plaintext
---
|
|
layout: docs
|
|
page_title: Federate access to AWS with Nomad Workload Identity
|
|
description: |-
|
|
Integrate Nomad as an OpenID Connect (OIDC) provider with AWS IAM identity and federate access to AWS resources.
|
|
---
|
|
|
|
# Federate access to AWS with Nomad Workload Identity
|
|
|
|
This page describes how to integrate Nomad with AWS IAM as an OpenID Connect (OIDC) provider and
|
|
use [Workload Identity] to federate access to AWS resources and services. In this workflow, Nomad
|
|
is the [OpenID Connect Provider] (OP or OIDC provider) and generates JSON Web Tokens (JWTs) which
|
|
serve as workload identities. AWS IAM is the Relying Party (RP) and validates these workload
|
|
identity tokens with Nomad before it permits federated access to resources and services.
|
|
|
|
## Prerequisites
|
|
|
|
To integrate Nomad and AWS IAM identity, you need a running Nomad cluster that
|
|
meets the following prerequisites:
|
|
|
|
- Nomad v1.7.x or later
|
|
- TLS enabled
|
|
|
|
The instructions on this page also assume the following:
|
|
|
|
- Your AWS account has the necessary permissions to create IAM roles, policies, hosted zones,
|
|
and certificates.
|
|
- You are using Terraform to manage your AWS infrastructure and you have
|
|
[configured it to communicate with AWS](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).
|
|
|
|
## Workflow
|
|
|
|
The process to integrate Nomad as an OIDC provider with AWS consists of the following steps:
|
|
|
|
1. Create the required AWS resources:
|
|
- [Route 53 hosted zone](#create-a-hosted-zone)
|
|
- [AWS Certificate Manager SSL certificates](#generate-ssl-certificates)
|
|
- [Load balancer and listener](#create-an-application-load-balancer)
|
|
- [DNS Alias for the load balancer](#create-a-dns-alias)
|
|
- [OIDC Identity Provider](#create-an-oidc-identity-provider)
|
|
- [AWS IAM role](#create-an-iam-policy-for-oidc-federated-users)
|
|
1. Update the Nomad server configuration.
|
|
1. Create a jobspec file that accesses AWS and then run it to verify your configuration.
|
|
|
|
## Create and configure AWS resources
|
|
|
|
To use Nomad as an identity provider, you need to have a trusted SSL certificate for the
|
|
domain used by the cluster. The following example uses AWS Certificate Manager (ACM).
|
|
|
|
### Create a hosted zone
|
|
|
|
Use the [`aws_route53_zone` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone).
|
|
|
|
```hcl
|
|
variable "domain_name" {
|
|
type = string
|
|
default = "<DOMAIN_NAME>"
|
|
}
|
|
|
|
resource "aws_route53_zone" "example" {
|
|
name = var.domain_name
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<DOMAIN_NAME>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone#name):
|
|
The domain name of your Nomad cluster without the protocol or port. `var.domain_name` is used
|
|
throughout the examples on this page to reference `<DOMAIN_NAME>`.
|
|
|
|
### Generate SSL certificates
|
|
|
|
Use the [`aws_acm_certificate`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate)
|
|
and [`aws_acm_certificate_validation`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) resources to create a request and provide validation for an SSL
|
|
certficiate.
|
|
|
|
```hcl
|
|
variable "zone_id" {
|
|
type = string
|
|
default = "<HOSTED_ZONE_ID>"
|
|
}
|
|
|
|
resource "aws_acm_certificate" "example" {
|
|
domain_name = var.domain_name
|
|
validation_method = "DNS"
|
|
|
|
lifecycle {
|
|
create_before_destroy = true
|
|
}
|
|
}
|
|
|
|
resource "aws_route53_record" "cert_dns" {
|
|
allow_overwrite = true
|
|
name = tolist(aws_acm_certificate.example.domain_validation_options)[0].resource_record_name
|
|
records = [tolist(aws_acm_certificate.example.domain_validation_options)[0].resource_record_value]
|
|
type = tolist(aws_acm_certificate.example.domain_validation_options)[0].resource_record_type
|
|
zone_id = var.zone_id
|
|
ttl = 60
|
|
}
|
|
|
|
resource "aws_acm_certificate_validation" "example" {
|
|
certificate_arn = aws_acm_certificate.example.arn
|
|
validation_record_fqdns = [aws_route53_record.cert_dns.fqdn]
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<HOSTED_ZONE_ID>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#zone_id):
|
|
The ID of the hosted zone. This will be `aws_route53_zone.example.zone_id` if you are using the
|
|
examples from this page.
|
|
|
|
### Create an Application Load Balancer
|
|
|
|
Use the [`aws_lb` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb).
|
|
|
|
```hcl
|
|
resource "aws_lb" "test" {
|
|
name = "test-lb-tf"
|
|
internal = false
|
|
load_balancer_type = "application"
|
|
security_groups = [<SECURITY_GROUP_ID>]
|
|
subnets = [<SUBNET_IDS>]
|
|
|
|
enable_deletion_protection = true
|
|
|
|
access_logs {
|
|
bucket = <S3_BUCKET_ID>
|
|
prefix = "test-lb"
|
|
enabled = true
|
|
}
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<SECURITY_GROUP_ID>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#security_groups):
|
|
A list of security groups IDs that you want to apply to the load balancer.
|
|
- [`<SUBNET_IDS>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#subnets):
|
|
A list of subnet IDs that you want to attach to the load balancer.
|
|
- [`<S3_BUCKET_ID>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#bucket):
|
|
The ID of an S3 bucket to store the load balancer access logs in.
|
|
|
|
### Create a load balancer listener
|
|
|
|
Use the [`aws_lb_listener` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener).
|
|
|
|
```hcl
|
|
resource "aws_lb_listener" "example" {
|
|
load_balancer_arn = <LB_ARN>
|
|
port = "443"
|
|
protocol = "HTTPS"
|
|
ssl_policy = "ELBSecurityPolicy-2016-08"
|
|
certificate_arn = <CERT_ARN>
|
|
|
|
default_action {
|
|
type = "forward"
|
|
target_group_arn = <LB_TARGET_GROUP_ARN>
|
|
}
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<LB_ARN>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener#load_balancer_arn):
|
|
The AWS resource name of the load balancer. This will be `aws_lb.test.arn` if you are using the
|
|
examples from this page.
|
|
- [`<CERT_ARN>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener#certificate_arn):
|
|
The AWS resource name of the load balancer's certificate. This will be
|
|
`aws_acm_certificate_validation.example.certificate_arn` if you are using the examples from this
|
|
page.
|
|
- [`<LB_TARGET_GROUP_ARN>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener#target_group_arn): The AWS resource name of the target group for the load balancer to route traffic to. This group
|
|
includes your Nomad server instances.
|
|
|
|
### Create a DNS Alias
|
|
|
|
Use the [`aws_route53_record` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record).
|
|
|
|
```hcl
|
|
resource "aws_route53_record" "www" {
|
|
zone_id = <HOSTED_ZONE_ID>
|
|
name = var.domain_name
|
|
type = "A"
|
|
|
|
alias {
|
|
name = <LB_ALIAS_DNS_NAME>
|
|
zone_id = <LB_ALIAS_ZONE_ID>
|
|
evaluate_target_health = true
|
|
}
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<HOSTED_ZONE_ID>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#zone_id):
|
|
The ID of the hosted zone. This will be `aws_route53_zone.example.zone_id` if you are using the
|
|
examples from this page.
|
|
- [`<LB_ALIAS_DNS_NAME>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#name):
|
|
The DNS
|
|
name of the load balancer. This will be `aws_lb.test.dns_name` if you are using the examples
|
|
from this page.
|
|
- [`<LB_ALIAS_ZONE_ID>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record#zone_id):
|
|
The zone ID of the load balancer. This will be `aws_lb.test.zone_id` if you are using the
|
|
examples from this page.
|
|
|
|
### Create an OIDC Identity Provider
|
|
|
|
Use the [`aws_iam_openid_connect_provider` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider).
|
|
|
|
```hcl
|
|
data "tls_certificate" "example" {
|
|
url = <CERT_DOMAIN_NAME>
|
|
}
|
|
|
|
resource "aws_iam_openid_connect_provider" "nomad" {
|
|
# Nomad HTTPS URL
|
|
url = <CERT_DOMAIN_NAME>
|
|
|
|
client_id_list = [
|
|
"aws",
|
|
]
|
|
|
|
thumbprint_list = [data.tls_certificate.example.certificates.0.sha1_fingerprint]
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<CERT_DOMAIN_NAME>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider#url):
|
|
The domain name of the load balancer certificate. This will be
|
|
`aws_acm_certificate.example.domain_name` if you are using the examples from this page.
|
|
|
|
### Create an IAM policy for OIDC Federated Users
|
|
|
|
Use the [`aws_iam_role` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role)
|
|
to create an appropriate IAM role for workloads acting as federated users. This will be
|
|
specific to your use case. The following example allows workloads access to S3 buckets.
|
|
|
|
```hcl
|
|
# Variables for OIDC provider and AWS account
|
|
variable "oidc_provider" {
|
|
description = "The OIDC provider URL"
|
|
type = string
|
|
default = "<DOMAIN_NAME>"
|
|
}
|
|
|
|
variable "aws_account_id" {
|
|
description = "AWS account ID"
|
|
type = string
|
|
default = "<AWS_ACCOUNT_ID>"
|
|
}
|
|
|
|
data "aws_iam_policy_document" "assume_role" {
|
|
statement {
|
|
effect = "Allow"
|
|
|
|
principals {
|
|
type = "Federated"
|
|
identifiers = ["arn:aws:iam::${var.aws_account_id}:oidc-provider/${var.oidc_provider}"]
|
|
}
|
|
|
|
actions = ["sts:AssumeRoleWithWebIdentity"]
|
|
|
|
condition {
|
|
test = "StringEquals"
|
|
variable = "${var.oidc_provider}:aud"
|
|
values = ["aws"]
|
|
}
|
|
}
|
|
}
|
|
|
|
# Create an IAM role with the assume role policy generated above
|
|
resource "aws_iam_role" "s3_all_access_role" {
|
|
name = "s3_all_access_role"
|
|
assume_role_policy = data.aws_iam_policy_document.assume_role.json
|
|
|
|
tags = {
|
|
tag-key = "tag-value"
|
|
}
|
|
}
|
|
|
|
# Inline policy that defines what the role can do (full S3 access)
|
|
data "aws_iam_policy_document" "s3_access_policy" {
|
|
statement {
|
|
effect = "Allow"
|
|
|
|
actions = [
|
|
"s3:*",
|
|
"s3-object-lambda:*"
|
|
]
|
|
|
|
resources = ["*"] # You can scope this down to specific S3 buckets if necessary
|
|
}
|
|
}
|
|
|
|
# Create a policy resource from the inline policy document above
|
|
resource "aws_iam_policy" "policy" {
|
|
name = "nomad-oidc-policy"
|
|
description = "A policy for federated Nomad OIDC"
|
|
policy = data.aws_iam_policy_document.s3_access_policy.json
|
|
}
|
|
|
|
# Attach the S3 access policy to the IAM role
|
|
resource "aws_iam_role_policy_attachment" "test-attach" {
|
|
role = aws_iam_role.s3_all_access_role.name
|
|
policy_arn = aws_iam_policy.policy.arn
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- [`<DOMAIN_NAME>`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone#name):
|
|
The domain name of your Nomad cluster without the protocol or port.
|
|
- `<AWS_ACCOUNT_ID>`: The ID of the AWS account where the IAM role from the previous step was
|
|
created.
|
|
|
|
## Update the Nomad server configuration
|
|
|
|
After you configure AWS, modify the Nomad server configuration file. Add the
|
|
[`oidc_issuer` attribute](/nomad/docs/configuration/server#oidc_issuer) and set the value
|
|
to the domain name for the Nomad cluster. This [enables the HTTP endpoint in Nomad](https://developer.hashicorp.com/nomad/docs/configuration/server#oidc_issuer) that allows third parties to discover Nomad's OIDC
|
|
configuration.
|
|
|
|
```hcl
|
|
server {
|
|
enabled = true
|
|
[...]
|
|
oidc_issuer = "https://<DOMAIN_NAME>"
|
|
[...]
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- `<DOMAIN_NAME>`: The domain name of your Nomad cluster without the protocol or port.
|
|
|
|
Restart the Nomad server agent to apply the configuration changes.
|
|
|
|
## Create and run a sample jobspec file
|
|
|
|
Create and run a jobspec file to validate your configuration. The following file is named `s3-upload.nomad.hcl`, add the following configuration to it, and
|
|
save the file.
|
|
|
|
```hcl
|
|
job "s3" {
|
|
type = "batch"
|
|
group "bucket" {
|
|
task "copy" {
|
|
driver = "docker"
|
|
config {
|
|
image = "public.ecr.aws/aws-cli/aws-cli"
|
|
command = "s3"
|
|
args = ["cp", "/local/test.txt", "s3://<S3_BUCKET_NAME>/test-nomad.txt"]
|
|
}
|
|
|
|
identity {
|
|
name = "aws"
|
|
aud = ["aws"]
|
|
file = true
|
|
ttl = "1h"
|
|
|
|
# AWS SDKs gracefully handle OIDC/WebIdentity reauthentication when the
|
|
# session or token expire, therefore a restart isn't needed
|
|
change_mode = "noop"
|
|
}
|
|
|
|
template {
|
|
destination = "local/test.txt"
|
|
change_mode = "restart"
|
|
data = <<EOF
|
|
Job: {{ env "NOMAD_JOB_NAME" }}
|
|
Alloc: {{ env "NOMAD_ALLOC_ID" }}
|
|
EOF
|
|
}
|
|
|
|
env {
|
|
AWS_ROLE_ARN = "arn:aws:iam::<AWS_ACCOUNT_ID>:role/<IAM_ROLE_NAME>"
|
|
# The format of the token file is nomad_$NAME_OF_IDENTITY.jwt
|
|
AWS_WEB_IDENTITY_TOKEN_FILE = "${NOMAD_SECRETS_DIR}/nomad_aws.jwt"
|
|
}
|
|
|
|
resources {
|
|
cpu = 500
|
|
memory = 256
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
This configuration requires the following information:
|
|
- `<S3_BUCKET_NAME>`: The name of the S3 bucket where the test file will be saved.
|
|
- `<AWS_ACCOUNT_ID>`: The ID of the AWS account where the IAM role from the previous step was
|
|
created.
|
|
- `<IAM_ROLE_NAME>`: The name of the IAM role from previous steps. This will be
|
|
`s3_all_access_role` if you are using the examples from this page.
|
|
|
|
Submit the job to Nomad.
|
|
|
|
```shell-session
|
|
$ nomad job run s3-upload.nomad.hcl
|
|
```
|
|
|
|
Verify that the job completed successfully.
|
|
|
|
```shell-session
|
|
$ nomad job status s3
|
|
```
|
|
|
|
Verify that the file was also uploaded to the S3 bucket.
|
|
|
|
|
|
[Workload Identity]: /nomad/docs/concepts/workload-identity
|
|
[OpenID Connect Provider]: https://openid.net/developers/how-connect-works/
|