Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/github.com/dvsekhvalno…
Browse files Browse the repository at this point in the history
…v/jose2go-1.6.0
  • Loading branch information
github-actions[bot] authored Feb 5, 2025
2 parents f92bb73 + 19ca30e commit d433850
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 0 deletions.
12 changes: 12 additions & 0 deletions kms-okta-app/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Auto-generated by fogg. Do not edit
# Make improvements in fogg, so that everyone can benefit.

export TERRAFORM_VERSION := 1.3.6
export TF_PLUGIN_CACHE_DIR := ../../..//.terraform.d/plugin-cache

include ../../..//scripts/module.mk


help: ## display help for this makefile
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: help
85 changes: 85 additions & 0 deletions kms-okta-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
This is a module that creates an Okta app that's bounded to a KMS Key. You can use the KMS key to sign JWTs that are compatible with the Okta /token endpoints. After you create the Okta App, you can work with the Okta Token endpoint by roughly following these steps:
1. Creating a header with this kind of structure: `{{"alg": "<algorithm>", "typ": "JWT"}}`. To identify algorithms. Here are the algorithm options as of Febuary 5, 2025:
```
signing_algs = (
('RSASSA_PSS_SHA_256', 'PS256', 'sha256'),
('RSASSA_PSS_SHA_384', 'PS384', 'sha384'),
('RSASSA_PSS_SHA_512', 'PS512', 'sha512'),
('RSASSA_PKCS1_V1_5_SHA_256', 'RS256', 'sha256'),
('RSASSA_PKCS1_V1_5_SHA_384', 'RS384', 'sha384'),
('RSASSA_PKCS1_V1_5_SHA_512', 'RS512', 'sha512'),
('ECDSA_SHA_256', 'ES256', 'sha256'),
('ECDSA_SHA_384', 'ES384', 'sha384'),
('ECDSA_SHA_512', 'ES512', 'sha512'),
)
```
source: https://github.com/jmtapio/python-jwt-kms/blob/552668588c5eec5eff9346740660239baef22428/jwt_kms/jwa.py
So if your KMS key has type `RSASSA_PKCS1_V1_5_SHA_256`, your `alg` is `RS256`. If you want to dive deeper, you can look at the RFC section here: https://datatracker.ietf.org/doc/html/rfc7518#section-3.1

2. Creating a payload with this kind of structure with this format:
```json
{
"exp": <future timestamp>,
"iat": <number of seconds since Jan 1, 1970 UTC>,
"iss": client_id,
"aud": [okta_token_endpoint],
"sub": client_id,
}
```
each attribute can be found here: https://developer.okta.com/docs/api/openapi/okta-oauth/guides/client-auth/#token-claims-for-client-authentication-with-client-secret-or-private-key-jwt

3. Base64-encode values from step #1 and #2 and strip out the equal signs (`=`).
4. Concatenate the values from step #3 with a header.payload format, then use the AWS KMS `sign` operation from whatever AWS SDK you use. If you used algorithm type `RS256`, then your KMS Sign operation should use SigningAlgorithm type `RSASSA_PKCS1_V1_5_SHA_256`. You should be able to parse out the "Signature" value from your kms `sign` output.
5. Construct the JWT in this way: base64header.base64payload.signature-from-step-4

After you construct the JWT, you're ready to use it with the Okta Token endpoint here: https://developer.okta.com/docs/api/openapi/okta-oauth/guides/client-auth/#jwt-with-private-key
you just need to set the JWT from step 5 to the "client_assertion" value while making the request.

