Updated docs
This commit is contained in:
@@ -1,157 +1,218 @@
|
||||
# proxy
|
||||
# Proxy
|
||||
|
||||
A simple reverse proxy and https termination using openresty/nginx with a managment API and GUI.
|
||||
A reverse proxy and HTTPS termination service using OpenResty/nginx with a management API and web GUI.
|
||||
|
||||
## API docs
|
||||
[API docs](api.md)
|
||||
**Documentation:** [https://theta42.github.io/proxy/](https://theta42.github.io/proxy/)
|
||||
|
||||
## Server set up
|
||||
## Features
|
||||
|
||||
The server requires:
|
||||
* NodeJS 8.x
|
||||
* inbound Internet access
|
||||
* OpenResty
|
||||
* redis
|
||||
* lua rocks
|
||||
- Automated HTTPS/SSL certificate management via Let's Encrypt
|
||||
- Support for HTTP-01 (auto-ssl) and DNS-01 (wildcard) ACME challenges
|
||||
- Multiple DNS provider integrations (CloudFlare, DigitalOcean, PorkBun)
|
||||
- Wildcard SSL certificate support with automatic renewal
|
||||
- Dynamic host routing with wildcard domain matching (*, **)
|
||||
- Web-based management interface
|
||||
- RESTful API for automation
|
||||
- User authentication and management
|
||||
- Unix socket-based host lookup for high-performance routing
|
||||
|
||||
This has been tested on ubuntu 16.04, but should work on any modern Linux
|
||||
distro.
|
||||
**Optional** Linux users for its user management, so this will
|
||||
**ONLY** work on Linux, no macOS, BSD or Windows and require root.
|
||||
## Requirements
|
||||
|
||||
The steps below are for a new ubuntu server, they should be mostly the same for
|
||||
other distros, but the paths and availability of packages may vary. A dedicated
|
||||
server is highly recommended (since it will make ever user a system user), a VPS
|
||||
like Digital Ocean will do just fine.
|
||||
- Node.js 18+ (tested with 18.x, 20.x, 22.x)
|
||||
- OpenResty (nginx with Lua support)
|
||||
- Redis
|
||||
- Modern Linux distribution (tested on Ubuntu 20.04+, Debian 11+)
|
||||
- Inbound internet access for Let's Encrypt validation
|
||||
- Root access (required for user management features)
|
||||
|
||||
* Install openresty
|
||||
## Quick Install
|
||||
|
||||
[OpenResty® Linux Packages](https://openresty.org/en/linux-packages.html)
|
||||
|
||||
* These packages are needed for the PAM node package
|
||||
|
||||
```bash
|
||||
apt install libpam0g-dev build-essential
|
||||
```
|
||||
|
||||
* Install redis
|
||||
|
||||
```bash
|
||||
apt install redis-server
|
||||
```
|
||||
|
||||
* install lua plugin
|
||||
An automated installer is available for modern Debian-based systems:
|
||||
|
||||
```bash
|
||||
apt install luarocks
|
||||
sudo luarocks install lua-resty-auto-ssl
|
||||
sudo luarocks install lua-resty-socket
|
||||
sudo luarocks install lua-socket
|
||||
sudo luarocks install socket
|
||||
sudo luarocks install luasocket
|
||||
sudo luarocks install luasocket-unix
|
||||
sudo luarocks install lua-cjson
|
||||
wget -O - https://raw.githubusercontent.com/theta42/proxy/master/ops/install.sh | sudo bash
|
||||
```
|
||||
|
||||
* openresty config
|
||||
This installer will:
|
||||
- Install Node.js 20.x
|
||||
- Install OpenResty and required dependencies
|
||||
- Install and configure Redis
|
||||
- Set up SSL fallback certificates
|
||||
- Install Lua dependencies (lua-resty-auto-ssl, luasocket)
|
||||
- Clone and install the proxy application
|
||||
- Configure systemd service
|
||||
- Start the proxy service
|
||||
|
||||
Set up fail back SSL certs
|
||||
## Manual Installation
|
||||
|
||||
For manual installation or other distributions, see the detailed steps below.
|
||||
|
||||
### System Dependencies
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
```bash
|
||||
mkdir /etc/ssl/
|
||||
|
||||
openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj '/CN=sni-support-required-for-valid-ssl' -keyout /etc/ssl/resty-auto-ssl-fallback.key -out /etc/ssl/resty-auto-ssl-fallback.crt
|
||||
|
||||
openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj '/CN=sni-support-required-for-valid-ssl' -keyout /etc/ssl/resty-auto-ssl-fallback.key -out /etc/ssl/resty-auto-ssl-fallback.crt
|
||||
|
||||
# openssl dhparam -out /etc/nginx/dhparam.pem 4096 # This takes a LONG time and is not needed.
|
||||
|
||||
apt install libpam0g-dev build-essential redis-server luarocks -y
|
||||
```
|
||||
|
||||
Change the `/etc/openresty/nginx.conf to have this config`
|
||||
|
||||
```
|
||||
#user nobody;
|
||||
worker_processes 4;
|
||||
|
||||
#error_log logs/error.log;
|
||||
#error_log logs/error.log notice;
|
||||
#error_log logs/error.log info;
|
||||
|
||||
#pid logs/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
client_max_body_size 4g;
|
||||
|
||||
|
||||
lua_shared_dict auto_ssl 100m;
|
||||
lua_shared_dict auto_ssl_settings 64k;
|
||||
|
||||
resolver 8.8.4.4 8.8.8.8;
|
||||
|
||||
init_by_lua_block {
|
||||
auto_ssl = (require "resty.auto-ssl").new()
|
||||
auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")
|
||||
auto_ssl:set("allow_domain", function(domain)
|
||||
return true
|
||||
end)
|
||||
auto_ssl:init()
|
||||
}
|
||||
|
||||
init_worker_by_lua_block {
|
||||
auto_ssl:init_worker()
|
||||
}
|
||||
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8999;
|
||||
|
||||
# Increase the body buffer size, to ensure the internal POSTs can always
|
||||
# parse the full POST contents into memory.
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 128k;
|
||||
|
||||
location / {
|
||||
content_by_lua_block {
|
||||
auto_ssl:hook_server()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
# '$status $body_bytes_sent "$http_referer" '
|
||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
#keepalive_timeout 0;
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
include sites-enabled/*;
|
||||
|
||||
}
|
||||
|
||||
**Node.js 20.x:**
|
||||
```bash
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
NODE_MAJOR=20
|
||||
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
|
||||
```
|
||||
|
||||
add the SSL config file `/etc/openresty/autossl.conf`, contents from here
|
||||
https://github.com/theta42/t42-common/blob/master/templates/openresty/autossl.conf.erb
|
||||
**OpenResty:**
|
||||
```bash
|
||||
wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list
|
||||
apt update && apt install openresty -y
|
||||
```
|
||||
|
||||
**Lua Dependencies:**
|
||||
```bash
|
||||
luarocks install lua-resty-auto-ssl
|
||||
luarocks install luasocket
|
||||
```
|
||||
|
||||
Add the proxy config `/etc/openresty/sites-enabled/000-proxy` contents from here
|
||||
https://github.com/theta42/t42-common/blob/master/templates/openresty/010-proxy.conf.erb
|
||||
### SSL Configuration
|
||||
|
||||
Create fallback SSL certificates:
|
||||
```bash
|
||||
mkdir -p /etc/ssl/
|
||||
openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
|
||||
-subj '/CN=sni-support-required-for-valid-ssl' \
|
||||
-keyout /etc/ssl/resty-auto-ssl-fallback.key \
|
||||
-out /etc/ssl/resty-auto-ssl-fallback.crt
|
||||
```
|
||||
|
||||
### OpenResty Configuration
|
||||
|
||||
Configuration files are provided in `ops/nginx_conf/`:
|
||||
- `nginx.conf` - Main nginx configuration
|
||||
- `autossl.conf` - Auto-SSL configuration for Let's Encrypt HTTP-01
|
||||
- `proxy.conf` - Proxy server configuration with host lookup
|
||||
- `targetinfo.lua` - Lua module for host lookup via Unix socket
|
||||
|
||||
Copy these files to `/etc/openresty/`:
|
||||
```bash
|
||||
mkdir -p /etc/openresty/sites-enabled/
|
||||
cp ops/nginx_conf/nginx.conf /etc/openresty/nginx.conf
|
||||
cp ops/nginx_conf/autossl.conf /etc/openresty/autossl.conf
|
||||
cp ops/nginx_conf/proxy.conf /etc/openresty/sites-enabled/000-proxy
|
||||
cp ops/nginx_conf/targetinfo.lua /usr/local/openresty/lualib/targetinfo.lua
|
||||
```
|
||||
|
||||
### Application Setup
|
||||
|
||||
Clone and install:
|
||||
```bash
|
||||
cd /var/www
|
||||
git clone https://github.com/theta42/proxy.git
|
||||
cd proxy/nodejs
|
||||
npm install
|
||||
```
|
||||
|
||||
Create systemd service:
|
||||
```bash
|
||||
cp ops/proxy.service /etc/systemd/system/proxy.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable proxy.service
|
||||
systemctl start proxy.service
|
||||
```
|
||||
|
||||
## DNS Provider Configuration
|
||||
|
||||
For wildcard SSL certificates, configure a DNS provider via the web UI or API:
|
||||
|
||||
**Supported providers:**
|
||||
- **CloudFlare** - Requires API token
|
||||
- **DigitalOcean** - Requires API token
|
||||
- **PorkBun** - Requires API key and secret API key
|
||||
|
||||
Once configured, create a wildcard host (e.g., `*.example.com`) and the system will automatically request and manage the DNS-01 challenge certificate.
|
||||
|
||||
## Architecture
|
||||
|
||||
The system consists of three main components:
|
||||
|
||||
1. **OpenResty/Nginx** - Frontend proxy with Lua-based routing
|
||||
- Handles SSL termination via lua-resty-auto-ssl
|
||||
- Queries Node.js backend via Unix socket for host routing
|
||||
- Proxies requests to configured backend servers
|
||||
|
||||
2. **Node.js API** - Backend management and control plane
|
||||
- RESTful API for host/user/DNS management
|
||||
- Wildcard SSL certificate orchestration
|
||||
- Host lookup tree with wildcard matching
|
||||
- User authentication and authorization
|
||||
|
||||
3. **Redis** - Data store
|
||||
- Host configurations
|
||||
- User accounts and tokens
|
||||
- SSL certificate storage
|
||||
- Domain and DNS provider configurations
|
||||
|
||||
## Host Lookup System
|
||||
|
||||
The proxy supports sophisticated domain matching:
|
||||
- **Exact match**: `example.com` matches only `example.com`
|
||||
- **Single wildcard**: `*.example.com` matches `sub.example.com` but not `deep.sub.example.com`
|
||||
- **Double wildcard**: `**.example.com` matches any depth (`sub.example.com`, `deep.sub.example.com`, etc.)
|
||||
- **Mixed wildcards**: `api.*.example.com` matches `api.v1.example.com`, `api.v2.example.com`, etc.
|
||||
|
||||
Priority: Exact match > Single wildcard > Double wildcard
|
||||
|
||||
## Development
|
||||
|
||||
**Running locally:**
|
||||
```bash
|
||||
cd nodejs
|
||||
npm install
|
||||
npm run dev # Runs with nodemon for auto-reload
|
||||
```
|
||||
|
||||
**Running tests:**
|
||||
```bash
|
||||
npm test # Run all tests
|
||||
npm run test:unit # Run unit tests only
|
||||
npm run test:watch # Watch mode for development
|
||||
```
|
||||
|
||||
Tests use Node.js built-in test runner (requires Node 18+).
|
||||
|
||||
## API Documentation
|
||||
|
||||
See [API Documentation](nodejs/api.md) for complete API reference.
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests are welcome. The project uses GitHub Actions for CI/CD:
|
||||
- Tests run automatically on all PRs
|
||||
- All tests must pass before merging to master
|
||||
- Tests run on Node.js 18.x, 20.x, and 22.x
|
||||
|
||||
## License
|
||||
|
||||
MIT - See LICENSE file for details.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
proxy/
|
||||
├── nodejs/ # Node.js backend application
|
||||
│ ├── bin/ # Entry point (www)
|
||||
│ ├── models/ # Data models (Host, User, DNS providers)
|
||||
│ ├── routes/ # API routes
|
||||
│ ├── services/ # Background services (host lookup, scheduler)
|
||||
│ ├── middleware/ # Express middleware
|
||||
│ ├── utils/ # Utility functions
|
||||
│ ├── public/ # Static web assets
|
||||
│ ├── views/ # EJS templates
|
||||
│ └── test/ # Test suite
|
||||
├── ops/ # Operations and deployment
|
||||
│ ├── nginx_conf/ # OpenResty configuration files
|
||||
│ ├── install.sh # Automated installer
|
||||
│ └── proxy.service # Systemd service definition
|
||||
└── .github/workflows/ # CI/CD workflows
|
||||
```
|
||||
|
||||
+479
-69
@@ -1,132 +1,542 @@
|
||||
## get host info
|
||||
# API Documentation
|
||||
|
||||
**GET** `/api/hosts<HOST>`
|
||||
All API endpoints require authentication via the `auth-token` header unless otherwise noted.
|
||||
|
||||
Base URL: `https://your-proxy-host.com/api`
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
### Login
|
||||
|
||||
**POST** `/api/auth/login`
|
||||
|
||||
Authenticate a user and receive an auth token.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" https://proxy-host.com/api/hostsmine.com
|
||||
curl -H "Content-Type: application/json" \
|
||||
-X POST \
|
||||
-d '{"username": "myuser", "password": "mypassword"}' \
|
||||
https://proxy-host.com/api/auth/login
|
||||
```
|
||||
|
||||
* 200 {"host":"yours.com","results":{"ip":"127.0.0.1:4000","updated":"1518595297563","username":"test10","forceSSL": false, "targetSSL": true, "targetPort": "443"}}
|
||||
* 404 {"name": "HostNotFound", "message": "Host does not exists"}
|
||||
**Responses:**
|
||||
- `200` `{"login": true, "token": "027d3964-7d81-4462-a6f9-2c1f9b40b4be", "message": "myuser logged in!"}`
|
||||
- `401` `{"name": "LoginFailed", "message": "Invalid Credentials, login failed."}`
|
||||
|
||||
### Logout
|
||||
|
||||
## view all hosts
|
||||
**ALL** `/api/auth/logout`
|
||||
|
||||
**GET** `/api/hosts`
|
||||
Invalidate the current auth token.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" https://proxy-host.com/api/hosts
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
https://proxy-host.com/api/auth/logout
|
||||
```
|
||||
|
||||
* 200 {"hosts":["mine.com","mine2.com"]}
|
||||
**Responses:**
|
||||
- `200` `{"message": "Bye"}`
|
||||
|
||||
---
|
||||
|
||||
## Add host
|
||||
## Users
|
||||
|
||||
**POST** `/api/hosts`
|
||||
All user endpoints require authentication.
|
||||
|
||||
Params
|
||||
* **host** -- Required, The domain name for the new record.
|
||||
* **ip** -- Required, The target IP or FQDN for the record.
|
||||
* **targetSSL** -- If the remote IP target is SSL. Default is false and this is
|
||||
not recommended.
|
||||
* **targetPort** -- Required, TCP port for the remote server. Unless you know
|
||||
otherwise, 80 for targetSSL false and 443 for true.
|
||||
* **forceSSL** -- If requests should be forced to use SSL from the client to
|
||||
the proxy. The default is false and this is HIGHLY recommended.
|
||||
* **
|
||||
### List Users
|
||||
|
||||
**GET** `/api/user`
|
||||
|
||||
Get list of all users.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" -X POST -d '{"host": "test.vm42.com", "ip": "192.168.1.21", "targetSSL": false, "targetPort": "443", "forceSSL": true} https://proxy-host.com/api/hosts
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/user
|
||||
```
|
||||
|
||||
* 200 {"message":"Host yours.com added."}
|
||||
* 409 {"name":"HostNameUsed", "message":"Host already exists"}
|
||||
* 422 {"name":"ObjectValidateError","message":[{"key":"ip","message":"ip is required."}]} Missing or incorrect keys/values. Returns a list with a message per key error.
|
||||
**Query Parameters:**
|
||||
- `detail` - Include full user details (optional)
|
||||
|
||||
## Edit
|
||||
**Responses:**
|
||||
- `200` `{"results": ["user1", "user2"]}`
|
||||
- `200` `{"results": [{"username": "user1", ...}, ...]}` (with `?detail=true`)
|
||||
|
||||
**PUT** `/api/hosts<host>`
|
||||
### Get Current User
|
||||
|
||||
Takes the same params as add, but none are required
|
||||
**GET** `/api/user/me`
|
||||
|
||||
curl -H "Content-Type: application/json" -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" -X POST -d '{"host": "test.vm42.com", "ip": "192.168.1.21", "targetSSL": false, "targetPort": "443", "forceSSL": true} https://proxy-host.com/api/hosts
|
||||
|
||||
* 200 {"message":"Host yours.com updated."}
|
||||
* 404 {"name": "HostNotFound", "message": "Host does not exists"}
|
||||
* 409 {"name":"HostNameUsed", "message":"Host already exists"}
|
||||
* 422 {"name":"ObjectValidateError","message":[{"key":"ip","message":"ip is required."}]} Missing or incorrect keys/values. Returns a list with a message per key error.
|
||||
|
||||
|
||||
## delete host
|
||||
|
||||
**DELETE** /`api/hosts/<host>`
|
||||
Get information about the currently authenticated user.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" -X DELETE https://proxy-host.com/api/hosts
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/user/me
|
||||
```
|
||||
|
||||
* 200 {"message":"Host yours.com deleted"}
|
||||
* 404 {"name": "HostNotFound", "message": "Host does not exists"}
|
||||
**Responses:**
|
||||
- `200` `{"username": "myuser"}`
|
||||
|
||||
### Create User
|
||||
|
||||
## create invite token
|
||||
**POST** `/api/user`
|
||||
|
||||
**post** `/users/invite`
|
||||
Create a new user.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -H "auth-token: 0b06eb2e-4ca4-4881-9a0f-b8df55431cd1" -X POST https://proxy-host.com/users/invite
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"username": "newuser", "password": "newpassword"}' \
|
||||
https://proxy-host.com/api/user
|
||||
```
|
||||
|
||||
* 200 {"token":"5caf94d2-2c91-4010-8df7-968d10802b9d"}
|
||||
**Responses:**
|
||||
- `200` User created successfully
|
||||
- `409` Username already exists
|
||||
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error
|
||||
|
||||
### Delete User
|
||||
|
||||
## sing up
|
||||
**DELETE** `/api/user/:username`
|
||||
|
||||
**post** `/auth/invite/<INVITE TOKEN>`
|
||||
Delete a user account.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -X POST -d "{\"username\": \"test9\", \"password\": \"palm7\"}" https://proxy-host.com/auth/invite/b33d8819-ec64-4cf4-a6ec-77562d738fa4
|
||||
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X DELETE \
|
||||
https://proxy-host.com/api/user/olduser
|
||||
```
|
||||
|
||||
* 200 {"user":"test9","token":"af662d8b-3d44-4110-8ad9-047dc752d97f"}
|
||||
* 400 {"message":"Missing fields"}
|
||||
* 401 {"message":"Token not valid"}
|
||||
* 409 {"message":"username taken"}
|
||||
**Responses:**
|
||||
- `200` `{"username": "olduser", "results": ...}`
|
||||
- `404` User not found
|
||||
|
||||
### Change Password (Self)
|
||||
|
||||
## login
|
||||
**PUT** `/api/user/password`
|
||||
|
||||
**post** `/auth/login`
|
||||
Change the password for the currently authenticated user.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"username": "test8", "password": "mypassword"}' https://proxy-host.com/auth/login
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X PUT \
|
||||
-d '{"password": "newpassword"}' \
|
||||
https://proxy-host.com/api/user/password
|
||||
```
|
||||
|
||||
* 200 {"login":true,"token":"027d3964-7d81-4462-a6f9-2c1f9b40b4be"}
|
||||
* 401 {"login":false}
|
||||
**Responses:**
|
||||
- `200` `{"results": ...}` Password changed successfully
|
||||
|
||||
### Change Password (Other User)
|
||||
|
||||
## verify SSH key
|
||||
**PUT** `/api/user/password/:username`
|
||||
|
||||
**post** `/auth/verifykey`
|
||||
Change the password for another user (admin function).
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -X POST -d "{\"key\":\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDM9vboz5YGgESsrR2e4JOeP2qtmQo2S8BjI+Y/VxPQ6WbNFzAkXxDniHcnPCrhkeX36SKINvMjWnt4XOK2S+X+1tCoXJzqtcKKyK0gx8ijBxcWVPxsMWjMYTGSVSKiKnt6CyQzrbVGJMh3iAQ8Yv1JwH+6SAtMgT8it7iLyntNFJCesh4I/znEG58A5VBbdUle1Ztz9afjj1CZns17jk7KPm9ig5DmuvdvnMEfhFjfKv1Rp6S5nxacMoTP4tJNSEUh55IicoWk94ii5GwUVLYgyMmzdlA32TqVLFpU2yAvdA9WSnBaI/ZyktlfI7YAmK2wFBsagr9Pq1TcUAY6rZ/GTMjDxExgdYn/FxlufcuqeNJsJXs2A+0xDS/9mv/yGQzNZrL8DrVhY2OKKLoH4Q7enDbhSgEFmJUJMqPxuPEgLEvKfzcURSvIwRj1iCEw6S4dhdaLJl2RRBb1ZWBQbE5ogIbvAl7GFJUAhj3pqYJnd30VENv1MkK+IoCS7EEP0caqL9RNAId0Plud7q2XElHqzkYUE+z+Q/LvGgclXK1ZmZejNaMnV53wfhAevfwVyNGK9i5gbwc1P2lplIa5laXCcVWezqELEkTpdjp4AeKmMuCr8rY8EnLKIcKWEOsX5UumztCow6e1E55v3VeHvRZLpw4DZP7EE0Q8B/jPFWqbCw== wmantly@gmail.com\"}" https://proxy-host.com/auth/verifykey
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X PUT \
|
||||
-d '{"password": "newpassword"}' \
|
||||
https://proxy-host.com/api/user/password/otheruser
|
||||
```
|
||||
|
||||
* 200 {"info":"4096 SHA256:dfdCYzt0atMBXVZTJzUxsu99IjXXFXpocSox5q+jOs8 wmantly@gmail.com (RSA)\n"}
|
||||
* 400 {"message":"Key is not a public key file!"}
|
||||
**Responses:**
|
||||
- `200` `{"results": ...}` Password changed successfully
|
||||
- `404` User not found
|
||||
|
||||
### Create Invite Token
|
||||
|
||||
## add ssh key to current user
|
||||
**POST** `/api/user/invite`
|
||||
|
||||
**post** `/users/key`
|
||||
Create an invitation token for new user registration.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -H "auth-token: 8eff4f16-086d-40fd-acbd-7634b9a36117" -X POST -d "{\"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAjWnt4XOK2S+X+1tCoXJzqtcKKyK0gx8ijBxcWVPxsMWjMYTGSVSKiKnt6CyQzrbVGJMh3iAQ8Yv1JwH+6SAtMgT8it7iLyntNFJCesh4I/znEG58A5VBbdUle1Ztz9afjj1CZns17jk7KPm9ig5DmuvdvnMEfhFjfKv1Rp6S5nxacMoTP4tJNSEUh55IicoWk94ii5GwUVLYgyMmzdlA32TqVLFpU2yAvdA9WSnBaI/ZyktlfI7YAmK2wFBsagr9Pq1TcUAY6rZ/GTMjDxExgdYn/FxlufcuqeNJsJXs2A+0xDS/9mv/yGQzNZrL8DrVhY2OKKLoH4Q7enDbhSgEFmJUJMqPxuPEgLEvKfzcURSvIwRj1iCEw6S4dhdaLJl2RRBb1ZWBQbE5ogIbvAl7GFJUAhj3pqYJnd30VENv1MkK+IoCS7EEP0caqL9RNAId0Plud7q2XElHqzkYUE+z+Q/LvGgclXK1ZmZejNaMnV53wfhAevfwVyNGK9i5gbwc1P2lplIa5laXCcVWezqELEkTpdjp4AeKmMuCr8rY8EnLKIcKWEOsX5UumztCow6e1E55v3VeHvRZLpw4DZP7EE0Q8B/jPFWqbCw== wmantly@gmail.co\"}" https://proxy-host.com/users/key
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
https://proxy-host.com/api/user/invite
|
||||
```
|
||||
|
||||
* 200 {"message":true}
|
||||
* 400 {"message":"Bad SSH key"}
|
||||
**Responses:**
|
||||
- `200` `{"token": "5caf94d2-2c91-4010-8df7-968d10802b9d"}`
|
||||
|
||||
### Add SSH Key
|
||||
|
||||
**POST** `/api/user/key`
|
||||
|
||||
Add an SSH public key to the current user's account.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"key": "ssh-rsa AAAAB3..."}' \
|
||||
https://proxy-host.com/api/user/key
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": true}` Key added successfully
|
||||
- `400` `{"message": "Bad SSH key"}` Invalid key format
|
||||
|
||||
---
|
||||
|
||||
## Hosts
|
||||
|
||||
Manage proxy host configurations.
|
||||
|
||||
### List Hosts
|
||||
|
||||
**GET** `/api/host`
|
||||
|
||||
Get list of all configured hosts.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/host
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
- `detail` - Include full host details (optional)
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": ["example.com", "*.wildcard.com"]}`
|
||||
- `200` `{"results": [{"host": "example.com", "ip": "192.168.1.10", ...}, ...]}` (with `?detail=true`)
|
||||
|
||||
### Get Host
|
||||
|
||||
**GET** `/api/host/:host`
|
||||
|
||||
Get configuration for a specific host.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/host/example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"item": "example.com", "results": {"host": "example.com", "ip": "192.168.1.10", "targetPort": 8080, ...}}`
|
||||
- `404` `{"name": "HostNotFound", "message": "Host does not exists"}`
|
||||
|
||||
### Lookup Host
|
||||
|
||||
**GET** `/api/host/lookup/:domain`
|
||||
|
||||
Test the host lookup algorithm (supports wildcard matching).
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/host/lookup/sub.example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"string": "sub.example.com", "results": {"host": "*.example.com", ...}}`
|
||||
- `200` `{"string": "sub.example.com", "results": null}` (no match)
|
||||
|
||||
### Get Lookup Tree
|
||||
|
||||
**GET** `/api/host/lookupobj`
|
||||
|
||||
Get the internal lookup tree structure (for debugging).
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/host/lookupobj
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": {"com": {"example": {...}}}}`
|
||||
|
||||
### Create Host
|
||||
|
||||
**POST** `/api/host`
|
||||
|
||||
Add a new host configuration.
|
||||
|
||||
**Parameters:**
|
||||
- `host` (required) - Domain name (e.g., `example.com`, `*.example.com`)
|
||||
- `ip` (required) - Target IP address or FQDN
|
||||
- `targetPort` (required) - Target port number (1-65535)
|
||||
- `forcessl` (optional) - Force HTTPS redirect (default: true)
|
||||
- `targetssl` (optional) - Use HTTPS to backend (default: false)
|
||||
- `challengeType` (optional) - For wildcards: `DNS-01-wildcard` or `wildcardChild`
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"host": "example.com", "ip": "192.168.1.10", "targetPort": 8080, "forcessl": true, "targetssl": false}' \
|
||||
https://proxy-host.com/api/host
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "\"example.com\" added.", "host": "example.com", ...}`
|
||||
- `409` `{"name": "HostNameUsed", "message": "Host already exists"}`
|
||||
- `422` `{"name": "ObjectValidateError", "message": ...}` Validation error
|
||||
|
||||
### Update Host
|
||||
|
||||
**PUT** `/api/host/:host`
|
||||
|
||||
Update an existing host configuration.
|
||||
|
||||
**Parameters:** Same as Create Host (all optional)
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X PUT \
|
||||
-d '{"ip": "192.168.1.20", "targetPort": 9000}' \
|
||||
https://proxy-host.com/api/host/example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "\"example.com\" updated.", ...}`
|
||||
- `404` `{"name": "HostNotFound", "message": "Host does not exists"}`
|
||||
- `422` Validation error
|
||||
|
||||
### Delete Host
|
||||
|
||||
**DELETE** `/api/host/:host`
|
||||
|
||||
Remove a host configuration.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X DELETE \
|
||||
https://proxy-host.com/api/host/example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "example.com deleted", ...}`
|
||||
- `404` `{"name": "HostNotFound", "message": "Host does not exists"}`
|
||||
|
||||
### Renew Wildcard Certificate
|
||||
|
||||
**PUT** `/api/host/:host/renew`
|
||||
|
||||
Manually trigger wildcard certificate renewal.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X PUT \
|
||||
https://proxy-host.com/api/host/*.example.com/renew
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "Requesting wildcard cert for *.example.com"}`
|
||||
- `404` Host not found
|
||||
|
||||
---
|
||||
|
||||
## DNS Providers
|
||||
|
||||
Manage DNS provider integrations for wildcard SSL certificates.
|
||||
|
||||
### List DNS Providers
|
||||
|
||||
**GET** `/api/dns`
|
||||
|
||||
Get list of configured DNS providers.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/dns
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
- `detail` - Include full provider details (optional)
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": ["provider-id-1", "provider-id-2"]}`
|
||||
|
||||
### List Available Provider Types
|
||||
|
||||
**OPTIONS** `/api/dns`
|
||||
|
||||
Get list of supported DNS provider types and their configuration requirements.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X OPTIONS \
|
||||
https://proxy-host.com/api/dns
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": [{"name": "CloudFlare", "fields": {...}}, {"name": "DigitalOcean", ...}, ...]}`
|
||||
|
||||
### Create DNS Provider
|
||||
|
||||
**POST** `/api/dns`
|
||||
|
||||
Configure a new DNS provider.
|
||||
|
||||
**CloudFlare:**
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"name": "My CloudFlare", "dnsProvider": "Cloudflare", "token": "your-api-token"}' \
|
||||
https://proxy-host.com/api/dns
|
||||
```
|
||||
|
||||
**DigitalOcean:**
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"name": "My DO", "dnsProvider": "DigitalOcean", "token": "your-api-token"}' \
|
||||
https://proxy-host.com/api/dns
|
||||
```
|
||||
|
||||
**PorkBun:**
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
-d '{"name": "My PorkBun", "dnsProvider": "PorkBun", "apiKey": "pk_xxx", "secretApiKey": "sk_xxx"}' \
|
||||
https://proxy-host.com/api/dns
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "\"provider-id\" added.", ...}`
|
||||
- `422` Validation error or invalid API credentials
|
||||
|
||||
### Get DNS Provider
|
||||
|
||||
**GET** `/api/dns/:id`
|
||||
|
||||
Get a specific DNS provider configuration.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/dns/provider-id
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"item": "provider-id", "results": {...}}`
|
||||
- `404` Provider not found
|
||||
|
||||
### Update DNS Provider
|
||||
|
||||
**PUT** `/api/dns/:id`
|
||||
|
||||
Update DNS provider configuration.
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" \
|
||||
-H "auth-token: your-token-here" \
|
||||
-X PUT \
|
||||
-d '{"name": "Updated Name"}' \
|
||||
https://proxy-host.com/api/dns/provider-id
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "\"provider-id\" updated.", ...}`
|
||||
- `404` Provider not found
|
||||
|
||||
### Delete DNS Provider
|
||||
|
||||
**DELETE** `/api/dns/:id`
|
||||
|
||||
Remove a DNS provider and all associated domains.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X DELETE \
|
||||
https://proxy-host.com/api/dns/provider-id
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"message": "provider-id deleted", ...}`
|
||||
- `404` Provider not found
|
||||
|
||||
### List Domains
|
||||
|
||||
**GET** `/api/dns/domain`
|
||||
|
||||
List all domains from all configured providers.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/dns/domain
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
- `detail` - Include full domain details (optional)
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": ["example.com", "test.com"]}`
|
||||
|
||||
### Get Domain
|
||||
|
||||
**GET** `/api/dns/domain/:domain`
|
||||
|
||||
Get details for a specific domain.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/dns/domain/example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": [{"domain": "example.com", "zoneId": "...", ...}]}`
|
||||
- `404` Domain not found
|
||||
|
||||
### Refresh Domains
|
||||
|
||||
**POST** `/api/dns/domain/refresh/:providerId`
|
||||
|
||||
Refresh the domain list from a DNS provider's API.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
-X POST \
|
||||
https://proxy-host.com/api/dns/domain/refresh/provider-id
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` `{"results": ...}` Updated domain list
|
||||
- `404` Provider not found
|
||||
|
||||
---
|
||||
|
||||
## Certificates
|
||||
|
||||
Retrieve SSL certificate information.
|
||||
|
||||
### Get Certificate
|
||||
|
||||
**GET** `/api/cert/:host`
|
||||
|
||||
Get the SSL certificate for a host.
|
||||
|
||||
```bash
|
||||
curl -H "auth-token: your-token-here" \
|
||||
https://proxy-host.com/api/cert/example.com
|
||||
```
|
||||
|
||||
**Responses:**
|
||||
- `200` Certificate data including `cert_pem`, `fullchain_pem`, `privkey_pem`, expiry information
|
||||
- `404` Certificate not found
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints may return the following error responses:
|
||||
|
||||
- `401` `{"name": "LoginFailed", "message": "Invalid Credentials, login failed."}` - Authentication required or invalid
|
||||
- `404` `{"name": "NotFound", "message": "..."}` - Resource not found
|
||||
- `422` `{"name": "ObjectValidateError", "message": [...], "keys": [...]}` - Validation errors
|
||||
- `500` Internal server error
|
||||
|
||||
## Notes
|
||||
|
||||
- All timestamps are in milliseconds since epoch
|
||||
- The `auth-token` header is required for all authenticated endpoints
|
||||
- Host names support wildcards: `*` (single level) and `**` (multi-level)
|
||||
- DNS providers are validated on creation - invalid API credentials will be rejected
|
||||
- Wildcard certificates are automatically renewed 30 days before expiration
|
||||
|
||||
+8
-1
@@ -10,7 +10,14 @@
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node ./bin/www",
|
||||
"dev": "npx nodemon --ignore public/ ./bin/www"
|
||||
"dev": "npx nodemon --ignore public/ ./bin/www",
|
||||
"test": "node --test test/**/*.test.js",
|
||||
"test:unit": "node --test test/unit/**/*.test.js",
|
||||
"test:integration": "node --test test/integration/**/*.test.js",
|
||||
"test:watch": "node --test --watch test/**/*.test.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
|
||||
Reference in New Issue
Block a user