add cloudflare and documentation
This commit is contained in:
78
README.md
Normal file
78
README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
SafelineAPI
|
||||
=================
|
||||
|
||||
Small CLI to request and upsert TLS certificates (DNS-01) with multiple DNS provider backends. This repo now includes Cloudflare DNS provider support via the lego provider.
|
||||
|
||||
Prerequisites
|
||||
- Go toolchain (only required to build from source)
|
||||
- A SafeLine API token (set in `config.json`)
|
||||
- Cloudflare API token (scoped) or Global API key + account email
|
||||
|
||||
Quick start
|
||||
|
||||
1. Copy `config.example.json` to `config.json` and fill in your values.
|
||||
|
||||
2. Build (optional):
|
||||
```powershell
|
||||
cd C:\Users\samge\coding\SafelineAPI-1
|
||||
go build -o safelineApi.exe ./cmd/safelineApi
|
||||
```
|
||||
|
||||
3. Run:
|
||||
```powershell
|
||||
# using built binary
|
||||
.\safelineApi.exe
|
||||
|
||||
# or directly with go
|
||||
go run ./cmd/safelineApi -- -t "<SafeLineApiToken>" -D "Cloudflare" -e "you@example.com"
|
||||
```
|
||||
|
||||
Configuration notes
|
||||
- The main configuration file is `config.json` in the project root.
|
||||
- To use Cloudflare for DNS-01, set `ApplyCert.DNSProviderConfig.DNSProvider` to `Cloudflare` and set `ApplyCert.DNSProviderConfig.Cloudflare.APIToken` to a scoped API token with `Zone:DNS:Edit` permission.
|
||||
- If you must use the global API key, set `Cloudflare.APIKey` and `Cloudflare.Email` instead (less secure).
|
||||
|
||||
Docs
|
||||
- See `docs/CONFIGURATION.md` for detailed configuration and troubleshooting steps.
|
||||
- See `docs/cloudflare.md` for a short Cloudflare-specific guide.
|
||||
|
||||
Security
|
||||
- Prefer scoped API tokens over global keys.
|
||||
- Keep `config.json` out of source control; use environment variables or secret management in production.
|
||||
|
||||
Need anything else?
|
||||
- I can add a small PowerShell script to run the app with environment variable support or create a release artifact (Windows exe) if you'd like.
|
||||
|
||||
Linux usage (systemd)
|
||||
|
||||
Most users run this on a Linux host. Below are recommended steps to install and run SafelineAPI as a service.
|
||||
|
||||
1. Build on the target machine (or cross-compile):
|
||||
```bash
|
||||
cd /opt
|
||||
git clone <your-repo-url> safelineapi
|
||||
cd safelineapi
|
||||
go build -o safelineApi ./cmd/safelineApi
|
||||
```
|
||||
|
||||
2. Place your `config.json` in `/opt/safelineapi/config.json` (or edit accordingly). You can use `config.example.json` as a starting point.
|
||||
|
||||
3. Install systemd unit (example unit available at `contrib/safelineapi.service`):
|
||||
```bash
|
||||
sudo cp contrib/safelineapi.service /etc/systemd/system/
|
||||
sudo useradd --system --no-create-home safeline || true
|
||||
sudo chown -R safeline:safeline /opt/safelineapi
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now safelineapi.service
|
||||
sudo journalctl -u safelineapi.service -f
|
||||
```
|
||||
|
||||
4. Alternatively run with the provided helper script (uses environment variables or builds if missing):
|
||||
```bash
|
||||
chmod +x scripts/run.sh
|
||||
SAFELINE_API_TOKEN="..." DNS_PROVIDER=Cloudflare CONTACT_EMAIL="you@example.com" ./scripts/run.sh
|
||||
```
|
||||
|
||||
Notes
|
||||
- The `contrib/safelineapi.service` unit assumes files live in `/opt/safelineapi` and the binary is `/opt/safelineapi/safelineApi`. Adjust paths to fit your setup.
|
||||
- For production, run the service as a dedicated unprivileged user and keep `config.json` permissions restricted.
|
||||
11
cmd/safelineApi/config_test.go
Normal file
11
cmd/safelineApi/config_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"SafelineAPI/internal/app/config"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
var c config.Config
|
||||
c.Default()
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package main
|
||||
//go:build test
|
||||
// +build test
|
||||
|
||||
import "SafelineAPI/internal/app/config"
|
||||
package main_test
|
||||
|
||||
func main() {
|
||||
import (
|
||||
"testing"
|
||||
"SafelineAPI/internal/app/config"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
var c config.Config
|
||||
c.Default()
|
||||
}
|
||||
|
||||
22
config.example.json
Normal file
22
config.example.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"SafeLine": {
|
||||
"Host": {
|
||||
"HostName": "127.0.0.1",
|
||||
"Port": "1443"
|
||||
},
|
||||
"ApiToken": "<YOUR_SAFELINE_API_TOKEN>"
|
||||
},
|
||||
"ApplyCert": {
|
||||
"Days": 30,
|
||||
"Email": "you@example.com",
|
||||
"SavePath": "./ssl",
|
||||
"DNSProviderConfig": {
|
||||
"DNSProvider": "Cloudflare",
|
||||
"Cloudflare": {
|
||||
"APIToken": "<YOUR_CLOUDFLARE_API_TOKEN>",
|
||||
"APIKey": "<GLOBAL_API_KEY_OPTIONAL>",
|
||||
"Email": "you@cloudflare-account.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,11 @@
|
||||
"RainYun": {
|
||||
"ApiKey": "xxx"
|
||||
},
|
||||
"Cloudflare": {
|
||||
"APIToken": "xxx",
|
||||
"APIKey": "xxx (optional)",
|
||||
"Email": "your-email@example.com (optional)"
|
||||
},
|
||||
"Dode": {
|
||||
"Token": "xxx"
|
||||
}
|
||||
|
||||
12
contrib/safelineapi.env
Normal file
12
contrib/safelineapi.env
Normal file
@@ -0,0 +1,12 @@
|
||||
## Example environment file for systemd (placed at /etc/default/safelineapi)
|
||||
## Values here are sourced by the systemd unit via EnvironmentFile
|
||||
# Path to config.json
|
||||
CONFIG_PATH=/opt/safelineapi/config.json
|
||||
|
||||
# Optional runtime overrides
|
||||
#SAFELINE_API_TOKEN=
|
||||
#DNS_PROVIDER=Cloudflare
|
||||
#CONTACT_EMAIL=you@example.com
|
||||
|
||||
# Path to binary (if you installed somewhere else)
|
||||
#BIN=/opt/safelineapi/safelineApi
|
||||
16
contrib/safelineapi.service
Normal file
16
contrib/safelineapi.service
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=SafelineAPI Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/safelineapi
|
||||
ExecStart=/opt/safelineapi/safelineApi
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
EnvironmentFile=/etc/default/safelineapi
|
||||
User=safeline
|
||||
Group=safeline
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
86
docs/CONFIGURATION.md
Normal file
86
docs/CONFIGURATION.md
Normal file
@@ -0,0 +1,86 @@
|
||||
**SafelineAPI Configuration and Cloudflare DNS Guide**
|
||||
|
||||
This guide explains how to configure SafelineAPI to use Cloudflare for DNS-01 challenges and how to run the program.
|
||||
|
||||
**Quick Start**
|
||||
- **Prerequisites:** Go is only required for building from source. If you prefer, use the built binary produced by `go build`.
|
||||
- **Minimal steps:** create a Cloudflare API token, update `config.json`, and run the program.
|
||||
|
||||
**Config File Location**
|
||||
- The primary configuration file is `config.json` in the repository root. See `docs/cloudflare.md` for a short Cloudflare-specific note.
|
||||
|
||||
**Important fields**
|
||||
- **SafeLine.ApiToken:** API token used to connect to the SafeLine API.
|
||||
- **ApplyCert.Email:** Contact email used when requesting certificates.
|
||||
- **ApplyCert.DNSProviderConfig.DNSProvider:** Set this to `Cloudflare` to use Cloudflare.
|
||||
- **ApplyCert.DNSProviderConfig.Cloudflare.APIToken:** Recommended — a scoped Cloudflare API Token with `Zone:DNS:Edit` on your zone(s).
|
||||
- **ApplyCert.DNSProviderConfig.Cloudflare.APIKey** and **Email:** Optional — use only if you must authenticate with the Global API key.
|
||||
|
||||
**Example `config.json` snippet**
|
||||
|
||||
```json
|
||||
{
|
||||
"SafeLine": {
|
||||
"Host": { "HostName": "192.168.1.4", "Port": "1443" },
|
||||
"ApiToken": "<your-safeline-api-token>"
|
||||
},
|
||||
"ApplyCert": {
|
||||
"Days": 30,
|
||||
"Email": "you@example.com",
|
||||
"SavePath": "C:/path/to/ssl",
|
||||
"DNSProviderConfig": {
|
||||
"DNSProvider": "Cloudflare",
|
||||
"Cloudflare": {
|
||||
"APIToken": "<your-cloudflare-scoped-token>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Create a Cloudflare API Token**
|
||||
1. Log into the Cloudflare dashboard and open **My Profile → API Tokens**.
|
||||
2. Click **Create Token** and choose the **Edit zone DNS** template or set custom permissions:
|
||||
- Zone:Zone:Read
|
||||
- Zone:DNS:Edit
|
||||
3. Scope the token to the specific zone(s) you need and create the token.
|
||||
4. Put the token value in `ApplyCert.DNSProviderConfig.Cloudflare.APIToken`.
|
||||
|
||||
**Run commands**
|
||||
- Build the binary (optional):
|
||||
```powershell
|
||||
cd C:\Users\samge\coding\SafelineAPI-1
|
||||
go build -o safelineApi.exe ./cmd/safelineApi
|
||||
```
|
||||
- Run with the built binary:
|
||||
```powershell
|
||||
.\safelineApi.exe
|
||||
```
|
||||
- Or run directly with Go:
|
||||
```powershell
|
||||
go run ./cmd/safelineApi -- -t "<SafeLineApiToken>" -D "Cloudflare" -e "you@example.com"
|
||||
```
|
||||
|
||||
Notes on flags: the project reads flags and `config.json`. If a flag is present it will be used for that run.
|
||||
|
||||
**Troubleshooting**
|
||||
- Warning about missing values: If you see warnings like `未设置 DNS服务提供商`, set `ApplyCert.DNSProviderConfig.DNSProvider` or pass `-D` on the command line.
|
||||
- Dependency/download issues: If `go build` stalls on module downloads, try setting a proxy:
|
||||
```powershell
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go clean -modcache
|
||||
go mod tidy
|
||||
go build -v ./...
|
||||
```
|
||||
- Cloudflare auth mismatch: Use `APIToken` (recommended). If using `APIKey` (global key), also provide the account `Email`.
|
||||
|
||||
**Security recommendations**
|
||||
- Prefer scoped API tokens over the global API key.
|
||||
- Store secrets outside source control. Use environment variables or an external secret store in production.
|
||||
- Limit token scope to required zones.
|
||||
|
||||
**Files added/edited**
|
||||
- Documentation: [docs/cloudflare.md](docs/cloudflare.md)
|
||||
- Configuration example: `config.json` at project root
|
||||
|
||||
If you'd like, I can also add a short `README.md` or copy a minimal example `config.example.json` to the repo root for easy onboarding. Which would you prefer next?
|
||||
42
docs/cloudflare.md
Normal file
42
docs/cloudflare.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Cloudflare DNS provider
|
||||
|
||||
This project supports using Cloudflare for the DNS-01 challenge via the lego DNS provider.
|
||||
|
||||
Supported config fields (in `config.json` under `ApplyCert.DNSProviderConfig`):
|
||||
|
||||
- `DNSProvider`: set to `Cloudflare`
|
||||
- `Cloudflare.APIToken`: Recommended — create a scoped API Token in Cloudflare (Zone.DNS edit).
|
||||
- `Cloudflare.APIKey`: Optional — Global API Key (not recommended when token available).
|
||||
- `Cloudflare.Email`: Optional — account email (used with Global API Key if needed).
|
||||
|
||||
Example `config.json` snippet:
|
||||
|
||||
{
|
||||
"ApplyCert": {
|
||||
"DNSProviderConfig": {
|
||||
"DNSProvider": "Cloudflare",
|
||||
"Cloudflare": {
|
||||
"APIToken": "your-cloudflare-api-token",
|
||||
"APIKey": "optional-global-api-key",
|
||||
"Email": "you@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
How to create a Cloudflare API token
|
||||
|
||||
1. Log into the Cloudflare dashboard.
|
||||
2. Visit "My Profile" → "API Tokens" → "Create Token".
|
||||
3. Use the "Edit zone DNS" template or create a custom token with the following permissions scoped to your zone(s):
|
||||
- Zone:Zone:Read
|
||||
- Zone:DNS:Edit
|
||||
4. Save the token and put it into `Cloudflare.APIToken`.
|
||||
|
||||
Notes and links
|
||||
|
||||
- The integration uses the lego v4 Cloudflare provider.
|
||||
- Cloudflare API docs: https://developers.cloudflare.com/api/
|
||||
- Certbot cloudflare plugin docs (useful for end users): https://certbot-dns-cloudflare.readthedocs.io/en/stable/
|
||||
|
||||
If you want, I can run `go build` and fix any compile errors from these changes, or adjust field names to match the exact lego provider struct names on your machine. Would you like me to build and test now?
|
||||
3
go.mod
3
go.mod
@@ -10,6 +10,8 @@ require (
|
||||
require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -29,6 +31,7 @@ require (
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
||||
14
go.sum
14
go.sum
@@ -10,6 +10,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g=
|
||||
github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -25,6 +27,8 @@ github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -60,6 +64,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -72,7 +78,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk=
|
||||
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
|
||||
@@ -84,6 +89,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -197,6 +204,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -232,8 +241,9 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -7,6 +7,7 @@ type DNSProviderConfig struct {
|
||||
HuaweiCloud `json:"HuaweiCloud,omitempty"`
|
||||
WestCN `json:"WestCN,omitempty"`
|
||||
RainYun `json:"RainYun,omitempty"`
|
||||
Cloudflare `json:"Cloudflare,omitempty"`
|
||||
Dode `json:"Dode,omitempty"`
|
||||
}
|
||||
|
||||
@@ -40,3 +41,9 @@ type RainYun struct {
|
||||
type Dode struct {
|
||||
Token string `json:"Token,omitempty"`
|
||||
}
|
||||
|
||||
type Cloudflare struct {
|
||||
APIToken string `json:"APIToken,omitempty"`
|
||||
APIKey string `json:"APIKey,omitempty"`
|
||||
Email string `json:"Email,omitempty"`
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dode"
|
||||
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||
@@ -52,6 +53,24 @@ func (rain RainYun) Provider() (challenge.Provider, error) {
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (cloud Cloudflare) Provider() (challenge.Provider, error) {
|
||||
cfg := cloudflare.NewDefaultConfig()
|
||||
// lego's cloudflare.Config uses AuthToken / AuthKey / AuthEmail / ZoneToken
|
||||
if cloud.APIToken != "" {
|
||||
cfg.AuthToken = cloud.APIToken
|
||||
}
|
||||
if cloud.APIKey != "" {
|
||||
cfg.AuthKey = cloud.APIKey
|
||||
}
|
||||
if cloud.Email != "" {
|
||||
cfg.AuthEmail = cloud.Email
|
||||
}
|
||||
// support ZoneToken if provided in future (keep empty if not set)
|
||||
// if you add ZoneToken to the config struct, map it here: cfg.ZoneToken = cloud.ZoneToken
|
||||
p, err := cloudflare.NewDNSProviderConfig(cfg)
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (Dode Dode) Provider() (challenge.Provider, error) {
|
||||
cfg := dode.NewDefaultConfig()
|
||||
cfg.Token = Dode.Token
|
||||
|
||||
@@ -17,6 +17,8 @@ func ChooseDNSProvider(config config.DNSProviderConfig) (challenge.Provider, err
|
||||
return config.WestCN.Provider()
|
||||
} else if config.DNSProvider == "RainYun" {
|
||||
return config.RainYun.Provider()
|
||||
} else if config.DNSProvider == "Cloudflare" {
|
||||
return config.Cloudflare.Provider()
|
||||
} else if config.DNSProvider == "Dode" {
|
||||
return config.Dode.Provider()
|
||||
}
|
||||
|
||||
BIN
safelineApi.exe
Normal file
BIN
safelineApi.exe
Normal file
Binary file not shown.
73
scripts/install.sh
Normal file
73
scripts/install.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Install script for SafelineAPI (systemd)
|
||||
# Usage: sudo ./scripts/install.sh [install_dir]
|
||||
# Default install_dir: /opt/safelineapi
|
||||
|
||||
PREFIX=${1:-/opt/safelineapi}
|
||||
SERVICE_NAME=safelineapi
|
||||
SYSTEMD_UNIT=/etc/systemd/system/${SERVICE_NAME}.service
|
||||
ENV_FILE=/etc/default/${SERVICE_NAME}
|
||||
USER_NAME=safeline
|
||||
|
||||
echo "Install SafelineAPI to ${PREFIX}"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run as root (or via sudo)" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$PREFIX"
|
||||
|
||||
# Build binary if not present in repo
|
||||
if [ ! -f "$PREFIX/safelineApi" ]; then
|
||||
echo "Building safelineApi..."
|
||||
# build from repo root (assumes script run from repo root)
|
||||
if [ -f ./cmd/safelineApi/main.go ]; then
|
||||
GOOS=${GOOS:-linux} GOARCH=${GOARCH:-amd64} go build -o "$PREFIX/safelineApi" ./cmd/safelineApi
|
||||
else
|
||||
echo "Cannot find source to build. Place binary at ${PREFIX}/safelineApi or run from repo root." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copy config.example.json to config.json if no config exists
|
||||
if [ -f "$PREFIX/config.json" ]; then
|
||||
echo "Found existing config.json in ${PREFIX}, leaving in place."
|
||||
else
|
||||
if [ -f ./config.example.json ]; then
|
||||
cp ./config.example.json "$PREFIX/config.json"
|
||||
echo "Copied example config to ${PREFIX}/config.json — edit and add secrets before starting service."
|
||||
else
|
||||
echo "No config.example.json found in repo root. Create ${PREFIX}/config.json before starting service." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# copy service unit
|
||||
cp contrib/safelineapi.service "$SYSTEMD_UNIT"
|
||||
chmod 644 "$SYSTEMD_UNIT"
|
||||
|
||||
# copy example env file if not exists
|
||||
if [ -f /etc/default/${SERVICE_NAME} ]; then
|
||||
echo "/etc/default/${SERVICE_NAME} already exists — leaving in place."
|
||||
else
|
||||
cp contrib/safelineapi.env "$ENV_FILE"
|
||||
chmod 644 "$ENV_FILE"
|
||||
echo "Wrote ${ENV_FILE}. Edit it to set CONFIG_PATH and optional runtime vars."
|
||||
fi
|
||||
|
||||
# create system user if missing
|
||||
if id -u "$USER_NAME" >/dev/null 2>&1; then
|
||||
echo "User $USER_NAME exists"
|
||||
else
|
||||
useradd --system --no-create-home --shell /usr/sbin/nologin "$USER_NAME"
|
||||
echo "Created system user $USER_NAME"
|
||||
fi
|
||||
|
||||
# chown files
|
||||
chown -R ${USER_NAME}:${USER_NAME} "$PREFIX"
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ${SERVICE_NAME}.service
|
||||
|
||||
echo "Installation complete. Check status with: systemctl status ${SERVICE_NAME}.service"
|
||||
30
scripts/run.sh
Normal file
30
scripts/run.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Simple runner: builds binary if missing then runs it.
|
||||
# Environment variables supported:
|
||||
# - BIN (path to binary, default ./safelineApi)
|
||||
# - CONFIG (path to config.json, default ./config.json)
|
||||
# - SAFELINE_API_TOKEN, DNS_PROVIDER, CONTACT_EMAIL (optional flags passed to binary)
|
||||
|
||||
BIN=${BIN:-./safelineApi}
|
||||
CONFIG=${CONFIG:-./config.json}
|
||||
|
||||
if [ ! -f "$BIN" ]; then
|
||||
echo "Binary not found, building..."
|
||||
GOOS=${GOOS:-linux} GOARCH=${GOARCH:-amd64} go build -o "$BIN" ./cmd/safelineApi
|
||||
fi
|
||||
|
||||
ARGS=()
|
||||
if [ -n "${SAFELINE_API_TOKEN:-}" ]; then
|
||||
ARGS+=("-t" "$SAFELINE_API_TOKEN")
|
||||
fi
|
||||
if [ -n "${DNS_PROVIDER:-}" ]; then
|
||||
ARGS+=("-D" "$DNS_PROVIDER")
|
||||
fi
|
||||
if [ -n "${CONTACT_EMAIL:-}" ]; then
|
||||
ARGS+=("-e" "$CONTACT_EMAIL")
|
||||
fi
|
||||
|
||||
echo "Starting SafelineAPI ($BIN) with config: $CONFIG"
|
||||
"$BIN" "${ARGS[@]}"
|
||||
Reference in New Issue
Block a user