Documentation cleanup for public release (#123)
Prepares the docs for the public release announcement: removes obsolete/dead material, fixes drift between the API reference and the actual routes, and standardizes on the default GitHub Pages URL. - Remove Vagrant entirely: delete Vagrantfile, docs/dev_setup.md, and stale vagrant references in .gitignore/.dockerignore; rewrite openresty/README.md to describe the actual (currently unused) directory and point to ops/nginx_conf/ for the real OpenResty config. - Delete docs/Update 4.11.md (personal scratch changelog) and drop both its and dev_setup.md's references from docs/README.md's Legacy Documentation section. - Remove checkmark emoji from docs/contributing.md's PR Requirements list. - Bring the auth model docs up to date with the code: document GET /api/auth/oidc/start + /callback, the /api/permission and /api/group RBAC routers, the /api/dns/dynamic/* sub-API, and /api/api-token (self -service PATs) in both nodejs/api.md and docs/api.md; add the missing "Clear Host Cache" section; drop the invite-token/SSH-key endpoints that no longer exist in nodejs/routes/user.js; note admin-only routes. Mention OIDC/LDAP/RBAC as core features in README.md. - Keep nodejs/api.md and docs/api.md fully in sync (same body, differing only in Jekyll front matter / relative links) instead of letting them drift. - Fix Node.js version references (20.x -> 22.x) in README.md and docs/installation.md to match ops/install.sh and the Dockerfile. - Note that the manual nginx-conf/systemd install steps in README.md and docs/installation.md won't auto-track repo changes the way install.sh's symlink approach does, and recommend install.sh. - Update the stale test/unit file lists in docs/contributing.md and nodejs/test/README.md to match the actual directory contents. - Add npm run test:integration to README.md's Running Tests section. - Add nodejs/conf/, nodejs/controller/, and nodejs/migrations/ to the project structure diagrams in README.md, docs/architecture.md, and docs/contributing.md. - Standardize "CloudFlare" -> "Cloudflare" everywhere to match the actual API value in nodejs/models/dns_provider.js. - Add the missing app_auth__adminGroups row to DEPLOYMENT.md's app_* table. - Delete docs/CNAME (custom domain) so GitHub Pages serves from the default https://theta42.github.io/proxy/, matching docs/README.md. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+1
-2
@@ -7,13 +7,12 @@ nodejs/node_modules/
|
|||||||
nodejs/test/
|
nodejs/test/
|
||||||
nodejs/npm-debug.log*
|
nodejs/npm-debug.log*
|
||||||
|
|
||||||
# Bare-metal installer + chef/vagrant ops not needed in the image.
|
# Bare-metal installer + chef ops not needed in the image.
|
||||||
ops/install.sh
|
ops/install.sh
|
||||||
ops/cert.sh
|
ops/cert.sh
|
||||||
ops/cookbooks/
|
ops/cookbooks/
|
||||||
ops/roles/
|
ops/roles/
|
||||||
ops/proxy.service
|
ops/proxy.service
|
||||||
Vagrantfile
|
|
||||||
|
|
||||||
# Docs site (served via GitHub Pages, not from the image).
|
# Docs site (served via GitHub Pages, not from the image).
|
||||||
docs/
|
docs/
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ typings/
|
|||||||
# dotenv environment variables file
|
# dotenv environment variables file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
.vagrant
|
|
||||||
*~
|
*~
|
||||||
*#
|
*#
|
||||||
.#*
|
.#*
|
||||||
|
|||||||
@@ -39,8 +39,12 @@ are `JSON.parse`-coerced when possible and kept as raw strings otherwise.
|
|||||||
| `app_ldap__tlsOptions__rejectUnauthorized` | `conf.ldap.tlsOptions.rejectUnauthorized` | `false` for self-signed LDAPS |
|
| `app_ldap__tlsOptions__rejectUnauthorized` | `conf.ldap.tlsOptions.rejectUnauthorized` | `false` for self-signed LDAPS |
|
||||||
| `app_ldap__tlsOptions__ca` | `conf.ldap.tlsOptions.ca` | path to a CA cert for strict trust |
|
| `app_ldap__tlsOptions__ca` | `conf.ldap.tlsOptions.ca` | path to a CA cert for strict trust |
|
||||||
| `app_auth__adminUsers` | `conf.auth.adminUsers` | local anti-lockout admin (uid) |
|
| `app_auth__adminUsers` | `conf.auth.adminUsers` | local anti-lockout admin (uid) |
|
||||||
|
| `app_auth__adminGroups` | `conf.auth.adminGroups` | SSO/LDAP groups that are global admin (JSON array) |
|
||||||
| `app_redis__prefix` | `conf.redis.prefix` | default `proxy_` |
|
| `app_redis__prefix` | `conf.redis.prefix` | default `proxy_` |
|
||||||
|
|
||||||
|
See [`docs/docker.md`](docs/docker.md) for a shorter, container-focused version
|
||||||
|
of this reference.
|
||||||
|
|
||||||
> **Requires `@simpleworkjs/conf` >= 1.1.0.** The Docker image will not honor
|
> **Requires `@simpleworkjs/conf` >= 1.1.0.** The Docker image will not honor
|
||||||
> `app_*` env vars on 1.0.0. The lock is already on `^1.1.0`; if you regenerate it:
|
> `app_*` env vars on 1.0.0. The lock is already on `^1.1.0`; if you regenerate it:
|
||||||
> ```bash
|
> ```bash
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ A reverse proxy and HTTPS termination service using OpenResty/nginx with a manag
|
|||||||
|
|
||||||
- Automated HTTPS/SSL certificate management via Let's Encrypt
|
- Automated HTTPS/SSL certificate management via Let's Encrypt
|
||||||
- Support for HTTP-01 (auto-ssl) and DNS-01 (wildcard) ACME challenges
|
- Support for HTTP-01 (auto-ssl) and DNS-01 (wildcard) ACME challenges
|
||||||
- Multiple DNS provider integrations (CloudFlare, DigitalOcean, PorkBun)
|
- Multiple DNS provider integrations (Cloudflare, DigitalOcean, PorkBun)
|
||||||
- Wildcard SSL certificate support with automatic renewal
|
- Wildcard SSL certificate support with automatic renewal
|
||||||
- Dynamic host routing with wildcard domain matching (*, **)
|
- Dynamic host routing with wildcard domain matching (*, **)
|
||||||
- Web-based management interface
|
- Web-based management interface
|
||||||
- RESTful API for automation
|
- RESTful API for automation
|
||||||
- User authentication and management
|
- **OIDC login** — the proxy is an OpenID Connect client of an external SSO
|
||||||
|
(e.g. [`theta42/sso-manager-node`](https://github.com/theta42/sso-manager-node))
|
||||||
|
- **Direct LDAP lookups**, independent of the OIDC flow
|
||||||
|
- **Role-based access control (RBAC)** — global admins, local groups, and
|
||||||
|
per-domain permissions (viewer/manager) via `/api/permission` and `/api/group`
|
||||||
|
- Self-service API tokens (PATs) for scripting/CI without a browser session
|
||||||
- Unix socket-based host lookup for high-performance routing
|
- Unix socket-based host lookup for high-performance routing
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@@ -34,7 +39,7 @@ wget -O - https://raw.githubusercontent.com/theta42/proxy/master/ops/install.sh
|
|||||||
```
|
```
|
||||||
|
|
||||||
This installer will:
|
This installer will:
|
||||||
- Install Node.js 20.x
|
- Install Node.js 22.x
|
||||||
- Install OpenResty and required dependencies
|
- Install OpenResty and required dependencies
|
||||||
- Install and configure Redis
|
- Install and configure Redis
|
||||||
- Set up SSL fallback certificates
|
- Set up SSL fallback certificates
|
||||||
@@ -68,6 +73,14 @@ docker compose logs --tail=200 --since=10m proxy
|
|||||||
|
|
||||||
For manual installation or other distributions, see the detailed steps below.
|
For manual installation or other distributions, see the detailed steps below.
|
||||||
|
|
||||||
|
> **Recommended path:** `ops/install.sh` is idempotent and safe to re-run — it
|
||||||
|
> symlinks the OpenResty/systemd config from the repo checkout, so updates
|
||||||
|
> stay in sync automatically. The manual steps below copy those same files
|
||||||
|
> instead of symlinking them, so they will **not** auto-track future changes
|
||||||
|
> to `ops/nginx_conf/` or `ops/proxy.service` — you'd need to re-copy them
|
||||||
|
> yourself after every update. Use the manual path only if `install.sh`
|
||||||
|
> doesn't fit your distribution.
|
||||||
|
|
||||||
### System Dependencies
|
### System Dependencies
|
||||||
|
|
||||||
**Ubuntu/Debian:**
|
**Ubuntu/Debian:**
|
||||||
@@ -75,10 +88,10 @@ For manual installation or other distributions, see the detailed steps below.
|
|||||||
apt install libpam0g-dev build-essential redis-server luarocks -y
|
apt install libpam0g-dev build-essential redis-server luarocks -y
|
||||||
```
|
```
|
||||||
|
|
||||||
**Node.js 20.x:**
|
**Node.js 22.x:**
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
NODE_MAJOR=20
|
NODE_MAJOR=22
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||||
apt update && apt install nodejs -y
|
apt update && apt install nodejs -y
|
||||||
```
|
```
|
||||||
@@ -147,7 +160,7 @@ systemctl start proxy.service
|
|||||||
For wildcard SSL certificates, configure a DNS provider via the web UI or API:
|
For wildcard SSL certificates, configure a DNS provider via the web UI or API:
|
||||||
|
|
||||||
**Supported providers:**
|
**Supported providers:**
|
||||||
- **CloudFlare** - Requires API token
|
- **Cloudflare** - Requires API token
|
||||||
- **DigitalOcean** - Requires API token
|
- **DigitalOcean** - Requires API token
|
||||||
- **PorkBun** - Requires API key and secret API key
|
- **PorkBun** - Requires API key and secret API key
|
||||||
|
|
||||||
@@ -195,9 +208,10 @@ npm run dev # Runs with nodemon for auto-reload
|
|||||||
|
|
||||||
**Running tests:**
|
**Running tests:**
|
||||||
```bash
|
```bash
|
||||||
npm test # Run all tests
|
npm test # Run all tests
|
||||||
npm run test:unit # Run unit tests only
|
npm run test:unit # Run unit tests only
|
||||||
npm run test:watch # Watch mode for development
|
npm run test:integration # Run integration tests only
|
||||||
|
npm run test:watch # Watch mode for development
|
||||||
```
|
```
|
||||||
|
|
||||||
Tests use Node.js built-in test runner (requires Node 18+).
|
Tests use Node.js built-in test runner (requires Node 18+).
|
||||||
@@ -223,6 +237,9 @@ MIT - See LICENSE file for details.
|
|||||||
proxy/
|
proxy/
|
||||||
├── nodejs/ # Node.js backend application
|
├── nodejs/ # Node.js backend application
|
||||||
│ ├── bin/ # Entry point (www)
|
│ ├── bin/ # Entry point (www)
|
||||||
|
│ ├── conf/ # Configuration (base.js, environment overlays, secrets.js)
|
||||||
|
│ ├── controller/ # App-level wiring (pubsub, startup)
|
||||||
|
│ ├── migrations/ # One-off Redis data migration scripts
|
||||||
│ ├── models/ # Data models (Host, User, DNS providers)
|
│ ├── models/ # Data models (Host, User, DNS providers)
|
||||||
│ ├── routes/ # API routes
|
│ ├── routes/ # API routes
|
||||||
│ ├── services/ # Background services (host lookup, scheduler)
|
│ ├── services/ # Background services (host lookup, scheduler)
|
||||||
|
|||||||
Vendored
-142
@@ -1,142 +0,0 @@
|
|||||||
require 'json'
|
|
||||||
begin
|
|
||||||
secrets = JSON.parse(File.read('secrets.json'))
|
|
||||||
puts 'Loading secrets file'
|
|
||||||
rescue
|
|
||||||
secrets = {}
|
|
||||||
puts 'Secrets file not found'
|
|
||||||
end
|
|
||||||
|
|
||||||
class ::Hash
|
|
||||||
def deep_merge(second)
|
|
||||||
second.each do |key, value|
|
|
||||||
if value.class == Hash and self[key.to_sym]
|
|
||||||
self[key.to_sym].deep_merge(value)
|
|
||||||
else
|
|
||||||
self[key.to_sym] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# -*- mode: ruby -*-
|
|
||||||
# vi: set ft=ruby :
|
|
||||||
|
|
||||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
|
||||||
# configures the configuration version (we support older styles for
|
|
||||||
# backwards compatibility). Please don't change it unless you know what
|
|
||||||
# you're doing.
|
|
||||||
Vagrant.configure("2") do |config|
|
|
||||||
# The most common configuration options are documented and commented below.
|
|
||||||
# For a complete reference, please see the online documentation at
|
|
||||||
# https://docs.vagrantup.com.
|
|
||||||
|
|
||||||
# Every Vagrant development environment requires a box. You can search for
|
|
||||||
# boxes at https://vagrantcloud.com/search.
|
|
||||||
config.vm.box = "ubuntu/jammy64"
|
|
||||||
config.vm.synced_folder '.', '/vagrant' # The vagrant dir just stopped automounting
|
|
||||||
|
|
||||||
# Create a forwarded port mapping which allows access to a specific port
|
|
||||||
# within the machine from a port on the host machine. In the example below,
|
|
||||||
# accessing "localhost:8080" will access port 80 on the guest machine.
|
|
||||||
# NOTE: This will enable public access to the opened port
|
|
||||||
config.vm.network "forwarded_port", guest: 80, host: 8080
|
|
||||||
config.vm.network "forwarded_port", guest: 443, host: 8443
|
|
||||||
config.vm.network "forwarded_port", guest: 3000, host: 8300
|
|
||||||
|
|
||||||
|
|
||||||
# Provider-specific configuration so you can fine-tune various
|
|
||||||
# backing providers for Vagrant. These expose provider-specific options.
|
|
||||||
# Example for VirtualBox:
|
|
||||||
#
|
|
||||||
config.vm.provider 'virtualbox' do |vb|
|
|
||||||
# Customize the amount of memory on the VM:
|
|
||||||
vb.memory = '1024'
|
|
||||||
vb.cpus = "2"
|
|
||||||
# vb.default_nic_type = "virtio"
|
|
||||||
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
|
|
||||||
end
|
|
||||||
#
|
|
||||||
# View the documentation for the provider you are using for more
|
|
||||||
# information on available options.
|
|
||||||
|
|
||||||
# Enable provisioning with a shell script. Additional provisioners such as
|
|
||||||
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
|
|
||||||
# documentation for more information about their specific syntax and use.
|
|
||||||
config.vm.provision "shell", inline: <<~SHELL
|
|
||||||
apt-get update
|
|
||||||
if ! apt list ruby-dev | grep installed; then
|
|
||||||
# apt-add-repository ppa:brightbox/ruby-ng -y
|
|
||||||
sudo apt-get install -y build-essential resolvconf ruby-full gem
|
|
||||||
gem install chef -v 17.10.0
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! which berks >/dev/null; then
|
|
||||||
gem install ruby-shadow berkshelf --no-document
|
|
||||||
ln -s /opt/chef/embedded/bin/berks /usr/local/bin/berks
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /vagrant
|
|
||||||
|
|
||||||
cd /vagrant/ops/cookbooks
|
|
||||||
rm -rf vendor
|
|
||||||
rm -rf $HOME/.berksfile
|
|
||||||
if [ -f ".Berksfile.lock" ]; then
|
|
||||||
berks update
|
|
||||||
else
|
|
||||||
berks install
|
|
||||||
fi
|
|
||||||
berks vendor vendor
|
|
||||||
SHELL
|
|
||||||
|
|
||||||
config.vm.provision 'chef_solo' do |chef|
|
|
||||||
chef.arguments = "--chef-license accept"
|
|
||||||
# chef.version = '15.7.31' # version 14.12.9 fails to run
|
|
||||||
chef.cookbooks_path = [
|
|
||||||
'ops/cookbooks/',
|
|
||||||
'ops/cookbooks/vendor/'
|
|
||||||
]
|
|
||||||
chef.roles_path ='ops/roles'
|
|
||||||
chef.add_role('common')
|
|
||||||
chef.json = {
|
|
||||||
'working-dir': '/vagrant',
|
|
||||||
'app': {
|
|
||||||
'name': 't42-proxy',
|
|
||||||
'run_user': 'root',
|
|
||||||
'domain': 'proxy.local',
|
|
||||||
},
|
|
||||||
'python': {
|
|
||||||
# 'working-dir': 'django',
|
|
||||||
'version': '2.7'
|
|
||||||
},
|
|
||||||
'nodejs': {
|
|
||||||
'working-dir': 'nodejs',
|
|
||||||
'port': '3000',
|
|
||||||
'install_version': 18,
|
|
||||||
'exec_file': 'bin/www',
|
|
||||||
'service': true,
|
|
||||||
},
|
|
||||||
'redis':{
|
|
||||||
'unix': {
|
|
||||||
'perm': '777'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'web':{
|
|
||||||
'admin_email': 'admin2342@example.com',
|
|
||||||
'do_ssl': true,
|
|
||||||
't42-proxy': true
|
|
||||||
},
|
|
||||||
}.deep_merge(secrets);
|
|
||||||
end
|
|
||||||
|
|
||||||
config.vm.provision "shell", inline: <<~SHELL
|
|
||||||
cd /vagrant/openresty
|
|
||||||
shopt -s extglob
|
|
||||||
rm -frv !("README.md")
|
|
||||||
cp -a "/usr/local/openresty/nginx/conf/." /vagrant/openresty
|
|
||||||
rm -rf /usr/local/openresty/nginx/conf/
|
|
||||||
ln -s /vagrant/openresty/ /usr/local/openresty/nginx/conf
|
|
||||||
SHELL
|
|
||||||
end
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
proxy.projects.theta42.com
|
|
||||||
@@ -37,8 +37,3 @@ The site uses the Cayman theme (`jekyll-theme-cayman`). Configuration is in `_co
|
|||||||
2. Commit and push to master branch
|
2. Commit and push to master branch
|
||||||
3. GitHub Pages automatically rebuilds (may take 1-2 minutes)
|
3. GitHub Pages automatically rebuilds (may take 1-2 minutes)
|
||||||
4. Changes visible at https://theta42.github.io/proxy/
|
4. Changes visible at https://theta42.github.io/proxy/
|
||||||
|
|
||||||
## Legacy Documentation
|
|
||||||
|
|
||||||
- `dev_setup.md` - Old development setup notes (kept for reference)
|
|
||||||
- `Update 4.11.md` - Old update notes (kept for reference)
|
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# Update 4.11
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
The API routes have changed a bit and I will update the documentation soon.
|
|
||||||
|
|
||||||
All the endpoints now have key validations and return standardized errors.
|
|
||||||
Any 500 class error should be reported as an issue.
|
|
||||||
|
|
||||||
End points for working with user have been added.
|
|
||||||
|
|
||||||
A GUI as need added and can be accessed from root of port 3000,
|
|
||||||
`http://server.ip:3000/`
|
|
||||||
|
|
||||||
## How to get it
|
|
||||||
|
|
||||||
* Navigate to where the project is installed. This should be done as root.
|
|
||||||
|
|
||||||
* Pull the newest version from master
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git pull origin master
|
|
||||||
```
|
|
||||||
|
|
||||||
* Move to the NodeJS folder
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd nodejs
|
|
||||||
```
|
|
||||||
|
|
||||||
* Update and install the NPM packages
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm update
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
* Run the migration script for the redis Hosts.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
node migrations/host_1.js
|
|
||||||
```
|
|
||||||
|
|
||||||
* Restart the proxy service or container.
|
|
||||||
|
|
||||||
* Enoy the GUI!
|
|
||||||
+433
-26
@@ -7,7 +7,26 @@ title: API Reference
|
|||||||
|
|
||||||
[← Back to Home](index.html)
|
[← Back to Home](index.html)
|
||||||
|
|
||||||
All API endpoints require authentication via the `auth-token` header unless otherwise noted.
|
All API endpoints require authentication unless otherwise noted. Three
|
||||||
|
authentication methods are supported:
|
||||||
|
|
||||||
|
- **`auth-token` header** — a browser-session token from `POST /api/auth/login`
|
||||||
|
or the OIDC flow (below).
|
||||||
|
- **`Authorization: Bearer <token>` header** — a self-service API token (PAT,
|
||||||
|
see [API Tokens](#api-tokens)), for scripts/CI without a browser session.
|
||||||
|
- **OIDC (browser)** — if the proxy is configured as an OIDC client of an SSO
|
||||||
|
(`app_oidc__*` / `conf.oidc`, see [DEPLOYMENT.md](https://github.com/theta42/proxy/blob/master/DEPLOYMENT.md)),
|
||||||
|
users can log in via `GET /api/auth/oidc/start` instead of posting a
|
||||||
|
username/password.
|
||||||
|
|
||||||
|
The proxy can also be configured as a **direct LDAP client** (`app_ldap__*` /
|
||||||
|
`conf.ldap`) for looking up/validating users, independent of the OIDC flow —
|
||||||
|
see DEPLOYMENT.md for the full configuration reference.
|
||||||
|
|
||||||
|
Authenticated requests also carry **RBAC** (role-based access control):
|
||||||
|
global admins can manage everything; other users are scoped to `viewer` or
|
||||||
|
`manager` rights on specific domains via [Permissions](#permissions) and
|
||||||
|
[Groups](#groups).
|
||||||
|
|
||||||
Base URL: `https://your-proxy-host.com/api`
|
Base URL: `https://your-proxy-host.com/api`
|
||||||
|
|
||||||
@@ -47,17 +66,167 @@ curl -H "auth-token: your-token-here" \
|
|||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"message": "Bye"}`
|
- `200` `{"message": "Bye"}`
|
||||||
|
|
||||||
|
### OIDC Login (start)
|
||||||
|
|
||||||
|
**GET** `/api/auth/oidc/start`
|
||||||
|
|
||||||
|
Begin the OIDC authorization-code flow: creates a PKCE + state challenge and
|
||||||
|
redirects the browser to the configured SSO's authorize endpoint. Only
|
||||||
|
available when `conf.oidc.enabled` is true.
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
- `redirect` - Internal path to return to after login (optional; sanitized to same-origin)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -i "https://proxy-host.com/api/auth/oidc/start?redirect=/hosts"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `302` Redirect to the SSO's authorization endpoint
|
||||||
|
- `404` `{"name": "OidcDisabled", "message": "OIDC login is not enabled."}`
|
||||||
|
|
||||||
|
### OIDC Callback
|
||||||
|
|
||||||
|
**GET** `/api/auth/oidc/callback`
|
||||||
|
|
||||||
|
Redirect target for the SSO after login. Validates the one-time `state`,
|
||||||
|
exchanges the authorization `code` for tokens, reads identity from the
|
||||||
|
userinfo endpoint, establishes a session, and redirects the browser back to
|
||||||
|
the login page with the app's own `auth-token` in a URL fragment.
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
- `code` (required) - Authorization code from the SSO
|
||||||
|
- `state` (required) - State value from the `start` step
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Not called directly — the SSO redirects the browser here after login.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `302` Redirect to `/login#token=...&redirect=...`
|
||||||
|
- `400` `{"name": "OidcCallbackInvalid", "message": "Missing code or state."}` or expired/unknown state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Tokens
|
||||||
|
|
||||||
|
Self-service personal access tokens (PATs) for scripting/CI without a browser
|
||||||
|
session. Every endpoint is owner-scoped: a user only sees/manages tokens they
|
||||||
|
created. Mounted at `/api/api-token`.
|
||||||
|
|
||||||
|
### List API Tokens
|
||||||
|
|
||||||
|
**GET** `/api/api-token`
|
||||||
|
|
||||||
|
List the current user's API tokens.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/api-token
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"id": "...", "name": "ci", ...}, ...]}`
|
||||||
|
|
||||||
|
### Create API Token
|
||||||
|
|
||||||
|
**POST** `/api/api-token`
|
||||||
|
|
||||||
|
Create a new API token. The raw token string is only returned once, at
|
||||||
|
creation.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (required) - Display name
|
||||||
|
- `description` (optional)
|
||||||
|
- `expires_in_days` (optional) - `0` or omitted means no expiry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"name": "ci", "expires_in_days": 90}' \
|
||||||
|
https://proxy-host.com/api/api-token
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}, "token": "prx_<id>_<secret>", "message": "API token 'ci' created. Save it now — it will not be shown again."}`
|
||||||
|
|
||||||
|
### Get API Token
|
||||||
|
|
||||||
|
**GET** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Get a token's metadata (not the raw secret, which is never stored/returned again).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}}`
|
||||||
|
- `403` Not your token
|
||||||
|
|
||||||
|
### Update API Token
|
||||||
|
|
||||||
|
**PUT** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Update a token's name/description/expiry.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X PUT \
|
||||||
|
-d '{"name": "ci-updated"}' \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}, "message": "API token 'ci-updated' updated."}`
|
||||||
|
|
||||||
|
### Delete (Revoke) API Token
|
||||||
|
|
||||||
|
**DELETE** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Revoke a token immediately.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"id": "<id>", "message": "API token 'ci' revoked."}`
|
||||||
|
|
||||||
|
### Rotate API Token
|
||||||
|
|
||||||
|
**POST** `/api/api-token/:id/rotate`
|
||||||
|
|
||||||
|
Issue a new secret for an existing token (same id, new raw value shown once).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
https://proxy-host.com/api/api-token/<id>/rotate
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"token": "prx_<id>_<new-secret>", "message": "API token 'ci' rotated. Save it — it will not be shown again."}`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Users
|
## Users
|
||||||
|
|
||||||
All user endpoints require authentication.
|
All user endpoints require authentication. `GET /me` and `PUT /password`
|
||||||
|
(self-service) work for any authenticated user; everything else (listing,
|
||||||
|
creating, deleting users, resetting another user's password) requires global
|
||||||
|
admin.
|
||||||
|
|
||||||
### List Users
|
### List Users
|
||||||
|
|
||||||
**GET** `/api/user`
|
**GET** `/api/user`
|
||||||
|
|
||||||
Get list of all users.
|
Get list of all users. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -70,12 +239,14 @@ curl -H "auth-token: your-token-here" \
|
|||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ["user1", "user2"]}`
|
- `200` `{"results": ["user1", "user2"]}`
|
||||||
- `200` `{"results": [{"username": "user1", ...}, ...]}` (with `?detail=true`)
|
- `200` `{"results": [{"username": "user1", ...}, ...]}` (with `?detail=true`)
|
||||||
|
- `403` Not an admin
|
||||||
|
|
||||||
### Get Current User
|
### Get Current User
|
||||||
|
|
||||||
**GET** `/api/user/me`
|
**GET** `/api/user/me`
|
||||||
|
|
||||||
Get information about the currently authenticated user.
|
Get the currently authenticated user's identity and effective RBAC rights
|
||||||
|
(drives the web UI's nav/button gating).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -83,13 +254,13 @@ curl -H "auth-token: your-token-here" \
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"username": "myuser"}`
|
- `200` `{"username": "myuser", "groups": [...], "localGroups": [...], "externalGroups": [...], "isAdmin": false, "global": null, "domains": {...}}`
|
||||||
|
|
||||||
### Create User
|
### Create User
|
||||||
|
|
||||||
**POST** `/api/user`
|
**POST** `/api/user`
|
||||||
|
|
||||||
Create a new user.
|
Create a new local user. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
@@ -101,14 +272,15 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` User created successfully
|
- `200` User created successfully
|
||||||
|
- `403` Not an admin
|
||||||
- `409` Username already exists
|
- `409` Username already exists
|
||||||
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error
|
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error (also returned for weak passwords)
|
||||||
|
|
||||||
### Delete User
|
### Delete User
|
||||||
|
|
||||||
**DELETE** `/api/user/:username`
|
**DELETE** `/api/user/:username`
|
||||||
|
|
||||||
Delete a user account.
|
Delete a user account. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -118,6 +290,7 @@ curl -H "auth-token: your-token-here" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"username": "olduser", "results": ...}`
|
- `200` `{"username": "olduser", "results": ...}`
|
||||||
|
- `403` Not an admin
|
||||||
- `404` User not found
|
- `404` User not found
|
||||||
|
|
||||||
### Change Password (Self)
|
### Change Password (Self)
|
||||||
@@ -136,12 +309,13 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ...}` Password changed successfully
|
- `200` `{"results": ...}` Password changed successfully
|
||||||
|
- `422` Weak password rejected by the password policy
|
||||||
|
|
||||||
### Change Password (Other User)
|
### Change Password (Other User)
|
||||||
|
|
||||||
**PUT** `/api/user/password/:username`
|
**PUT** `/api/user/password/:username`
|
||||||
|
|
||||||
Change the password for another user (admin function).
|
Change the password for another user. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
@@ -153,40 +327,163 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ...}` Password changed successfully
|
- `200` `{"results": ...}` Password changed successfully
|
||||||
|
- `403` Not an admin
|
||||||
- `404` User not found
|
- `404` User not found
|
||||||
|
|
||||||
### Create Invite Token
|
---
|
||||||
|
|
||||||
**POST** `/api/user/invite`
|
## Permissions
|
||||||
|
|
||||||
Create an invitation token for new user registration.
|
RBAC: grants a `viewer` or `manager` role to a user or group, either globally
|
||||||
|
or scoped to one domain. Global-admin-only. Mounted at `/api/permission`.
|
||||||
|
|
||||||
|
### List Permissions
|
||||||
|
|
||||||
|
**GET** `/api/permission`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
-X POST \
|
https://proxy-host.com/api/permission
|
||||||
https://proxy-host.com/api/user/invite
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"token": "5caf94d2-2c91-4010-8df7-968d10802b9d"}`
|
- `200` `{"results": [{"id": "...", "subjectType": "user", "subject": "alice", "role": "manager", "scope": "domain", "domain": "example.com", ...}, ...]}`
|
||||||
|
|
||||||
### Add SSH Key
|
### List Permission Subjects
|
||||||
|
|
||||||
**POST** `/api/user/key`
|
**GET** `/api/permission/subjects`
|
||||||
|
|
||||||
Add an SSH public key to the current user's account.
|
Autocomplete source for the "Subject" field: known usernames plus known group
|
||||||
|
names (local groups, groups already used in permissions, and groups from
|
||||||
|
`conf.auth.adminGroups` / `conf.auth.groupRoleMap`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/permission/subjects
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"users": ["alice", "bob"], "groups": ["ops", "sre"]}`
|
||||||
|
|
||||||
|
### Create Permission
|
||||||
|
|
||||||
|
**POST** `/api/permission`
|
||||||
|
|
||||||
|
Grant a role to a subject.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `subjectType` (required) - `user` or `group`
|
||||||
|
- `subject` (required) - username or group name
|
||||||
|
- `role` (required) - `viewer` or `manager`
|
||||||
|
- `scope` (required) - `global` or `domain`
|
||||||
|
- `domain` (required if `scope` is `domain`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
-H "auth-token: your-token-here" \
|
-H "auth-token: your-token-here" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d '{"key": "ssh-rsa AAAAB3..."}' \
|
-d '{"subjectType": "user", "subject": "alice", "role": "manager", "scope": "domain", "domain": "example.com"}' \
|
||||||
https://proxy-host.com/api/user/key
|
https://proxy-host.com/api/permission
|
||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"message": true}` Key added successfully
|
- `200` `{"message": "Granted manager to user \"alice\" on example.com.", ...}`
|
||||||
- `400` `{"message": "Bad SSH key"}` Invalid key format
|
- `422` Validation error
|
||||||
|
|
||||||
|
### Delete Permission
|
||||||
|
|
||||||
|
**DELETE** `/api/permission/:id`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/permission/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Permission <id> removed."}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Groups
|
||||||
|
|
||||||
|
Local groups (independent of any SSO/LDAP groups) used as subjects for
|
||||||
|
permission grants. Global-admin-only. Mounted at `/api/group`.
|
||||||
|
|
||||||
|
### List Groups
|
||||||
|
|
||||||
|
**GET** `/api/group`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/group
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"name": "ops", "members": ["alice", "bob"], ...}, ...]}`
|
||||||
|
|
||||||
|
### Create Group
|
||||||
|
|
||||||
|
**POST** `/api/group`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (required)
|
||||||
|
- `members` (optional) - array of usernames
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"name": "ops", "members": ["alice"]}' \
|
||||||
|
https://proxy-host.com/api/group
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Group \"ops\" created.", ...}`
|
||||||
|
|
||||||
|
### Delete Group
|
||||||
|
|
||||||
|
**DELETE** `/api/group/:name`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/group/ops
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Group \"ops\" removed."}`
|
||||||
|
|
||||||
|
### Add Group Member
|
||||||
|
|
||||||
|
**POST** `/api/group/:name/members`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `username` (required)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"username": "bob"}' \
|
||||||
|
https://proxy-host.com/api/group/ops/members
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Added \"bob\" to \"ops\".", ...}`
|
||||||
|
|
||||||
|
### Remove Group Member
|
||||||
|
|
||||||
|
**DELETE** `/api/group/:name/members/:username`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/group/ops/members/bob
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Removed \"bob\" from \"ops\".", ...}`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -320,6 +617,24 @@ curl -H "auth-token: your-token-here" \
|
|||||||
- `200` `{"message": "example.com deleted", ...}`
|
- `200` `{"message": "example.com deleted", ...}`
|
||||||
- `404` `{"name": "HostNotFound", "message": "Host does not exists"}`
|
- `404` `{"name": "HostNotFound", "message": "Host does not exists"}`
|
||||||
|
|
||||||
|
### Clear Host Cache
|
||||||
|
|
||||||
|
**DELETE** `/api/host/cache`
|
||||||
|
|
||||||
|
Remove all cached wildcard-subdomain host lookups. Cache entries are created on
|
||||||
|
demand when a wildcard host serves a subdomain; clearing them forces the next
|
||||||
|
request for each subdomain to be resolved fresh through the lookup tree.
|
||||||
|
Admin only.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/host/cache
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Cleared 3 cached hosts.", "count": 3}`
|
||||||
|
|
||||||
### Renew Wildcard Certificate
|
### Renew Wildcard Certificate
|
||||||
|
|
||||||
**PUT** `/api/host/:host/renew`
|
**PUT** `/api/host/:host/renew`
|
||||||
@@ -372,7 +687,7 @@ curl -H "auth-token: your-token-here" \
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": [{"name": "CloudFlare", "fields": {...}}, {"name": "DigitalOcean", ...}, ...]}`
|
- `200` `{"results": [{"name": "Cloudflare", "fields": {...}}, {"name": "DigitalOcean", ...}, ...]}`
|
||||||
|
|
||||||
### Create DNS Provider
|
### Create DNS Provider
|
||||||
|
|
||||||
@@ -380,12 +695,12 @@ curl -H "auth-token: your-token-here" \
|
|||||||
|
|
||||||
Configure a new DNS provider.
|
Configure a new DNS provider.
|
||||||
|
|
||||||
**CloudFlare:**
|
**Cloudflare:**
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
-H "auth-token: your-token-here" \
|
-H "auth-token: your-token-here" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d '{"name": "My CloudFlare", "dnsProvider": "Cloudflare", "token": "your-api-token"}' \
|
-d '{"name": "My Cloudflare", "dnsProvider": "Cloudflare", "token": "your-api-token"}' \
|
||||||
https://proxy-host.com/api/dns
|
https://proxy-host.com/api/dns
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -508,6 +823,97 @@ curl -H "auth-token: your-token-here" \
|
|||||||
- `200` `{"results": ...}` Updated domain list
|
- `200` `{"results": ...}` Updated domain list
|
||||||
- `404` Provider not found
|
- `404` Provider not found
|
||||||
|
|
||||||
|
### Dynamic DNS
|
||||||
|
|
||||||
|
A-records kept automatically pointed at this box's public (WAN) IP. All
|
||||||
|
`/api/dns/dynamic*` routes are viewer/manager scoped to the record's domain
|
||||||
|
(via [Permissions](#permissions)), not admin-only like the rest of `/api/dns`.
|
||||||
|
|
||||||
|
#### Get Current Public IP
|
||||||
|
|
||||||
|
**GET** `/api/dns/dynamic/ip`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/ip
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"ip": "203.0.113.5"}`
|
||||||
|
|
||||||
|
#### List Dynamic Records
|
||||||
|
|
||||||
|
**GET** `/api/dns/dynamic`
|
||||||
|
|
||||||
|
Lists records the caller may view (their own/granted domains, or all for admins).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/dns/dynamic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"id": "...", "domain": "example.com", "name": "home", "last_status": "ok", ...}, ...]}`
|
||||||
|
|
||||||
|
#### Create Dynamic Record
|
||||||
|
|
||||||
|
**POST** `/api/dns/dynamic`
|
||||||
|
|
||||||
|
Requires `manager` rights on the target domain. Applies the record immediately
|
||||||
|
against the current public IP (best-effort — failures are recorded in
|
||||||
|
`last_status` and retried by the scheduler).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `domain` (required)
|
||||||
|
- `name` (required) - sub-label, or `@` for the apex
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"domain": "example.com", "name": "home"}' \
|
||||||
|
https://proxy-host.com/api/dns/dynamic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "\"home.example.com\" added.", ...}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
- `422` Validation error
|
||||||
|
|
||||||
|
#### Refresh Dynamic Record
|
||||||
|
|
||||||
|
**POST** `/api/dns/dynamic/:id/refresh`
|
||||||
|
|
||||||
|
Force an immediate refresh of one record against the current public IP.
|
||||||
|
Requires `manager` rights on the record's domain.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/<id>/refresh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Refreshed \"home.example.com\".", "result": {...}}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
|
||||||
|
#### Delete Dynamic Record
|
||||||
|
|
||||||
|
**DELETE** `/api/dns/dynamic/:id`
|
||||||
|
|
||||||
|
Stop managing a record. Requires `manager` rights on the record's domain.
|
||||||
|
Leaves the provider's A record in place at its last value.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "home.example.com removed.", ...}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Certificates
|
## Certificates
|
||||||
@@ -543,7 +949,8 @@ All endpoints may return the following error responses:
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- All timestamps are in milliseconds since epoch
|
- All timestamps are in milliseconds since epoch
|
||||||
- The `auth-token` header is required for all authenticated endpoints
|
- Authenticated endpoints accept either the `auth-token` header (browser
|
||||||
|
session / OIDC login) or an `Authorization: Bearer <token>` API token
|
||||||
- Host names support wildcards: `*` (single level) and `**` (multi-level)
|
- Host names support wildcards: `*` (single level) and `**` (multi-level)
|
||||||
- DNS providers are validated on creation - invalid API credentials will be rejected
|
- DNS providers are validated on creation - invalid API credentials will be rejected
|
||||||
- Wildcard certificates are automatically renewed 30 days before expiration
|
- Wildcard certificates are automatically renewed 30 days before expiration
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ The proxy system consists of three main components working together to provide h
|
|||||||
▼
|
▼
|
||||||
┌──────────────────────┐
|
┌──────────────────────┐
|
||||||
│ DNS Providers │
|
│ DNS Providers │
|
||||||
│ - CloudFlare │
|
│ - Cloudflare │
|
||||||
│ - DigitalOcean │
|
│ - DigitalOcean │
|
||||||
│ - PorkBun │
|
│ - PorkBun │
|
||||||
│ (DNS-01 challenges) │
|
│ (DNS-01 challenges) │
|
||||||
@@ -84,6 +84,9 @@ The proxy system consists of three main components working together to provide h
|
|||||||
```
|
```
|
||||||
nodejs/
|
nodejs/
|
||||||
├── bin/www # Application entry point
|
├── bin/www # Application entry point
|
||||||
|
├── conf/ # Configuration (base.js, environment overlays, secrets.js)
|
||||||
|
├── controller/ # App-level wiring (pubsub, startup)
|
||||||
|
├── migrations/ # One-off Redis data migration scripts
|
||||||
├── models/ # Data models
|
├── models/ # Data models
|
||||||
│ ├── host.js # Host configuration and lookup
|
│ ├── host.js # Host configuration and lookup
|
||||||
│ ├── auth.js # Authentication logic
|
│ ├── auth.js # Authentication logic
|
||||||
@@ -93,7 +96,10 @@ nodejs/
|
|||||||
│ ├── host.js # Host CRUD operations
|
│ ├── host.js # Host CRUD operations
|
||||||
│ ├── dns.js # DNS provider management
|
│ ├── dns.js # DNS provider management
|
||||||
│ ├── user.js # User management
|
│ ├── user.js # User management
|
||||||
│ └── auth.js # Authentication
|
│ ├── auth.js # Authentication (login + OIDC)
|
||||||
|
│ ├── permission.js # RBAC permission management
|
||||||
|
│ ├── group.js # Local group management
|
||||||
|
│ └── api_token.js # Self-service API (PAT) tokens
|
||||||
├── services/ # Background services
|
├── services/ # Background services
|
||||||
│ ├── host_lookup.js # Unix socket server
|
│ ├── host_lookup.js # Unix socket server
|
||||||
│ └── host_scheduler.js # Cert renewal scheduler
|
│ └── host_scheduler.js # Cert renewal scheduler
|
||||||
|
|||||||
+21
-8
@@ -71,9 +71,19 @@ npm run test:watch
|
|||||||
```
|
```
|
||||||
test/
|
test/
|
||||||
├── unit/ # Unit tests for isolated components
|
├── unit/ # Unit tests for isolated components
|
||||||
|
│ ├── basicauth.test.js
|
||||||
│ ├── callback_queue.test.js
|
│ ├── callback_queue.test.js
|
||||||
|
│ ├── dynamic_record.test.js
|
||||||
|
│ ├── host_features.test.js
|
||||||
│ ├── host_lookup.test.js
|
│ ├── host_lookup.test.js
|
||||||
│ └── unix_socket.test.js
|
│ ├── hostname_validate.test.js
|
||||||
|
│ ├── host_sso.test.js
|
||||||
|
│ ├── oidc.test.js
|
||||||
|
│ ├── password_policy.test.js
|
||||||
|
│ ├── roles.test.js
|
||||||
|
│ ├── safe_redirect.test.js
|
||||||
|
│ ├── unix_socket.test.js
|
||||||
|
│ └── wildcard_matchany.test.js
|
||||||
├── integration/ # Integration tests
|
├── integration/ # Integration tests
|
||||||
│ └── dns_provider.test.js
|
│ └── dns_provider.test.js
|
||||||
└── helpers/ # Test utilities
|
└── helpers/ # Test utilities
|
||||||
@@ -171,6 +181,9 @@ Understanding the codebase:
|
|||||||
|
|
||||||
```
|
```
|
||||||
nodejs/
|
nodejs/
|
||||||
|
├── conf/ # Configuration (base.js, environment overlays, secrets.js)
|
||||||
|
├── controller/ # App-level wiring (pubsub, startup)
|
||||||
|
├── migrations/ # One-off Redis data migration scripts
|
||||||
├── models/ # Data models (Host, User, DNS providers)
|
├── models/ # Data models (Host, User, DNS providers)
|
||||||
├── routes/ # API route handlers
|
├── routes/ # API route handlers
|
||||||
├── services/ # Background services (lookup, scheduler)
|
├── services/ # Background services (lookup, scheduler)
|
||||||
@@ -232,12 +245,12 @@ nodejs/
|
|||||||
|
|
||||||
### PR Requirements
|
### PR Requirements
|
||||||
|
|
||||||
- ✅ All tests must pass (CI/CD runs automatically)
|
- All tests must pass (CI/CD runs automatically)
|
||||||
- ✅ Tests run on Node.js 18.x, 20.x, and 22.x
|
- Tests run on Node.js 18.x, 20.x, and 22.x
|
||||||
- ✅ No merge conflicts with `master`
|
- No merge conflicts with `master`
|
||||||
- ✅ Code follows project conventions
|
- Code follows project conventions
|
||||||
- ✅ New features include tests
|
- New features include tests
|
||||||
- ✅ Documentation updated if needed
|
- Documentation updated if needed
|
||||||
|
|
||||||
### CI/CD Process
|
### CI/CD Process
|
||||||
|
|
||||||
@@ -305,7 +318,7 @@ class Host extends Table {
|
|||||||
### Adding API Endpoints
|
### Adding API Endpoints
|
||||||
|
|
||||||
1. **Add route** in appropriate file (`routes/`)
|
1. **Add route** in appropriate file (`routes/`)
|
||||||
2. **Update API documentation** (`nodejs/api.md`)
|
2. **Update API documentation** (`nodejs/api.md` and `docs/api.md` — keep them in sync)
|
||||||
3. **Test the endpoint** manually and add integration tests if needed
|
3. **Test the endpoint** manually and add integration tests if needed
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
# Development environment setup
|
|
||||||
|
|
||||||
This project used vagrant for as standard development environment. This should
|
|
||||||
easy setting things up and reduce environment related errors.
|
|
||||||
|
|
||||||
## What you need
|
|
||||||
|
|
||||||
There are 3 things you will need to get before your local environment is up and
|
|
||||||
running.
|
|
||||||
|
|
||||||
### Git
|
|
||||||
|
|
||||||
This should already be installed on your system. If you are using Windows,
|
|
||||||
install [git bash for windows](https://git-scm.com/download/win)
|
|
||||||
|
|
||||||
### VirtualBox
|
|
||||||
|
|
||||||
Virtual box will be used to create a manged Linux VM on your computer. Please
|
|
||||||
install version 5.9 as version 6 is not supported.
|
|
||||||
|
|
||||||
### Vagrant
|
|
||||||
|
|
||||||
Vagrant is used to managed the local virtual environment and provision the VM.
|
|
||||||
**MAKE SURE GIT VIRTUAL BOX ARE INSTALLED FIRST!!!** At install time, vagrant
|
|
||||||
will integrate with them.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Once you have everything installed and the projected cloned on your local
|
|
||||||
computer, open a terminal( Git Bash for windows users ) and move to the root of
|
|
||||||
the project.
|
|
||||||
|
|
||||||
### Chef secrets
|
|
||||||
|
|
||||||
This project like many other used secret API tokens that we do want tracked in
|
|
||||||
the git repo. in the root of the project, create a file called 'secrets.json'
|
|
||||||
and populate like so:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"django": {
|
|
||||||
"github": {
|
|
||||||
"id": "<ID>",
|
|
||||||
"secret": "<SECRET>",
|
|
||||||
"token": "<TOKEN>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Basic vagrant usage and commands
|
|
||||||
|
|
||||||
We will interact with the project using vagrant. The work flow is `vagrant up`
|
|
||||||
creates a VM for your project and runs the provisioner to set everything up.
|
|
||||||
This command should be ran after the project is cloned or when you sit down to
|
|
||||||
start development. This command may take some time to complete depending on how
|
|
||||||
complex the project is. Once the VM is set up, you may now interact with it.
|
|
||||||
Vagrant will forward ports from the project to the user localhost address. For
|
|
||||||
example with this project, port 80 from the container will be mapped to
|
|
||||||
localhost:8000 and you will be able to access there. If you make a change to any
|
|
||||||
provisioning chef recipes, `vagrant provision` will need to ran. This will
|
|
||||||
run the chef provisioner on the VM making any changes needed.
|
|
||||||
|
|
||||||
Because we use the `secrets.json` file to store untracked configuration, all
|
|
||||||
vagrant command need to be ran where in the root of the project, where
|
|
||||||
`secrets.json` lives.
|
|
||||||
|
|
||||||
### `vagrant up`
|
|
||||||
|
|
||||||
Will start the local VM, creating it if needed. This command should always be
|
|
||||||
ran when a development session is started.
|
|
||||||
|
|
||||||
### `vagrant provision`
|
|
||||||
|
|
||||||
Will run the chef-solo provisioner. This will need to be ran anytime the chef
|
|
||||||
recipes or roles are changed.
|
|
||||||
|
|
||||||
### `vagrant status`
|
|
||||||
|
|
||||||
Will show you the status of local manged VM
|
|
||||||
|
|
||||||
### `vagrant halt`
|
|
||||||
|
|
||||||
Will shutdown the local VM. This should be done when you are finished working on
|
|
||||||
the project so you dont have a VM running in the background eating CPU/RAM and
|
|
||||||
battery. `vagrant up` can be used later to turn the VM back on.
|
|
||||||
|
|
||||||
### `vagrant destroy`
|
|
||||||
|
|
||||||
This will shutdown the VM and delete it. This command is useful if you have
|
|
||||||
messed up the VM and want to start from scratch. Or if you are done with the
|
|
||||||
project and want to free space from the computer.
|
|
||||||
|
|
||||||
### `vagrant ssh`
|
|
||||||
|
|
||||||
This will bring you into the local VM as the vagrant user. The vagrant user has
|
|
||||||
sudo. Use this only for debugging! **DO NOT INSTALL OR CHANGE THE STATE OF
|
|
||||||
PROJECT OR VM FROM HERE!!!!** that will break the concept of provisioning make
|
|
||||||
chef useless. Also make installation and configuration changes with chef.
|
|
||||||
+1
-1
@@ -11,7 +11,7 @@ A reverse proxy and HTTPS termination service using OpenResty/nginx with a manag
|
|||||||
|
|
||||||
- **Automated HTTPS/SSL** - Let's Encrypt integration with HTTP-01 and DNS-01 challenges
|
- **Automated HTTPS/SSL** - Let's Encrypt integration with HTTP-01 and DNS-01 challenges
|
||||||
- **Wildcard SSL Certificates** - Support for wildcard domains with automatic renewal
|
- **Wildcard SSL Certificates** - Support for wildcard domains with automatic renewal
|
||||||
- **Multiple DNS Providers** - CloudFlare, DigitalOcean, PorkBun integrations
|
- **Multiple DNS Providers** - Cloudflare, DigitalOcean, PorkBun integrations
|
||||||
- **Advanced Routing** - Sophisticated wildcard domain matching (*, **)
|
- **Advanced Routing** - Sophisticated wildcard domain matching (*, **)
|
||||||
- **RESTful API** - Full programmatic control
|
- **RESTful API** - Full programmatic control
|
||||||
- **Web Interface** - User-friendly management GUI
|
- **Web Interface** - User-friendly management GUI
|
||||||
|
|||||||
+13
-4
@@ -16,7 +16,7 @@ wget -O - https://raw.githubusercontent.com/theta42/proxy/master/ops/install.sh
|
|||||||
```
|
```
|
||||||
|
|
||||||
This automated installer will:
|
This automated installer will:
|
||||||
- Install Node.js 20.x
|
- Install Node.js 22.x
|
||||||
- Install OpenResty and required dependencies
|
- Install OpenResty and required dependencies
|
||||||
- Install and configure Redis
|
- Install and configure Redis
|
||||||
- Set up SSL fallback certificates
|
- Set up SSL fallback certificates
|
||||||
@@ -27,6 +27,15 @@ This automated installer will:
|
|||||||
|
|
||||||
## Manual Installation
|
## Manual Installation
|
||||||
|
|
||||||
|
> **Recommended path:** `ops/install.sh` (above) is idempotent and safe to
|
||||||
|
> re-run — it symlinks the OpenResty/systemd config from the repo checkout,
|
||||||
|
> so future updates stay in sync automatically (`git pull` + re-run). The
|
||||||
|
> manual steps below *copy* those same files instead of symlinking them, so
|
||||||
|
> they will **not** auto-track later changes to `ops/nginx_conf/` or
|
||||||
|
> `ops/proxy.service` — you'd need to re-copy them by hand after every
|
||||||
|
> update. Prefer the manual path only if `install.sh` doesn't fit your
|
||||||
|
> distribution.
|
||||||
|
|
||||||
### System Requirements
|
### System Requirements
|
||||||
|
|
||||||
- Modern Linux distribution (Ubuntu 20.04+, Debian 11+, or equivalent)
|
- Modern Linux distribution (Ubuntu 20.04+, Debian 11+, or equivalent)
|
||||||
@@ -41,13 +50,13 @@ This automated installer will:
|
|||||||
apt install libpam0g-dev build-essential redis-server luarocks -y
|
apt install libpam0g-dev build-essential redis-server luarocks -y
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 2: Install Node.js 20.x
|
### Step 2: Install Node.js 22.x
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | \
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | \
|
||||||
sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
|
|
||||||
NODE_MAJOR=20
|
NODE_MAJOR=22
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | \
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | \
|
||||||
sudo tee /etc/apt/sources.list.d/nodesource.list
|
sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||||
|
|
||||||
@@ -56,7 +65,7 @@ apt update && apt install nodejs -y
|
|||||||
|
|
||||||
Verify installation:
|
Verify installation:
|
||||||
```bash
|
```bash
|
||||||
node --version # Should show v20.x.x
|
node --version # Should show v22.x.x
|
||||||
npm --version
|
npm --version
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+416
-26
@@ -1,6 +1,25 @@
|
|||||||
# API Documentation
|
# API Documentation
|
||||||
|
|
||||||
All API endpoints require authentication via the `auth-token` header unless otherwise noted.
|
All API endpoints require authentication unless otherwise noted. Three
|
||||||
|
authentication methods are supported:
|
||||||
|
|
||||||
|
- **`auth-token` header** — a browser-session token from `POST /api/auth/login`
|
||||||
|
or the OIDC flow (below).
|
||||||
|
- **`Authorization: Bearer <token>` header** — a self-service API token (PAT,
|
||||||
|
see [API Tokens](#api-tokens)), for scripts/CI without a browser session.
|
||||||
|
- **OIDC (browser)** — if the proxy is configured as an OIDC client of an SSO
|
||||||
|
(`app_oidc__*` / `conf.oidc`, see [DEPLOYMENT.md](../DEPLOYMENT.md)),
|
||||||
|
users can log in via `GET /api/auth/oidc/start` instead of posting a
|
||||||
|
username/password.
|
||||||
|
|
||||||
|
The proxy can also be configured as a **direct LDAP client** (`app_ldap__*` /
|
||||||
|
`conf.ldap`) for looking up/validating users, independent of the OIDC flow —
|
||||||
|
see DEPLOYMENT.md for the full configuration reference.
|
||||||
|
|
||||||
|
Authenticated requests also carry **RBAC** (role-based access control):
|
||||||
|
global admins can manage everything; other users are scoped to `viewer` or
|
||||||
|
`manager` rights on specific domains via [Permissions](#permissions) and
|
||||||
|
[Groups](#groups).
|
||||||
|
|
||||||
Base URL: `https://your-proxy-host.com/api`
|
Base URL: `https://your-proxy-host.com/api`
|
||||||
|
|
||||||
@@ -40,17 +59,167 @@ curl -H "auth-token: your-token-here" \
|
|||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"message": "Bye"}`
|
- `200` `{"message": "Bye"}`
|
||||||
|
|
||||||
|
### OIDC Login (start)
|
||||||
|
|
||||||
|
**GET** `/api/auth/oidc/start`
|
||||||
|
|
||||||
|
Begin the OIDC authorization-code flow: creates a PKCE + state challenge and
|
||||||
|
redirects the browser to the configured SSO's authorize endpoint. Only
|
||||||
|
available when `conf.oidc.enabled` is true.
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
- `redirect` - Internal path to return to after login (optional; sanitized to same-origin)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -i "https://proxy-host.com/api/auth/oidc/start?redirect=/hosts"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `302` Redirect to the SSO's authorization endpoint
|
||||||
|
- `404` `{"name": "OidcDisabled", "message": "OIDC login is not enabled."}`
|
||||||
|
|
||||||
|
### OIDC Callback
|
||||||
|
|
||||||
|
**GET** `/api/auth/oidc/callback`
|
||||||
|
|
||||||
|
Redirect target for the SSO after login. Validates the one-time `state`,
|
||||||
|
exchanges the authorization `code` for tokens, reads identity from the
|
||||||
|
userinfo endpoint, establishes a session, and redirects the browser back to
|
||||||
|
the login page with the app's own `auth-token` in a URL fragment.
|
||||||
|
|
||||||
|
**Query Parameters:**
|
||||||
|
- `code` (required) - Authorization code from the SSO
|
||||||
|
- `state` (required) - State value from the `start` step
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Not called directly — the SSO redirects the browser here after login.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `302` Redirect to `/login#token=...&redirect=...`
|
||||||
|
- `400` `{"name": "OidcCallbackInvalid", "message": "Missing code or state."}` or expired/unknown state
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Tokens
|
||||||
|
|
||||||
|
Self-service personal access tokens (PATs) for scripting/CI without a browser
|
||||||
|
session. Every endpoint is owner-scoped: a user only sees/manages tokens they
|
||||||
|
created. Mounted at `/api/api-token`.
|
||||||
|
|
||||||
|
### List API Tokens
|
||||||
|
|
||||||
|
**GET** `/api/api-token`
|
||||||
|
|
||||||
|
List the current user's API tokens.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/api-token
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"id": "...", "name": "ci", ...}, ...]}`
|
||||||
|
|
||||||
|
### Create API Token
|
||||||
|
|
||||||
|
**POST** `/api/api-token`
|
||||||
|
|
||||||
|
Create a new API token. The raw token string is only returned once, at
|
||||||
|
creation.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (required) - Display name
|
||||||
|
- `description` (optional)
|
||||||
|
- `expires_in_days` (optional) - `0` or omitted means no expiry
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"name": "ci", "expires_in_days": 90}' \
|
||||||
|
https://proxy-host.com/api/api-token
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}, "token": "prx_<id>_<secret>", "message": "API token 'ci' created. Save it now — it will not be shown again."}`
|
||||||
|
|
||||||
|
### Get API Token
|
||||||
|
|
||||||
|
**GET** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Get a token's metadata (not the raw secret, which is never stored/returned again).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}}`
|
||||||
|
- `403` Not your token
|
||||||
|
|
||||||
|
### Update API Token
|
||||||
|
|
||||||
|
**PUT** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Update a token's name/description/expiry.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X PUT \
|
||||||
|
-d '{"name": "ci-updated"}' \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": {...}, "message": "API token 'ci-updated' updated."}`
|
||||||
|
|
||||||
|
### Delete (Revoke) API Token
|
||||||
|
|
||||||
|
**DELETE** `/api/api-token/:id`
|
||||||
|
|
||||||
|
Revoke a token immediately.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/api-token/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"id": "<id>", "message": "API token 'ci' revoked."}`
|
||||||
|
|
||||||
|
### Rotate API Token
|
||||||
|
|
||||||
|
**POST** `/api/api-token/:id/rotate`
|
||||||
|
|
||||||
|
Issue a new secret for an existing token (same id, new raw value shown once).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
https://proxy-host.com/api/api-token/<id>/rotate
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"token": "prx_<id>_<new-secret>", "message": "API token 'ci' rotated. Save it — it will not be shown again."}`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Users
|
## Users
|
||||||
|
|
||||||
All user endpoints require authentication.
|
All user endpoints require authentication. `GET /me` and `PUT /password`
|
||||||
|
(self-service) work for any authenticated user; everything else (listing,
|
||||||
|
creating, deleting users, resetting another user's password) requires global
|
||||||
|
admin.
|
||||||
|
|
||||||
### List Users
|
### List Users
|
||||||
|
|
||||||
**GET** `/api/user`
|
**GET** `/api/user`
|
||||||
|
|
||||||
Get list of all users.
|
Get list of all users. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -63,12 +232,14 @@ curl -H "auth-token: your-token-here" \
|
|||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ["user1", "user2"]}`
|
- `200` `{"results": ["user1", "user2"]}`
|
||||||
- `200` `{"results": [{"username": "user1", ...}, ...]}` (with `?detail=true`)
|
- `200` `{"results": [{"username": "user1", ...}, ...]}` (with `?detail=true`)
|
||||||
|
- `403` Not an admin
|
||||||
|
|
||||||
### Get Current User
|
### Get Current User
|
||||||
|
|
||||||
**GET** `/api/user/me`
|
**GET** `/api/user/me`
|
||||||
|
|
||||||
Get information about the currently authenticated user.
|
Get the currently authenticated user's identity and effective RBAC rights
|
||||||
|
(drives the web UI's nav/button gating).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -76,13 +247,13 @@ curl -H "auth-token: your-token-here" \
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"username": "myuser"}`
|
- `200` `{"username": "myuser", "groups": [...], "localGroups": [...], "externalGroups": [...], "isAdmin": false, "global": null, "domains": {...}}`
|
||||||
|
|
||||||
### Create User
|
### Create User
|
||||||
|
|
||||||
**POST** `/api/user`
|
**POST** `/api/user`
|
||||||
|
|
||||||
Create a new user.
|
Create a new local user. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
@@ -94,14 +265,15 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` User created successfully
|
- `200` User created successfully
|
||||||
|
- `403` Not an admin
|
||||||
- `409` Username already exists
|
- `409` Username already exists
|
||||||
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error
|
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error (also returned for weak passwords)
|
||||||
|
|
||||||
### Delete User
|
### Delete User
|
||||||
|
|
||||||
**DELETE** `/api/user/:username`
|
**DELETE** `/api/user/:username`
|
||||||
|
|
||||||
Delete a user account.
|
Delete a user account. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -111,6 +283,7 @@ curl -H "auth-token: your-token-here" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"username": "olduser", "results": ...}`
|
- `200` `{"username": "olduser", "results": ...}`
|
||||||
|
- `403` Not an admin
|
||||||
- `404` User not found
|
- `404` User not found
|
||||||
|
|
||||||
### Change Password (Self)
|
### Change Password (Self)
|
||||||
@@ -129,12 +302,13 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ...}` Password changed successfully
|
- `200` `{"results": ...}` Password changed successfully
|
||||||
|
- `422` Weak password rejected by the password policy
|
||||||
|
|
||||||
### Change Password (Other User)
|
### Change Password (Other User)
|
||||||
|
|
||||||
**PUT** `/api/user/password/:username`
|
**PUT** `/api/user/password/:username`
|
||||||
|
|
||||||
Change the password for another user (admin function).
|
Change the password for another user. Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
@@ -146,40 +320,163 @@ curl -H "Content-Type: application/json" \
|
|||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": ...}` Password changed successfully
|
- `200` `{"results": ...}` Password changed successfully
|
||||||
|
- `403` Not an admin
|
||||||
- `404` User not found
|
- `404` User not found
|
||||||
|
|
||||||
### Create Invite Token
|
---
|
||||||
|
|
||||||
**POST** `/api/user/invite`
|
## Permissions
|
||||||
|
|
||||||
Create an invitation token for new user registration.
|
RBAC: grants a `viewer` or `manager` role to a user or group, either globally
|
||||||
|
or scoped to one domain. Global-admin-only. Mounted at `/api/permission`.
|
||||||
|
|
||||||
|
### List Permissions
|
||||||
|
|
||||||
|
**GET** `/api/permission`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
-X POST \
|
https://proxy-host.com/api/permission
|
||||||
https://proxy-host.com/api/user/invite
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"token": "5caf94d2-2c91-4010-8df7-968d10802b9d"}`
|
- `200` `{"results": [{"id": "...", "subjectType": "user", "subject": "alice", "role": "manager", "scope": "domain", "domain": "example.com", ...}, ...]}`
|
||||||
|
|
||||||
### Add SSH Key
|
### List Permission Subjects
|
||||||
|
|
||||||
**POST** `/api/user/key`
|
**GET** `/api/permission/subjects`
|
||||||
|
|
||||||
Add an SSH public key to the current user's account.
|
Autocomplete source for the "Subject" field: known usernames plus known group
|
||||||
|
names (local groups, groups already used in permissions, and groups from
|
||||||
|
`conf.auth.adminGroups` / `conf.auth.groupRoleMap`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/permission/subjects
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"users": ["alice", "bob"], "groups": ["ops", "sre"]}`
|
||||||
|
|
||||||
|
### Create Permission
|
||||||
|
|
||||||
|
**POST** `/api/permission`
|
||||||
|
|
||||||
|
Grant a role to a subject.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `subjectType` (required) - `user` or `group`
|
||||||
|
- `subject` (required) - username or group name
|
||||||
|
- `role` (required) - `viewer` or `manager`
|
||||||
|
- `scope` (required) - `global` or `domain`
|
||||||
|
- `domain` (required if `scope` is `domain`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
-H "auth-token: your-token-here" \
|
-H "auth-token: your-token-here" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d '{"key": "ssh-rsa AAAAB3..."}' \
|
-d '{"subjectType": "user", "subject": "alice", "role": "manager", "scope": "domain", "domain": "example.com"}' \
|
||||||
https://proxy-host.com/api/user/key
|
https://proxy-host.com/api/permission
|
||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"message": true}` Key added successfully
|
- `200` `{"message": "Granted manager to user \"alice\" on example.com.", ...}`
|
||||||
- `400` `{"message": "Bad SSH key"}` Invalid key format
|
- `422` Validation error
|
||||||
|
|
||||||
|
### Delete Permission
|
||||||
|
|
||||||
|
**DELETE** `/api/permission/:id`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/permission/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Permission <id> removed."}`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Groups
|
||||||
|
|
||||||
|
Local groups (independent of any SSO/LDAP groups) used as subjects for
|
||||||
|
permission grants. Global-admin-only. Mounted at `/api/group`.
|
||||||
|
|
||||||
|
### List Groups
|
||||||
|
|
||||||
|
**GET** `/api/group`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/group
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"name": "ops", "members": ["alice", "bob"], ...}, ...]}`
|
||||||
|
|
||||||
|
### Create Group
|
||||||
|
|
||||||
|
**POST** `/api/group`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name` (required)
|
||||||
|
- `members` (optional) - array of usernames
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"name": "ops", "members": ["alice"]}' \
|
||||||
|
https://proxy-host.com/api/group
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Group \"ops\" created.", ...}`
|
||||||
|
|
||||||
|
### Delete Group
|
||||||
|
|
||||||
|
**DELETE** `/api/group/:name`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/group/ops
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Group \"ops\" removed."}`
|
||||||
|
|
||||||
|
### Add Group Member
|
||||||
|
|
||||||
|
**POST** `/api/group/:name/members`
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `username` (required)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"username": "bob"}' \
|
||||||
|
https://proxy-host.com/api/group/ops/members
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Added \"bob\" to \"ops\".", ...}`
|
||||||
|
|
||||||
|
### Remove Group Member
|
||||||
|
|
||||||
|
**DELETE** `/api/group/:name/members/:username`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/group/ops/members/bob
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Removed \"bob\" from \"ops\".", ...}`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -320,6 +617,7 @@ curl -H "auth-token: your-token-here" \
|
|||||||
Remove all cached wildcard-subdomain host lookups. Cache entries are created on
|
Remove all cached wildcard-subdomain host lookups. Cache entries are created on
|
||||||
demand when a wildcard host serves a subdomain; clearing them forces the next
|
demand when a wildcard host serves a subdomain; clearing them forces the next
|
||||||
request for each subdomain to be resolved fresh through the lookup tree.
|
request for each subdomain to be resolved fresh through the lookup tree.
|
||||||
|
Admin only.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -H "auth-token: your-token-here" \
|
curl -H "auth-token: your-token-here" \
|
||||||
@@ -382,7 +680,7 @@ curl -H "auth-token: your-token-here" \
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Responses:**
|
**Responses:**
|
||||||
- `200` `{"results": [{"name": "CloudFlare", "fields": {...}}, {"name": "DigitalOcean", ...}, ...]}`
|
- `200` `{"results": [{"name": "Cloudflare", "fields": {...}}, {"name": "DigitalOcean", ...}, ...]}`
|
||||||
|
|
||||||
### Create DNS Provider
|
### Create DNS Provider
|
||||||
|
|
||||||
@@ -390,12 +688,12 @@ curl -H "auth-token: your-token-here" \
|
|||||||
|
|
||||||
Configure a new DNS provider.
|
Configure a new DNS provider.
|
||||||
|
|
||||||
**CloudFlare:**
|
**Cloudflare:**
|
||||||
```bash
|
```bash
|
||||||
curl -H "Content-Type: application/json" \
|
curl -H "Content-Type: application/json" \
|
||||||
-H "auth-token: your-token-here" \
|
-H "auth-token: your-token-here" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d '{"name": "My CloudFlare", "dnsProvider": "Cloudflare", "token": "your-api-token"}' \
|
-d '{"name": "My Cloudflare", "dnsProvider": "Cloudflare", "token": "your-api-token"}' \
|
||||||
https://proxy-host.com/api/dns
|
https://proxy-host.com/api/dns
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -518,6 +816,97 @@ curl -H "auth-token: your-token-here" \
|
|||||||
- `200` `{"results": ...}` Updated domain list
|
- `200` `{"results": ...}` Updated domain list
|
||||||
- `404` Provider not found
|
- `404` Provider not found
|
||||||
|
|
||||||
|
### Dynamic DNS
|
||||||
|
|
||||||
|
A-records kept automatically pointed at this box's public (WAN) IP. All
|
||||||
|
`/api/dns/dynamic*` routes are viewer/manager scoped to the record's domain
|
||||||
|
(via [Permissions](#permissions)), not admin-only like the rest of `/api/dns`.
|
||||||
|
|
||||||
|
#### Get Current Public IP
|
||||||
|
|
||||||
|
**GET** `/api/dns/dynamic/ip`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/ip
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"ip": "203.0.113.5"}`
|
||||||
|
|
||||||
|
#### List Dynamic Records
|
||||||
|
|
||||||
|
**GET** `/api/dns/dynamic`
|
||||||
|
|
||||||
|
Lists records the caller may view (their own/granted domains, or all for admins).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
https://proxy-host.com/api/dns/dynamic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"results": [{"id": "...", "domain": "example.com", "name": "home", "last_status": "ok", ...}, ...]}`
|
||||||
|
|
||||||
|
#### Create Dynamic Record
|
||||||
|
|
||||||
|
**POST** `/api/dns/dynamic`
|
||||||
|
|
||||||
|
Requires `manager` rights on the target domain. Applies the record immediately
|
||||||
|
against the current public IP (best-effort — failures are recorded in
|
||||||
|
`last_status` and retried by the scheduler).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `domain` (required)
|
||||||
|
- `name` (required) - sub-label, or `@` for the apex
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
-d '{"domain": "example.com", "name": "home"}' \
|
||||||
|
https://proxy-host.com/api/dns/dynamic
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "\"home.example.com\" added.", ...}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
- `422` Validation error
|
||||||
|
|
||||||
|
#### Refresh Dynamic Record
|
||||||
|
|
||||||
|
**POST** `/api/dns/dynamic/:id/refresh`
|
||||||
|
|
||||||
|
Force an immediate refresh of one record against the current public IP.
|
||||||
|
Requires `manager` rights on the record's domain.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X POST \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/<id>/refresh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "Refreshed \"home.example.com\".", "result": {...}}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
|
||||||
|
#### Delete Dynamic Record
|
||||||
|
|
||||||
|
**DELETE** `/api/dns/dynamic/:id`
|
||||||
|
|
||||||
|
Stop managing a record. Requires `manager` rights on the record's domain.
|
||||||
|
Leaves the provider's A record in place at its last value.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "auth-token: your-token-here" \
|
||||||
|
-X DELETE \
|
||||||
|
https://proxy-host.com/api/dns/dynamic/<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Responses:**
|
||||||
|
- `200` `{"message": "home.example.com removed.", ...}`
|
||||||
|
- `403` Missing `manager` rights on the domain
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Certificates
|
## Certificates
|
||||||
@@ -553,7 +942,8 @@ All endpoints may return the following error responses:
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- All timestamps are in milliseconds since epoch
|
- All timestamps are in milliseconds since epoch
|
||||||
- The `auth-token` header is required for all authenticated endpoints
|
- Authenticated endpoints accept either the `auth-token` header (browser
|
||||||
|
session / OIDC login) or an `Authorization: Bearer <token>` API token
|
||||||
- Host names support wildcards: `*` (single level) and `**` (multi-level)
|
- Host names support wildcards: `*` (single level) and `**` (multi-level)
|
||||||
- DNS providers are validated on creation - invalid API credentials will be rejected
|
- DNS providers are validated on creation - invalid API credentials will be rejected
|
||||||
- Wildcard certificates are automatically renewed 30 days before expiration
|
- Wildcard certificates are automatically renewed 30 days before expiration
|
||||||
|
|||||||
+12
-2
@@ -23,9 +23,19 @@ npm run test:watch
|
|||||||
```
|
```
|
||||||
test/
|
test/
|
||||||
├── unit/ # Unit tests for isolated components
|
├── unit/ # Unit tests for isolated components
|
||||||
|
│ ├── basicauth.test.js
|
||||||
│ ├── callback_queue.test.js
|
│ ├── callback_queue.test.js
|
||||||
|
│ ├── dynamic_record.test.js
|
||||||
|
│ ├── host_features.test.js
|
||||||
│ ├── host_lookup.test.js
|
│ ├── host_lookup.test.js
|
||||||
│ └── unix_socket.test.js
|
│ ├── hostname_validate.test.js
|
||||||
|
│ ├── host_sso.test.js
|
||||||
|
│ ├── oidc.test.js
|
||||||
|
│ ├── password_policy.test.js
|
||||||
|
│ ├── roles.test.js
|
||||||
|
│ ├── safe_redirect.test.js
|
||||||
|
│ ├── unix_socket.test.js
|
||||||
|
│ └── wildcard_matchany.test.js
|
||||||
├── integration/ # Integration tests for complex interactions
|
├── integration/ # Integration tests for complex interactions
|
||||||
│ └── dns_provider.test.js
|
│ └── dns_provider.test.js
|
||||||
└── helpers/ # Test utilities and contracts
|
└── helpers/ # Test utilities and contracts
|
||||||
@@ -58,7 +68,7 @@ test/
|
|||||||
|
|
||||||
**dns_provider.test.js**
|
**dns_provider.test.js**
|
||||||
- DNS provider contract compliance
|
- DNS provider contract compliance
|
||||||
- All existing providers (CloudFlare, DigitalOcean, PorkBun)
|
- All existing providers (Cloudflare, DigitalOcean, PorkBun)
|
||||||
- Method signatures
|
- Method signatures
|
||||||
- Key mapping
|
- Key mapping
|
||||||
- Type validation
|
- Type validation
|
||||||
|
|||||||
+8
-7
@@ -1,9 +1,10 @@
|
|||||||
The folder will hold a link to the openresty conf file. Nothing in here will be
|
This directory is currently unused.
|
||||||
tracked, and edits are only valid on you local VM. The contents of this folder
|
|
||||||
is also reset during each provision run. Be mind full of that when making
|
|
||||||
changes here.
|
|
||||||
|
|
||||||
To apply any changes made in this folder, run the following command from outside
|
The real OpenResty/nginx configuration for this project lives in
|
||||||
the VM:
|
[`ops/nginx_conf/`](../ops/nginx_conf/) (`nginx.conf`, `autossl.conf`,
|
||||||
|
`proxy.conf`, `targetinfo.lua`, `hostfeatures.lua`). `ops/install.sh` symlinks
|
||||||
|
those files into place on a bare-metal host, and the Docker image copies them
|
||||||
|
in at build time — see [`DEPLOYMENT.md`](../DEPLOYMENT.md) for details.
|
||||||
|
|
||||||
`vagrant ssh -c "sudo service openresty restart"`
|
If you're looking for how to change the nginx/Lua configuration, edit the
|
||||||
|
files under `ops/nginx_conf/` instead of this directory.
|
||||||
|
|||||||
Reference in New Issue
Block a user