
Security News
NIST Under Federal Audit for NVD Processing Backlog and Delays
As vulnerability data bottlenecks grow, the federal government is formally investigating NISTβs handling of the National Vulnerability Database.
pydantic-file-secrets
Advanced tools
Use file secrets in nested pydantic-settings models instead of built-in SecretsSettingsSource
Use file secrets in nested pydantic-settings models instead of built-in SecretsSettingsSource
This project is inspired by discussions in Pydantic Settings repository and proposes solution to #30 and #154.
secrets/dir__key
or secrets/dir/key
env_prefix
, env_nested_delimiter
and other config optionssecrets_prefix
, secrets_nested_delimiter
and more to configure secrets and env vars independentlySecretsSettingsSource
EnvSettingsSource
pydantic-settings
$ pip install pydantic-file-secrets
Nested Pydantic config can contain nested models with secret entries, as well as secrets in top level config. In dockerized environment, these entries may be read from file system, e.g. /run/secrets
when using Docker Secrets:
from pydantic import BaseModel, Secret
from pydantic_settings import BaseSettings, SettingsConfigDict
class DbSettings(BaseModel):
user: str
passwd: Secret[str] # secret in nested model
class Settings(BaseSettings):
app_key: Secret[str] # secret in root model
db: DbSettings
model_config = SettingsConfigDict(
secrets_dir='/run/secrets',
)
π secrets
βββ π app_key
βββ π db__passwd
from pydantic import BaseModel, SecretStr
from pydantic_file_secrets import FileSecretsSettingsSource, SettingsConfigDict
from pydantic_settings import BaseSettings
from pydantic_settings.sources import PydanticBaseSettingsSource
class DbSettings(BaseModel):
passwd: SecretStr
class Settings(BaseSettings):
app_key: SecretStr
db: DbSettings
model_config = SettingsConfigDict(
secrets_dir='secrets',
secrets_nested_delimiter='__',
)
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
env_settings,
dotenv_settings,
FileSecretsSettingsSource(file_secret_settings),
)
>>> Settings().model_dump()
{'app_key': SecretStr('**********'), 'db': {'passwd': SecretStr('**********')}}
Config option secrets_nested_delimiter
overrides env_nested_delimiter
for files. In particular, this allows to use nested directory layout along with environmemt variables for other non-secret settings:
π secrets
βββ π app_key
βββ π db
βββ π passwd
model_config = SettingsConfigDict(
secrets_dir='secrets',
secrets_nested_subdir=True,
)
>>> Settings().model_dump()
{'app_key': SecretStr('**********'), 'db': {'passwd': SecretStr('**********')}}
secrets_dir
π secrets
βββ π layer1
β βββ π app_key
βββ π layer2
βββ π db__passwd
model_config = SettingsConfigDict(
secrets_dir=['secrets/layer1', 'secrets/layer2'],
secrets_nested_delimiter='__',
)
>>> Settings().model_dump()
{'app_key': SecretStr('**********'), 'db': {'passwd': SecretStr('**********')}}
[!CAUTION] This syntax may change at any time. Pin current
pydantic-file-secrets
version if decided to use it.
Few important things to note:
@with_builtin_sources
decorator enables NamedTuple
argument src: BuiltinSources
encapsulating default builtins settings sourcesBaseSource
alias is shorter than PydanticBaseSettingsSource
and is easier to use in type hintssettings_cls
was removed from settings_customise_sources
signature: cls
seems to be sufficientfrom pydantic import BaseModel, SecretStr
from pydantic_file_secrets import (
BaseSource,
BuiltinSources,
FileSecretsSettingsSource,
SettingsConfigDict,
with_builtin_sources,
)
from pydantic_settings import BaseSettings
class DbSettings(BaseModel):
passwd: SecretStr
class Settings(BaseSettings):
app_key: SecretStr
db: DbSettings
model_config = SettingsConfigDict(
secrets_dir='secrets',
secrets_nested_delimiter='__',
)
@classmethod
@with_builtin_sources
def settings_customise_sources(cls, src: BuiltinSources) -> tuple[BaseSource, ...]:
return (
src.init_settings,
src.env_settings,
src.dotenv_settings,
FileSecretsSettingsSource(src.file_secret_settings),
)
>>> Settings().model_dump()
{'app_key': SecretStr('**********'), 'db': {'passwd': SecretStr('**********')}}
Path to secrets directory. Same as SecretsSettingsSource.secrets_dir
if str
or Path
. If list
, the last match wins. If secrets_dir
is passed in both source constructor and model config, values are not merged (constructor takes priority).
If secrets_dir
does not exist, original SecretsSettingsSource
issues a warning. However, this may be undesirable, for example if we don't mount Docker Secrets in e.g. dev environment. Now you have a choice:
'ok'
β do nothing if secrets_dir
does not exist'warn'
(default) β print warning, same as SecretsSettingsSource
'error'
β raise SettingsError
If multiple secrets_dir
passed, the same secrets_dir_missing
action applies to each of them.
Limit the size of secrets_dir
for security reasons, defaults to SECRETS_DIR_MAX_SIZE
equal to 16 MiB.
FileSecretsSettingsSource
is a thin wrapper around EnvSettingsSource
, which loads all potential secrets on initialization. This could lead to MemoryError
if we mount a large file under secrets_dir
.
If multiple secrets_dir
passed, the limit applies to each directory independently.
Same as case_sensitive
, but works for secrets only. If not specified, defaults to case_sensitive
.
Same as env_nested_delimiter
, but works for secrets only. If not specified, defaults to env_nested_delimiter
. This option is used to implement nested secrets directory layout and allows to do even nastier things like /run/secrets/model/delim/nested1/delim/nested2
.
Boolean flag to turn on nested secrets directory mode, False
by default. If True
, sets secrets_nested_delimiter
to os.sep
. Raises SettingsError
if secrets_nested_delimiter
is already specified.
Secret path prefix, similar to env_prefix
, but works for secrets only. Defaults to env_prefix
if not specified. Works in both plain and nested directory modes, like '/run/secrets/prefix_model__nested'
and '/run/secrets/prefix_model/nested'
.
Some config options that are declared in SecretsSettingsSource
interface are actually not working and are not supported in FileSecretsSettingsSource
:
env_ignore_empty
env_parse_none_str
env_parse_enums
However, we make sure that the behaviour of FileSecretsSettingsSource
matches SecretsSettingsSource
to provide a drop-in replacement, although it is somewhat wierd (e.g. env_parse_enums
is always True
).
100% test coverage is provided for latest Python and pydantic-settings version. Tests are run for all minor pydantic-settings v2 versions and all minor Python 3 versions supported by them:
pyXY
β Python 3.{8,9,10,11,12,13}psXY
β pydantic-settings v2.{2,3,4,5,6,7,8}ps29 | ps28 | ps27 | ps26 | ps25 | ps24 | ps23 | ps22 | |
---|---|---|---|---|---|---|---|---|
py313 | β³οΈ | β³οΈ | β | β | β | βοΈ | β³οΈ | βοΈ |
py312 | β | β | β | β | β | βοΈ | βοΈ | βοΈ |
py311 | β | β | β | β | β | βοΈ | βοΈ | βοΈ |
py310 | β | β | β | β | β | βοΈ | βοΈ | βοΈ |
py39 | β | β | β | β | β | βοΈ | βοΈ | βοΈ |
py38 | β | β | β | β | β | βοΈ | βοΈ | βοΈ |
Pull requests, feature requests, and bug reports are welcome!
FAQs
Use file secrets in nested pydantic-settings models instead of built-in SecretsSettingsSource
We found that pydantic-file-secrets demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
As vulnerability data bottlenecks grow, the federal government is formally investigating NISTβs handling of the National Vulnerability Database.
Research
Security News
Socketβs Threat Research Team has uncovered 60 npm packages using post-install scripts to silently exfiltrate hostnames, IP addresses, DNS servers, and user directories to a Discord-controlled endpoint.
Security News
TypeScript Native Previews offers a 10x faster Go-based compiler, now available on npm for public testing with early editor and language support.