Skip to content

Commit

Permalink
Add ability to autogenerate & serve LetsEncrypt SSL/TLS Certificate (#4)
Browse files Browse the repository at this point in the history
This change uses the autocert package to add initial support for generating and serving a real live SSL certificate from LetsEncrypt via `ssl-proxy`. Now, serving your application with SSL is as easy as:

```sh
./ssl-proxy -from=0.0.0.0:443 -to=localhost:8080 -domain=mydomain.com
```

this will attempt to fetch a certificate for mydomain.com from LetsEncrypt (port 443 must be bindable by this process) and then begin proxying HTTPS traffic on :443 to any server running on :8080. Easy. This closes #1.
  • Loading branch information
suyashkumar authored Oct 12, 2018
1 parent 5f303c7 commit 10b4cd0
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 3 deletions.
18 changes: 18 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true


[[constraint]]
branch = "master"
name = "golang.org/x/crypto"

[prune]
go-tests = true
unused-packages = true
40 changes: 37 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import (
"strings"

"github.com/suyashkumar/ssl-proxy/gen"
"golang.org/x/crypto/acme/autocert"
)

var (
to = flag.String("to", "http://127.0.0.1:80", "the address and port for which to proxy requests to")
fromURL = flag.String("from", "127.0.0.1:4430", "the tcp address and port this proxy should listen for requests on")
certFile = flag.String("cert", "", "path to a tls certificate file. If not provided, ssl-proxy will generate one for you in ~/.ssl-proxy/")
keyFile = flag.String("key", "", "path to a private key file. If not provided, ssl-proxy will generate one for you in ~/.ssl-proxy/")
domain = flag.String("domain", "", "domain to mint letsencrypt certificates for. Usage of this parameter implies acceptance of the LetsEncrypt terms of service.")
)

const (
Expand All @@ -31,7 +33,12 @@ const (
func main() {
flag.Parse()

if *certFile == "" || *keyFile == "" {
validCertFile := *certFile != ""
validKeyFile := *keyFile != ""
validDomain := *domain != ""

// Determine if we need to generate self-signed certs
if (!validCertFile || !validKeyFile) && !validDomain {
// Use default file paths
*certFile = DefaultCertFile
*keyFile = DefaultKeyFile
Expand Down Expand Up @@ -66,13 +73,40 @@ func main() {
log.Println("Assuming -to URL is using http://")
}

// Parse toURL as a URL
toURL, err := url.Parse(*to)
if err != nil {
log.Fatal("Unable to parse 'to' url: ", err)
}

// Setup ServeMux
localProxy := httputil.NewSingleHostReverseProxy(toURL)
http.Handle("/", localProxy)
mux := http.NewServeMux()
mux.Handle("/", localProxy)

log.Printf("Proxying calls from https://%s (SSL/TLS) to %s", *fromURL, toURL)
log.Fatal(http.ListenAndServeTLS(*fromURL, *certFile, *keyFile, nil))

// Determine if we should serve with autogenerated LetsEncrypt certificates or not
if validDomain {
// Domain is present, use autocert
// TODO: validate domain (though, autocert may do this)
// TODO: for some reason this seems to only work on :443
log.Printf("Domain specified, using LetsEncrypt to autogenerate and serve certs for %s\n", *domain)
if !strings.HasSuffix(*fromURL, ":443") {
log.Println("WARN: Right now, you must serve on port :443 to use autogenerated LetsEncrypt certs using the -domain flag, this may NOT WORK")
}
m := &autocert.Manager{
Cache: autocert.DirCache("certs"),
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(*domain),
}
s := &http.Server{
Addr: *fromURL,
TLSConfig: m.TLSConfig(),
}
s.Handler = mux
s.ListenAndServeTLS("", "")
} else {
log.Fatal(http.ListenAndServeTLS(*fromURL, *certFile, *keyFile, mux))
}
}

0 comments on commit 10b4cd0

Please sign in to comment.