Merge pull request #19 from 0xERR0R/development

[pull] development from 0xERR0R:development
This commit is contained in:
Kwitsch 2022-09-09 20:53:30 +02:00 committed by GitHub
commit 0850346569
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 255 additions and 29 deletions

View File

@ -11,30 +11,36 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract branch name
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
id: extract_branch
- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
@ -42,6 +48,9 @@ jobs:
tags: |
ghcr.io/0xerr0r/blocky:${{ steps.extract_branch.outputs.branch }}
spx01/blocky:${{ steps.extract_branch.outputs.branch }}
cache-from: type=registry,ref=ghcr.io/0xerr0r/blocky:buildcache
cache-to: type=registry,ref=ghcr.io/0xerr0r/blocky:buildcache,mode=max
- name: Scan image
uses: anchore/scan-action@v3
id: scan
@ -49,6 +58,7 @@ jobs:
image: "spx01/blocky:${{ steps.extract_branch.outputs.branch }}"
fail-build: false
acs-report-enable: true
- name: upload Anchore scan SARIF report
uses: github/codeql-action/upload-sarif@v1
with:

View File

@ -35,32 +35,34 @@ jobs:
images: spx01/blocky,ghcr.io/0xerr0r/blocky
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/0xerr0r/blocky:buildcache
cache-to: type=registry,ref=ghcr.io/0xerr0r/blocky:buildcache,mode=max
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2

View File

@ -1,12 +1,19 @@
# build stage
FROM golang:1-alpine AS build-env
RUN apk add --no-cache \
build-base \
linux-headers \
coreutils \
binutils \
libtool \
musl-dev \
git \
make \
gcc \
libc-dev \
zip \
ca-certificates
ca-certificates \
libcap
ENV GO111MODULE=on \
CGO_ENABLED=0
@ -20,24 +27,35 @@ RUN go mod download
ADD . .
ARG opts
RUN env ${opts} make build
RUN env ${opts} make build-static
RUN setcap 'cap_net_bind_service=+ep' /src/bin/blocky
RUN adduser -S -D -H -h /app -s /sbin/nologin blocky
RUN chown blocky /src/bin/blocky
RUN tail -n 1 /etc/passwd > /tmp/blocky_passwd
# get all required files and build a root directory
FROM scratch AS combine-env
COPY --from=build-env /src/bin/blocky /app/blocky
COPY --from=build-env /tmp/blocky_passwd /etc/passwd
COPY --from=build-env /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# final stage
FROM alpine:3.16
FROM scratch
LABEL org.opencontainers.image.source="https://github.com/0xERR0R/blocky" \
org.opencontainers.image.url="https://github.com/0xERR0R/blocky" \
org.opencontainers.image.title="DNS proxy as ad-blocker for local network"
COPY --from=build-env /src/bin/blocky /app/blocky
RUN apk add --no-cache ca-certificates bind-tools tini tzdata libcap && \
adduser -S -D -H -h /app -s /sbin/nologin blocky && \
setcap 'cap_net_bind_service=+ep' /app/blocky
HEALTHCHECK --interval=1m --timeout=3s CMD dig @127.0.0.1 -p 53 healthcheck.blocky +tcp +short || exit 1
COPY --from=combine-env / /
USER blocky
WORKDIR /app
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["sh", "-c", "/app/blocky --config ${CONFIG_FILE:-/app/config.yml}"]
ENV BLOCKY_CONFIG_FILE=/app/config.yml
ENTRYPOINT ["/app/blocky"]
HEALTHCHECK --interval=1m --timeout=3s CMD ["/app/blocky", "healthcheck"]

View File

@ -5,7 +5,7 @@
VERSION := $(shell git describe --always --tags)
BUILD_TIME=$(shell date '+%Y%m%d-%H%M%S')
DOCKER_IMAGE_NAME="spx01/blocky"
DOCKER_IMAGE_NAME=spx01/blocky
BINARY_NAME=blocky
BIN_OUT_DIR=bin
@ -29,6 +29,10 @@ build: ## Build binary
go generate ./...
go build -v -ldflags="-w -s -X github.com/0xERR0R/blocky/util.Version=${VERSION} -X github.com/0xERR0R/blocky/util.BuildTime=${BUILD_TIME}" -o $(BIN_OUT_DIR)/$(BINARY_NAME)$(BINARY_SUFFIX)
build-static: ## Build static binary
go generate ./...
go build -tags static -v -ldflags="-linkmode external -extldflags -static -X github.com/0xERR0R/blocky/util.Version=${VERSION} -X github.com/0xERR0R/blocky/util.BuildTime=${BUILD_TIME}" -o $(BIN_OUT_DIR)/$(BINARY_NAME)$(BINARY_SUFFIX)
test: ## run tests
go run github.com/onsi/ginkgo/v2/ginkgo -v --coverprofile=coverage.txt --covermode=atomic -cover ./...
@ -45,7 +49,7 @@ fmt: ## gofmt and goimports all go files
find . -name '*.go' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
docker-build: ## Build docker image
docker build --network=host --tag ${DOCKER_IMAGE_NAME} .
docker buildx build -o type=docker --network=host -t ${DOCKER_IMAGE_NAME} .
help: ## Shows help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

44
cmd/healthcheck.go Normal file
View File

