auth-ingress 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. auth_ingress-0.1.0/.gitignore +24 -0
  2. auth_ingress-0.1.0/CHANGELOG.md +35 -0
  3. auth_ingress-0.1.0/LICENSE +21 -0
  4. auth_ingress-0.1.0/PKG-INFO +237 -0
  5. auth_ingress-0.1.0/README.md +194 -0
  6. auth_ingress-0.1.0/SECURITY.md +40 -0
  7. auth_ingress-0.1.0/pyproject.toml +79 -0
  8. auth_ingress-0.1.0/src/auth_ingress/__init__.py +2 -0
  9. auth_ingress-0.1.0/src/auth_ingress/cli.py +153 -0
  10. auth_ingress-0.1.0/src/auth_ingress/config.py +103 -0
  11. auth_ingress-0.1.0/src/auth_ingress/main.py +86 -0
  12. auth_ingress-0.1.0/src/auth_ingress/models/__init__.py +9 -0
  13. auth_ingress-0.1.0/src/auth_ingress/models/audit_event.py +27 -0
  14. auth_ingress-0.1.0/src/auth_ingress/models/identity.py +72 -0
  15. auth_ingress-0.1.0/src/auth_ingress/models/installation.py +22 -0
  16. auth_ingress-0.1.0/src/auth_ingress/models/password_reset.py +25 -0
  17. auth_ingress-0.1.0/src/auth_ingress/models/proxy_launch_ticket.py +25 -0
  18. auth_ingress-0.1.0/src/auth_ingress/models/service_entry.py +45 -0
  19. auth_ingress-0.1.0/src/auth_ingress/models/session.py +24 -0
  20. auth_ingress-0.1.0/src/auth_ingress/repositories/__init__.py +2 -0
  21. auth_ingress-0.1.0/src/auth_ingress/repositories/database.py +35 -0
  22. auth_ingress-0.1.0/src/auth_ingress/repositories/schema.py +101 -0
  23. auth_ingress-0.1.0/src/auth_ingress/security/__init__.py +2 -0
  24. auth_ingress-0.1.0/src/auth_ingress/security/cookies.py +65 -0
  25. auth_ingress-0.1.0/src/auth_ingress/security/csrf.py +15 -0
  26. auth_ingress-0.1.0/src/auth_ingress/security/dependencies.py +40 -0
  27. auth_ingress-0.1.0/src/auth_ingress/security/passwords.py +21 -0
  28. auth_ingress-0.1.0/src/auth_ingress/security/proxy_destination.py +73 -0
  29. auth_ingress-0.1.0/src/auth_ingress/security/proxy_host.py +50 -0
  30. auth_ingress-0.1.0/src/auth_ingress/security/rate_limit.py +49 -0
  31. auth_ingress-0.1.0/src/auth_ingress/services/__init__.py +2 -0
  32. auth_ingress-0.1.0/src/auth_ingress/services/access_service.py +61 -0
  33. auth_ingress-0.1.0/src/auth_ingress/services/audit_service.py +68 -0
  34. auth_ingress-0.1.0/src/auth_ingress/services/authentication_service.py +16 -0
  35. auth_ingress-0.1.0/src/auth_ingress/services/bootstrap_service.py +81 -0
  36. auth_ingress-0.1.0/src/auth_ingress/services/cli_user_auth.py +20 -0
  37. auth_ingress-0.1.0/src/auth_ingress/services/cli_user_output.py +42 -0
  38. auth_ingress-0.1.0/src/auth_ingress/services/downstream_service.py +39 -0
  39. auth_ingress-0.1.0/src/auth_ingress/services/password_reset_service.py +96 -0
  40. auth_ingress-0.1.0/src/auth_ingress/services/proxy_authorization_service.py +110 -0
  41. auth_ingress-0.1.0/src/auth_ingress/services/proxy_header_policy.py +115 -0
  42. auth_ingress-0.1.0/src/auth_ingress/services/proxy_http_service.py +101 -0
  43. auth_ingress-0.1.0/src/auth_ingress/services/proxy_websocket_service.py +116 -0
  44. auth_ingress-0.1.0/src/auth_ingress/services/recovery_delivery.py +39 -0
  45. auth_ingress-0.1.0/src/auth_ingress/services/service_admin_service.py +100 -0
  46. auth_ingress-0.1.0/src/auth_ingress/services/service_compatibility_service.py +49 -0
  47. auth_ingress-0.1.0/src/auth_ingress/services/session_service.py +62 -0
  48. auth_ingress-0.1.0/src/auth_ingress/services/user_admin_service.py +208 -0
  49. auth_ingress-0.1.0/src/auth_ingress/services/user_management_types.py +49 -0
  50. auth_ingress-0.1.0/src/auth_ingress/web/__init__.py +2 -0
  51. auth_ingress-0.1.0/src/auth_ingress/web/routes/__init__.py +12 -0
  52. auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_audit.py +36 -0
  53. auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_services.py +165 -0
  54. auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_users.py +134 -0
  55. auth_ingress-0.1.0/src/auth_ingress/web/routes/auth.py +136 -0
  56. auth_ingress-0.1.0/src/auth_ingress/web/routes/password_reset.py +46 -0
  57. auth_ingress-0.1.0/src/auth_ingress/web/routes/portal.py +25 -0
  58. auth_ingress-0.1.0/src/auth_ingress/web/routes/proxy.py +142 -0
  59. auth_ingress-0.1.0/src/auth_ingress/web/routes/services.py +66 -0
  60. auth_ingress-0.1.0/src/auth_ingress/web/static/portal.css +20 -0
  61. auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/audit.html +5 -0
  62. auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/services.html +20 -0
  63. auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/user_detail.html +119 -0
  64. auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/users.html +12 -0
  65. auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/change_password.html +16 -0
  66. auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/reset_password.html +3 -0
  67. auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/setup_required.html +11 -0
  68. auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/sign_in.html +18 -0
  69. auth_ingress-0.1.0/src/auth_ingress/web/templates/base.html +13 -0
  70. auth_ingress-0.1.0/src/auth_ingress/web/templates/errors/access_denied.html +4 -0
  71. auth_ingress-0.1.0/src/auth_ingress/web/templates/portal/index.html +10 -0
  72. auth_ingress-0.1.0/src/auth_ingress/web/web.py +22 -0
  73. auth_ingress-0.1.0/uv.lock +878 -0
