Enable TLS by default
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,6 +20,7 @@ go.work
|
|||||||
# Build artifacts
|
# Build artifacts
|
||||||
/middleware
|
/middleware
|
||||||
/dist/
|
/dist/
|
||||||
|
/certs/
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
.dockerignore
|
.dockerignore
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ EXPOSE 8080
|
|||||||
|
|
||||||
# Health check
|
# Health check
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
CMD wget --no-verbose --tries=1 --spider https://localhost:8080/health || exit 1
|
||||||
|
|
||||||
# Run the binary
|
# Run the binary
|
||||||
CMD ["./middleware"]
|
CMD ["./middleware"]
|
||||||
55
README.md
55
README.md
@@ -31,17 +31,25 @@ A lightweight Go service that acts as a middleware between Slack webhooks and nt
|
|||||||
3. **Configure Slack**:
|
3. **Configure Slack**:
|
||||||
- Go to Slack Integrations → Incoming Webhooks
|
- Go to Slack Integrations → Incoming Webhooks
|
||||||
- Add new webhook
|
- Add new webhook
|
||||||
- Webhook URL: `http://your-server-ip:8080/your-topic-name`
|
- Webhook URL: `https://your-server-ip:8080/your-topic-name`
|
||||||
|
|
||||||
4. **Test the service**:
|
4. **Test the service**:
|
||||||
```bash
|
```bash
|
||||||
# Test webhook
|
# Test webhook with HTTP (if TLS is disabled)
|
||||||
curl -X POST http://localhost:8080/test-topic \
|
curl -X POST https://localhost:8080/test-topic \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{"text": "Test alert from Slack to ntfy"}'
|
-d '{"text": "Test alert from Slack to ntfy"}'
|
||||||
|
|
||||||
# Check health
|
# Test webhook with HTTPS (if TLS is enabled, and if using self-signed certs, add -k or --insecure)
|
||||||
|
curl -k -X POST https://localhost:8080/test-topic -k \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"text": "Test alert from Slack to ntfy (TLS)"}'
|
||||||
|
|
||||||
|
# Check health with HTTP (if TLS is disabled)
|
||||||
curl http://localhost:8080/health
|
curl http://localhost:8080/health
|
||||||
|
|
||||||
|
# Check health with HTTPS (if TLS is enabled, and if using self-signed certs, add -k or --insecure)
|
||||||
|
curl https://localhost:8080/health -k
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -54,6 +62,43 @@ A lightweight Go service that acts as a middleware between Slack webhooks and nt
|
|||||||
| `NTFY_PASSWORD` | `""` | Password for ntfy basic authentication |
|
| `NTFY_PASSWORD` | `""` | Password for ntfy basic authentication |
|
||||||
| `BIND_ADDRESS` | `0.0.0.0` | Interface to bind to |
|
| `BIND_ADDRESS` | `0.0.0.0` | Interface to bind to |
|
||||||
| `BIND_PORT` | `8080` | Port to listen on |
|
| `BIND_PORT` | `8080` | Port to listen on |
|
||||||
|
| `TLS_CERT_FILE` | `""` | Path to TLS certificate file (e.g., `/app/certs/server.crt`) |
|
||||||
|
| `TLS_KEY_FILE` | `""` | Path to TLS private key file (e.g., `/app/certs/server.key`) |
|
||||||
|
|
||||||
|
### Enabling TLS
|
||||||
|
|
||||||
|
TLS is enabled by default. If `TLS_CERT_FILE` and `TLS_KEY_FILE` environment variables are not set, a self-signed certificate and key will be automatically generated on startup.
|
||||||
|
|
||||||
|
**To provide your own certificate and key files (optional)**:
|
||||||
|
|
||||||
|
1. **Create a `certs` directory** in the root of your project:
|
||||||
|
```bash
|
||||||
|
mkdir certs
|
||||||
|
# Copy your server.crt and server.key into the certs/ directory
|
||||||
|
```
|
||||||
|
2. **Uncomment and set `TLS_CERT_FILE` and `TLS_KEY_FILE`** in your `docker-compose.yml` (e.g., pointing to `/app/certs/server.crt` and `/app/certs/server.key`):
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
# ... existing environment variables ...
|
||||||
|
- TLS_CERT_FILE=/app/certs/server.crt
|
||||||
|
- TLS_KEY_FILE=/app/certs/server.key
|
||||||
|
```
|
||||||
|
3. Ensure the `volumes` section is uncommented and correctly mounts the `certs` directory:
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./certs:/app/certs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** Regardless of whether you use generated or custom certificates:
|
||||||
|
|
||||||
|
* **Update your Slack webhook URL** to use `https`.
|
||||||
|
* **Restart your Docker service**:
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
* When testing with `curl` against a self-signed certificate, you may need to add the `-k` or `--insecure` flag to bypass certificate validation.
|
||||||
|
* **Exposing on standard HTTPS port (443) in production**: While the service runs on port 8080 internally, it's common to map it to port 443 externally (e.g., `- "443:8080"` in `docker-compose.yml`) or use a reverse proxy to handle TLS termination on port 443 and forward traffic to the container's port 8080.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ services:
|
|||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
environment:
|
environment:
|
||||||
# Replace with your ntfy server URL (without topic path)
|
# Replace with your ntfy server URL (without topic path)
|
||||||
- NTFY_BASE_URL=https://ntfy.dangerzone.dev
|
- NTFY_BASE_URL=https://your-ntfy-server.com
|
||||||
|
|
||||||
# For token-based authentication, uncomment and replace with your ntfy Bearer token (e.g., tk_xxxx)
|
# For token-based authentication, uncomment and replace with your ntfy Bearer token (e.g., tk_xxxx)
|
||||||
- NTFY_TOKEN=tk_your_bearer_token_here
|
# - NTFY_TOKEN=tk_your_bearer_token_here
|
||||||
|
|
||||||
# For username/password authentication, uncomment and replace with your ntfy credentials
|
# For username/password authentication, uncomment and replace with your ntfy credentials
|
||||||
# - NTFY_USERNAME=your_username
|
# - NTFY_USERNAME=your_username
|
||||||
@@ -20,7 +20,15 @@ services:
|
|||||||
# Bind configuration
|
# Bind configuration
|
||||||
- BIND_ADDRESS=0.0.0.0
|
- BIND_ADDRESS=0.0.0.0
|
||||||
- BIND_PORT=8080
|
- BIND_PORT=8080
|
||||||
|
# TLS Configuration
|
||||||
|
# Uncomment and replace with your certificate and key file paths (relative to the container)
|
||||||
|
# - TLS_CERT_FILE=/app/certs/server.crt
|
||||||
|
# - TLS_KEY_FILE=/app/certs/server.key
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
# Mount the local 'certs' directory into the container to provide TLS certificates
|
||||||
|
# Ensure you have server.crt and server.key in a 'certs' directory next to docker-compose.yml
|
||||||
|
- ./certs:/app/certs
|
||||||
|
|
||||||
# Optional: Resource limits
|
# Optional: Resource limits
|
||||||
deploy:
|
deploy:
|
||||||
@@ -34,7 +42,7 @@ services:
|
|||||||
|
|
||||||
# Health check using the built-in endpoint
|
# Health check using the built-in endpoint
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:8080/health"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|||||||
130
main.go
130
main.go
@@ -2,18 +2,25 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SlackWebhookPayload represents the incoming Slack webhook format
|
|
||||||
type SlackWebhookPayload struct {
|
type SlackWebhookPayload struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
@@ -27,8 +34,6 @@ type SlackWebhookPayload struct {
|
|||||||
Timestamp int64 `json:"ts,omitempty"`
|
Timestamp int64 `json:"ts,omitempty"`
|
||||||
} `json:"attachments,omitempty"`
|
} `json:"attachments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config holds the middleware configuration
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
NtfyBaseURL string
|
NtfyBaseURL string
|
||||||
NtfyToken string
|
NtfyToken string
|
||||||
@@ -36,9 +41,9 @@ type Config struct {
|
|||||||
NtfyPassword string
|
NtfyPassword string
|
||||||
BindAddress string
|
BindAddress string
|
||||||
BindPort string
|
BindPort string
|
||||||
|
TLSCertFile string
|
||||||
|
TLSKeyFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Middleware handles the webhook forwarding
|
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
config Config
|
config Config
|
||||||
client *http.Client
|
client *http.Client
|
||||||
@@ -53,14 +58,11 @@ func NewMiddleware(config Config) *Middleware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractMessage extracts the alert message from various Slack webhook formats
|
|
||||||
func (m *Middleware) extractMessage(payload *SlackWebhookPayload) string {
|
func (m *Middleware) extractMessage(payload *SlackWebhookPayload) string {
|
||||||
// Primary message text
|
|
||||||
if payload.Text != "" {
|
if payload.Text != "" {
|
||||||
return payload.Text
|
return payload.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check attachments for additional content
|
|
||||||
if len(payload.Attachments) > 0 {
|
if len(payload.Attachments) > 0 {
|
||||||
var parts []string
|
var parts []string
|
||||||
|
|
||||||
@@ -81,7 +83,6 @@ func (m *Middleware) extractMessage(payload *SlackWebhookPayload) string {
|
|||||||
return "Generic Message"
|
return "Generic Message"
|
||||||
}
|
}
|
||||||
|
|
||||||
// forwardToNtfy sends the message to the ntfy server
|
|
||||||
func (m *Middleware) forwardToNtfy(topic, message string) error {
|
func (m *Middleware) forwardToNtfy(topic, message string) error {
|
||||||
ntfyURL := fmt.Sprintf("%s/%s", strings.TrimRight(m.config.NtfyBaseURL, "/"), topic)
|
ntfyURL := fmt.Sprintf("%s/%s", strings.TrimRight(m.config.NtfyBaseURL, "/"), topic)
|
||||||
|
|
||||||
@@ -118,28 +119,24 @@ func (m *Middleware) forwardToNtfy(topic, message string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// webhookHandler handles incoming Slack webhook requests
|
|
||||||
func (m *Middleware) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
func (m *Middleware) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract topic from URL path
|
|
||||||
topic := strings.TrimPrefix(r.URL.Path, "/")
|
topic := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
if topic == "" {
|
if topic == "" {
|
||||||
http.Error(w, "Topic is required in URL path", http.StatusBadRequest)
|
http.Error(w, "Topic is required in URL path", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean topic (remove any path traversal attempts)
|
|
||||||
topic = path.Clean(topic)
|
topic = path.Clean(topic)
|
||||||
if strings.Contains(topic, "..") {
|
if strings.Contains(topic, "..") {
|
||||||
http.Error(w, "Invalid topic name", http.StatusBadRequest)
|
http.Error(w, "Invalid topic name", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read request body
|
|
||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to read request body: %v", err)
|
log.Printf("Failed to read request body: %v", err)
|
||||||
@@ -149,13 +146,11 @@ func (m *Middleware) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var message string
|
var message string
|
||||||
|
|
||||||
// Try to parse as JSON (Slack webhook format)
|
|
||||||
var payload SlackWebhookPayload
|
var payload SlackWebhookPayload
|
||||||
if err := json.Unmarshal(body, &payload); err == nil {
|
if err := json.Unmarshal(body, &payload); err == nil {
|
||||||
message = m.extractMessage(&payload)
|
message = m.extractMessage(&payload)
|
||||||
log.Printf("Received Slack webhook for topic '%s': %s", topic, message)
|
log.Printf("Received Slack webhook for topic '%s': %s", topic, message)
|
||||||
} else {
|
} else {
|
||||||
// Fallback to raw text
|
|
||||||
message = string(body)
|
message = string(body)
|
||||||
if message == "" {
|
if message == "" {
|
||||||
message = "Slack to ntfy Alert (no content)"
|
message = "Slack to ntfy Alert (no content)"
|
||||||
@@ -163,14 +158,12 @@ func (m *Middleware) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
log.Printf("Received raw webhook for topic '%s': %s", topic, message)
|
log.Printf("Received raw webhook for topic '%s': %s", topic, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward to ntfy
|
|
||||||
if err := m.forwardToNtfy(topic, message); err != nil {
|
if err := m.forwardToNtfy(topic, message); err != nil {
|
||||||
log.Printf("Failed to forward to ntfy: %v", err)
|
log.Printf("Failed to forward to ntfy: %v", err)
|
||||||
http.Error(w, "Failed to forward alert", http.StatusInternalServerError)
|
http.Error(w, "Failed to forward alert", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return success response
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
response := map[string]string{
|
response := map[string]string{
|
||||||
"status": "success",
|
"status": "success",
|
||||||
@@ -180,7 +173,6 @@ func (m *Middleware) webhookHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// healthHandler provides a health check endpoint
|
|
||||||
func (m *Middleware) healthHandler(w http.ResponseWriter, r *http.Request) {
|
func (m *Middleware) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
response := map[string]interface{}{
|
response := map[string]interface{}{
|
||||||
@@ -200,6 +192,8 @@ func loadConfig() Config {
|
|||||||
NtfyPassword: getEnv("NTFY_PASSWORD", ""),
|
NtfyPassword: getEnv("NTFY_PASSWORD", ""),
|
||||||
BindAddress: getEnv("BIND_ADDRESS", "0.0.0.0"),
|
BindAddress: getEnv("BIND_ADDRESS", "0.0.0.0"),
|
||||||
BindPort: getEnv("BIND_PORT", "8080"),
|
BindPort: getEnv("BIND_PORT", "8080"),
|
||||||
|
TLSCertFile: getEnv("TLS_CERT_FILE", ""),
|
||||||
|
TLSKeyFile: getEnv("TLS_KEY_FILE", ""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,15 +204,81 @@ func getEnv(key, defaultValue string) string {
|
|||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateSelfSignedCert(certFile, keyFile string) error {
|
||||||
|
log.Printf("Generating self-signed TLS certificate: %s and %s", certFile, keyFile)
|
||||||
|
|
||||||
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to generate private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notBefore := time.Now()
|
||||||
|
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to generate serial number: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"Slack to ntfy Middleware"},
|
||||||
|
CommonName: "localhost",
|
||||||
|
},
|
||||||
|
NotBefore: notBefore,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certDir := filepath.Dir(certFile)
|
||||||
|
if err := os.MkdirAll(certDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create certs directory %s: %w", certDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certOut, err := os.Create(certFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open cert.pem for writing: %w", err)
|
||||||
|
}
|
||||||
|
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||||
|
return fmt.Errorf("failed to write data to cert.pem: %w", err)
|
||||||
|
}
|
||||||
|
if err := certOut.Close(); err != nil {
|
||||||
|
return fmt.Errorf("error closing cert.pem: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open key.pem for writing: %w", err)
|
||||||
|
}
|
||||||
|
if err := pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
|
||||||
|
return fmt.Errorf("failed to write data to key.pem: %w", err)
|
||||||
|
}
|
||||||
|
if err := keyOut.Close(); err != nil {
|
||||||
|
return fmt.Errorf("error closing key.pem: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Self-signed TLS certificate generated successfully.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config := loadConfig()
|
config := loadConfig()
|
||||||
middleware := NewMiddleware(config)
|
middleware := NewMiddleware(config)
|
||||||
|
|
||||||
// Setup routes
|
|
||||||
http.HandleFunc("/health", middleware.healthHandler)
|
http.HandleFunc("/health", middleware.healthHandler)
|
||||||
http.HandleFunc("/", middleware.webhookHandler)
|
http.HandleFunc("/", middleware.webhookHandler)
|
||||||
|
|
||||||
// Start server
|
|
||||||
bindAddr := fmt.Sprintf("%s:%s", config.BindAddress, config.BindPort)
|
bindAddr := fmt.Sprintf("%s:%s", config.BindAddress, config.BindPort)
|
||||||
|
|
||||||
log.Printf("Starting Slack to ntfy middleware server")
|
log.Printf("Starting Slack to ntfy middleware server")
|
||||||
@@ -226,7 +286,33 @@ func main() {
|
|||||||
log.Printf("ntfy URL: %s", config.NtfyBaseURL)
|
log.Printf("ntfy URL: %s", config.NtfyBaseURL)
|
||||||
log.Printf("Authentication: %t", config.NtfyToken != "" || config.NtfyUsername != "")
|
log.Printf("Authentication: %t", config.NtfyToken != "" || config.NtfyUsername != "")
|
||||||
|
|
||||||
if err := http.ListenAndServe(bindAddr, nil); err != nil {
|
certFile := config.TLSCertFile
|
||||||
log.Fatalf("Server failed to start: %v", err)
|
keyFile := config.TLSKeyFile
|
||||||
|
|
||||||
|
if certFile == "" || keyFile == "" {
|
||||||
|
log.Println("TLS_CERT_FILE/TLS_KEY_FILE are not set. Attempting to generate self-signed certificates.")
|
||||||
|
|
||||||
|
certFile = "/app/certs/generated_server.crt"
|
||||||
|
keyFile = "/app/certs/generated_server.key"
|
||||||
|
|
||||||
|
if err := generateSelfSignedCert(certFile, keyFile); err != nil {
|
||||||
|
log.Printf("Failed to generate self-signed TLS certificate: %v. Falling back to HTTP.", err)
|
||||||
|
certFile = ""
|
||||||
|
keyFile = ""
|
||||||
|
} else {
|
||||||
|
log.Printf("Using generated TLS certificates: %s and %s", certFile, keyFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if certFile != "" && keyFile != "" {
|
||||||
|
log.Printf("TLS enabled: using %s and %s", certFile, keyFile)
|
||||||
|
if err := http.ListenAndServeTLS(bindAddr, certFile, keyFile, nil); err != nil {
|
||||||
|
log.Fatalf("Server failed to start with TLS: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("TLS disabled")
|
||||||
|
if err := http.ListenAndServe(bindAddr, nil); err != nil {
|
||||||
|
log.Fatalf("Server failed to start: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,4 +53,4 @@ echo
|
|||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
echo "1. Test the service: ./scripts/test.sh"
|
echo "1. Test the service: ./scripts/test.sh"
|
||||||
echo "2. Check logs: docker compose logs -f"
|
echo "2. Check logs: docker compose logs -f"
|
||||||
echo "3. Configure Slack webhook URL: http://your-server-ip:8080/your-topic-name"
|
echo "3. Configure Slack webhook URL: https://your-server-ip:8080/your-topic-name"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
MIDDLEWARE_URL="http://localhost:8080"
|
MIDDLEWARE_URL="https://localhost:8080"
|
||||||
TEST_TOPIC="test-topic"
|
TEST_TOPIC="test-topic"
|
||||||
|
|
||||||
echo "🧪 Testing Slack to ntfy middleware..."
|
echo "🧪 Testing Slack to ntfy middleware..."
|
||||||
|
|
||||||
# Check if service is running
|
# Check if service is running
|
||||||
echo "Checking if middleware is running..."
|
echo "Checking if middleware is running..."
|
||||||
if ! curl -s "$MIDDLEWARE_URL/health" > /dev/null; then
|
if ! curl -k -s "$MIDDLEWARE_URL/health" > /dev/null; then
|
||||||
echo "❌ Middleware is not running on $MIDDLEWARE_URL"
|
echo "❌ Middleware is not running on $MIDDLEWARE_URL"
|
||||||
echo "Please start it with: docker compose up -d"
|
echo "Please start it with: docker compose up -d"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -18,19 +18,19 @@ echo "✅ Middleware is running"
|
|||||||
|
|
||||||
# Test health endpoint
|
# Test health endpoint
|
||||||
echo "Testing health endpoint..."
|
echo "Testing health endpoint..."
|
||||||
HEALTH_RESPONSE=$(curl -s "$MIDDLEWARE_URL/health")
|
HEALTH_RESPONSE=$(curl -k -s "$MIDDLEWARE_URL/health")
|
||||||
echo "Health response: $HEALTH_RESPONSE"
|
echo "Health response: $HEALTH_RESPONSE"
|
||||||
|
|
||||||
# Test Slack webhook format
|
# Test Slack webhook format
|
||||||
echo "Testing Slack webhook format..."
|
echo "Testing Slack webhook format..."
|
||||||
curl -X POST "$MIDDLEWARE_URL/$TEST_TOPIC" \
|
curl -k -X POST "$MIDDLEWARE_URL/$TEST_TOPIC" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d '{"text": "🧪 Test alert from Slack to ntfy middleware test script"}' \
|
-d '{"text": "🧪 Test alert from Slack to ntfy middleware test script"}' \
|
||||||
-w "\nHTTP Status: %{http_code}\n"
|
-w "\nHTTP Status: %{http_code}\n"
|
||||||
|
|
||||||
# Test plain text format
|
# Test plain text format
|
||||||
echo "Testing plain text format..."
|
echo "Testing plain text format..."
|
||||||
curl -X POST "$MIDDLEWARE_URL/$TEST_TOPIC" \
|
curl -k -X POST "$MIDDLEWARE_URL/$TEST_TOPIC" \
|
||||||
-H 'Content-Type: text/plain' \
|
-H 'Content-Type: text/plain' \
|
||||||
-d 'Plain text test alert' \
|
-d 'Plain text test alert' \
|
||||||
-w "\nHTTP Status: %{http_code}\n"
|
-w "\nHTTP Status: %{http_code}\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user