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.
- auth_ingress-0.1.0/.gitignore +24 -0
- auth_ingress-0.1.0/CHANGELOG.md +35 -0
- auth_ingress-0.1.0/LICENSE +21 -0
- auth_ingress-0.1.0/PKG-INFO +237 -0
- auth_ingress-0.1.0/README.md +194 -0
- auth_ingress-0.1.0/SECURITY.md +40 -0
- auth_ingress-0.1.0/pyproject.toml +79 -0
- auth_ingress-0.1.0/src/auth_ingress/__init__.py +2 -0
- auth_ingress-0.1.0/src/auth_ingress/cli.py +153 -0
- auth_ingress-0.1.0/src/auth_ingress/config.py +103 -0
- auth_ingress-0.1.0/src/auth_ingress/main.py +86 -0
- auth_ingress-0.1.0/src/auth_ingress/models/__init__.py +9 -0
- auth_ingress-0.1.0/src/auth_ingress/models/audit_event.py +27 -0
- auth_ingress-0.1.0/src/auth_ingress/models/identity.py +72 -0
- auth_ingress-0.1.0/src/auth_ingress/models/installation.py +22 -0
- auth_ingress-0.1.0/src/auth_ingress/models/password_reset.py +25 -0
- auth_ingress-0.1.0/src/auth_ingress/models/proxy_launch_ticket.py +25 -0
- auth_ingress-0.1.0/src/auth_ingress/models/service_entry.py +45 -0
- auth_ingress-0.1.0/src/auth_ingress/models/session.py +24 -0
- auth_ingress-0.1.0/src/auth_ingress/repositories/__init__.py +2 -0
- auth_ingress-0.1.0/src/auth_ingress/repositories/database.py +35 -0
- auth_ingress-0.1.0/src/auth_ingress/repositories/schema.py +101 -0
- auth_ingress-0.1.0/src/auth_ingress/security/__init__.py +2 -0
- auth_ingress-0.1.0/src/auth_ingress/security/cookies.py +65 -0
- auth_ingress-0.1.0/src/auth_ingress/security/csrf.py +15 -0
- auth_ingress-0.1.0/src/auth_ingress/security/dependencies.py +40 -0
- auth_ingress-0.1.0/src/auth_ingress/security/passwords.py +21 -0
- auth_ingress-0.1.0/src/auth_ingress/security/proxy_destination.py +73 -0
- auth_ingress-0.1.0/src/auth_ingress/security/proxy_host.py +50 -0
- auth_ingress-0.1.0/src/auth_ingress/security/rate_limit.py +49 -0
- auth_ingress-0.1.0/src/auth_ingress/services/__init__.py +2 -0
- auth_ingress-0.1.0/src/auth_ingress/services/access_service.py +61 -0
- auth_ingress-0.1.0/src/auth_ingress/services/audit_service.py +68 -0
- auth_ingress-0.1.0/src/auth_ingress/services/authentication_service.py +16 -0
- auth_ingress-0.1.0/src/auth_ingress/services/bootstrap_service.py +81 -0
- auth_ingress-0.1.0/src/auth_ingress/services/cli_user_auth.py +20 -0
- auth_ingress-0.1.0/src/auth_ingress/services/cli_user_output.py +42 -0
- auth_ingress-0.1.0/src/auth_ingress/services/downstream_service.py +39 -0
- auth_ingress-0.1.0/src/auth_ingress/services/password_reset_service.py +96 -0
- auth_ingress-0.1.0/src/auth_ingress/services/proxy_authorization_service.py +110 -0
- auth_ingress-0.1.0/src/auth_ingress/services/proxy_header_policy.py +115 -0
- auth_ingress-0.1.0/src/auth_ingress/services/proxy_http_service.py +101 -0
- auth_ingress-0.1.0/src/auth_ingress/services/proxy_websocket_service.py +116 -0
- auth_ingress-0.1.0/src/auth_ingress/services/recovery_delivery.py +39 -0
- auth_ingress-0.1.0/src/auth_ingress/services/service_admin_service.py +100 -0
- auth_ingress-0.1.0/src/auth_ingress/services/service_compatibility_service.py +49 -0
- auth_ingress-0.1.0/src/auth_ingress/services/session_service.py +62 -0
- auth_ingress-0.1.0/src/auth_ingress/services/user_admin_service.py +208 -0
- auth_ingress-0.1.0/src/auth_ingress/services/user_management_types.py +49 -0
- auth_ingress-0.1.0/src/auth_ingress/web/__init__.py +2 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/__init__.py +12 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_audit.py +36 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_services.py +165 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/admin_users.py +134 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/auth.py +136 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/password_reset.py +46 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/portal.py +25 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/proxy.py +142 -0
- auth_ingress-0.1.0/src/auth_ingress/web/routes/services.py +66 -0
- auth_ingress-0.1.0/src/auth_ingress/web/static/portal.css +20 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/audit.html +5 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/services.html +20 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/user_detail.html +119 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/admin/users.html +12 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/change_password.html +16 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/reset_password.html +3 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/setup_required.html +11 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/auth/sign_in.html +18 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/base.html +13 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/errors/access_denied.html +4 -0
- auth_ingress-0.1.0/src/auth_ingress/web/templates/portal/index.html +10 -0
- auth_ingress-0.1.0/src/auth_ingress/web/web.py +22 -0
- 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"
|