@@ -0,0 +1,24 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.db
4
+ *.sqlite*
5
+ .venv/
6
+ venv/
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .pytest_cache/
11
+ .coverage
12
+ htmlcov/
13
+ test-results/
14
+ playwright-report/
15
+ .env*
16
+ !.env.example
17
+ *.log
18
+ .DS_Store
19
+ Thumbs.db
20
+ *.tmp
21
+ *.swp
22
+ .vscode/
23
+ .idea/
24
+
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ All notable changes to auth-ingress are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Authenticated entry portal with administrator-managed service access rules.
13
+ - Signed server-side sessions, Argon2 password hashing, CSRF protection, rate
14
+ limiting, and non-sensitive audit events.
15
+ - Full web-application proxying for HTTP assets, interactions, streaming
16
+ transfers, service-isolated cookies, redirects, and WebSockets.
17
+ - Package distribution under `auth-ingress` with the `auth_ingress` import
18
+ namespace, preferred `auth-ingress` command, and compatibility `auth-portal`
19
+ command.
20
+
21
+ ### Changed
22
+
23
+ - Renamed the canonical GitHub repository to `zondatw/auth-ingress`.
24
+ - Renamed the Python import namespace from `auth_portal` to
25
+ `auth_ingress` before the first public release.
26
+
27
+ ### Upgrade Notes
28
+
29
+ - Source-based installations must update imports from historical namespaces to
30
+ `auth_ingress`.
31
+ - The `auth-portal` command and `AUTH_PORTAL_*` environment variables remain as
32
+ compatibility aliases. Cookie names, audit logger names, and the default
33
+ database filename may retain old internal labels to preserve security state.
34
+
35
+ [Unreleased]: https://github.com/zondatw/auth-ingress/commits/main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zondatw
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: auth-ingress
3
+ Version: 0.1.0
4
+ Summary: A small authenticated ingress portal for internal services
5
+ Project-URL: Homepage, https://github.com/zondatw/auth-ingress
6
+ Project-URL: Documentation, https://github.com/zondatw/auth-ingress#readme
7
+ Project-URL: Source, https://github.com/zondatw/auth-ingress
8
+ Project-URL: Issues, https://github.com/zondatw/auth-ingress/issues
9
+ Project-URL: Changelog, https://github.com/zondatw/auth-ingress/blob/main/CHANGELOG.md
10
+ Project-URL: Security, https://github.com/zondatw/auth-ingress/security/policy
11
+ Author: zondatw
12
+ Maintainer: zondatw
13
+ License-Expression: MIT
14
+ License-File: LICENSE
15
+ Keywords: authentication,authorization,ingress,portal,reverse-proxy
16
+ Classifier: Development Status :: 3 - Alpha
17
+ Classifier: Environment :: Web Environment
18
+ Classifier: Intended Audience :: System Administrators
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
25
+ Classifier: Topic :: Security
26
+ Requires-Python: >=3.12
27
+ Requires-Dist: argon2-cffi>=23.1
28
+ Requires-Dist: fastapi>=0.115
29
+ Requires-Dist: httpx>=0.27
30
+ Requires-Dist: itsdangerous>=2.2
31
+ Requires-Dist: jinja2>=3.1
32
+ Requires-Dist: python-multipart>=0.0.18
33
+ Requires-Dist: sqlalchemy>=2.0
34
+ Requires-Dist: uvicorn>=0.34
35
+ Requires-Dist: websockets>=15.0
36
+ Provides-Extra: test
37
+ Requires-Dist: packaging>=24.2; extra == 'test'
38
+ Requires-Dist: playwright>=1.55; extra == 'test'
39
+ Requires-Dist: pytest-cov>=6.0; extra == 'test'
40
+ Requires-Dist: pytest>=8.3; extra == 'test'
41
+ Requires-Dist: pyyaml>=6.0; extra == 'test'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # auth-ingress
45
+
46
+ A small server-rendered FastAPI portal that authenticates internal users before
47
+ allowing them to enter downstream services that do not implement their own login
48
+ flow.
49
+
50
+ ## Install from PyPI
51
+
52
+ auth-ingress requires Python 3.12 or newer. Install a stable release in a
53
+ virtual environment and pin the version in managed deployments:
54
+
55
+ ```bash
56
+ python -m venv .venv
57
+ source .venv/bin/activate
58
+ python -m pip install "auth-ingress==0.1.0"
59
+ auth-ingress --help
60
+ ```
61
+
62
+ The distribution name is `auth-ingress`, the Python import namespace is
63
+ `auth_ingress`, and the preferred command is `auth-ingress`:
64
+
65
+ ```python
66
+ from auth_ingress.main import create_app
67
+ ```
68
+
69
+ ## Rename compatibility
70
+
71
+ `auth-ingress` is the current product, package, repository, command, and
72
+ operator-facing configuration identity. The previous distribution name
73
+ `auth-entry-portal`, command `auth-portal`, and `AUTH_PORTAL_*` configuration
74
+ prefix may still appear in historical release notes, old automation, audit
75
+ evidence, and migration examples. During the migration window:
76
+
77
+ - use `auth-ingress` for new installs and current documentation;
78
+ - `auth-portal` remains available as a compatibility command;
79
+ - `AUTH_INGRESS_*` settings take precedence when both prefixes are present;
80
+ - `AUTH_PORTAL_*` settings are still accepted when the preferred key is absent;
81
+ - `auth_ingress` is the Python import namespace for runtime code;
82
+ - cookie names and signed-token salts may retain old internal labels to avoid
83
+ breaking active sessions or historical evidence.
84
+
85
+ Before first use, configure a high-entropy secret and an explicit database URL,
86
+ then initialize the schema:
87
+
88
+ ```bash
89
+ export AUTH_INGRESS_SECRET_KEY="replace-with-a-high-entropy-secret"
90
+ export AUTH_INGRESS_DATABASE_URL="sqlite:////absolute/path/auth_ingress.db"
91
+ auth-ingress init-db
92
+ auth-ingress serve --host 127.0.0.1 --port 8000
93
+ ```
94
+
95
+ Upgrade only after reviewing [CHANGELOG.md](CHANGELOG.md), then install the exact
96
+ new version and rerun application smoke checks:
97
+
98
+ ```bash
99
+ python -m pip install --upgrade "auth-ingress==<new-version>"
100
+ auth-ingress --help
101
+ ```
102
+
103
+ Verify the installed version with
104
+ `python -m pip show auth-ingress`. Remove the package with
105
+ `python -m pip uninstall auth-ingress`; database files and external
106
+ configuration are intentionally retained and must be removed separately if no
107
+ longer needed.
108
+
109
+ ## Trust boundary
110
+
111
+ The portal is an access-control boundary only when downstream services are
112
+ reachable exclusively from the portal or a trusted internal network. Never
113
+ configure a publicly reachable downstream URL: users who know it could bypass
114
+ the portal. Destinations are restricted to loopback, private IP addresses,
115
+ `.internal` hosts, and local `mock://` demo targets. Embedded credentials, query
116
+ parameters, and fragments are rejected.
117
+
118
+ Credentials are Argon2-hashed. Browser cookies contain only a signed opaque
119
+ reference to a server-side session and use HttpOnly and SameSite protections.
120
+ Production deployments must set `AUTH_INGRESS_SECRET_KEY` to a high-entropy value,
121
+ enable `AUTH_INGRESS_SECURE_COOKIES=true`, terminate TLS, and protect the SQLite
122
+ file at the operating-system level.
123
+
124
+ ## Setup and operation
125
+
126
+ For a production-style first installation, create the first administrator with
127
+ the one-time local bootstrap command. It prompts twice without echoing or
128
+ accepting the password as an argument:
129
+
130
+ ```bash
131
+ uv run auth-ingress bootstrap-admin \
132
+ --email admin@example.com \
133
+ --display-name "Administrator"
134
+ uv run auth-ingress serve --host 127.0.0.1 --port 8000
135
+ ```
136
+
137
+ Before bootstrap, sign-in displays local setup guidance and never exposes a
138
+ registration form. Repeating the command after any identity exists makes no
139
+ change. If setup fails before completion, correct the reported non-secret input
140
+ or storage problem and safely retry the same command.
141
+
142
+ For disposable development data only:
143
+
144
+ ```bash
145
+ uv sync --extra test
146
+ uv run auth-ingress init-db
147
+ uv run auth-ingress seed-demo
148
+ uv run auth-ingress serve --host 127.0.0.1 --port 8000
149
+ ```
150
+
151
+ Configuration uses these environment variables:
152
+
153
+ - `AUTH_INGRESS_DATABASE_URL`
154
+ - `AUTH_INGRESS_SECRET_KEY`
155
+ - `AUTH_INGRESS_SESSION_COOKIE`
156
+ - `AUTH_INGRESS_SESSION_TTL`
157
+ - `AUTH_INGRESS_SECURE_COOKIES`
158
+ - `AUTH_INGRESS_RATE_LIMIT_ATTEMPTS`
159
+ - `AUTH_INGRESS_RATE_LIMIT_WINDOW`
160
+ - `AUTH_INGRESS_AUDIT_RETENTION_DAYS` (minimum/default: 90)
161
+ - `AUTH_INGRESS_PASSWORD_RESET_TTL` (minimum: 300 seconds; default: 1800)
162
+ - `AUTH_INGRESS_SMTP_HOST`, `AUTH_INGRESS_SMTP_PORT`, `AUTH_INGRESS_SMTP_SENDER`
163
+ - `AUTH_INGRESS_SMTP_USERNAME`, `AUTH_INGRESS_SMTP_PASSWORD`, `AUTH_INGRESS_SMTP_STARTTLS`
164
+ - `AUTH_INGRESS_SMTP_TIMEOUT`
165
+ - `AUTH_INGRESS_USER_PAGE_SIZE` (10–100; default: 50)
166
+ - `AUTH_INGRESS_MANAGEMENT_RATE_LIMIT_ATTEMPTS`, `AUTH_INGRESS_MANAGEMENT_RATE_LIMIT_WINDOW`
167
+ - `AUTH_INGRESS_DOWNSTREAM_TIMEOUT`
168
+
169
+ Demo accounts use the addresses shown by the seed implementation. `seed-demo`
170
+ prompts for each password, or reads the local-only
171
+ `AUTH_INGRESS_DEMO_ADMIN_PASSWORD`, `AUTH_INGRESS_DEMO_MEMBER_PASSWORD`, and
172
+ `AUTH_INGRESS_DEMO_OUTSIDER_PASSWORD` values. Each must contain at least 12
173
+ characters. The CLI deliberately does not print credentials. Remove all demo
174
+ accounts and unset these variables before deployment.
175
+
176
+ Administrators manage users at `/admin/users` or with `auth-ingress users`.
177
+ Memberships remain the only per-user access-list input; user detail explains all
178
+ groups granting each service. Page and CLI mutations preview first and reject a
179
+ stale user revision. Creating a user generates a one-time temporary password
180
+ that is shown only in the create response; the user must change it immediately
181
+ after first sign-in. Later password resets can still use configured SMTP links;
182
+ reset secrets are stored only as digests and never shown to an operator.
183
+ Deactivation is the reversible soft-delete path; permanent removal deletes the
184
+ user account and authentication state while retaining audit history. See
185
+ [docs/user-management.md](docs/user-management.md) for commands, exit codes,
186
+ conflict recovery, lifecycle controls, and delivery troubleshooting.
187
+
188
+ ## Audit and recovery
189
+
190
+ Sign-in attempts, sign-out, allowed and denied service entry, and administrative
191
+ changes create structured database audit events. Context is allowlisted to a
192
+ correlation ID and coarse client category; passwords, cookies, session IDs,
193
+ secrets, request bodies, and unnecessary personal data are excluded. Retain
194
+ events for at least 90 days and back up the database according to organizational
195
+ policy.
196
+
197
+ If a downstream service fails, the portal returns a generic unavailable response
198
+ without retrying unsafe requests or exposing internal details. Operators should
199
+ use the response correlation ID and audit events to investigate. Revoke access by
200
+ disabling a user, removing group membership, disabling a service, or revoking its
201
+ active sessions; authorization is re-evaluated on every protected request.
202
+
203
+ ## Full web-app proxy deployment
204
+
205
+ Full proxy mode gives every service an isolated browser origin. Production
206
+ deployments require:
207
+
208
+ - A dedicated portal host configured with `AUTH_INGRESS_HOST`.
209
+ - Wildcard DNS and TLS for `*.AUTH_INGRESS_PROXY_BASE_DOMAIN`.
210
+ - `AUTH_INGRESS_PROXY_SCHEME=https` and secure cookies.
211
+ - Private network routing from the portal to every downstream destination;
212
+ downstream services must remain unreachable from user networks.
213
+ - A narrow comma-separated `AUTH_INGRESS_TRUSTED_DOWNSTREAM_NETWORKS` value.
214
+ - Explicit request/response byte limits, launch-ticket lifetime, upstream
215
+ timeout, and WebSocket maximum lifetime appropriate to the deployment.
216
+
217
+ Relevant settings are `AUTH_INGRESS_PROXY_BASE_DOMAIN`,
218
+ `AUTH_INGRESS_PROXY_SCHEME`, `AUTH_INGRESS_PROXY_COOKIE`,
219
+ `AUTH_INGRESS_PROXY_LAUNCH_TTL`, `AUTH_INGRESS_PROXY_MAX_REQUEST_BYTES`,
220
+ `AUTH_INGRESS_PROXY_MAX_RESPONSE_BYTES`,
221
+ `AUTH_INGRESS_PROXY_WEBSOCKET_LIFETIME`, and
222
+ `AUTH_INGRESS_TRUSTED_DOWNSTREAM_NETWORKS`.
223
+
224
+ Enable proxy mode per service only after its compatibility check succeeds.
225
+ Applications should use relative or root-relative URLs, or be configured with
226
+ their public service origin. Fixed private origins embedded in JavaScript are not
227
+ rewritten. Roll back by disabling proxy mode for the service; the legacy simple
228
+ entry flow remains available for non-proxy service entries.
229
+
230
+ ## Validation
231
+
232
+ ```bash
233
+ uv run pytest
234
+ ```
235
+
236
+ The full manual journey is documented in
237
+ `specs/002-full-web-app-proxy/quickstart.md`.
@@ -0,0 +1,194 @@
1
+ # auth-ingress
2
+
3
+ A small server-rendered FastAPI portal that authenticates internal users before
4
+ allowing them to enter downstream services that do not implement their own login
5
+ flow.
6
+
7
+ ## Install from PyPI
8
+
9
+ auth-ingress requires Python 3.12 or newer. Install a stable release in a
10
+ virtual environment and pin the version in managed deployments:
11
+
12
+ ```bash
13
+ python -m venv .venv
14
+ source .venv/bin/activate
15
+ python -m pip install "auth-ingress==0.1.0"
16
+ auth-ingress --help
17
+ ```
18
+
19
+ The distribution name is `auth-ingress`, the Python import namespace is
20
+ `auth_ingress`, and the preferred command is `auth-ingress`:
21
+
22
+ ```python
23
+ from auth_ingress.main import create_app
24
+ ```
25
+
26
+ ## Rename compatibility
27
+
28
+ `auth-ingress` is the current product, package, repository, command, and
29
+ operator-facing configuration identity. The previous distribution name
30
+ `auth-entry-portal`, command `auth-portal`, and `AUTH_PORTAL_*` configuration
31
+ prefix may still appear in historical release notes, old automation, audit
32
+ evidence, and migration examples. During the migration window:
33
+
34
+ - use `auth-ingress` for new installs and current documentation;
35
+ - `auth-portal` remains available as a compatibility command;
36
+ - `AUTH_INGRESS_*` settings take precedence when both prefixes are present;
37
+ - `AUTH_PORTAL_*` settings are still accepted when the preferred key is absent;
38
+ - `auth_ingress` is the Python import namespace for runtime code;
39
+ - cookie names and signed-token salts may retain old internal labels to avoid
40
+ breaking active sessions or historical evidence.
41
+
42
+ Before first use, configure a high-entropy secret and an explicit database URL,
43
+ then initialize the schema:
44
+
45
+ ```bash
46
+ export AUTH_INGRESS_SECRET_KEY="replace-with-a-high-entropy-secret"
47
+ export AUTH_INGRESS_DATABASE_URL="sqlite:////absolute/path/auth_ingress.db"
48
+ auth-ingress init-db
49
+ auth-ingress serve --host 127.0.0.1 --port 8000
50
+ ```
51
+
52
+ Upgrade only after reviewing [CHANGELOG.md](CHANGELOG.md), then install the exact
53
+ new version and rerun application smoke checks:
54
+
55
+ ```bash
56
+ python -m pip install --upgrade "auth-ingress==<new-version>"
57
+ auth-ingress --help
58
+ ```
59
+
60
+ Verify the installed version with
61
+ `python -m pip show auth-ingress`. Remove the package with
62
+ `python -m pip uninstall auth-ingress`; database files and external
63
+ configuration are intentionally retained and must be removed separately if no
64
+ longer needed.
65
+
66
+ ## Trust boundary
67
+
68
+ The portal is an access-control boundary only when downstream services are
69
+ reachable exclusively from the portal or a trusted internal network. Never
70
+ configure a publicly reachable downstream URL: users who know it could bypass
71
+ the portal. Destinations are restricted to loopback, private IP addresses,
72
+ `.internal` hosts, and local `mock://` demo targets. Embedded credentials, query
73
+ parameters, and fragments are rejected.
74
+
75
+ Credentials are Argon2-hashed. Browser cookies contain only a signed opaque
76
+ reference to a server-side session and use HttpOnly and SameSite protections.
77
+ Production deployments must set `AUTH_INGRESS_SECRET_KEY` to a high-entropy value,
78
+ enable `AUTH_INGRESS_SECURE_COOKIES=true`, terminate TLS, and protect the SQLite
79
+ file at the operating-system level.
80
+
81
+ ## Setup and operation
82
+
83
+ For a production-style first installation, create the first administrator with
84
+ the one-time local bootstrap command. It prompts twice without echoing or
85
+ accepting the password as an argument:
86
+
87
+ ```bash
88
+ uv run auth-ingress bootstrap-admin \
89
+ --email admin@example.com \
90
+ --display-name "Administrator"
91
+ uv run auth-ingress serve --host 127.0.0.1 --port 8000
92
+ ```
93
+
94
+ Before bootstrap, sign-in displays local setup guidance and never exposes a
95
+ registration form. Repeating the command after any identity exists makes no
96
+ change. If setup fails before completion, correct the reported non-secret input
97
+ or storage problem and safely retry the same command.
98
+
99
+ For disposable development data only:
100
+
101
+ ```bash
102
+ uv sync --extra test
103
+ uv run auth-ingress init-db
104
+ uv run auth-ingress seed-demo
105
+ uv run auth-ingress serve --host 127.0.0.1 --port 8000
106
+ ```
107
+
108
+ Configuration uses these environment variables:
109
+
110
+ - `AUTH_INGRESS_DATABASE_URL`
111
+ - `AUTH_INGRESS_SECRET_KEY`
112
+ - `AUTH_INGRESS_SESSION_COOKIE`
113
+ - `AUTH_INGRESS_SESSION_TTL`
114
+ - `AUTH_INGRESS_SECURE_COOKIES`
115
+ - `AUTH_INGRESS_RATE_LIMIT_ATTEMPTS`
116
+ - `AUTH_INGRESS_RATE_LIMIT_WINDOW`
117
+ - `AUTH_INGRESS_AUDIT_RETENTION_DAYS` (minimum/default: 90)
118
+ - `AUTH_INGRESS_PASSWORD_RESET_TTL` (minimum: 300 seconds; default: 1800)
119
+ - `AUTH_INGRESS_SMTP_HOST`, `AUTH_INGRESS_SMTP_PORT`, `AUTH_INGRESS_SMTP_SENDER`
120
+ - `AUTH_INGRESS_SMTP_USERNAME`, `AUTH_INGRESS_SMTP_PASSWORD`, `AUTH_INGRESS_SMTP_STARTTLS`
121
+ - `AUTH_INGRESS_SMTP_TIMEOUT`
122
+ - `AUTH_INGRESS_USER_PAGE_SIZE` (10–100; default: 50)
123
+ - `AUTH_INGRESS_MANAGEMENT_RATE_LIMIT_ATTEMPTS`, `AUTH_INGRESS_MANAGEMENT_RATE_LIMIT_WINDOW`
124
+ - `AUTH_INGRESS_DOWNSTREAM_TIMEOUT`
125
+
126
+ Demo accounts use the addresses shown by the seed implementation. `seed-demo`
127
+ prompts for each password, or reads the local-only
128
+ `AUTH_INGRESS_DEMO_ADMIN_PASSWORD`, `AUTH_INGRESS_DEMO_MEMBER_PASSWORD`, and
129
+ `AUTH_INGRESS_DEMO_OUTSIDER_PASSWORD` values. Each must contain at least 12
130
+ characters. The CLI deliberately does not print credentials. Remove all demo
131
+ accounts and unset these variables before deployment.
132
+
133
+ Administrators manage users at `/admin/users` or with `auth-ingress users`.
134
+ Memberships remain the only per-user access-list input; user detail explains all
135
+ groups granting each service. Page and CLI mutations preview first and reject a
136
+ stale user revision. Creating a user generates a one-time temporary password
137
+ that is shown only in the create response; the user must change it immediately
138
+ after first sign-in. Later password resets can still use configured SMTP links;
139
+ reset secrets are stored only as digests and never shown to an operator.
140
+ Deactivation is the reversible soft-delete path; permanent removal deletes the
141
+ user account and authentication state while retaining audit history. See
142
+ [docs/user-management.md](docs/user-management.md) for commands, exit codes,
143
+ conflict recovery, lifecycle controls, and delivery troubleshooting.
144
+
145
+ ## Audit and recovery
146
+
147
+ Sign-in attempts, sign-out, allowed and denied service entry, and administrative
148
+ changes create structured database audit events. Context is allowlisted to a
149
+ correlation ID and coarse client category; passwords, cookies, session IDs,
150
+ secrets, request bodies, and unnecessary personal data are excluded. Retain
151
+ events for at least 90 days and back up the database according to organizational
152
+ policy.
153
+
154
+ If a downstream service fails, the portal returns a generic unavailable response
155
+ without retrying unsafe requests or exposing internal details. Operators should
156
+ use the response correlation ID and audit events to investigate. Revoke access by
157
+ disabling a user, removing group membership, disabling a service, or revoking its
158
+ active sessions; authorization is re-evaluated on every protected request.
159
+
160
+ ## Full web-app proxy deployment
161
+
162
+ Full proxy mode gives every service an isolated browser origin. Production
163
+ deployments require:
164
+
165
+ - A dedicated portal host configured with `AUTH_INGRESS_HOST`.
166
+ - Wildcard DNS and TLS for `*.AUTH_INGRESS_PROXY_BASE_DOMAIN`.
167
+ - `AUTH_INGRESS_PROXY_SCHEME=https` and secure cookies.
168
+ - Private network routing from the portal to every downstream destination;
169
+ downstream services must remain unreachable from user networks.
170
+ - A narrow comma-separated `AUTH_INGRESS_TRUSTED_DOWNSTREAM_NETWORKS` value.
171
+ - Explicit request/response byte limits, launch-ticket lifetime, upstream
172
+ timeout, and WebSocket maximum lifetime appropriate to the deployment.
173
+
174
+ Relevant settings are `AUTH_INGRESS_PROXY_BASE_DOMAIN`,
175
+ `AUTH_INGRESS_PROXY_SCHEME`, `AUTH_INGRESS_PROXY_COOKIE`,
176
+ `AUTH_INGRESS_PROXY_LAUNCH_TTL`, `AUTH_INGRESS_PROXY_MAX_REQUEST_BYTES`,
177
+ `AUTH_INGRESS_PROXY_MAX_RESPONSE_BYTES`,
178
+ `AUTH_INGRESS_PROXY_WEBSOCKET_LIFETIME`, and
179
+ `AUTH_INGRESS_TRUSTED_DOWNSTREAM_NETWORKS`.
180
+
181
+ Enable proxy mode per service only after its compatibility check succeeds.
182
+ Applications should use relative or root-relative URLs, or be configured with
183
+ their public service origin. Fixed private origins embedded in JavaScript are not
184
+ rewritten. Roll back by disabling proxy mode for the service; the legacy simple
185
+ entry flow remains available for non-proxy service entries.
186
+
187
+ ## Validation
188
+
189
+ ```bash
190
+ uv run pytest
191
+ ```
192
+
193
+ The full manual journey is documented in
194
+ `specs/002-full-web-app-proxy/quickstart.md`.
@@ -0,0 +1,40 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Until the first public release, only the latest revision on the default branch is
6
+ eligible for security fixes. After release, the latest minor release series will
7
+ receive security fixes; older series may be supported when an advisory states so.
8
+
9
+ | Version | Supported |
10
+ |---------|-----------|
11
+ | Unreleased default branch | Yes |
12
+ | Latest published minor series | Yes |
13
+ | Older published series | Advisory-specific |
14
+
15
+ ## Reporting a Vulnerability
16
+
17
+ Report vulnerabilities privately through the repository's **Security** tab using
18
+ GitHub private vulnerability reporting:
19
+
20
+ https://github.com/zondatw/auth-ingress/security/advisories/new
21
+
22
+ Do not open a public issue for an unpatched vulnerability. Include the affected
23
+ version or revision, impact, reproduction details, and any suggested mitigation.
24
+ Do not include real passwords, session cookies, publisher tokens, private
25
+ database content, or unnecessary personal data.
26
+
27
+ Maintainers will acknowledge a complete report within five business days, assess
28
+ severity and affected versions, and coordinate remediation and disclosure. A
29
+ security advisory will identify fixed versions and operator actions when public
30
+ disclosure is appropriate.
31
+
32
+ ## Release-Publisher Compromise
33
+
34
+ If a release, GitHub Actions workflow, Trusted Publisher binding, or package-index
35
+ artifact appears compromised, use the same private reporting path and label the
36
+ report **release publisher compromise**. Maintainers will cancel pending release
37
+ jobs, revoke publisher bindings, preserve non-sensitive evidence, assess
38
+ published hashes, yank unverifiable releases, and publish a corrected new version
39
+ when required. Application credentials are rotated only when evidence indicates
40
+ they were exposed.
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["hatchling==1.30.1"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "auth-ingress"
7
+ version = "0.1.0"
8
+ description = "A small authenticated ingress portal for internal services"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "zondatw" }]
14
+ maintainers = [{ name = "zondatw" }]
15
+ keywords = ["authentication", "authorization", "ingress", "portal", "reverse-proxy"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Web Environment",
19
+ "Intended Audience :: System Administrators",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
26
+ "Topic :: Security",
27
+ ]
28
+ import-names = ["auth_ingress"]
29
+ dependencies = [
30
+ "argon2-cffi>=23.1",
31
+ "fastapi>=0.115",
32
+ "httpx>=0.27",
33
+ "itsdangerous>=2.2",
34
+ "jinja2>=3.1",
35
+ "python-multipart>=0.0.18",
36
+ "sqlalchemy>=2.0",
37
+ "uvicorn>=0.34",
38
+ "websockets>=15.0",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ test = [
43
+ "packaging>=24.2",
44
+ "playwright>=1.55",
45
+ "pytest>=8.3",
46
+ "pytest-cov>=6.0",
47
+ "pyyaml>=6.0",
48
+ ]
49
+
50
+ [project.scripts]
51
+ auth-ingress = "auth_ingress.cli:main"
52
+ auth-portal = "auth_ingress.cli:main"
53
+
54
+ [project.urls]
55
+ Homepage = "https://github.com/zondatw/auth-ingress"
56
+ Documentation = "https://github.com/zondatw/auth-ingress#readme"
57
+ Source = "https://github.com/zondatw/auth-ingress"
58
+ Issues = "https://github.com/zondatw/auth-ingress/issues"
59
+ Changelog = "https://github.com/zondatw/auth-ingress/blob/main/CHANGELOG.md"
60
+ Security = "https://github.com/zondatw/auth-ingress/security/policy"
61
+
62
+ [tool.hatch.build.targets.wheel]
63
+ packages = ["src/auth_ingress"]
64
+
65
+ [tool.hatch.build.targets.sdist]
66
+ include = [
67
+ "/src",
68
+ "/CHANGELOG.md",
69
+ "/LICENSE",
70
+ "/README.md",
71
+ "/SECURITY.md",
72
+ "/pyproject.toml",
73
+ "/uv.lock",
74
+ ]
75
+ exclude = [".gitignore"]
76
+
77
+ [tool.pytest.ini_options]
78
+ testpaths = ["tests"]
79
+ addopts = "-ra"
@@ -0,0 +1,2 @@
1
+ """Authenticated entry portal."""
2
+