Skip to content

Commit a4259f8

Browse files
committed
added mechanisms for mapping secondary data folder and individual secrets files
1 parent ecfa3a4 commit a4259f8

File tree

7 files changed

+346
-87
lines changed

7 files changed

+346
-87
lines changed

.dockerignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.git
22
.github
3+
.secrets
34
docs
45
test
56
LICENSE
@@ -11,4 +12,4 @@ default-stack.md
1112
versions.md
1213
output/
1314
src/output/
14-
src/data/
15+
src/data/

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Project
2+
.secrets
3+
archive
4+
todo
5+
16
# Byte-compiled / optimized / DLL files
27
__pycache__/
38
*.py[cod]

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ It is **strongly recommended to use an absolute path in the alias** (otherwise,
310310
311311
You can build isolated containers with Juypter Notebook and JupyterLab.
312312
313+
**Note:** This functionality is likely to become a separate project, see [Issue 15](https://github.com/mfhepp/py4docker/issues/15)
314+
315+
313316
### Building a Notebook Image
314317
315318
#### Using the default environment file `notebook.yaml`
@@ -390,6 +393,45 @@ nbh openai
390393
nbh foo
391394
```
392395
396+
#### Mapping a secondary data directory to `/mnt/data`
397+
398+
You can map any other directory from your system as read-only bind volume to `/mnt/data` inside the Docker container like so:
399+
400+
```bash
401+
# /home/foo/bar will be accessible as /mnt/data inside the container:
402+
./run_notebook.sh --data-dir /home/foo/bar
403+
```
404+
405+
#### Mapping API tokens and other secrets from local files to `/mnt/secrets/`
406+
407+
You can map one or more local files containing access tokens as a read-only bind mounts to `/mnt/secrets/` inside the Docker container like so:
408+
409+
```bash
410+
./run_notebook.sh --add-secret ~/Documents/.access_tokens/TESTTOKEN1 FOO \
411+
--add-secret ~/Documents/.access_tokens/TESTTOKEN2 BAR
412+
```
413+
You will then be able to access them inside the notebook like so:
414+
415+
```bash
416+
# Inside a notebook cell, run Bash commands with a ! directive;
417+
!cat /mnt/secrets/FOO
418+
!cat /mnt/secrets/BAR
419+
# Contents of the two files TESTTOKEN1 and TESTTOKEN2
420+
SUPERSECRET_TOKEN1
421+
API_TOKEN_FOR_ACME
422+
```
423+
424+
A Python example is in [examples/secrets_test.ipynb](examples/secrets_test.ipynb).
425+
426+
**Warnings:**
427+
428+
1. This is a ***simplistic*** substitute for Docker's mechanisms for managing secrets, but IMO more secure than using environment variables that may be leaked in logfiles etc. **Keep in mind that in the current version, ALL files inside that directory will be available from inside the container!**
429+
2. Make sure **that you DO NOT LEAK YOUR SECRETS TO YOUR Git repository**.
430+
3. Make sure **THAT YOUR SECRETS folder is NOT below your current working directory.** Otherwise, it will be accessible for read- and write-access from within `/usr/app/data` (e.g. as `/usr/app/data/.access_tokens/`)!!!
431+
4. On OSX, do not use `~/.access_tokens`, but rather `~/Documents/.access_tokens`, `~/Documents/.access_tokens`, or any place in the predefined subfolders below the user directory, because
432+
1. OSX grants ANY user on your machine read-access to any user's home folder.
433+
2. The OSX permissions model for applications will ask you only if an application tries to access one of the specific folders ***below the user directory***. I.e., any application COULD READ from `/Users/yourusername/.access_tokens`!!!
434+
393435
## Advanced Topics
394436
395437
### Access to the Local File System

examples/secrets_test.ipynb

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "3c697b0f-4fa4-4dd0-a86b-90b5ed0fbaaa",
6+
"metadata": {},
7+
"source": [
8+
"# Example: Accessing API tokens and credentials\n",
9+
"\n",
10+
"## Starting Jupyter\n",
11+
"\n",
12+
"Start Jupyter with\n",
13+
"\n",
14+
"```bash\n",
15+
"./run_notebook.sh --add-secret ~/.my_access_tokens/TESTTOKEN1 FOO --add-secret ~/.my_access_tokens/TESTTOKEN2 BAR\n",
16+
"```\n",
17+
"\n",
18+
"The two **files** \n",
19+
"\n",
20+
"`~/.my_access_tokens/TESTTOKEN1`\n",
21+
"\n",
22+
"and\n",
23+
"\n",
24+
"`~/.my_access_tokens/TESTTOKEN2`\n",
25+
"\n",
26+
"must exist on the host systems!\n"
27+
]
28+
},
29+
{
30+
"cell_type": "markdown",
31+
"id": "91d1053e-31d8-43a0-9c53-819f9f15f7cc",
32+
"metadata": {},
33+
"source": [
34+
"## Accessing the contents of the secrets from a notebook"
35+
]
36+
},
37+
{
38+
"cell_type": "markdown",
39+
"id": "11af385e-37bf-4d72-adce-2e0e9caa8489",
40+
"metadata": {},
41+
"source": [
42+
"### With shell commands"
43+
]
44+
},
45+
{
46+
"cell_type": "code",
47+
"execution_count": 1,
48+
"id": "92b59d9d-6bc2-40ca-afb7-abb7903f7dc8",
49+
"metadata": {},
50+
"outputs": [
51+
{
52+
"name": "stdout",
53+
"output_type": "stream",
54+
"text": [
55+
"SUPERSECRET_TOKEN1\n",
56+
"API_TOKEN_FOR_ACME\n"
57+
]
58+
}
59+
],
60+
"source": [
61+
"!cat /mnt/secrets/FOO\n",
62+
"!cat /mnt/secrets/BAR"
63+
]
64+
},
65+
{
66+
"cell_type": "code",
67+
"execution_count": 2,
68+
"id": "67300fec-69df-42af-b962-5eab6799cd0b",
69+
"metadata": {},
70+
"outputs": [
71+
{
72+
"name": "stdout",
73+
"output_type": "stream",
74+
"text": [
75+
"total 16\n",
76+
"drwxr-xr-x 2 root root 4096 May 10 11:43 .\n",
77+
"drwxr-xr-x 1 root root 4096 May 10 11:43 ..\n",
78+
"-rw-r--r-- 1 root root 19 May 10 11:32 BAR\n",
79+
"-rw-r--r-- 1 root root 19 May 10 11:32 FOO\n"
80+
]
81+
}
82+
],
83+
"source": [
84+
"!ls -la /mnt/secrets/"
85+
]
86+
},
87+
{
88+
"cell_type": "markdown",
89+
"id": "29b93823-53ea-4dc8-98bc-077440b8b54a",
90+
"metadata": {},
91+
"source": [
92+
"### Python"
93+
]
94+
},
95+
{
96+
"cell_type": "code",
97+
"execution_count": 4,
98+
"id": "cde13d39-2d0a-4cba-992d-2e9e06511d4d",
99+
"metadata": {},
100+
"outputs": [
101+
{
102+
"name": "stdout",
103+
"output_type": "stream",
104+
"text": [
105+
"SUPERSECRET_TOKEN1\n",
106+
"API_TOKEN_FOR_ACME\n"
107+
]
108+
}
109+
],
110+
"source": [
111+
"SECRET_1_PATH = \"/mnt/secrets/FOO\"\n",
112+
"SECRET_2_PATH = \"/mnt/secrets/BAR\"\n",
113+
"\n",
114+
"with open(SECRET_1_PATH, \"r\") as file:\n",
115+
" SECRET_TOKEN = file.read().strip()\n",
116+
"\n",
117+
"with open(SECRET_2_PATH, \"r\") as file:\n",
118+
" API_KEY_ACME = file.read().strip()\n",
119+
"\n",
120+
"print(SECRET_TOKEN)\n",
121+
"print(API_KEY_ACME)"
122+
]
123+
}
124+
],
125+
"metadata": {
126+
"kernelspec": {
127+
"display_name": "Python 3 (ipykernel)",
128+
"language": "python",
129+
"name": "python3"
130+
},
131+
"language_info": {
132+
"codemirror_mode": {
133+
"name": "ipython",
134+
"version": 3
135+
},
136+
"file_extension": ".py",
137+
"mimetype": "text/x-python",
138+
"name": "python",
139+
"nbconvert_exporter": "python",
140+
"pygments_lexer": "ipython3",
141+
"version": "3.12.3"
142+
}
143+
},
144+
"nbformat": 4,
145+
"nbformat_minor": 5
146+
}

