Skip to content

Commit

Permalink
feat: support Automatically install TLS certificates from Let's Encry…
Browse files Browse the repository at this point in the history
…pt. (#205)

* feat: support Automatically install TLS certificates from Let's Encrypt.

* refactor: update tls server.

* fix: missing tls package.

* fix: drop 1.6.x support

* docs: update readme.

* fix: listen tcp :443: bind: permission denied
  • Loading branch information
appleboy authored Apr 5, 2017
1 parent 9b793cf commit 97eae1f
Show file tree
Hide file tree
Showing 19 changed files with 2,723 additions and 25 deletions.
1 change: 0 additions & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,3 @@ matrix:
GO_VERSION:
- 1.8.0
- 1.7.5
- 1.6.4
2 changes: 1 addition & 1 deletion .drone.yml.sig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIHBhdGg6IC9nby9zcmMvZ2l0aHViLmNvbS9hcHBsZWJveS9nb3J1c2gKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIGJ1aWxkOgogICAgaW1hZ2U6IGFwcGxlYm95L2dvbGFuZy10ZXN0aW5nOiR7R09fVkVSU0lPTn0KICAgIGVudmlyb25tZW50OgogICAgICAtIEdPUEFUSD0vZ28KICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwcwogICAgICAtIG1ha2UgdmV0CiAgICAgIC0gbWFrZSBsaW50CiAgICAgIC0gbWFrZSBidWlsZAogICAgICAtIG1ha2UgZW1iZWRtZAogICAgICAtIGNvdmVyYWdlIGFsbAogICAgICAjIHNlbmQgY292ZXJhZ2UgcmVwb3J0CiAgICAgIC0gbWFrZSBjb3ZlcmFnZQogICAgICAjIGJ1aWxkIGJpbmFyeSBmb3IgZG9ja2VyIGltYWdlCiAgICAgIC0gbWFrZSBkb2NrZXJfYnVpbGQKCiAgcmVsZWFzZToKICAgIGltYWdlOiBhcHBsZWJveS9nb2xhbmctdGVzdGluZzoke0dPX1ZFUlNJT059CiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogbmV0Z28KICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgcmVsZWFzZQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KICAgICAgbWF0cml4OgogICAgICAgIEdPX1ZFUlNJT046IDEuOC4wCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogYXBwbGVib3kvZ29ydXNoCiAgICB0YWdzOiBbICcke0RST05FX1RBR30nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogYXBwbGVib3kvZ29ydXNoCiAgICB0YWdzOiBbICdsYXRlc3QnIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgbWFzdGVyIF0KCiAgZmFjZWJvb2s6CiAgICBpbWFnZTogYXBwbGVib3kvZHJvbmUtZmFjZWJvb2sKICAgIHB1bGw6IHRydWUKICAgIHRvOiAxMjM0OTczMzg2NTI0NjEwCiAgICB3aGVuOgogICAgICBzdGF0dXM6IFsgc3VjY2VzcywgZmFpbHVyZSBdCiAgICAgIG1hdHJpeDoKICAgICAgICBHT19WRVJTSU9OOiAxLjguMAoKICBnaXRodWI6CiAgICBpbWFnZTogcGx1Z2lucy9naXRodWItcmVsZWFzZQogICAgZmlsZXM6CiAgICAgIC0gZGlzdC9yZWxlYXNlLyoKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCiAgICAgIG1hdHJpeDoKICAgICAgICBHT19WRVJTSU9OOiAxLjguMAoKc2VydmljZXM6CiAgcmVkaXM6CiAgICBpbWFnZTogcmVkaXM6YWxwaW5lCgptYXRyaXg6CiAgR09fVkVSU0lPTjoKICAgIC0gMS44LjAKICAgIC0gMS43LjUKICAgIC0gMS42LjQK.7ieZ2R6o0gwph45wl0Dbu9RRm_H36d0b9_fw38tqLOU
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIHBhdGg6IC9nby9zcmMvZ2l0aHViLmNvbS9hcHBsZWJveS9nb3J1c2gKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIGJ1aWxkOgogICAgaW1hZ2U6IGFwcGxlYm95L2dvbGFuZy10ZXN0aW5nOiR7R09fVkVSU0lPTn0KICAgIGVudmlyb25tZW50OgogICAgICAtIEdPUEFUSD0vZ28KICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwcwogICAgICAtIG1ha2UgdmV0CiAgICAgIC0gbWFrZSBsaW50CiAgICAgIC0gbWFrZSBidWlsZAogICAgICAtIG1ha2UgZW1iZWRtZAogICAgICAtIGNvdmVyYWdlIGFsbAogICAgICAjIHNlbmQgY292ZXJhZ2UgcmVwb3J0CiAgICAgIC0gbWFrZSBjb3ZlcmFnZQogICAgICAjIGJ1aWxkIGJpbmFyeSBmb3IgZG9ja2VyIGltYWdlCiAgICAgIC0gbWFrZSBkb2NrZXJfYnVpbGQKCiAgcmVsZWFzZToKICAgIGltYWdlOiBhcHBsZWJveS9nb2xhbmctdGVzdGluZzoke0dPX1ZFUlNJT059CiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogbmV0Z28KICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgcmVsZWFzZQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KICAgICAgbWF0cml4OgogICAgICAgIEdPX1ZFUlNJT046IDEuOC4wCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogYXBwbGVib3kvZ29ydXNoCiAgICB0YWdzOiBbICcke0RST05FX1RBR30nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogYXBwbGVib3kvZ29ydXNoCiAgICB0YWdzOiBbICdsYXRlc3QnIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgbWFzdGVyIF0KCiAgZmFjZWJvb2s6CiAgICBpbWFnZTogYXBwbGVib3kvZHJvbmUtZmFjZWJvb2sKICAgIHB1bGw6IHRydWUKICAgIHRvOiAxMjM0OTczMzg2NTI0NjEwCiAgICB3aGVuOgogICAgICBzdGF0dXM6IFsgc3VjY2VzcywgZmFpbHVyZSBdCiAgICAgIG1hdHJpeDoKICAgICAgICBHT19WRVJTSU9OOiAxLjguMAoKICBnaXRodWI6CiAgICBpbWFnZTogcGx1Z2lucy9naXRodWItcmVsZWFzZQogICAgZmlsZXM6CiAgICAgIC0gZGlzdC9yZWxlYXNlLyoKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCiAgICAgIG1hdHJpeDoKICAgICAgICBHT19WRVJTSU9OOiAxLjguMAoKc2VydmljZXM6CiAgcmVkaXM6CiAgICBpbWFnZTogcmVkaXM6YWxwaW5lCgptYXRyaXg6CiAgR09fVkVSU0lPTjoKICAgIC0gMS44LjAKICAgIC0gMS43LjUK.nKdD7bTOsm5KnlEADkTBhhu3r3oJV37rAanuF7GtT9w
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ A push notification micro server using [Gin](https://github.com/gin-gonic/gin) f
* Support for HTTP proxy to Google server (GCM).
* Support retry send notification if server response is fail.
* Support expose [prometheus](https://prometheus.io/) metrics.
* Support install TLS certificates from [Let's Encrypt](https://letsencrypt.org/) automatically.

See the [YAML config example](config/config.yml):

[embedmd]:# (config/config.yml yaml)
```yaml
core:
port: "8088"
port: "8088" # ignore this port number if auto_tls is enabled (listen 443).
worker_num: 0 # default worker number is runtime.NumCPU()
queue_num: 0 # default queue number is 8192
max_notification: 100
Expand All @@ -71,6 +72,10 @@ core:
enabled: false
path: "gorush.pid"
override: true
auto_tls:
enabled: false # Automatically install TLS certificates from Let's Encrypt.
folder: ".cache" # folder for storing TLS certificates
host: "" # which domains the Let's Encrypt will attempt

api:
push_uri: "/api/push"
Expand Down Expand Up @@ -233,7 +238,7 @@ $ gorush -c config.yml
Get go status of api server using [httpie](https://github.com/jkbrzt/httpie) tool:

```bash
$ http -v --verify=no --json GET https://localhost:8088/api/stat/go
$ http -v --verify=no --json GET http://localhost:8088/api/stat/go
```

## Web API
Expand Down
31 changes: 21 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,24 @@ type ConfYaml struct {

// SectionCore is sub section of config.
type SectionCore struct {
Port string `yaml:"port"`
MaxNotification int64 `yaml:"max_notification"`
WorkerNum int64 `yaml:"worker_num"`
QueueNum int64 `yaml:"queue_num"`
Mode string `yaml:"mode"`
SSL bool `yaml:"ssl"`
CertPath string `yaml:"cert_path"`
KeyPath string `yaml:"key_path"`
HTTPProxy string `yaml:"http_proxy"`
PID SectionPID `yaml:"pid"`
Port string `yaml:"port"`
MaxNotification int64 `yaml:"max_notification"`
WorkerNum int64 `yaml:"worker_num"`
QueueNum int64 `yaml:"queue_num"`
Mode string `yaml:"mode"`
SSL bool `yaml:"ssl"`
CertPath string `yaml:"cert_path"`
KeyPath string `yaml:"key_path"`
HTTPProxy string `yaml:"http_proxy"`
PID SectionPID `yaml:"pid"`
AutoTLS SectionAutoTLS `yaml:"auto_tls"`
}

// SectionAutoTLS support Let's Encrypt setting.
type SectionAutoTLS struct {
Enabled bool `yaml:"enabled"`
Folder string `yaml:"folder"`
Host string `yaml:"host"`
}

// SectionAPI is sub section of config.
Expand Down Expand Up @@ -123,6 +131,9 @@ func BuildDefaultPushConf() ConfYaml {
conf.Core.PID.Enabled = false
conf.Core.PID.Path = "gorush.pid"
conf.Core.PID.Override = false
conf.Core.AutoTLS.Enabled = false
conf.Core.AutoTLS.Folder = ".cache"
conf.Core.AutoTLS.Host = ""

// Api
conf.API.PushURI = "/api/push"
Expand Down
6 changes: 5 additions & 1 deletion config/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
core:
port: "8088"
port: "8088" # ignore this port number if auto_tls is enabled (listen 443).
worker_num: 0 # default worker number is runtime.NumCPU()
queue_num: 0 # default queue number is 8192
max_notification: 100
Expand All @@ -12,6 +12,10 @@ core:
enabled: false
path: "gorush.pid"
override: true
auto_tls:
enabled: false # Automatically install TLS certificates from Let's Encrypt.
folder: ".cache" # folder for storing TLS certificates
host: "" # which domains the Let's Encrypt will attempt

api:
push_uri: "/api/push"
Expand Down
6 changes: 6 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func (suite *ConfigTestSuite) TestValidateConfDefault() {
assert.Equal(suite.T(), false, suite.ConfGorushDefault.Core.PID.Enabled)
assert.Equal(suite.T(), "gorush.pid", suite.ConfGorushDefault.Core.PID.Path)
assert.Equal(suite.T(), false, suite.ConfGorushDefault.Core.PID.Override)
assert.Equal(suite.T(), false, suite.ConfGorushDefault.Core.AutoTLS.Enabled)
assert.Equal(suite.T(), ".cache", suite.ConfGorushDefault.Core.AutoTLS.Folder)
assert.Equal(suite.T(), "", suite.ConfGorushDefault.Core.AutoTLS.Host)

// Api
assert.Equal(suite.T(), "/api/push", suite.ConfGorushDefault.API.PushURI)
Expand Down Expand Up @@ -124,6 +127,9 @@ func (suite *ConfigTestSuite) TestValidateConf() {
assert.Equal(suite.T(), false, suite.ConfGorush.Core.PID.Enabled)
assert.Equal(suite.T(), "gorush.pid", suite.ConfGorush.Core.PID.Path)
assert.Equal(suite.T(), true, suite.ConfGorush.Core.PID.Override)
assert.Equal(suite.T(), false, suite.ConfGorush.Core.AutoTLS.Enabled)
assert.Equal(suite.T(), ".cache", suite.ConfGorush.Core.AutoTLS.Folder)
assert.Equal(suite.T(), "", suite.ConfGorush.Core.AutoTLS.Host)

// Api
assert.Equal(suite.T(), "/api/push", suite.ConfGorush.API.PushURI)
Expand Down
16 changes: 16 additions & 0 deletions gorush/server.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package gorush

import (
"crypto/tls"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/crypto/acme/autocert"
api "gopkg.in/appleboy/gin-status-api.v1"
)

Expand Down Expand Up @@ -70,6 +72,20 @@ func metricsHandler(c *gin.Context) {
promhttp.Handler().ServeHTTP(c.Writer, c.Request)
}

func autoTLSServer() *http.Server {
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(PushConf.Core.AutoTLS.Host),
Cache: autocert.DirCache(PushConf.Core.AutoTLS.Folder),
}

return &http.Server{
Addr: ":https",
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
Handler: routerEngine(),
}
}

func routerEngine() *gin.Engine {
// set server mode
gin.SetMode(PushConf.Core.Mode)
Expand Down
11 changes: 11 additions & 0 deletions gorush/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ func TestRunTLSServer(t *testing.T) {
gofight.TestRequest(t, "https://localhost:8087/api/stat/go")
}

func TestRunAutoTLSServer(t *testing.T) {
initTest()
PushConf.Core.AutoTLS.Enabled = true
go func() {
assert.NoError(t, RunHTTPServer())
}()
// have to wait for the goroutine to start and run the server
// otherwise the main thread will complete
time.Sleep(5 * time.Millisecond)
}

func TestLoadTLSCertError(t *testing.T) {
initTest()

Expand Down
10 changes: 5 additions & 5 deletions gorush/server_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
)

// RunHTTPServer provide run http or https protocol.
func RunHTTPServer() error {
var err error

if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" {
func RunHTTPServer() (err error) {
if PushConf.Core.AutoTLS.Enabled {
err = gracehttp.Serve(autoTLSServer())
} else if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" {
config := &tls.Config{
MinVersion: tls.VersionTLS10,
}
Expand Down Expand Up @@ -41,5 +41,5 @@ func RunHTTPServer() error {
})
}

return err
return
}
11 changes: 6 additions & 5 deletions gorush/server_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
)

// RunHTTPServer provide run http or https protocol.
func RunHTTPServer() error {
var err error

if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" {
func RunHTTPServer() (err error) {
if PushConf.Core.AutoTLS.Enabled {
s := autoTLSServer()
err = s.ListenAndServeTLS("", "")
} else if PushConf.Core.SSL && PushConf.Core.CertPath != "" && PushConf.Core.KeyPath != "" {
err = http.ListenAndServeTLS(":"+PushConf.Core.Port, PushConf.Core.CertPath, PushConf.Core.KeyPath, routerEngine())
} else {
err = http.ListenAndServe(":"+PushConf.Core.Port, routerEngine())
}

return err
return
}
Loading

0 comments on commit 97eae1f

Please sign in to comment.