Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support importing multiple versions of the "same" key #50

Merged
merged 2 commits into from
Jan 12, 2025
Merged
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
1 change: 1 addition & 0 deletions .github/helpers/gen-compose-env
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ cat <<EOF
HV4GHA_ACCOUNT=andreaso
HV4GHA_APP_ID=368468
HV4GHA_APP_KEY_B64=${TEST_APP_KEY_B64}
HV4GHA_REVOKED_APP_KEY_B64=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBMEFFYTFDV212UitaQ3p1QmFYeFNnc1pDcjVvL0JhOEJ1dVNYWXYreXhhNTk4cktDCkFwQThHNHpSNys3cTM2UHNFYmJRZTgrS081L2ZqRGhZOXZuTHBrTlBZV2pGQ0wxSTlMcWZYekwyYi9iUmFzYUUKMklXemlZaEZ0TnBoK2lDeXZpZG55VnM2TmsrRnJmYXFKUVlCbG9MRXcycWZ3WVdsVEgvUDNibXFMQnAwWitkdgpob1MybmZzUlRlVWJTK2tEbXBYWFFHcmJGRWdRazVSeldLZ0JQVzNYalprMHBOTVpPbjZ3Sk5JYXdTbGZUdUczCjdVZEtsN2FacC9SV3E4Tk1TZTNSSTl2cE9ZRVVibHh6RHRPMUlpUUQwT2RiUXlHM3Y3TjNjN1lvNmFuQ2Z5SjMKWWVTOVVVcDZFQ0VKUlF3N013RFVvbXI1c3l5WGZicG0zVFRxMHdJREFRQUJBb0lCQVFDTkNoSzlBd0s2Zm5CeAowMWwrdzFQWFpNUFcra245ZXBzN3RNQ0oya1BJRXZkSDE0NmNXbHpIZVlib29DSVducm4xa1BadzNWc243dEhQCjdHMDJtazVpWUlXMDBTdTVMMUFHMFh3N092MHJFanNSb2FaMmdzSW9ZWVNTaXZtZ2R3ZDhOSkFGVXB4NE14ZTcKeStPMjB3bkVtc3I4anBIZzBOUXl1UCtqb3I4bXcxaGtNUWZJSkZMdzlLSWZ4NzJtVVhQTkZYWnRDTmc2cWVDTwp3bWNCV0lRcFhRTXRmQjY3RXlPN3FIZDVpZktiUm00Zmw5WGl6cUxiUWJjcUV0V0NpRGJ1RU5meGUyNC8zbVhrCngxa0paNnppc01NWUM3M3c4MlkxTXM4L3B1TlcyUDllN0xGZ2tDSk83QWxSbTdpbjdHUWhVNnlRVXlGT0lnaFMKODFHemdPbWhBb0dCQU91K2xzaEMzRk4yNVVnMEw4SVkwK1F5emFhMlNERjFhSk5RRFpTRU1nZjRSR01VMlM4UwpCQzVZWCtZeDFSVlJpYnFzR3JoNXJkdjFVWXFGQm5sSmxuanAwZVZZdmhJNysxbHV6YmNWY2Q2V2NUdDJ3anU2CmIwZElQTDg3c1V2eCtVVWx2ZW9WMTJ5cTRHS0hSQjhJekxLQzd0cTlNUUk2OHowV1k4cHZzY1RaQW9HQkFPSGcKV0l1K2FMWmExTlRvWW9peEIvUGxmOHBPRU12MVFCZHJpVHZWbE1rV1ZNTUdDR2xEZTNrUlQ5ZHoyWjFDOWFyWQpqSmoyYk1hdS93VlNPY0tyRzk1dlF1a01wTmM4ZndEUUZNUlZwTVFLTDIvVGxCOSswNlpOMFkzTEdwWmFUU3Z0CkRkeDBpcjhYT0pyZlIvQXhzRFE4VFRXdWJsNk82ZjAreTYzU0hiR0xBb0dCQUtQRjl3aE84ck9GUU1vRmZ6d3YKZFZ6dU1sSmtCZ2xlRUhWdXd0QnZlalp4TWtsSEhZNkd6S0xKd0cyeUp0ODFreUk5R0I4YVlUOFMwVUFUSkNrMApoeldlOHJPTEVCaW8xUGdQY3hpQzdHVi9URkRNTXltOVhqcytJdjJUWVp0cUVnc0lxa1FxUWt0NjlvRmNpV1dwCi9sTHhoVEF2Q1JJTWxCalhLaUpqc0RNWkFvR0JBTFI2ZXFXbURhcnZKckc3d01keGxHWW4wUlRIalpvNkt1ekwKcXNxWlhKblQ4d1FsTk9GTzMwQ2NPWXh3YjhlOU1laEJ3UTJUa05Tc0RPNm1oYXBxNkFpeUkrZHNoK1hHMjcrOQpnMnBnK0JjQUFHazh5RlRtRkowRC90VnFIS05ZSWVOZ05Ud2FEcWFqR2tKODk5RVBFcmlhR2lNemJkSzJKSGFLCkQxcWkwY0VEQW9HQVRudFNmUEtJeEhlRk5hTkRaT0pTdkhUOGtFblJhd2lKd2haZUZrVkdORWgzaHlrRks1aUIKb0lob0RpNWZCOTNiQTB0ZnN1Ujd1dHF2U1ExS0FKbWRFWVdpSWpBT3VxbHlYcFlUTVR3dVo3VTN5SWtWdThBWApJaDJSaEZHQ2pwOWd3UnNxN1ZMd3hGMm9iMzFVU3BiazFwaVJET2Q3T25xTGZHK3A5bEsyZmJzPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
HV4GHA_TEST_REPO=hv4gha
EOF
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ from hv4gha import import_app_key
with open("/path/to/github-app.private-key.pem", "r") as akh:
my_app_key = akh.read()