<!-- START -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
| <a name="requirement_okta"></a> [okta](#requirement\_okta) | ~> 4.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |
| <a name="provider_external"></a> [external](#provider\_external) | n/a |
| <a name="provider_okta"></a> [okta](#provider\_okta) | ~> 4.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_params"></a> [params](#module\_params) | github.com/chanzuckerberg/cztack//aws-ssm-params-writer | v0.63.3 |

## Resources

| Name | Type |
|------|------|
| [okta_app_oauth.idp_api](https://registry.terraform.io/providers/okta/okta/latest/docs/resources/app_oauth) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [external_external.jwks_info](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_friendly_key_identifier"></a> [friendly\_key\_identifier](#input\_friendly\_key\_identifier) | A name for the key configuration in the okta app, something you will recognize for yourself and the project. | `string` | n/a | yes |
| <a name="input_kms_key_id"></a> [kms\_key\_id](#input\_kms\_key\_id) | The Key ID or alias of the AWS KMS Key. It has to be available in the same region and account as the configured provider. | `string` | n/a | yes |
| <a name="input_okta_configuration"></a> [okta\_configuration](#input\_okta\_configuration) | Details needed to configure an okta app. Its token auth method is private\_key\_jwt | <pre>object({<br> label = string<br> type = string<br> grant_types = list(string)<br> omit_secret = bool<br> response_types = list(string)<br> pkce_required = bool<br> })</pre> | n/a | yes |
| <a name="input_tags"></a> [tags](#input\_tags) | These values are used to derive the path in the param store where to write the Okta App Configuration metadata. | <pre>object({<br> project = string,<br> env = string,<br> service = string,<br> owner = string,<br> })</pre> | n/a | yes |
| <a name="input_write_metadata_to_params"></a> [write\_metadata\_to\_params](#input\_write\_metadata\_to\_params) | Whether you want to include the clientID and KMS Key Alias grouped together as securestring parameters in JSON format. If true, module will write these details to a path based on the env, project and service"<br> They will be written following path:<br> /<project>-<env>-<service>/client\_id<br> /<project>-<env>-<service>/kms\_key\_id<br><br> (the module may add secrets over time)<br><br> Note that these values should correspond with the consuming service's tagset so secrets are placed in the path they expect. | `bool` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_client_id"></a> [client\_id](#output\_client\_id) | n/a |
| <a name="output_kms_id"></a> [kms\_id](#output\_kms\_id) | n/a |
<!-- END -->
2 changes: 2 additions & 0 deletions kms-okta-app/fogg.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto-generated by fogg. Do not edit
# Make improvements in fogg, so that everyone can benefit.
28 changes: 28 additions & 0 deletions kms-okta-app/get_jwks_for_okta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import boto3, json
from cryptography.hazmat.primitives.serialization import load_der_public_key
from jose import jwk, constants
import sys

aws_account_id = sys.argv[1]
kms_key_id = sys.argv[2]
region = sys.argv[3]

sts_client = boto3.client("sts", region_name=region)
assume_role_client = sts_client.assume_role(RoleArn=f"arn:aws:iam::{aws_account_id}:role/tfe-si", RoleSessionName='FetchKMSInformation')
credentials = assume_role_client["Credentials"]
kms_session = boto3.Session(
aws_access_key_id=credentials["AccessKeyId"],
aws_secret_access_key= credentials["SecretAccessKey"],
aws_session_token= credentials["SessionToken"],
region_name=region,
)
kms_client = kms_session.client("kms", region_name=region)

# try to get the public key in bytes
output = kms_client.get_public_key(
KeyId=kms_key_id,
)
assert output.get("PublicKey") is not None
public_key = load_der_public_key(output["PublicKey"])
jwks_vals = jwk.RSAKey(algorithm=constants.Algorithms.RS256, key=public_key).to_dict()
print(json.dumps(jwks_vals))
35 changes: 35 additions & 0 deletions kms-okta-app/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
# uses a key in core-platform-infra to generate credentials for this workspace
data "external" "jwks_info" {
program = ["bash", "${path.module}/run.sh", path.module, data.aws_caller_identity.current.account_id, var.kms_key_id, data.aws_region.current.name]
}

resource "okta_app_oauth" "idp_api" {
label = var.okta_configuration.label
type = var.okta_configuration.type
grant_types = var.okta_configuration.grant_types
response_types = var.okta_configuration.response_types
token_endpoint_auth_method = "private_key_jwt"
pkce_required = var.okta_configuration.pkce_required
jwks {
e = data.external.jwks_info.result.e
kty = data.external.jwks_info.result.kty
kid = var.friendly_key_identifier
n = data.external.jwks_info.result.n
}
}


module "params" {
source = "github.com/chanzuckerberg/cztack//aws-ssm-params-writer?ref=v0.63.3"
project = var.tags.project
env = var.tags.env
service = var.tags.service
owner = var.tags.owner

parameters = {
"client_id" = okta_app_oauth.idp_api.client_id
"kms_key_id" = var.kms_key_id
}
}
7 changes: 7 additions & 0 deletions kms-okta-app/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "client_id" {
value = okta_app_oauth.idp_api.id
}

output "kms_id" {
value = var.kms_key_id
}
5 changes: 5 additions & 0 deletions kms-okta-app/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

# need to install python-jose instead of jose: https://stackoverflow.com/a/65103147
pip install boto3 cryptography python-jose 2>&1 > /dev/null
python3 $1/get_jwks_for_okta.py $2 $3 $4
4 changes: 4 additions & 0 deletions kms-okta-app/terragrunt.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies {
paths = [
]
}
48 changes: 48 additions & 0 deletions kms-okta-app/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
variable "kms_key_id" {
type = string
description = "The Key ID or alias of the AWS KMS Key. It has to be available in the same region and account as the configured provider."
}

variable "okta_configuration" {
type = object({
label = string
type = string
grant_types = list(string)
omit_secret = bool
response_types = list(string)
pkce_required = bool
})
description = "Details needed to configure an okta app. Its token auth method is private_key_jwt"
}

variable "friendly_key_identifier" {
type = string
description = "A name for the key configuration in the okta app, something you will recognize for yourself and the project."
}

variable "write_metadata_to_params" {
type = bool
description = <<EOF
Whether you want to include the clientID and KMS Key Alias grouped together as securestring parameters in JSON format. If true, module will write these details to a path based on the env, project and service"
They will be written following path:
/<project>-<env>-<service>/client_id
/<project>-<env>-<service>/kms_key_id
(the module may add secrets over time)
Note that these values should correspond with the consuming service's tagset so secrets are placed in the path they expect.
EOF
}

variable "tags" {
type = object({
project = string,
env = string,
service = string,
owner = string,
})

description = <<EOF
These values are used to derive the path in the param store where to write the Okta App Configuration metadata.
EOF
}
10 changes: 10 additions & 0 deletions kms-okta-app/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_providers {
okta = {
source = "okta/okta"
version = "~> 4.0"
}
}

required_version = ">= 1.3"
}

0 comments on commit d433850

Please sign in to comment.