@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"net"
"github.com/miekg/dns"
"github.com/spf13/cobra"
)
const (
defaultDNSPort = 53
)
func NewHealthcheckCommand() *cobra.Command {
c := &cobra.Command{
Use: "healthcheck",
Short: "performs healthcheck",
RunE: healthcheck,
}
c.Flags().Uint16P("port", "p", defaultDNSPort, "healthcheck port 5333")
return c
}
func healthcheck(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetUint16("port")
c := new(dns.Client)
c.Net = "tcp"
m := new(dns.Msg)
m.SetQuestion("healthcheck.blocky.", dns.TypeA)
_, _, err := c.Exchange(m, net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", port)))
if err == nil {
fmt.Println("OK")
} else {
fmt.Println("NOT OK")
}
return err
}

63
cmd/healthcheck_test.go Normal file
View File

@ -0,0 +1,63 @@
package cmd
import (
"fmt"
"github.com/miekg/dns"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Healthcheck command", func() {
Describe("Call healthcheck command", func() {
It("should fail", func() {
c := NewHealthcheckCommand()
c.SetArgs([]string{"-p", "5344"})
err := c.Execute()
Expect(err).Should(HaveOccurred())
})
It("shoul succeed", func() {
srv := createMockServer()
go func() {
defer GinkgoRecover()
err := srv.ListenAndServe()
Expect(err).Should(Succeed())
}()
DeferCleanup(srv.Shutdown)
Eventually(func() error {
c := NewHealthcheckCommand()
c.SetArgs([]string{"-p", "5333"})
return c.Execute()
}, "1s").Should(Succeed())
})
})
})
func createMockServer() *dns.Server {
res := &dns.Server{
Addr: "127.0.0.1:5333",
Net: "tcp",
Handler: dns.NewServeMux(),
NotifyStartedFunc: func() {
fmt.Println("Mock helthcheck server is up")
},
}
th := res.Handler.(*dns.ServeMux)
th.HandleFunc("healthcheck.blocky", func(w dns.ResponseWriter, request *dns.Msg) {
resp := new(dns.Msg)
resp.SetReply(request)
resp.Rcode = dns.RcodeSuccess
err := w.WriteMsg(resp)
Expect(err).Should(Succeed())
})
return res
}

View File

@ -20,9 +20,11 @@ var (
)
const (
defaultPort = 4000
defaultHost = "localhost"
defaultConfigPath = "./config.yml"
defaultPort = 4000
defaultHost = "localhost"
defaultConfigPath = "./config.yml"
configFileEnvVar = "BLOCKY_CONFIG_FILE"
configFileEnvVarOld = "CONFIG_FILE"
)
// NewRootCommand creates a new root cli command instance
@ -49,7 +51,8 @@ Complete documentation is available at https://github.com/0xERR0R/blocky`,
NewVersionCommand(),
newServeCommand(),
newBlockingCommand(),
NewListsCommand())
NewListsCommand(),
NewHealthcheckCommand())
return c
}
@ -64,6 +67,18 @@ func init() {
}
func initConfig() {
if configPath == defaultConfigPath {
val, present := os.LookupEnv(configFileEnvVar)
if present {
configPath = val
} else {
val, present = os.LookupEnv(configFileEnvVarOld)
if present {
configPath = val
}
}
}
cfg, err := config.LoadConfig(configPath, false)
if err != nil {
util.FatalOnError("unable to load configuration: ", err)

View File

@ -2,14 +2,17 @@ package cmd
import (
"io"
"os"
"github.com/0xERR0R/blocky/log"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/0xERR0R/blocky/helpertest"
)
var _ = Describe("Version command", func() {
var _ = Describe("root command", func() {
When("Version command is called", func() {
log.Log().ExitFunc = nil
It("should execute without error", func() {
@ -20,4 +23,51 @@ var _ = Describe("Version command", func() {
Expect(err).Should(Succeed())
})
})
When("Config provided", func() {
var (
tmpDir *TmpFolder
tmpFile *TmpFile
)
BeforeEach(func() {
configPath = defaultConfigPath
tmpDir = NewTmpFolder("RootCommand")
Expect(tmpDir.Error).Should(Succeed())
DeferCleanup(tmpDir.Clean)
tmpFile = tmpDir.CreateStringFile("config",
"upstream:",
" default:",
" - 1.1.1.1",
"blocking:",
" blackLists:",
" ads:",
" - https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt",
" clientGroupsBlock:",
" default:",
" - ads",
"port: 5333",
)
Expect(tmpFile.Error).Should(Succeed())
})
It("should accept old env var", func() {
os.Setenv(configFileEnvVarOld, tmpFile.Path)
DeferCleanup(func() { os.Unsetenv(configFileEnvVarOld) })
initConfig()
Expect(configPath).Should(Equal(tmpFile.Path))
})
It("should accept new env var", func() {
os.Setenv(configFileEnvVar, tmpFile.Path)
DeferCleanup(func() { os.Unsetenv(configFileEnvVar) })
initConfig()
Expect(configPath).Should(Equal(tmpFile.Path))
})
})
})

3
go.mod
View File

@ -183,6 +183,7 @@ require (
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
github.com/ramr/go-reaper v0.2.1
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryancurrah/gomodguard v1.2.4 // indirect
github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect
@ -242,3 +243,5 @@ require (
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
mvdan.cc/unparam v0.0.0-20220706161116-678bad134442 // indirect
)
require github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751

2
go.sum
View File

@ -610,6 +610,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/ramr/go-reaper v0.2.1 h1:zww+wlQOvTjBZuk1920R/e0GFEb6O7+B0WQLV6dM924=
github.com/ramr/go-reaper v0.2.1/go.mod h1:AVypdzrcCXjSc/JYnlXl8TsB+z84WyFzxWE8Jh0MOJc=
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=

15
main_static.go Normal file
View File

@ -0,0 +1,15 @@
//go:build linux
// +build linux
package main
import (
_ "time/tzdata"
reaper "github.com/ramr/go-reaper"
)
//nolint:gochecknoinits
func init() {
go reaper.Reap()
}