diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml new file mode 100644 index 0000000..43a205a --- /dev/null +++ b/.github/workflows/builder.yaml @@ -0,0 +1,69 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# GitHub recommends pinning actions to a commit SHA. +# To get a newer version, you will need to update the SHA. +# You can also reference a tag or branch, but the action may change without warning. + +name: Build & Release + +on: + release: + types: [created] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: alash3al/httpsify + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Github Container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + file: Dockerfile + context: . + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max + go: + name: Release Go Binary + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin] + goarch: [amd64, arm64, arm64] + steps: + - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: "https://dl.google.com/go/go1.20.2.linux-amd64.tar.gz" + project_path: "." + binary_name: "httpsify" + ldflags: "-s -w" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/httpsify.iml b/.idea/httpsify.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/httpsify.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7aa24f9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3b7e030..f890a8a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,21 @@ -FROM golang:alpine AS builder +FROM golang:1.18-alpine As builder -RUN apk add --no-cache git && CGO_ENABLED=0 GOOS=linux go get github.com/alash3al/httpsify -RUN apk add -U --no-cache ca-certificates +WORKDIR /httpsify/ -FROM scratch -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=builder /go/bin/httpsify /go/bin/httpsify +RUN apk update && apk add git -ENTRYPOINT ["/go/bin/httpsify"] +COPY go.mod go.sum ./ + +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o /usr/bin/httpsify . + +FROM alpine + +WORKDIR /httpsify/ + +COPY --from=builder /usr/bin/httpsify /usr/bin/httpsify + +CMD httpsify \ No newline at end of file diff --git a/README.md b/README.md index 19e3631..e12b1a8 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,13 @@ HTTPSify ======== A `Let'sEncrypt` based reverse proxy, that will automatically generate & renew valid `ssl` certs for your domains, it also enables the `http/2` protocol by default, and uses `roundrobin` as an algorithm to loadbalance the incoming requests between multiple `upstreams`, as well as redirecting the traffic from `http` traffic to `https` just if you enabled the flag `--redirect`. -NOTES -======= -> HTTPSify only supports `http-01` challenge because [Let's Encrypt disables TLS-SNI-01 validation](http://www.zdnet.com/article/lets-encrypt-disables-tls-sni-01-validation/) # Quick Start ### # Using Docker > Just run the following and then have fun !! ```bash -$ docker run --network host -v ~/.httpsify:/.httpsify -p 443:443 alash3al/httpsify +$ docker run --network host -v ~/.httpsify:/.httpsify -p 443:443 ghcr.io/alash3al/httpsify ``` ## # From Binaries @@ -27,14 +24,14 @@ $ go get -u github.com/alash3al/httpsify > Goto your `$HOME` Directory and edit the `hosts.json` to something like this ```json { - "example1.com": ["http://localhost:9080"], - "example2.com": ["http://localhost:8080", "http://localhost:8081"] + "example1.com": ["localhost:9080"], + "example2.com": ["localhost:8080", "localhost:8081"] } ``` > As you see, the configuration file accepts a `JSON` object/hashmap of `domain` -> `upstreams`, -and yes, it can loadbalance the requests between multiple upstreams using `roundrobin` algorithm. +and yes, it can load-balance the requests between multiple upstreams using `roundrobin` algorithm. -> Also You don't need to restart the server to reload the configurations, because `httpsify` automatically watches the +> Also, You don't need to restart the server to reload the configurations, because `httpsify` automatically watches the configurations file and reload it on any change. # License diff --git a/go.mod b/go.mod index ab1374e..4be4675 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.13 require ( github.com/fsnotify/fsnotify v1.4.7 - github.com/labstack/echo v3.3.10+incompatible github.com/labstack/echo/v4 v4.1.11 github.com/mitchellh/go-homedir v1.1.0 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 diff --git a/go.sum b/go.sum index c9068a1..2064a6f 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,9 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= -github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo/v4 v4.1.11 h1:z0BZoArY4FqdpUEl+wlHp4hnr/oSR6MTmQmv8OHSoww= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= @@ -16,15 +15,16 @@ github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -37,5 +37,7 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/hosts.go b/hosts.go index 2dc72b8..ad82dd2 100644 --- a/hosts.go +++ b/hosts.go @@ -76,10 +76,8 @@ func watchHostsChanges(filename string, fn func()) error { watcher.Add(filename) for { - select { - case <-watcher.Events: - fn() - } + <-watcher.Events + fn() } } diff --git a/main.go b/main.go index 62de27b..aee5fc8 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,11 @@ package main import ( "log" + "net/http" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" ) @@ -13,8 +15,11 @@ func main() { e.HideBanner = true e.AutoTLSManager.HostPolicy = autocert.HostWhitelist(getAvailableHosts()...) e.AutoTLSManager.Cache = autocert.DirCache(*flagAutocertCacheDir) + e.AutoTLSManager.Client = &acme.Client{ + DirectoryURL: *flagACMEDirectory, + UserAgent: "https://github.com/alash3al/httpsify", + } - e.Use(middleware.HTTPSRedirect()) e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { @@ -22,16 +27,22 @@ func main() { if *flagSendXSecuredBy { c.Response().Header().Set("X-Secured-By", "https://github.com/alash3al/httpsify") } - return next(c) + + hosts := hosts.Load().(map[string]*echo.Echo) + host := hosts[c.Request().Host] + + if host == nil { + return echo.ErrNotFound + } + + return echo.WrapHandler(host)(c) } }) - e.Any("/*", handler) - errChan := make(chan error) go (func() { - errChan <- e.Start(*flagHTTPAddr) + errChan <- http.ListenAndServe(*flagHTTPAddr, e.AutoTLSManager.HTTPHandler(nil)) })() go (func() { diff --git a/vars.go b/vars.go index c9d1e4f..70406e4 100644 --- a/vars.go +++ b/vars.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/mitchellh/go-homedir" + "golang.org/x/crypto/acme/autocert" ) var ( @@ -18,5 +19,6 @@ var ( flagHTTPAddr = flag.String("http", ":80", "the port to listen for http requests on, it is recommended to leave it as is") flagAutocertCacheDir = flag.String("certs", path.Join(homeDir, ".httpsify/certs"), "the certs directory") flagHostsFile = flag.String("hosts", path.Join(homeDir, ".httpsify/hosts.json"), "the file containing hosts mappings to upstreams") + flagACMEDirectory = flag.String("acme-directory", autocert.DefaultACMEDirectory, "the server to request certificates from") flagSendXSecuredBy = flag.Bool("x-secured-by", true, "whether to enable x-secured-by header or not") )