mirror of
https://github.com/gl-inet/glkvm-cloud.git
synced 2026-10-04 12:41:42 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c99b96ca01 | |||
| 27792291ed | |||
| 4adb10f577 | |||
| cd231e996b |
+4
-1
@@ -1,4 +1,7 @@
|
||||
FROM alpine:latest
|
||||
WORKDIR /home
|
||||
COPY ./rttys /usr/bin/rttys
|
||||
|
||||
ARG TARGETARCH
|
||||
COPY ./dist/rttys-linux-${TARGETARCH} /usr/bin/rttys
|
||||
|
||||
ENTRYPOINT ["/usr/bin/rttys"]
|
||||
|
||||
@@ -1,29 +1,54 @@
|
||||
# Makefile
|
||||
|
||||
# Go binary name
|
||||
BINARY_NAME = rttys
|
||||
# ---------------- Project ----------------
|
||||
BINARY_NAME ?= rttys
|
||||
UI_DIR ?= ui
|
||||
CONF_FILE ?= ./rttys.conf
|
||||
|
||||
# Go build flags
|
||||
BUILD_FLAGS := -ldflags "-s -w"
|
||||
BUILD_FLAGS ?= -ldflags "-s -w"
|
||||
|
||||
# Go build command
|
||||
# Output dir for cross builds
|
||||
DIST_DIR ?= dist
|
||||
|
||||
# Image name
|
||||
IMAGE_NAME ?= glkvm-cloud
|
||||
IMAGE_TAG ?= build
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
# Map uname -m -> goarch
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
HOST_GOARCH := amd64
|
||||
else ifeq ($(UNAME_M),aarch64)
|
||||
HOST_GOARCH := arm64
|
||||
else ifeq ($(UNAME_M),arm64)
|
||||
HOST_GOARCH := arm64
|
||||
else
|
||||
HOST_GOARCH := $(GOARCH)
|
||||
endif
|
||||
|
||||
# ---------------- Commands ----------------
|
||||
GO_BUILD_CMD = go build $(BUILD_FLAGS) -o $(BINARY_NAME)
|
||||
|
||||
# Paths
|
||||
UI_DIR = ui
|
||||
CONF_FILE = ./rttys.conf
|
||||
.PHONY: all ui build run build-all build-run full-run \
|
||||
build-linux-amd64 build-linux-arm64 build-linux-all \
|
||||
docker-build docker-fullbuild docker-buildx docker-buildx-full
|
||||
|
||||
.PHONY: all ui build run build-run full-run
|
||||
all: build
|
||||
|
||||
# Build frontend files only
|
||||
ui:
|
||||
cd $(UI_DIR) && npm install && npm run build
|
||||
|
||||
# Build Go binary only
|
||||
# Build for current env (native)
|
||||
build:
|
||||
CGO_ENABLED=0 $(GO_BUILD_CMD)
|
||||
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO_BUILD_CMD)
|
||||
|
||||
# Run Go program only
|
||||
# Run Go program only (native binary)
|
||||
run:
|
||||
./$(BINARY_NAME) -c $(CONF_FILE)
|
||||
|
||||
@@ -36,10 +61,52 @@ build-run: build run
|
||||
# Build frontend, build Go binary, and run
|
||||
full-run: ui build run
|
||||
|
||||
# Build Docker image without updating ui
|
||||
docker-build: build
|
||||
docker build -t glkvm-cloud:build .
|
||||
# ---------------- Cross compile (Linux) ----------------
|
||||
# Produce: dist/rttys-linux-amd64 , dist/rttys-linux-arm64
|
||||
build-linux-amd64:
|
||||
@mkdir -p $(DIST_DIR)
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build $(BUILD_FLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64
|
||||
|
||||
# Full Build Docker image
|
||||
build-linux-arm64:
|
||||
@mkdir -p $(DIST_DIR)
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \
|
||||
go build $(BUILD_FLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-arm64
|
||||
|
||||
build-linux-all: build-linux-amd64 build-linux-arm64
|
||||
|
||||
# ---------------- Docker (single-arch) ----------------
|
||||
# Build Docker image using current host arch
|
||||
docker-build: build
|
||||
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
|
||||
|
||||
# Full build Docker image
|
||||
docker-fullbuild: ui build
|
||||
docker build -t glkvm-cloud:build .
|
||||
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
|
||||
|
||||
# ---------------- Docker Buildx ----------------
|
||||
# Multi-arch build
|
||||
# Usage:
|
||||
# make docker-buildx GOARCH=amd64 IMAGE_TAG=build-amd64
|
||||
# make docker-buildx GOARCH=arm64 IMAGE_TAG=build-arm64
|
||||
PLATFORMS ?= linux/amd64,linux/arm64
|
||||
REGISTRY ?=
|
||||
|
||||
# If REGISTRY is set, tag becomes: REGISTRY/IMAGE_NAME:IMAGE_TAG
|
||||
ifdef REGISTRY
|
||||
IMAGE_REF := $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
|
||||
else
|
||||
IMAGE_REF := $(IMAGE_NAME):$(IMAGE_TAG)
|
||||
endif
|
||||
|
||||
docker-buildx:
|
||||
@docker buildx version >/dev/null 2>&1 || (echo "docker buildx not available" && exit 1)
|
||||
@echo "==> buildx (load local image): $(IMAGE_REF) [linux/$(GOARCH)]"
|
||||
docker buildx build \
|
||||
--platform linux/$(GOARCH) \
|
||||
-t $(IMAGE_REF) \
|
||||
--load .
|
||||
|
||||
|
||||
docker-buildx-full: ui
|
||||
@$(MAKE) docker-buildx
|
||||
|
||||
@@ -17,6 +17,9 @@ Self-Deployed Lightweight Cloud is a lightweight KVM remote cloud platform tailo
|
||||
- **Lightweight Design** - Optimized for small businesses and individual users
|
||||
- **Enterprise Authentication** - Supports both **LDAP** and **OIDC** login methods for enterprise users.
|
||||
|
||||
- **Deployment** - Supports both **internal network** and **public internet** deployments
|
||||
- **Platform Compatibility** - Supports both **x86_64** and **arm64** platforms
|
||||
|
||||
## Self-Hosting Guide
|
||||
|
||||
The following mainstream operating systems have been tested and verified
|
||||
@@ -61,9 +64,10 @@ If your server provider uses a **cloud security group** (e.g., AWS, Aliyun, etc.
|
||||
|
||||
We provide **two** ways to install GLKVM Cloud:
|
||||
|
||||
#### A) One-line installer (recommended)
|
||||
#### A) One-line installer (recommended, x86_64/amd64)
|
||||
|
||||
> **Note:** The one-line installer is **Docker-based**. It automates Docker/Compose setup, pulls images, renders configs from templates, and starts services for you.
|
||||
> **Platform:** currently supports **x86_64 (amd64)** only.
|
||||
|
||||
Run **as root**:
|
||||
|
||||
@@ -74,6 +78,8 @@ Run **as root**:
|
||||
#### B) Docker manual install
|
||||
|
||||
> Full reference: see [`docker-compose/README.md`](https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/README.md)
|
||||
>
|
||||
> **Platform:** supports both **x86_64 (amd64)** and **arm64 (AArch64)**.
|
||||
|
||||
### 🌐 Platform Access
|
||||
|
||||
|
||||
+6
-1
@@ -18,6 +18,9 @@
|
||||
* **轻量设计** - 专为小型企业和个人优化
|
||||
* **企业级认证** - 同时支持 **LDAP** 和 **OIDC** 登录方式,适用于企业用户。
|
||||
|
||||
- **部署方式** - 同时支持 **内网部署** 和 **公网部署**
|
||||
- **平台兼容性** - 同时支持 **x86_64** 和 **arm64** 平台
|
||||
|
||||
## 自部署指南
|
||||
|
||||
以下主流操作系统已通过测试验证:
|
||||
@@ -61,9 +64,10 @@
|
||||
|
||||
我们提供 **两种** 安装 GLKVM Cloud 的方式:
|
||||
|
||||
#### A) 一键安装脚本(推荐)
|
||||
#### A) 一键安装脚本(推荐,仅支持 x86_64 / amd64)
|
||||
|
||||
> **注意:** 一键安装脚本基于 **Docker**。它会自动完成 Docker / Docker Compose 的安装、拉取镜像、根据模板渲染配置文件,并启动所有服务。
|
||||
> **平台支持:** 当前仅支持 **x86_64(amd64)** 平台。
|
||||
|
||||
使用 **root 权限** 运行以下命令安装 GLKVM 轻量云:
|
||||
|
||||
@@ -74,6 +78,7 @@
|
||||
#### B) 使用 Docker 手动安装
|
||||
|
||||
> 完整参考文档请查看:[`docker-compose/README-CN.md`](https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/README-CN.md)
|
||||
> 平台支持: 同时支持 x86_64(amd64) 与 arm64(AArch64) 平台。
|
||||
|
||||
|
||||
### 🌐 平台访问
|
||||
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
# Images
|
||||
GLKVM_IMAGE=glzhitong/glkvm-cloud:latest-arm64
|
||||
COTURN_IMAGE=coturn/coturn:edge-alpine-arm64v8
|
||||
|
||||
# Enable reverse proxy mode (e.g. Nginx in front of GLKVM Cloud).
|
||||
# When enabled, TLS is handled by the proxy and GLKVM Cloud runs in plain HTTP.
|
||||
#
|
||||
# Note:
|
||||
# In reverse-proxy mode, remote device access depends on the correct forwarded headers
|
||||
# from the front-end proxy. If these headers are missing or incorrect, GLKVM Cloud may
|
||||
# generate redirect URLs with the internal port (e.g. :10443).
|
||||
#
|
||||
# Please make sure your Nginx config includes:
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Forwarded-Host $host;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Forwarded-Port $server_port;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
#
|
||||
# Reference (verified working example):
|
||||
# https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/nginx-reverse-proxy-example.conf
|
||||
REVERSE_PROXY_ENABLED=false
|
||||
|
||||
# GLKVM access IP seen by devices/users.
|
||||
# Leave empty to auto-detect at container start.
|
||||
GLKVM_ACCESS_IP=
|
||||
|
||||
# rttys
|
||||
RTTYS_TOKEN=DeviceTokenYouCanChangeMe
|
||||
RTTYS_PASS=StrongP@ssw0rd
|
||||
RTTYS_DEVICE_PORT=5912
|
||||
RTTYS_WEBUI_PORT=443
|
||||
RTTYS_HTTP_PROXY_PORT=10443
|
||||
|
||||
# TURN
|
||||
TURN_PORT=3478
|
||||
TURN_USER=glkvmcloudwebrtcuser
|
||||
TURN_PASS=AnotherS3cret
|
||||
|
||||
# LDAP Authentication (Optional)
|
||||
LDAP_ENABLED=false
|
||||
LDAP_SERVER=your-ldap-server.com
|
||||
LDAP_PORT=389
|
||||
LDAP_USE_TLS=false
|
||||
LDAP_BIND_DN=cn=service-account,ou=users,dc=company,dc=com
|
||||
LDAP_BIND_PASSWORD=service-password
|
||||
LDAP_BASE_DN=ou=users,dc=company,dc=com
|
||||
|
||||
# User filter examples for different LDAP implementations:
|
||||
# Active Directory: (&(objectClass=person)(sAMAccountName=%s))
|
||||
# OpenLDAP: (&(objectClass=inetOrgPerson)(uid=%s))
|
||||
# FreeIPA: (&(objectClass=person)(uid=%s))
|
||||
# Generic LDAP: (uid=%s)
|
||||
LDAP_USER_FILTER=(uid=%s)
|
||||
|
||||
LDAP_ALLOWED_GROUPS=admins,operators
|
||||
LDAP_ALLOWED_USERS=user1,user2
|
||||
|
||||
# OIDC Authentication (Optional, generic OIDC provider)
|
||||
OIDC_ENABLED=false
|
||||
OIDC_ISSUER=
|
||||
OIDC_CLIENT_ID=
|
||||
OIDC_CLIENT_SECRET=
|
||||
OIDC_AUTH_URL=
|
||||
OIDC_TOKEN_URL=
|
||||
|
||||
# Redirect URL registered in your OIDC provider.
|
||||
# The path part (/auth/oidc/callback) is fixed by GLKVM Cloud and must not be changed.
|
||||
# Example:
|
||||
# OIDC_REDIRECT_URL=https://your-domain.example.com/auth/oidc/callback
|
||||
OIDC_REDIRECT_URL=
|
||||
|
||||
OIDC_SCOPES="openid profile email"
|
||||
|
||||
# Email-based whitelist (exact email or domain like @example.com)
|
||||
OIDC_ALLOWED_USERS=
|
||||
# Subject (sub) whitelist (stable user IDs)
|
||||
OIDC_ALLOWED_SUBS=
|
||||
# Username whitelist (preferred_username or name)
|
||||
OIDC_ALLOWED_USERNAMES=
|
||||
# Groups whitelist (e.g. admin, devops)
|
||||
OIDC_ALLOWED_GROUPS=
|
||||
@@ -4,6 +4,22 @@ COTURN_IMAGE=coturn/coturn:edge-alpine
|
||||
|
||||
# Enable reverse proxy mode (e.g. Nginx in front of GLKVM Cloud).
|
||||
# When enabled, TLS is handled by the proxy and GLKVM Cloud runs in plain HTTP.
|
||||
#
|
||||
# Note:
|
||||
# In reverse-proxy mode, remote device access depends on the correct forwarded headers
|
||||
# from the front-end proxy. If these headers are missing or incorrect, GLKVM Cloud may
|
||||
# generate redirect URLs with the internal port (e.g. :10443).
|
||||
#
|
||||
# Please make sure your Nginx config includes:
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Forwarded-Host $host;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_set_header X-Forwarded-Port $server_port;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
#
|
||||
# Reference (verified working example):
|
||||
# https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/nginx-reverse-proxy-example.conf
|
||||
REVERSE_PROXY_ENABLED=false
|
||||
|
||||
# GLKVM access IP seen by devices/users.
|
||||
|
||||
@@ -7,9 +7,19 @@
|
||||
```bash
|
||||
git clone https://github.com/gl-inet/glkvm-cloud.git
|
||||
cd glkvm-cloud/docker-compose/
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
* **x86_64(amd64)平台**:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
* **arm64(AArch64)平台**:
|
||||
|
||||
```bash
|
||||
cp .env.arm64.example .env
|
||||
```
|
||||
|
||||
|
||||
### 2. **配置环境变量**
|
||||
|
||||
编辑 `.env` 文件,并根据需求更新关键参数:
|
||||
|
||||
@@ -7,8 +7,16 @@
|
||||
```bash
|
||||
git clone https://github.com/gl-inet/glkvm-cloud.git
|
||||
cd glkvm-cloud/docker-compose/
|
||||
cp .env.example .env
|
||||
```
|
||||
* For **x86_64 (amd64)**:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
* For **arm64 (AArch64)**:
|
||||
```bash
|
||||
cp .env.arm64.example .env
|
||||
```
|
||||
|
||||
2. **Configure environment variables**
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ services:
|
||||
image: ${GLKVM_IMAGE:-glzhitong/glkvm-cloud:latest}
|
||||
container_name: glkvm_cloud
|
||||
restart: always
|
||||
network_mode: "host"
|
||||
environment:
|
||||
# Preferred: set GLKVM_ACCESS_IP explicitly; if empty, entrypoint will auto-detect once.
|
||||
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
|
||||
@@ -60,15 +61,12 @@ services:
|
||||
- ./database:/home/database:rw
|
||||
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
|
||||
command: ["rttys"]
|
||||
ports:
|
||||
- "${RTTYS_WEBUI_PORT:-443}:${RTTYS_WEBUI_PORT:-443}"
|
||||
- "${RTTYS_HTTP_PROXY_PORT:-10443}:${RTTYS_HTTP_PROXY_PORT:-10443}"
|
||||
- "${RTTYS_DEVICE_PORT:-5912}:${RTTYS_DEVICE_PORT:-5912}"
|
||||
|
||||
coturn:
|
||||
image: ${COTURN_IMAGE:-coturn/coturn:edge-alpine}
|
||||
container_name: glkvm_coturn
|
||||
restart: always
|
||||
network_mode: "host"
|
||||
environment:
|
||||
# Same semantics as above: prefer explicit value, else auto-detect
|
||||
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
|
||||
@@ -79,7 +77,4 @@ services:
|
||||
command: ["coturn"]
|
||||
volumes:
|
||||
- ./templates/turnserver.conf.template:/tpl/turnserver.conf.tmpl:ro
|
||||
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
ports:
|
||||
- "${TURN_PORT:-3478}:3478/tcp"
|
||||
- "${TURN_PORT:-3478}:3478/udp"
|
||||
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
@@ -351,6 +351,27 @@ func httpProxyRedirect(srv *RttyServer, c *gin.Context, group string) {
|
||||
location = fmt.Sprintf("https://%s%s?sid=%s", redirHost, cfg.AddrHttpProxy, sid)
|
||||
log.Info().Msgf("Using domain redirect: %s", location)
|
||||
} else {
|
||||
// ---- verify forwarded headers from reverse proxy ----
|
||||
rawHost := c.GetHeader("Host")
|
||||
xfHost := c.GetHeader("X-Forwarded-Host")
|
||||
xfProto := c.GetHeader("X-Forwarded-Proto")
|
||||
xfPort := c.GetHeader("X-Forwarded-Port")
|
||||
xRealIP := c.GetHeader("X-Real-IP")
|
||||
xFF := c.GetHeader("X-Forwarded-For")
|
||||
|
||||
log.Info().Msgf(
|
||||
"reverse-proxy info: method=%s uri=%s host=%q tls=%v remoteIP=%q",
|
||||
c.Request.Method,
|
||||
c.Request.URL.String(),
|
||||
rawHost,
|
||||
c.Request.TLS != nil,
|
||||
c.ClientIP(),
|
||||
)
|
||||
log.Info().Msgf(
|
||||
"reverse-proxy headers: Host=%q X-Forwarded-Host=%q X-Forwarded-Proto=%q X-Forwarded-Port=%q X-Real-IP=%q X-Forwarded-For=%q",
|
||||
rawHost, xfHost, xfProto, xfPort, xRealIP, xFF,
|
||||
)
|
||||
|
||||
// 0) scheme: follow reverse proxy
|
||||
scheme := ""
|
||||
if v := strings.TrimSpace(c.GetHeader("X-Forwarded-Proto")); v != "" {
|
||||
@@ -361,34 +382,17 @@ func httpProxyRedirect(srv *RttyServer, c *gin.Context, group string) {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
// 1) external port: prefer the one user actually accessed (Host or forwarded headers)
|
||||
// 1) external port: prefer the one user actually accessed
|
||||
port := ""
|
||||
|
||||
// Prefer port from Host
|
||||
if _, p, err := net.SplitHostPort(c.Request.Host); err == nil && p != "" {
|
||||
port = p
|
||||
}
|
||||
|
||||
// Fallback to forwarded headers
|
||||
if port == "" {
|
||||
if fp := strings.TrimSpace(c.GetHeader("X-Forwarded-Port")); fp != "" {
|
||||
port = strings.TrimSpace(strings.Split(fp, ",")[0])
|
||||
} else if fh := strings.TrimSpace(c.GetHeader("X-Forwarded-Host")); fh != "" {
|
||||
fh = strings.TrimSpace(strings.Split(fh, ",")[0])
|
||||
if _, p, err := net.SplitHostPort(fh); err == nil && p != "" {
|
||||
port = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) If still empty, fallback to cfg.AddrHttpProxy (which is a PORT, not a path)
|
||||
if port == "" && strings.TrimSpace(cfg.AddrHttpProxy) != "" {
|
||||
portTmp := strings.TrimSpace(cfg.AddrHttpProxy)
|
||||
// Common cases: ":10443", "0.0.0.0:10443", "[::]:10443"
|
||||
if _, p, err := net.SplitHostPort(portTmp); err == nil {
|
||||
if fp := strings.TrimSpace(c.GetHeader("X-Forwarded-Port")); fp != "" {
|
||||
port = strings.TrimSpace(strings.Split(fp, ",")[0])
|
||||
} else if fh := strings.TrimSpace(c.GetHeader("X-Forwarded-Host")); fh != "" {
|
||||
fh = strings.TrimSpace(strings.Split(fh, ",")[0])
|
||||
if _, p, err := net.SplitHostPort(fh); err == nil && p != "" {
|
||||
port = p
|
||||
}
|
||||
}
|
||||
log.Info().Msgf("port: %s", port)
|
||||
|
||||
// 3) Build host: in proxy mode redirect domain to be redirHost
|
||||
hostPort := redirHost
|
||||
|
||||
Reference in New Issue
Block a user