From 79ae26f1b5f8c6c63d1b8644243e65ed1cb404cc Mon Sep 17 00:00:00 2001 From: yusing Date: Mon, 23 Sep 2024 22:10:13 +0800 Subject: [PATCH] new simpler setup method, readme and doc update --- Dockerfile | 3 + README.md | 24 +++++--- config.example.yml | 15 +++-- docs/dns_providers.md | 68 ++++++++++++++------- docs/docker.md | 58 ++++++------------ docs/docker_socket_proxy.md | 2 +- setup-docker.sh | 14 ----- src/common/args.go | 2 + src/common/constants.go | 23 +++----- src/common/env.go | 14 ++--- src/main.go | 6 ++ src/setup.go | 115 ++++++++++++++++++++++++++++++++++++ 12 files changed, 231 insertions(+), 113 deletions(-) delete mode 100644 setup-docker.sh create mode 100644 src/setup.go 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`, `https`, `tcp`, `udp` | -| `host` | proxy host | | 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`
| -| `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`, `https`, `tcp`, `udp` | +| `host` | proxy host | | 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`
| +| `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) +}