-
Notifications
You must be signed in to change notification settings - Fork 641
Secrets v1 #2564
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
base: main
Are you sure you want to change the base?
Secrets v1 #2564
Changes from all commits
37def44
3aedd9b
f004a81
1c28e60
1a6b468
0861aa3
6a2d123
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would love to see Is the intended flow for the |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import base64 | ||
| import os | ||
| from pathlib import Path | ||
|
|
||
| import requests | ||
| from cryptography.hazmat.backends import default_backend | ||
| from cryptography.hazmat.primitives import hashes, serialization | ||
| from cryptography.hazmat.primitives.asymmetric import padding, rsa | ||
| from dotenv import dotenv_values | ||
|
|
||
| __all__ = [ | ||
| "load_secret", | ||
| "default_secret_provider", | ||
| ] | ||
|
|
||
|
|
||
| def load_secret(name: str, secret_provider: SecretProvider | None) -> str: | ||
| if not secret_provider: | ||
| secret_provider = default_secret_provider | ||
| return secret_provider.get_secret(name) | ||
|
|
||
|
|
||
| class SecretProvider: | ||
| def __init__( | ||
| self, | ||
| cog_env_location: str = ".cog/.env", | ||
| cog_public_key_env_var: str = "COG_PUBLIC_KEY_LOCATION", | ||
| ) -> None: | ||
| self.env = {} | ||
| self.no_public_key = False | ||
| self.key = rsa.generate_private_key( | ||
| backend=default_backend(), | ||
| public_exponent=65537, | ||
| key_size=2048, | ||
| ) | ||
| self.secret_url: str | None = None | ||
| public_pem = self.key.public_key().public_bytes( | ||
| encoding=serialization.Encoding.PEM, | ||
| format=serialization.PublicFormat.SubjectPublicKeyInfo, | ||
| ) | ||
| public_key_path_raw = os.getenv(cog_public_key_env_var) | ||
| if not public_key_path_raw: | ||
| self.no_public_key = True | ||
| return | ||
| public_key_path = Path(public_key_path_raw) | ||
| public_key_path.parent.mkdir(mode=0o700, exist_ok=True) | ||
| public_key_path.touch() | ||
| public_key_path.write_bytes(public_pem) | ||
| if not os.path.isfile(cog_env_location): | ||
| return | ||
| self.env = dotenv_values(cog_env_location) | ||
|
|
||
| def get_secret(self, secret_name: str) -> str: | ||
| # Try to get the secret from the remote. Fall back to the local | ||
| # env file (local development only) | ||
| try: | ||
| if not self.secret_url: | ||
| raise ValueError("No secret URL passed") | ||
| if self.no_public_key: | ||
| raise ValueError("No public key for encryption") | ||
| raw_secret = os.getenv(secret_name) | ||
| if not raw_secret: | ||
| raise ValueError("No matching secret") | ||
| response = requests.post( | ||
| f"{self.secret_url}", | ||
| json={ | ||
| "value": raw_secret, | ||
| }, | ||
| ) | ||
| response.raise_for_status() | ||
|
|
||
| plaintext_bytes = self.key.decrypt( | ||
| base64.b64decode(response.text), | ||
| padding.OAEP( | ||
| mgf=padding.MGF1(algorithm=hashes.SHA256()), | ||
| algorithm=hashes.SHA256(), | ||
| label=None, | ||
| ), | ||
| ) | ||
|
|
||
| return plaintext_bytes.decode("utf-8") | ||
| except Exception: | ||
| return self.env.get(secret_name) or "" | ||
|
|
||
|
|
||
| default_secret_provider = SecretProvider() |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit weird to me that class Always:
def __init__(self, bla: str | None):
self.bla = bla
class Sometimes:
def __init__(self, bla: str | None):
if bla is not None:
self.bla = bla
a0 = Always("ok")
a1 = Always(None)
s0 = Sometimes("ok")
s1 = Sometimes(None)
print("a0", a0.bla)
print("a1", a1.bla)
print("s0", s0.bla)
print("s1", s1.bla)$ python ~/tmp/always_sometimes.py
a0 ok
a1 None
s0 ok
Traceback (most recent call last):
File "/Users/me/tmp/always_sometimes.py", line 21, in <module>
print("s1", s1.bla)
^^^^^^
AttributeError: 'Sometimes' object has no attribute 'bla' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| curl -X GET https://api.coreweave.com/v1beta1/cks/clusters/{id} -H "Content-Type: application/json" -H "Authorization: Bearer {API_ACCESS_TOKEN}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existence of both
secretandSecretwith very different implementations is a source of confusion I would really love to avoid 😅 WDYT about a verb prefix likeget_secretorload_secretor rolling it together with non-secret config values like acog.getenvthat automatically handles encrypted values?