Skip to content

Commit 10e2e83

Browse files
committed
add aws,azure support
Signed-off-by: sal rashid <[email protected]>
1 parent 12c0764 commit 10e2e83

21 files changed

+1002
-65
lines changed

README.md

Lines changed: 196 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,25 @@
22

33
Python library which supports TPM embedded credentials for various cloud providers.
44

5-
Ath the moment, only GCP but followup with AWS and Azure when i find time (they're all implemented in golang, its just tedious to map to python)
6-
7-
For GCP:
8-
9-
This is an extension of GCP [google-auth-python](https://github.com/googleapis/google-auth-library-python) specifically intended to use service account credentials which are embedded inside a `Trusted Platform Module (TPM)`.
10-
115
on python pypi: [https://pypi.org/project/cloud-auth-tpm/](https://pypi.org/project/cloud-auth-tpm/)
126

13-
147
> **>>WARNING<<**: This code is not affiliated with or supported by google
158
169
---
1710

1811
### Usage
1912

20-
- **GCPCredentials**
13+
You need to first embed an RSA key into a TPM thats readable by [python-tss](https://github.com/tpm2-software/tpm2-pytss). See the [Setup](#setup)
14+
15+
##### **GCPCredentials**
2116

2217
```python
2318
from google.cloud import storage
2419
from cloud_auth_tpm.gcp.gcpcredentials import GCPCredentials
2520

26-
### acquire credential source on the TPM
2721
#### pip3 install cloud_auth_tpm[gcp]
2822
pc = GCPCredentials(tcti="device:/dev/tpmrm0",
29-
path="/HS/SRK/sign1",
23+
path="/HS/SRK/gcpsign1",
3024
email="jwt-access-svc-account@$PROJECT_ID.iam.gserviceaccount.com")
3125

3226
storage_client = storage.Client(project="$PROJECT_ID", credentials=pc)
@@ -36,19 +30,54 @@ for bkt in buckets:
3630
print(bkt.name)
3731
```
3832

39-
- **AWSHmacCredentials**
40-
41-
TODO
42-
43-
- **AWSRolesAnywhereCredentials**
44-
45-
TODO
33+
##### **AWSCredentials**
4634

47-
- **AzureCredentials**
48-
49-
TODO
35+
```python
36+
import boto3
37+
from cloud_auth_tpm.aws.awscredentials import AWSCredentials
38+
39+
#### pip3 install cloud_auth_tpm[aws]
40+
pc = AWSCredentials(tcti="device:/dev/tpmrm0",
41+
path="/HS/SRK/awssign1",
42+
profile_dir="./profiles",
43+
public_certificate_file="certs/alice-cert.crt",
44+
region="us-east-2",
45+
duration_seconds=3600,
46+
trust_anchor_arn='arn:aws:rolesanywhere:us-east-2:291738886522:trust-anchor/a545a1fc-5d86-4032-8f4c-61cdd6ff92ac',
47+
session_name="foo",
48+
role_arn="arn:aws:iam::291738886522:role/rolesanywhere1",
49+
profile_arn="arn:aws:rolesanywhere:us-east-2:291738886522:profile/6f4943fb-13d4-4242-89c4-be367595c560")
50+
51+
session = pc.get_session()
52+
53+
s3 = session.resource('s3')
54+
for bucket in s3.buckets.all():
55+
print(bucket.name)
56+
```
5057

58+
##### **AzureCredentials**
5159

60+
```python
61+
from azure.storage.blob import BlobServiceClient
62+
from cloud_auth_tpm.azure.azurecredentials import AzureCredentials
63+
64+
#### pip3 install cloud_auth_tpm[azure]
65+
pc = AzureCredentials(tcti="device:/dev/tpmrm0",
66+
path="/HS/SRK/azuresign1",
67+
profile_dir="./profiles",
68+
tenant_id="45243fbe-b73f-4f7d-8213-a104a99e428e",
69+
client_id="cffeaee2-5617-4784-8a4b-b647efd676e1",
70+
certificate_path="certs/azclient.crt")
71+
72+
blob_service_client = BlobServiceClient(
73+
account_url="https://$STORAGE_ACCOUNT.blob.core.windows.net",
74+
credential=pc
75+
)
76+
container_client = blob_service_client.get_container_client('mineral-minutia')
77+
blob_list = container_client.list_blobs()
78+
for blob in blob_list:
79+
print(blob.name)
80+
```
5281

5382
---
5483

@@ -63,75 +92,84 @@ for bkt in buckets:
6392
| **`profile_dir`** | FAPI profile_dir (optional; default: `"/etc/tpm2-tss/fapi-profiles"`) |
6493
| **`user_dir`** | FAPI user_dirs (optional; default: `"~/.local/share/tpm2-tss/user/keystore/" `) |
6594

66-
- **GCPCredentials**
95+
##### **GCPCredentials**
6796

6897
| Option | Description |
6998
|:------------|-------------|
7099
| **`email`** | ServiceAccount email (required; default: ``) |
71-
| **`scopes`** | Signed Jwt Scopes (optional default: `"https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/userinfo.email"`) |
100+
| **`scopes`** | Signed Jwt Scopes (optional default: `"https://www.googleapis.com/auth/cloud-platform"`) |
72101
| **`keyid`** | ServiceAccount keyid (optional; default: ``) |
73102
| **`expire_in`** | Token expiration in seconds (optional; default: `3600`) |
74103

75-
- **AWSHmacCredentials**
76-
77-
>> TODO:
104+
##### **AWSCredentials**
78105

79-
- **AWSRolesAnywhereCredentials**
80-
81-
>> TODO
106+
| Option | Description |
107+
|:------------|-------------|
108+
| **`public_certificate_file`** | Path to public x509 (required; default: ``) |
109+
| **`region`** | AWS Region (optional default: ``) |
110+
| **`duration_seconds`** | Duration in seconds for the token lifetime (optional; default: `3600`) |
111+
| **`trust_anchor_arn`** | RolesAnywhere Trust anchor ARN (required; default: ``) |
112+
| **`role_arn`** | RolesAnywhere RoleArn (required; default: ``) |
113+
| **`profile_arn`** | RolesAnywhere Profile Arn (Required; default: ``) |
114+
| **`session_name`** | AWS Session Name (optional; default: ``) |
82115

83-
- **AzureCredentials**
116+
##### **AzureCredentials**
84117

85-
>> TODO
118+
| Option | Description |
119+
|:------------|-------------|
120+
| **`tenant_id`** | Azure TentantID (required; default: ``) |
121+
| **`client_id`** | Azure Application (client) ID (required; default: ``) |
122+
| **`certificate_path`** | x509 certificate to authenticate with (required; default ``) |
86123

87124
---
88125

89126
### Setup
90127

91128
This library uses the [Feature API](https://tpm2-pytss.readthedocs.io/en/latest/fapi.html) provided through `tpm2_pytss`.
92129

93-
To install that:
130+
You need to first install [tpm2-tss](https://github.com/tpm2-software/tpm2-tss) `version>=4.1.0` (see [issue#596](https://github.com/tpm2-software/tpm2-pytss/issues/596))
94131

95132
```bash
96133
## tpm2-tss > 4.1.0 https://github.com/tpm2-software/tpm2-tss
97134
apt-get install libtss2-dev
98135
python3 -m pip install tpm2-pytss
99136
```
100137

101-
#### Setup - GCP
138+
#### Using RSA Keys on TPM
102139

103-
There are several ways you can have a TPM based service account key:
140+
You can initialize a TPM based RSA key and optional certificate in several ways:
104141

105-
1. create a key on the tpm, use it to create an x509 and upload the certificate
142+
1. create a key on the tpm
106143
2. import an the raw private key into the TPM
107144
3. securely transfer a key from on machine to the machine with the TPM and then import
108145

109-
This example will just cover (2) for simplicity. For more info, see [oauth2/tpm2tokensource](https://github.com/salrashid123/oauth2?tab=readme-ov-file#usage)
146+
This example will just cover (2) for simplicity which for the FAPI, is done using the [example/load.py](example/load.py) utility.
147+
148+
For more info, see [oauth2/tpm2tokensource](https://github.com/salrashid123/oauth2?tab=readme-ov-file#usage)
110149

111150
For additional examples on using FAPI with python to perform operations, see [salrashid123/tpm2/pytss](https://github.com/salrashid123/tpm2/tree/master/pytss)
112151

113-
Once you install the FAPI, you will need to embed a service account key into the TPM.
152+
153+
#### Setup - GCP
154+
155+
This is an extension of GCP [google-auth-python](https://github.com/googleapis/google-auth-library-python) specifically intended to use service account credentials which are embedded inside a `Trusted Platform Module (TPM)`.
114156

115157
Setup a new key and download the json
116158

117159
```bash
118160
export PROJECT_ID=`gcloud config get-value core/project`
119-
120-
gcloud iam service-accounts create jwt-access-svc-account --display-name "Test Service Account"
121-
122161
export SERVICE_ACCOUNT_EMAIL=jwt-access-svc-account@$PROJECT_ID.iam.gserviceaccount.com
123162

163+
gcloud iam service-accounts create jwt-access-svc-account --display-name "Test Service Account"
124164
gcloud iam service-accounts keys create jwt-access-svc-account.json --iam-account=$SERVICE_ACCOUNT_EMAIL
125-
126165
gcloud projects add-iam-policy-binding $PROJECT_ID --member=serviceAccount:$SERVICE_ACCOUNT_EMAIL --role=roles/storage.admin
127166
```
128167

129168
Extract the `key_id`, `email` and the raw RSA key
130169

131170
```bash
132171
export KEYID=`cat jwt-access-svc-account.json | jq -r '.private_key_id'`
133-
134-
export EMAIL=`cat jwt-access-svc-account.json | jq -r '.client_email'`
172+
export SERVICE_ACCOUNT_EMAIL=`cat jwt-access-svc-account.json | jq -r '.client_email'`
135173

136174
cat jwt-access-svc-account.json | jq -r '.private_key' > /tmp/private.pem
137175
```
@@ -140,14 +178,18 @@ Now use the `load.py` FAPI commands to embed the key into the TPM and save it at
140178

141179
```bash
142180
cd example/
143-
pip3 install -r requirements.txt
144-
145-
# rm -rf ~/.local/share/tpm2-tss # warning, this'll delete fapi objects you have
146-
python3 load_gcp.py --path="/HS/SRK/sign1" --private_key=/tmp/private.pem --tcti=device:/dev/tpmrm0 # --tcti="swtpm:port=2321"
181+
# rm -rf ~/.local/share/tpm2-tss # warning, this will clear any FAPI objects
147182

183+
python3 load.py --path="/HS/SRK/gcpsign1" \
184+
--private_key=/tmp/private.pem \
185+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
148186

149187
### then run:
150-
python3 main_gcp.py --path=/HS/SRK/sign1 --email=$EMAIL --project_id=$PROJECT_ID --tcti=device:/dev/tpmrm0 # --tcti="swtpm:port=2321"
188+
pip3 install -r requirements-gcp.txt
189+
190+
python3 main_gcp.py --path=/HS/SRK/gcpsign1 \
191+
--email=$SERVICE_ACCOUNT_EMAIL --project_id=$PROJECT_ID \
192+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
151193
```
152194

153195
##### How it works - GCP
@@ -175,19 +217,106 @@ So since we have the RSA key on the TPM, we can use the FAPI to make it "sign" d
175217

176218
#### Setup - AWS
177219

178-
##### HMAC
220+
[AWS Roles Anywhere](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html) allows for client authentication based on digital signature from trusted private keys.
179221

180-
TODO: once FAPI supports hmac
222+
The trusted client RSA or EC key is embedded within a TPM and that is used to sign the RolesAnywhere header values.
181223

182-
ref: [AWS Credentials for Hardware Security Modules and TPM based AWS_SECRET_ACCESS_KEY](https://github.com/salrashid123/aws_hmac)
224+
In the example in this repo, we will use a *EXAMPLE* CA and key. If you follow this setup, you are using a the rsa key and CA found in this repo....so *please* remember to use test resources and promptly delete/disable this.
183225

184-
##### Roles Anywhere
226+
The specific certificate CA and private key is the same as described in the sample here:
185227

186-
TODO: support [AWA RolesAnywhere Signer](https://github.com/salrashid123/aws_rolesanywhere_signer)
228+
* [AWA RolesAnywhere Signer](https://github.com/salrashid123/aws_rolesanywhere_signer)
229+
230+
When you setup RolesAnywhere, note down the ARN for the `TrustAnchorArn`, `ProfileArn` and `RoleArn` as well as the `region`. Ideally, the role has `AmazonS3ReadOnlyAccess` to list buckets.
231+
232+
We'll use `example/load.py` FAPI commands to embed the key into the TPM and save it at FAPI path of your choice, eg `/HS/SRK/awssign1`:
233+
234+
```bash
235+
cd example/
236+
# rm -rf ~/.local/share/tpm2-tss # warning, this will clear any FAPI objects
237+
238+
python3 load.py --path="/HS/SRK/awssign1" \
239+
--private_key=certs/alice-cert.key \
240+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
241+
```
242+
243+
Then attempt to use the credentials and specify the specific ARN values
244+
245+
```bash
246+
export CERTIFICATE="certs/alice-cert.crt"
247+
export REGION="us-east-2"
248+
export TRUST_ANCHOR_ARN="arn:aws:rolesanywhere:us-east-2:291738886522:trust-anchor/a545a1fc-5d86-4032-8f4c-61cdd6ff92ac"
249+
export ROLE_ARN="arn:aws:iam::291738886522:role/rolesanywhere1"
250+
export PROFILE_ARN="arn:aws:rolesanywhere:us-east-2:291738886522:profile/6f4943fb-13d4-4242-89c4-be367595c560"
251+
252+
pip3 install -r requirements-aws.txt
253+
254+
python3 main_aws.py --public_certificate_file=$CERTIFICATE \
255+
--region=$REGION --trust_anchor_arn=$TRUST_ANCHOR_ARN \
256+
--role_arn=$ROLE_ARN \
257+
--profile_arn=$PROFILE_ARN \
258+
--path="/HS/SRK/awssign1" \
259+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
260+
```
261+
262+
Currently ONLY RSASSA keys are supported (its easy enough to support others, TODO)
263+
264+
An alternative to using this library is invoking a process to acquire credentials from any SDK. See: [AWS Process Credentials for Trusted Platform Module (TPM)](https://github.com/salrashid123/aws-tpm-process-credential).
265+
266+
TODO: once FAPI supports hmac, i'll try to add on HMAC auth too ref: [AWS Credentials for Hardware Security Modules and TPM based AWS_SECRET_ACCESS_KEY](https://github.com/salrashid123/aws_hmac) and specifically [AWS v4 signed request using Trusted Platform Module](https://gist.github.com/salrashid123/bca7a24e1d59567adb89fef093d8564d)
187267

188268
#### Setup - Azure
189269

190-
TODO: [KMS, TPM and HSM based Azure Certificate Credentials](https://github.com/salrashid123/azsigner)
270+
Azure authentication uses an the basic [Microsoft identity platform application authentication certificate credentials](https://learn.microsoft.com/en-us/entra/identity-platform/certificate-credentials) where the variation here is that the client rsa key is on the TPM
271+
272+
The following example assumes you have set this up. You can find an example/test setup here:
273+
274+
* [KMS, TPM and HSM based Azure Certificate Credentials](https://github.com/salrashid123/azsigner)
275+
276+
We'll use `example/load.py` FAPI commands to embed the key into the TPM and save it at FAPI path of your choice, eg `/HS/SRK/azuresign1`:
277+
278+
```bash
279+
cd example/
280+
# rm -rf ~/.local/share/tpm2-tss # warning, this will clear any FAPI objects
281+
282+
python3 load.py --path="/HS/SRK/azuresign1" \
283+
--private_key=certs/azclient.key \
284+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
285+
```
286+
287+
Then configure env vars (the )
288+
289+
```bash
290+
## this is just the public cert and key pem in one file
291+
export CERTIFICATE_PATH_COMBINED_DER="certs/azclient-cert-key.pem"
292+
## this is just the public cert
293+
export CERTIFICATE_PATH="certs/azclient.crt"
294+
export CLIENT_ID="cffeaee2-5617-4784-8a4b-b647efd676e1"
295+
export TENANT_ID="45243fbe-b73f-4f7d-8213-a104a99e428e"
296+
297+
## test that you have the cert based auth working
298+
az login --service-principal -u $CLIENT_ID -p $CERTIFICATE_PATH_COMBINED_DER --tenant=$TENANT_ID
299+
az account get-access-token --scope="api://$CLIENT_ID/.default"
300+
301+
## if the principal has access to a storage container, test that
302+
export STORAGE_ACCOUNT=your-storage-account
303+
export CONTAINER=your-container
304+
export AZURE_TOKEN=$(az account get-access-token --resource https://storage.azure.com/ --query accessToken -o tsv)
305+
306+
curl -s --oauth2-bearer "$AZURE_TOKEN" -H 'x-ms-version: 2017-11-09' \
307+
"https://$STORAGE_ACCOUNT.blob.core.windows.net/$CONTAINER?restype=container&comp=list" | xmllint - --format
308+
309+
## now you're ready to test with the client using the embedded TPM key
310+
311+
pip3 install -r requirements-azure.txt
312+
313+
python3 main_azure.py --certificate_path=$CERTIFICATE_PATH \
314+
--client_id=$CLIENT_ID --tenant_id=$TENANT_ID \
315+
--path="/HS/SRK/azuresign1" \
316+
--tcti="swtpm:port=2321" # --tcti=device:/dev/tpmrm0
317+
```
318+
319+
Currently ONLY RSASSA keys are supported (its easy enough to support others, TODO)
191320

192321
#### Local Build
193322

@@ -201,7 +330,20 @@ virtualenv env
201330
source env/bin/activate
202331

203332
pip3 install ../
204-
pip3 install -r requirements-gcp.txt
333+
## depending on the variant provider
334+
# pip3 install -r requirements-gcp.txt
335+
# pip3 install -r requirements-aws.txt
336+
# pip3 install -r requirements-azure.txt
337+
338+
339+
### to deploy/upload
340+
# virtualenv env
341+
# source env/bin/activate
342+
# python3 -m pip install --upgrade build
343+
# python3 -m pip install --upgrade twine
344+
# python3 -m build
345+
# python3 -m twine upload --repository testpypi dist/*
346+
# python3 -m twine upload dist/*
205347
```
206348

207349
#### Software TPM
@@ -222,4 +364,3 @@ export TPM2OPENSSL_TCTI="swtpm:port=2321"
222364

223365
tpm2_flushcontext -t && tpm2_flushcontext -s && tpm2_flushcontext -l
224366
```
225-

cloud_auth_tpm/aws/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from cloud_auth_tpm.aws.awscredentials import AWSCredentials

0 commit comments

Comments
 (0)