notebook.yaml.lock

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ dependencies:
4545
- importlib_metadata=7.1.0=hd8ed1ab_0
4646
- importlib_resources=6.4.0=pyhd8ed1ab_0
4747
- ipykernel=6.29.3=pyhd33586a_0
48-
- ipython=8.22.2=pyh707e725_0
49-
- ipywidgets=8.1.2=pyhd8ed1ab_0
48+
- ipython=8.24.0=pyh707e725_0
49+
- ipywidgets=8.1.2=pyhd8ed1ab_1
5050
- isodate=0.6.1=pyhd8ed1ab_0
5151
- isoduration=20.11.0=pyhd8ed1ab_0
5252
- jedi=0.19.1=pyhd8ed1ab_0
53-
- jinja2=3.1.3=pyhd8ed1ab_0
53+
- jinja2=3.1.4=pyhd8ed1ab_0
5454
- json5=0.9.25=pyhd8ed1ab_0
5555
- jsonpointer=2.4=py312h996f985_3
5656
- jsonschema=4.22.0=pyhd8ed1ab_0
@@ -68,7 +68,7 @@ dependencies:
6868
- jupyterlab_pygments=0.3.0=pyhd8ed1ab_1
6969
- jupyterlab_server=2.27.1=pyhd8ed1ab_0
7070
- jupyterlab_widgets=3.0.10=pyhd8ed1ab_0
71-
- jupytext=1.16.1=pyhd8ed1ab_0
71+
- jupytext=1.16.2=pyhd8ed1ab_1
7272
- keyutils=1.6.1=h4e544f5_0
7373
- krb5=1.21.2=hc419048_0
7474
- ld_impl_linux-aarch64=2.40=hba4e955_0
@@ -77,17 +77,17 @@ dependencies:
7777
- libedit=3.1.20191231=he28a2e2_2
7878
- libexpat=2.6.2=h2f0025b_0
7979
- libffi=3.4.2=h3557bc0_5
80-
- libgcc-ng=13.2.0=he277a41_6
81-
- libgfortran-ng=13.2.0=he9431aa_6
82-
- libgfortran5=13.2.0=h87d9d71_6
83-
- libgomp=13.2.0=he277a41_6
80+
- libgcc-ng=13.2.0=he277a41_7
81+
- libgfortran-ng=13.2.0=he9431aa_7
82+
- libgfortran5=13.2.0=h87d9d71_7
83+
- libgomp=13.2.0=he277a41_7
8484
- libiconv=1.17=h31becfc_2
8585
- liblapack=3.9.0=22_linuxaarch64_openblas
8686
- libnsl=2.0.1=h31becfc_0
8787
- libopenblas=0.3.27=pthreads_h5a5ec62_0
8888
- libsodium=1.0.18=hb9de7d4_1
8989
- libsqlite=3.45.3=h194ca79_0
90-
- libstdcxx-ng=13.2.0=h3f4de04_6
90+
- libstdcxx-ng=13.2.0=h3f4de04_7
9191
- libuuid=2.38.1=hb4cce97_0
9292
- libxcrypt=4.4.36=h31becfc_1
9393
- libxml2=2.12.6=h3091e33_2
@@ -106,7 +106,7 @@ dependencies:
106106
- nbconvert-core=7.16.4=pyhd8ed1ab_0
107107
- nbconvert-pandoc=7.16.4=hd8ed1ab_0
108108
- nbformat=5.10.4=pyhd8ed1ab_0
109-
- ncurses=6.4.20240210=h0425590_0
109+
- ncurses=6.5=h0425590_0
110110
- nest-asyncio=1.6.0=pyhd8ed1ab_0
111111
- notebook=7.1.3=pyhd8ed1ab_0
112112
- notebook-shim=0.2.4=pyhd8ed1ab_0
@@ -134,7 +134,7 @@ dependencies:
134134
- ptyprocess=0.7.0=pyhd3deb0d_0
135135
- pure_eval=0.2.2=pyhd8ed1ab_0
136136
- pycparser=2.22=pyhd8ed1ab_0
137-
- pygments=2.17.2=pyhd8ed1ab_0
137+
- pygments=2.18.0=pyhd8ed1ab_0
138138
- pyparsing=3.1.2=pyhd8ed1ab_0
139139
- pyshacl=0.25.0=pyhd8ed1ab_0
140140
- pysocks=1.7.1=pyha2e5f31_6
@@ -146,8 +146,8 @@ dependencies:
146146
- python_abi=3.12=4_cp312
147147
- pytz=2024.1=pyhd8ed1ab_0
148148
- pyyaml=6.0.1=py312hdd3e373_1
149-
- pyzmq=26.0.2=py312h7059f03_0
150-
- qtconsole-base=5.5.1=pyha770c72_0
149+
- pyzmq=26.0.3=py312h7059f03_0
150+
- qtconsole-base=5.5.2=pyha770c72_0
151151
- qtpy=2.4.1=pyhd8ed1ab_0
152152
- rdflib=7.0.0=pyhd8ed1ab_0
153153
- readline=8.2=h8fc344f_1
@@ -156,7 +156,7 @@ dependencies:
156156
- rfc3339-validator=0.1.4=pyhd8ed1ab_0
157157
- rfc3986-validator=0.1.1=pyh9f0ad1d_0
158158
- rich=13.7.1=pyhd8ed1ab_0
159-
- rpds-py=0.18.0=py312h2a5c9d6_0
159+
- rpds-py=0.18.1=py312heb99873_0
160160
- send2trash=1.8.3=pyh0d859eb_0
161161
- setuptools=69.5.1=pyhd8ed1ab_0
162162
- shellingham=1.5.4=pyhd8ed1ab_0
@@ -168,7 +168,6 @@ dependencies:
168168
- terminado=0.18.1=pyh0d859eb_0
169169
- tinycss2=1.3.0=pyhd8ed1ab_0
170170
- tk=8.6.13=h194ca79_0
171-
- toml=0.10.2=pyhd8ed1ab_0
172171
- tomli=2.0.1=pyhd8ed1ab_0
173172
- tornado=6.4=py312h9ef2f89_0
174173
- traitlets=5.14.3=pyhd8ed1ab_0

0 commit comments

Comments
 (0)