diff --git a/Dockerfile b/Dockerfile
index c2a94ce..80c1cf2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,6 +19,9 @@ COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /src/go-proxy /app/
COPY schema/ /app/schema
+# copy cert required for setup
+COPY --from=builder /etc/ssl/certs /etc/ssl/certs
+
ENV DOCKER_HOST=unix:///var/run/docker.sock
ENV GOPROXY_DEBUG=0
diff --git a/README.md b/README.md
index 9271bdd..7f1142d 100755
--- a/README.md
+++ b/README.md
@@ -32,11 +32,12 @@ A lightweight, easy-to-use, and [performant](docs/benchmark_result.md) reverse p
- Easy to use
- Effortless configuration
+ - Simple multi-node setup
- Error messages is clear and detailed, easy troubleshooting
- Auto certificate obtaining and renewal (See [Supported DNS Challenge Providers](docs/dns_providers.md))
- Auto configuration for docker containers
- Auto hot-reload on container state / config file changes
-- Stop containers on idle, wake it up on traffic _(optional, see [showcase](#idlesleeper))_
+- **idlesleeper**: stop containers on idle, wake it up on traffic _(optional, see [showcase](#idlesleeper))_
- HTTP(s) reserve proxy
- TCP and UDP port forwarding
- Web UI for configuration and monitoring (See [screenshots](https://github.com/yusing/go-proxy-frontend?tab=readme-ov-file#screenshots))
@@ -48,18 +49,23 @@ A lightweight, easy-to-use, and [performant](docs/benchmark_result.md) reverse p
### Setup
-1. Setup DNS Records, e.g.
+1. Pull docker image `docker pull ghcr.io/yusing/go-proxy:latest`
- - A Record: `*.y.z` -> `10.0.10.1`
- - AAAA Record: `*.y.z` -> `::ffff:a00:a01`
+2. Create new directory, `cd` into it, then run setup
-2. Setup `go-proxy` [See here](docs/docker.md)
+ `docker run --rm -v .:/setup ghcr.io/yusing/go-proxy /app/go-proxy setup`
-3. Setup `docker-socket-proxy` (see [example](docs/docker_socket_proxy.md) other machine that is running docker (if any)
+3. Setup DNS Records point to machine which runs `go-proxy`, e.g.
-4. Configure `go-proxy`
- - with text editor (e.g. Visual Studio Code)
- - or with web config editor via `http://gp.y.z`
+ - A Record: `*.y.z` -> `10.0.10.1`
+ - AAAA Record: `*.y.z` -> `::ffff:a00:a01`
+
+4. Setup `docker-socket-proxy` other docker nodes _(if any)_ (see [example](docs/docker_socket_proxy.md)) and then them inside `config.yml`
+
+5. Done. You may now do some extra configuration
+ - With text editor (e.g. Visual Studio Code)
+ - With Web UI via `gp.y.z`
+ - For more info, [See docker.md](docs/docker.md)
[🔼Back to top](#table-of-content)
diff --git a/config.example.yml b/config.example.yml
index 01bab15..759771a 100644
--- a/config.example.yml
+++ b/config.example.yml
@@ -15,14 +15,14 @@
# options:
# - auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token
-# 3. other providers, check readme for more
+# 3. other providers, check docs/dns_providers.md for more
providers:
- include:
- - providers.yml # config/providers.yml
- # add some more below if you want
- # - file1.yml # config/file_1.yml
- # - file2.yml
+ # include:
+ # - providers.yml # config/providers.yml
+ # # add some more below if you want
+ # - file1.yml # config/file_1.yml
+ # - file2.yml
docker:
# for value format, see https://docs.docker.com/reference/cli/dockerd/
# $DOCKER_HOST implies unix:///var/run/docker.sock by default
@@ -30,8 +30,7 @@ providers:
# add more docker providers if needed
# remote-1: tcp://10.0.2.1:2375
# remote-2: ssh://root:1234@10.0.2.2
-
# Fixed options (optional, non hot-reloadable)
# timeout_shutdown: 5
-# redirect_to_https: false
+# redirect_to_https: false # redirect http requests to https (if enabled)
diff --git a/docs/dns_providers.md b/docs/dns_providers.md
index 57ee195..40be2a9 100644
--- a/docs/dns_providers.md
+++ b/docs/dns_providers.md
@@ -11,42 +11,70 @@
## Cloudflare
+```yaml
+autocert:
+ provider: cloudflare
+ options:
+ auth_token:
+```
+
`auth_token` your zone API token
Follow [this guide](https://cloudkul.com/blog/automcatic-renew-and-generate-ssl-on-your-website-using-lego-client/) to create a new token with `Zone.DNS` read and edit permissions
## CloudDNS
-- `client_id`
-
-- `email`
-
-- `password`
+```yaml
+autocert:
+ provider: clouddns
+ options:
+ client_id:
+ email:
+ password:
+```
## DuckDNS
-- `token`: DuckDNS Token
+```yaml
+autocert:
+ provider: duckdns
+ options:
+ token:
+```
Tested by [earvingad](https://github.com/earvingad)
## OVHCloud
+```yaml
+autocert:
+ provider: ovh
+ options:
+ api_endpoint:
+ application_key:
+ application_secret:
+ consumer_key:
+ oauth2_config:
+ client_id:
+ client_secret:
+```
+
_Note, `application_key` and `oauth2_config` **CANNOT** be used together_
-- `api_endpoint`: Endpoint URL, or one of
- - `ovh-eu`,
- - `ovh-ca`,
- - `ovh-us`,
- - `kimsufi-eu`,
- - `kimsufi-ca`,
- - `soyoustart-eu`,
- - `soyoustart-ca`
-- `application_secret`
-- `application_key`
-- `consumer_key`
-- `oauth2_config`: Client ID and Client Secret
- - `client_id`
- - `client_secret`
+- `api_endpoint`: Endpoint URL, or one of
+ - `ovh-eu`,
+ - `ovh-ca`,
+ - `ovh-us`,
+ - `kimsufi-eu`,
+ - `kimsufi-ca`,
+ - `soyoustart-eu`,
+ - `soyoustart-ca`
+- `application_secret`
+- `application_key`
+- `consumer_key`
+- `oauth2_config`: Client ID and Client Secret
+ - `client_id`
+ - `client_secret`
## Implement other DNS providers
diff --git a/docs/docker.md b/docs/docker.md
index 2c1aa06..29ec5d7 100644
--- a/docs/docker.md
+++ b/docs/docker.md
@@ -6,7 +6,7 @@
- [Docker compose guide](#docker-compose-guide)
- [Table of content](#table-of-content)
- - [Setup](#setup)
+ - [Additional setup](#additional-setup)
- [Labels](#labels)
- [Syntax](#syntax)
- [Fields](#fields)
@@ -16,34 +16,11 @@
- [Docker compose examples](#docker-compose-examples)
- [Services URLs for above examples](#services-urls-for-above-examples)
-## Setup
+## Additional setup
-1. Install `wget` if not already
+1. Enable HTTPs _(optional)_
- - Ubuntu based: `sudo apt install -y wget`
- - Fedora based: `sudo yum install -y wget`
- - Arch based: `sudo pacman -Sy wget`
-
-2. Run setup script
-
- `bash <(wget -qO- https://github.com/yusing/go-proxy/raw/main/setup-docker.sh)`
-
- It will setup folder structure and required config files
-
-3. Verify folder structure and then `cd go-proxy`
-
- ```plain
- go-proxy
- ├── certs
- ├── compose.yml
- └── config
- ├── config.yml
- └── providers.yml
- ```
-
-4. Enable HTTPs _(optional)_
-
- Mount a folder (to store obtained certs) or (containing existing cert)
+ Mount a folder to store obtained certs or to load existing cert
```yaml
services:
@@ -69,15 +46,16 @@
```yaml
autocert:
+ provider: local
cert_path: /app/certs/cert.crt
key_path: /app/certs/priv.key
```
-5. Modify `compose.yml` to fit your needs
+2. Modify `compose.yml` to fit your needs
-6. Run `docker compose up -d` to start the container
+3. Run `docker compose up -d` to start the container
-7. Navigate to Web panel `http://gp.yourdomain.com` or use **Visual Studio Code (provides schema check)** to edit proxy config
+4. Navigate to Web panel `http://gp.yourdomain.com` or use **Visual Studio Code (provides schema check)** to edit proxy config
[🔼Back to top](#table-of-content)
@@ -100,16 +78,16 @@
### Fields
-| Field | Description | Default | Allowed Values / Syntax |
-| --------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `scheme` | proxy protocol |
- `http` for numeric port
- `tcp` for `x:y` port
| `http`, `https`, `tcp`, `udp` |
-| `host` | proxy host | - Docker: docker client IP / hostname
- File: `localhost`
| IP address, hostname |
-| `port` | proxy port **(http/s)** | first port returned from docker | number in range of `1 - 65535` |
-| `port` **(required)** | proxy port **(tcp/udp)** | N/A | `x:y`
- **x**: port for `go-proxy` to listen on.
**x** can be 0, which means listen on a random port - **y**: port or [_service name_](../src/common/constants.go#L55) of target container
|
-| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
-| `path_patterns` | proxy path patterns **(http/s only)**
only requests that matched a pattern will be proxied | empty **(proxy all requests)** | yaml style list[1](#list-example) of ([path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
-| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[2](#key-value-mapping-example) of header-value pairs |
-| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[1](#list-example) of headers |
+| Field | Description | Default | Allowed Values / Syntax |
+| --------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `scheme` | proxy protocol | - `http` for numeric port
- `tcp` for `x:y` port
| `http`, `https`, `tcp`, `udp` |
+| `host` | proxy host | - Docker: docker client IP / hostname
- File: `localhost`
| IP address, hostname |
+| `port` | proxy port **(http/s)** | first port returned from docker | number in range of `1 - 65535` |
+| `port` | proxy port **(tcp/udp)** | `0:first_port` | `x:y`
- **x**: port for `go-proxy` to listen on.
**x** can be 0, which means listen on a random port - **y**: port or [_service name_](../src/common/constants.go#L55) of target container
|
+| `no_tls_verify` | whether skip tls verify **(https only)** | `false` | boolean |
+| `path_patterns` | proxy path patterns **(http/s only)**
only requests that matched a pattern will be proxied | `/` **(proxy all requests)** | yaml style list[1](#list-example) of ([path patterns](https://pkg.go.dev/net/http#hdr-Patterns-ServeMux)) |
+| `set_headers` | header to set **(http/s only)** | empty | yaml style key-value mapping[2](#key-value-mapping-example) of header-value pairs |
+| `hide_headers` | header to hide **(http/s only)** | empty | yaml style list[1](#list-example) of headers |
[🔼Back to top](#table-of-content)
diff --git a/docs/docker_socket_proxy.md b/docs/docker_socket_proxy.md
index c615371..0331851 100644
--- a/docs/docker_socket_proxy.md
+++ b/docs/docker_socket_proxy.md
@@ -23,7 +23,7 @@ docker-proxy:
ports:
- 2375:2375
# or more secure
- - :2375:2375
+ - :2375:2375
```
```yml
diff --git a/setup-docker.sh b/setup-docker.sh
deleted file mode 100644
index 55122d7..0000000
--- a/setup-docker.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-set -e
-if [ -z "$BRANCH" ]; then
- BRANCH="v0.5"
-fi
-BASE_URL="https://github.com/yusing/go-proxy/raw/${BRANCH}"
-mkdir -p go-proxy
-cd go-proxy
-mkdir -p config
-mkdir -p certs
-[ -f compose.yml ] || wget -cO - ${BASE_URL}/compose.example.yml > compose.yml
-[ -f config/config.yml ] || wget -cO - ${BASE_URL}/config.example.yml > config/config.yml
-[ -f config/providers.yml ] || touch config/providers.yml
\ No newline at end of file
diff --git a/src/common/args.go b/src/common/args.go
index c8d0c2c..e84d6f9 100644
--- a/src/common/args.go
+++ b/src/common/args.go
@@ -13,6 +13,7 @@ type Args struct {
const (
CommandStart = ""
+ CommandSetup = "setup"
CommandValidate = "validate"
CommandListConfigs = "ls-config"
CommandListRoutes = "ls-routes"
@@ -23,6 +24,7 @@ const (
var ValidCommands = []string{
CommandStart,
+ CommandSetup,
CommandValidate,
CommandListConfigs,
CommandListRoutes,
diff --git a/src/common/constants.go b/src/common/constants.go
index 51bf1a2..41f06dc 100644
--- a/src/common/constants.go
+++ b/src/common/constants.go
@@ -10,29 +10,24 @@ const (
KeepAlive = 5 * time.Second
)
-const (
- ProviderKind_Docker = "docker"
- ProviderKind_File = "file"
-)
-
// file, folder structure
const (
- ConfigBasePath = "config/"
- ConfigFileName = "config.yml"
- ConfigPath = ConfigBasePath + ConfigFileName
+ ConfigBasePath = "config"
+ ConfigFileName = "config.yml"
+ ConfigExampleFileName = "config.example.yml"
+ ConfigPath = ConfigBasePath + "/" + ConfigFileName
)
const (
- TemplatesBasePath = "templates/"
- PanelTemplatePath = TemplatesBasePath + "panel/index.html"
- ConfigEditorTemplatePath = TemplatesBasePath + "config_editor/index.html"
+ SchemaBasePath = "schema"
+ ConfigSchemaPath = SchemaBasePath + "/config.schema.json"
+ FileProviderSchemaPath = SchemaBasePath + "/providers.schema.json"
)
const (
- SchemaBasePath = "schema/"
- ConfigSchemaPath = SchemaBasePath + "config.schema.json"
- FileProviderSchemaPath = SchemaBasePath + "providers.schema.json"
+ ComposeFileName = "compose.yml"
+ ComposeExampleFileName = "compose.example.yml"
)
const DockerHostFromEnv = "$DOCKER_HOST"
diff --git a/src/common/env.go b/src/common/env.go
index 9eec6a9..9885be5 100644
--- a/src/common/env.go
+++ b/src/common/env.go
@@ -7,18 +7,18 @@ import (
)
var (
- NoSchemaValidation = getEnvBool("GOPROXY_NO_SCHEMA_VALIDATION")
- IsDebug = getEnvBool("GOPROXY_DEBUG")
- ProxyHTTPAddr = getEnv("GOPROXY_HTTP_ADDR", ":80")
- ProxyHTTPSAddr = getEnv("GOPROXY_HTTPS_ADDR", ":443")
- APIHTTPAddr = getEnv("GOPROXY_API_ADDR", "127.0.0.1:8888")
+ NoSchemaValidation = GetEnvBool("GOPROXY_NO_SCHEMA_VALIDATION")
+ IsDebug = GetEnvBool("GOPROXY_DEBUG")
+ ProxyHTTPAddr = GetEnv("GOPROXY_HTTP_ADDR", ":80")
+ ProxyHTTPSAddr = GetEnv("GOPROXY_HTTPS_ADDR", ":443")
+ APIHTTPAddr = GetEnv("GOPROXY_API_ADDR", "127.0.0.1:8888")
)
-func getEnvBool(key string) bool {
+func GetEnvBool(key string) bool {
return U.ParseBool(os.Getenv(key))
}
-func getEnv(key string, defaultValue string) string {
+func GetEnv(key string, defaultValue string) string {
value, ok := os.LookupEnv(key)
if !ok {
value = defaultValue
diff --git a/src/main.go b/src/main.go
index cdf122c..3dd7d06 100755
--- a/src/main.go
+++ b/src/main.go
@@ -30,6 +30,12 @@ import (
func main() {
args := common.GetArgs()
+
+ if args.Command == common.CommandSetup {
+ Setup()
+ return
+ }
+
l := logrus.WithField("module", "main")
onShutdown := F.NewSlice[func()]()
diff --git a/src/setup.go b/src/setup.go
new file mode 100644
index 0000000..4aaa95d
--- /dev/null
+++ b/src/setup.go
@@ -0,0 +1,115 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+
+ . "github.com/yusing/go-proxy/common"
+)
+
+var branch = GetEnv("GOPROXY_BRANCH", "v0.5")
+var baseUrl = fmt.Sprintf("https://github.com/yusing/go-proxy/raw/%s", branch)
+var requiredConfigs = []Config{
+ {ConfigBasePath, true, false, ""},
+ {ComposeFileName, false, true, ComposeExampleFileName},
+ {path.Join(ConfigBasePath, ConfigFileName), false, true, ConfigExampleFileName},
+}
+
+type Config struct {
+ Pathname string
+ IsDir bool
+ NeedDownload bool
+ DownloadFileName string
+}
+
+func Setup() {
+ log.Println("setting up go-proxy")
+ log.Println("branch:", branch)
+
+ os.Chdir("/setup")
+
+ for _, config := range requiredConfigs {
+ config.setup()
+ }
+
+ log.Println("done")
+}
+
+func (c *Config) setup() {
+ if c.IsDir {
+ mkdir(c.Pathname)
+ return
+ }
+ if !c.NeedDownload {
+ touch(c.Pathname)
+ return
+ }
+
+ fetch(c.DownloadFileName, c.Pathname)
+}
+
+func hasFileOrDir(path string) bool {
+ _, err := os.Stat(path)
+ return err == nil
+}
+
+func mkdir(pathname string) {
+ _, err := os.Stat(pathname)
+ if err != nil && os.IsNotExist(err) {
+ log.Printf("creating directory %q\n", pathname)
+ err := os.MkdirAll(pathname, 0o755)
+ if err != nil {
+ log.Fatalf("failed: %s\n", err)
+ }
+ return
+ }
+ if err != nil {
+ log.Fatalf("failed: %s\n", err)
+ }
+}
+
+func touch(pathname string) {
+ if hasFileOrDir(pathname) {
+ return
+ }
+ log.Printf("creating file %q\n", pathname)
+ _, err := os.Create(pathname)
+ if err != nil {
+ log.Fatalf("failed: %s\n", err)
+ }
+}
+func fetch(remoteFilename string, outFileName string) {
+ if hasFileOrDir(outFileName) {
+ return
+ }
+ log.Printf("downloading %q\n", remoteFilename)
+
+ url, err := url.JoinPath(baseUrl, remoteFilename)
+ if err != nil {
+ log.Fatalf("unexpected error: %s\n", err)
+ }
+
+ resp, err := http.Get(url)
+ if err != nil {
+ log.Fatalf("http request failed: %s\n", err)
+ }
+
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Fatalf("error reading response body: %s\n", err)
+ }
+
+ err = os.WriteFile(outFileName, body, 0o644)
+ if err != nil {
+ log.Fatalf("failed to write to file: %s\n", err)
+ }
+
+ log.Printf("downloaded %q\n", outFileName)
+}