Bark 加密服务
背景
用 Bark 推送通知很方便,但如果内容包含密码、API 密钥这类敏感信息,直接明文发送总会有些不放心。
Bark 本身不支持加密,但在自动化脚本或集成的第三方服务中,实现这一加密步骤通常较为复杂。为了解决这个痛点,用 Golang 写了个加密转发器,只需要把消息发给它,它就会自动帮你加密好再发给 Bark,省心又安全。
工作原理
这个服务本质上是一个反向代理,它会拦截发往特定路径(如 /push-ciphertext)的请求,提取其中的 JSON 数据,使用预设的密钥(BARK_AES_KEY)和请求中提供的初始化向量(iv)对 body 字段进行 AES-CBC 加密,然后将加密后的数据包重新组装,发送给真正的 Bark 服务器。
代码实现:
39 collapsed lines
package main
import ( "bytes" "crypto/aes" "crypto/cipher" "encoding/base64" "encoding/json" "io" "log" "net/http" "net/url" "os" "strings")
func PKCS7Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(ciphertext, padtext...)}
func encrypt(plainText, key, iv []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } plainText = PKCS7Padding(plainText, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, iv) encrypted := make([]byte, len(plainText)) blockMode.CryptBlocks(encrypted, plainText) return encrypted, nil}
type RequestData struct { DeviceKey string `json:"device_key"` Iv string `json:"iv"`}
func handler(w http.ResponseWriter, r *http.Request, aesKey []byte, barkDomain string) { defer func(Body io.ReadCloser) { _ = Body.Close() }(r.Body)
if r.Method != http.MethodPost { http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) return }
body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Failed to read request body", http.StatusInternalServerError) return }
var data RequestData if err := json.Unmarshal(body, &data); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return64 collapsed lines
} deviceKey := data.DeviceKey iv := data.Iv
encrypted, err := encrypt(body, aesKey, []byte(iv)) if err != nil { http.Error(w, "Encryption failed", http.StatusInternalServerError) return } ciphertextBase64 := base64.StdEncoding.EncodeToString(encrypted)
if len(barkDomain) <= 0 { data := map[string]interface{}{ "ciphertext": ciphertextBase64, "iv": iv, }
err := json.NewEncoder(w).Encode(data) if err != nil { http.Error(w, "Error encoding JSON response", http.StatusInternalServerError) return } return }
formData := url.Values{} formData.Set("ciphertext", ciphertextBase64) formData.Set("iv", iv)
targetURL := barkDomain + "/" + deviceKey resp, err := http.Post(targetURL, "application/x-www-form-urlencoded", strings.NewReader(formData.Encode())) if err != nil { http.Error(w, "Failed to forward request to Bark API", http.StatusBadGateway) return } defer func(Body io.ReadCloser) { _ = Body.Close() }(resp.Body)
log.Printf("Forwarded request for deviceKey ending in ...%s, status: %d", deviceKey[len(deviceKey)-4:], resp.StatusCode)
w.WriteHeader(resp.StatusCode) _, _ = io.Copy(w, resp.Body)}
func main() { aesKeyStr := os.Getenv("BARK_AES_KEY") if len(aesKeyStr) != 16 { log.Fatal("Fatal: BARK_AES_KEY environment variable not set or not 16 characters long.") }
barkDomain := os.Getenv("BARK_DOMAIN")
aesKeyBytes := []byte(aesKeyStr)
http.HandleFunc("/push-ciphertext", func(w http.ResponseWriter, r *http.Request) { handler(w, r, aesKeyBytes, barkDomain) })
log.Println("Starting encryption service on :9090") if err := http.ListenAndServe(":9090", nil); err != nil { log.Fatalf("Failed to start server: %v", err) }}反向代理加密服务
bark.example.com { encode zstd gzip
# (可选) 为你的服务增加一层基础认证,防止被滥用 basic_auth { user xxxx }
# 处理加密请求的路由, 所有发往 /push-ciphertext 的请求,都转发给加密服务 handle /push-ciphertext* { reverse_proxy bark-encrypt:9090 }
# 处理普通未加密请求的路由 (可选), 这样你的域名也能兼容普通的 Bark 请求 handle { reverse_proxy bark-server:8080 }}使用
配置环境变量
BARK_AES_KEY=1234567890123456 # 自行替换BARK_DOMAIN=bark.example.com路径 push 改为 push-ciphertext 参数加上iv 则可加密发送
-H "Content-Type: application/json" \ -d '{ "title": "test", "device_key": "xxx", "sound": "newsflash", "iv": "1234567890123456" }'注:这里假设你启用了 basic_auth,所以 URL 中包含了 user:password@。如果没启用,可以去掉。
参考
感谢 Bark 开源,也欢迎大家试用我的小工具!