import_app_key(
response = import_app_key(
pem_key=my_app_key,
key_name="my-github-app",
vault_addr="https://vault.example.com:8200",
vault_token="...",
)

key_version = response["key_version"]
```

### Issue Access Token
Expand Down Expand Up @@ -98,9 +99,17 @@ path "transit/wrapping_key" {
capabilities = ["read"]
}

path "transit/keys/my-github-app" {
capabilities = ["read"]
}

path "transit/keys/my-github-app/import" {
capabilities = ["update"]
}

path "transit/keys/my-github-app/import_version" {
capabilities = ["update"]
}
```

### Issue policy
Expand Down
13 changes: 10 additions & 3 deletions hv4gha/entry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Top-level functions"""

from .gh import GitHubApp, TokenResponse
from .vault import VaultTransit
from .vault import ImportResponse, VaultTransit


def import_app_key(
Expand All @@ -12,7 +12,7 @@ def import_app_key(
vault_token: str,
transit_backend: str = "transit",
revoke_vault_token: bool = False,
) -> None:
) -> ImportResponse:
"""
Import GitHub App key into Vault's Transit engine

Expand All @@ -22,6 +22,8 @@ def import_app_key(
:param vault_token: Vault instance VAULT_TOKEN.
:param transit_backend: Transit backend mount path. Defaults to "transit".
:param revoke_vault_token: Revoke `vault_token` once done? Defaults to False.

:return: Key import info, containing the key version.
"""

if isinstance(pem_key, str):
Expand All @@ -32,14 +34,16 @@ def import_app_key(
vault_token=vault_token,
transit_backend=transit_backend,
)
transit.import_key(
key_import: ImportResponse = transit.import_key(
key_name=key_name,
pem_app_key=pem_key,
)

if revoke_vault_token:
transit.revoke_token()

return key_import


def issue_access_token(
*,
Expand All @@ -48,6 +52,7 @@ def issue_access_token(
vault_token: str,
app_id: int | str,
account: str,
key_version: int = 0,
permissions: None | dict[str, str] = None,
repositories: None | list[str] = None,
transit_backend: str = "transit",
Expand All @@ -61,6 +66,7 @@ def issue_access_token(
:param vault_token: Vault instance VAULT_TOKEN.
:param app_id: GitHub App ID.
:param account: GitHub account to access, where the App is installed.
:param key_version: Imported key version to use. Defaults to 0, the latest version.
:param permissions: Optionally scope (down) token permissions.
:param repositories: Optionally limit accessible repositories.
:param transit_backend: Vault Transit backend mount path. Defaults to "transit".
Expand All @@ -80,6 +86,7 @@ def issue_access_token(
)
jwt: str = transit.sign_jwt(
key_name=key_name,
key_version=key_version,
app_id=app_id,
)

Expand Down
140 changes: 113 additions & 27 deletions hv4gha/vault.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import base64
import json
import re
from datetime import datetime, timezone
from typing import Any, Final

Expand Down Expand Up @@ -30,6 +31,10 @@ class TokenRevokeError(VaultAPIError):
"""Failure to self-revoke the Vault token"""


class VersionLookupError(VaultAPIError):
"""Failure to lookup the version of the imported key version"""


class WrappingKeyDownloadError(VaultAPIError):
"""Failure to download the Vault Transit wrapping key"""

Expand All @@ -42,6 +47,20 @@ class VaultErrors(BaseModel):
errors: list[str]


class VersionData(TypedDict):
"""Part of KeyLookup"""

latest_version: int


class KeyLookup(BaseModel):
"""
https://developer.hashicorp.com/vault/api-docs/secret/transit#read-key
"""

data: VersionData


class JWTData(TypedDict):
"""Part of SignedJWT"""

Expand Down Expand Up @@ -70,6 +89,12 @@ class WrappingKey(BaseModel):
data: KeyData


class ImportResponse(TypedDict):
"""Typing for key import response"""

key_version: int


class VaultTransit:
"""Interact with Vault's Transit Secrets Engine"""

Expand All @@ -83,16 +108,46 @@ def __init__(self, *, vault_addr: str, vault_token: str, transit_backend: str):
self.auth_headers: Final[dict[str, str]] = {"X-Vault-Token": vault_token}
self.transit_backend: Final[str] = transit_backend.strip("/")

def __api_read(
self,
api_path: str,
) -> requests.models.Response:
read_url = self.vault_addr + api_path

response = requests.get(
read_url,
headers=self.auth_headers,
timeout=10,
)
response.raise_for_status()

return response

def __api_write(
self,
api_path: str,
payload: None | dict[str, Any] = None,
) -> requests.models.Response:
update_url = self.vault_addr + api_path

if payload is None:
payload = {}

response = requests.post(
update_url,
headers=self.auth_headers,
data=json.dumps(payload),
timeout=10,
)
response.raise_for_status()

return response

def __download_wrapping_key(self) -> rsa.RSAPublicKey:
wrapping_key_url = self.vault_addr + f"/v1/{self.transit_backend}/wrapping_key"
api_path = f"/v1/{self.transit_backend}/wrapping_key"

try:
response = requests.get(
wrapping_key_url,
headers=self.auth_headers,
timeout=10,
)
response.raise_for_status()
response: requests.models.Response = self.__api_read(api_path)
except requests.exceptions.HTTPError as http_error:
raise WrappingKeyDownloadError(http_error.response.text) from http_error

Expand All @@ -110,27 +165,46 @@ def __download_wrapping_key(self) -> rsa.RSAPublicKey:

return wrapping_key

def __api_write(
self,
api_path: str,
payload: None | dict[str, Any] = None,
) -> requests.models.Response:
update_url = self.vault_addr + api_path
def __lookup_version(self, *, key_name: str) -> int:
api_path = f"/v1/{self.transit_backend}/keys/{key_name}"

if payload is None:
payload = {}
try:
response: requests.models.Response = self.__api_read(api_path)
except requests.exceptions.HTTPError as http_error:
raise VersionLookupError(http_error.response.text) from http_error

response = requests.post(
update_url,
headers=self.auth_headers,
data=json.dumps(payload),
timeout=10,
)
response.raise_for_status()
try:
key_lookup_bm = KeyLookup(**response.json())
except ValidationError as validation_error:
error_message = "<Failed to parse key lookup API response>"
raise VersionLookupError(error_message) from validation_error

return response
return key_lookup_bm.data["latest_version"]

@staticmethod
def __check_import_version_error(http_error: requests.exceptions.HTTPError) -> bool:
try:
errors_bm = VaultErrors(**http_error.response.json())
except ValidationError:
return False
return "use import-version" in errors_bm.errors[0]

def __import_version(self, *, key_name: str, wrapped_b64: str) -> int:
api_path = f"/v1/{self.transit_backend}/keys/{key_name}/import_version"
payload = {
"ciphertext": wrapped_b64,
"hash_function": "SHA256",
}

def import_key(self, *, key_name: str, pem_app_key: bytes) -> None:
try:
self.__api_write(api_path, payload)
except requests.exceptions.HTTPError as http_error:
raise AppKeyImportError(http_error.response.text) from http_error

key_version: int = self.__lookup_version(key_name=key_name)
return key_version

def import_key(self, *, key_name: str, pem_app_key: bytes) -> ImportResponse:
"""
Import GitHub App key

Expand All @@ -150,16 +224,27 @@ def import_key(self, *, key_name: str, pem_app_key: bytes) -> None:
"allow_plaintext_backup": False,
}

key_import: ImportResponse = {"key_version": 1}
try:
self.__api_write(api_path, payload)
return key_import
except requests.exceptions.HTTPError as http_error:
raise AppKeyImportError(http_error.response.text) from http_error
if not self.__check_import_version_error(http_error):
raise AppKeyImportError(http_error.response.text) from http_error

key_version: int = self.__import_version(
key_name=key_name, wrapped_b64=wrapped_b64
)
key_import["key_version"] = key_version

return key_import

def sign_jwt(self, *, key_name: str, app_id: str) -> str:
def sign_jwt(self, *, key_name: str, key_version: int, app_id: str) -> str:
"""
Sign JWT token to authenticate towards GitHub

:param key_name: Transit Engine key name.
:param key_version: Transit Engine key version.
:param app_id: GitHub App ID.


Expand All @@ -174,6 +259,7 @@ def sign_jwt(self, *, key_name: str, app_id: str) -> str:
"input": b64str(header_and_claims),
"hash_algorithm": "sha2-256",
"signature_algorithm": "pkcs1v15",
"key_version": key_version,
}

try:
Expand All @@ -187,7 +273,7 @@ def sign_jwt(self, *, key_name: str, app_id: str) -> str:
error_message = "<Failed to parse Sign JWT API response>"
raise JWTSigningError(error_message) from validation_error

signature = signature_bm.data["signature"].removeprefix("vault:v1:")
signature = re.sub(r"^vault:v[0-9]+:", "", signature_bm.data["signature"])
signature = b64str(base64.b64decode(signature), urlsafe=True)

jwt_token = header_and_claims + "." + signature
Expand Down
7 changes: 4 additions & 3 deletions integration/.local.sops.env
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
HV4GHA_ACCOUNT=andreaso
HV4GHA_APP_ID=368468
HV4GHA_APP_KEY_B64=ENC[AES256_GCM,data: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,iv:8nhW3XAPn8anR5OR4V8/gtn/J8XTZofNck0IFsuczuo=,tag:btBex56eQw1Y/H0Z7wceWA==,type:str]
HV4GHA_REVOKED_APP_KEY_B64=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBMEFFYTFDV212UitaQ3p1QmFYeFNnc1pDcjVvL0JhOEJ1dVNYWXYreXhhNTk4cktDCkFwQThHNHpSNys3cTM2UHNFYmJRZTgrS081L2ZqRGhZOXZuTHBrTlBZV2pGQ0wxSTlMcWZYekwyYi9iUmFzYUUKMklXemlZaEZ0TnBoK2lDeXZpZG55VnM2TmsrRnJmYXFKUVlCbG9MRXcycWZ3WVdsVEgvUDNibXFMQnAwWitkdgpob1MybmZzUlRlVWJTK2tEbXBYWFFHcmJGRWdRazVSeldLZ0JQVzNYalprMHBOTVpPbjZ3Sk5JYXdTbGZUdUczCjdVZEtsN2FacC9SV3E4Tk1TZTNSSTl2cE9ZRVVibHh6RHRPMUlpUUQwT2RiUXlHM3Y3TjNjN1lvNmFuQ2Z5SjMKWWVTOVVVcDZFQ0VKUlF3N013RFVvbXI1c3l5WGZicG0zVFRxMHdJREFRQUJBb0lCQVFDTkNoSzlBd0s2Zm5CeAowMWwrdzFQWFpNUFcra245ZXBzN3RNQ0oya1BJRXZkSDE0NmNXbHpIZVlib29DSVducm4xa1BadzNWc243dEhQCjdHMDJtazVpWUlXMDBTdTVMMUFHMFh3N092MHJFanNSb2FaMmdzSW9ZWVNTaXZtZ2R3ZDhOSkFGVXB4NE14ZTcKeStPMjB3bkVtc3I4anBIZzBOUXl1UCtqb3I4bXcxaGtNUWZJSkZMdzlLSWZ4NzJtVVhQTkZYWnRDTmc2cWVDTwp3bWNCV0lRcFhRTXRmQjY3RXlPN3FIZDVpZktiUm00Zmw5WGl6cUxiUWJjcUV0V0NpRGJ1RU5meGUyNC8zbVhrCngxa0paNnppc01NWUM3M3c4MlkxTXM4L3B1TlcyUDllN0xGZ2tDSk83QWxSbTdpbjdHUWhVNnlRVXlGT0lnaFMKODFHemdPbWhBb0dCQU91K2xzaEMzRk4yNVVnMEw4SVkwK1F5emFhMlNERjFhSk5RRFpTRU1nZjRSR01VMlM4UwpCQzVZWCtZeDFSVlJpYnFzR3JoNXJkdjFVWXFGQm5sSmxuanAwZVZZdmhJNysxbHV6YmNWY2Q2V2NUdDJ3anU2CmIwZElQTDg3c1V2eCtVVWx2ZW9WMTJ5cTRHS0hSQjhJekxLQzd0cTlNUUk2OHowV1k4cHZzY1RaQW9HQkFPSGcKV0l1K2FMWmExTlRvWW9peEIvUGxmOHBPRU12MVFCZHJpVHZWbE1rV1ZNTUdDR2xEZTNrUlQ5ZHoyWjFDOWFyWQpqSmoyYk1hdS93VlNPY0tyRzk1dlF1a01wTmM4ZndEUUZNUlZwTVFLTDIvVGxCOSswNlpOMFkzTEdwWmFUU3Z0CkRkeDBpcjhYT0pyZlIvQXhzRFE4VFRXdWJsNk82ZjAreTYzU0hiR0xBb0dCQUtQRjl3aE84ck9GUU1vRmZ6d3YKZFZ6dU1sSmtCZ2xlRUhWdXd0QnZlalp4TWtsSEhZNkd6S0xKd0cyeUp0ODFreUk5R0I4YVlUOFMwVUFUSkNrMApoeldlOHJPTEVCaW8xUGdQY3hpQzdHVi9URkRNTXltOVhqcytJdjJUWVp0cUVnc0lxa1FxUWt0NjlvRmNpV1dwCi9sTHhoVEF2Q1JJTWxCalhLaUpqc0RNWkFvR0JBTFI2ZXFXbURhcnZKckc3d01keGxHWW4wUlRIalpvNkt1ekwKcXNxWlhKblQ4d1FsTk9GTzMwQ2NPWXh3YjhlOU1laEJ3UTJUa05Tc0RPNm1oYXBxNkFpeUkrZHNoK1hHMjcrOQpnMnBnK0JjQUFHazh5RlRtRkowRC90VnFIS05ZSWVOZ05Ud2FEcWFqR2tKODk5RVBFcmlhR2lNemJkSzJKSGFLCkQxcWkwY0VEQW9HQVRudFNmUEtJeEhlRk5hTkRaT0pTdkhUOGtFblJhd2lKd2haZUZrVkdORWgzaHlrRks1aUIKb0lob0RpNWZCOTNiQTB0ZnN1Ujd1dHF2U1ExS0FKbWRFWVdpSWpBT3VxbHlYcFlUTVR3dVo3VTN5SWtWdThBWApJaDJSaEZHQ2pwOWd3UnNxN1ZMd3hGMm9iMzFVU3BiazFwaVJET2Q3T25xTGZHK3A5bEsyZmJzPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
HV4GHA_TEST_REPO=hv4gha
sops_encrypted_regex=^HV4GHA_APP_KEY_B64$
sops_lastmodified=2024-12-07T07:27:09Z
sops_mac=ENC[AES256_GCM,data:HgV15HFZI75xHsr1hs+4KrCAseGZbkW8cvVcfvKyuwUAHZzAVu/R7768MGIrHR1AXW6ZrknS26XERxEmjD2B7sUfpcgoQv7m7rwrNmPx1JSbHLQO2qjErMb6SQyY983TKSDpOfWPtzeYZCMMECA20bMlxV2Vb/j2+ueU7JtTrQU=,iv:pJPKmLbxz8EgAt8tOcdEohLq78bTXRPPaPlESG+qkys=,tag:b6kDt3+JoJuwMC5GIokuFA==,type:str]
sops_lastmodified=2025-01-12T06:53:39Z
sops_mac=ENC[AES256_GCM,data:H3pzCZQ4f89oKWGBD+hhq4EhIpHTjS1WqrFWqWm/dKBI77qHvuiSFGL8gGMs4DpUTCSUZdoxrsLugYHjwihJrXKABkPBPImuvmaucJ85U7uxcIbaTFp6m8J4rOky/j7YFGM77fDSGHskr7KuJp+LAdEVX/BBVWOUVsp/VFXGC+Q=,iv:57OPDfzxbhYwc6ramD6i9YS1m5gpyb4ujW51y9aRjME=,tag:Gdcl5zJFlO7d3rdwMC/8Yg==,type:str]
sops_pgp__list_0__map_created_at=2024-05-18T10:05:25Z
sops_pgp__list_0__map_enc=-----BEGIN PGP MESSAGE-----\n\nwV4D2VOvz+iLZv8SAQdARf9i6HtOiBdf+ugeHQ6YV3QKka/fQipO8ovZGY5AQQUw\nlyJ6ZB/W9EuPFTh1LwHZgqPhhC8Gy8c3A5Q5ysS1F1mNBYtRDJYGXPUa6m+f2+dE\n0lEBSlcdwsryiXgb0lrIgwDXoT1tmVf45vzUIzeLvnCleajycNZyHPprLLdkt52E\n1W2VdBFnr87wrcIErdEVyQbL25m/s3y5QvK9HASgMA/hT8k=\n=n7p3\n-----END PGP MESSAGE-----
sops_pgp__list_0__map_fp=17E608319C69AE121E3D2DA4B8D8531495B2E77C
sops_version=3.9.2
sops_version=3.9.3
8 changes: 8 additions & 0 deletions integration/setup
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,17 @@ path "transit/wrapping_key" {
capabilities = ["read"]
}

path "transit/keys/+" {
capabilities = ["read"]
}

path "transit/keys/+/import" {
capabilities = ["update"]
}

path "transit/keys/+/import_version" {
capabilities = ["update"]
}
EOF
vault policy write import-key -

Expand Down
7 changes: 7 additions & 0 deletions integration/testrun.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ def _check_repos(requested: list[str], result: list[str]) -> None:
def key_import() -> None:
"""Import App key into Vault"""

import_app_key(
pem_key=b64decode(os.environ["HV4GHA_REVOKED_APP_KEY_B64"]),
key_name=os.environ["HV4GHA_KEYNAME"],
vault_addr=os.environ["HV4GHA_VAULT_ADDR"],
vault_token=os.environ["HVGHA_VAULT_IMPORT_TOKEN"],
)

import_app_key(
pem_key=b64decode(os.environ["HV4GHA_APP_KEY_B64"]),
key_name=os.environ["HV4GHA_KEYNAME"],
Expand Down
Loading