diff --git a/.github/workflows/docker-image-agent.yml b/.github/workflows/docker-image-agent.yml index 6e03e7a..9b32deb 100644 --- a/.github/workflows/docker-image-agent.yml +++ b/.github/workflows/docker-image-agent.yml @@ -9,4 +9,4 @@ jobs: uses: ./.github/workflows/docker-image.yml with: make_args: "agent=1" - image_name: ${{ github.repository }}-agent + image_name: ${{ github.repository_owner }}/godoxy-agent diff --git a/.github/workflows/docker-image-main-old.yml b/.github/workflows/docker-image-main-old.yml new file mode 100644 index 0000000..5469770 --- /dev/null +++ b/.github/workflows/docker-image-main-old.yml @@ -0,0 +1,9 @@ +name: Docker Image CI (main) + +on: + push: + tags: ["*"] + +jobs: + call-main-workflow: + uses: ./.github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image-main.yml b/.github/workflows/docker-image-main.yml index 5469770..dded02f 100644 --- a/.github/workflows/docker-image-main.yml +++ b/.github/workflows/docker-image-main.yml @@ -7,3 +7,5 @@ on: jobs: call-main-workflow: uses: ./.github/workflows/docker-image.yml + with: + image_name: ${{ github.repository_owner }}/godoxy diff --git a/agent/cmd/main.go b/agent/cmd/main.go index d91ad3f..36bd2b2 100644 --- a/agent/cmd/main.go +++ b/agent/cmd/main.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "os" + "strings" "github.com/rs/zerolog" "github.com/yusing/go-proxy/agent/pkg/certs" @@ -44,20 +45,29 @@ func printNewClientHelp(ca *tls.Certificate) { E.LogFatal("marshal certs error", err) } - fmt.Printf("Add this host (%s) to main server config like below:\n", host) + fmt.Printf("On main server, run:\nnew-agent '%s' '%s'\n", host, base64.StdEncoding.EncodeToString(certsData)) + fmt.Printf("Then add this host (%s) to main server config like below:\n", host) fmt.Println(string(cfgYAML)) - fmt.Printf("On main server, run:\ngodoxy new-agent '%s' '%s'\n", host, base64.StdEncoding.EncodeToString(certsData)) } func machineIP() string { - addrs, err := net.InterfaceAddrs() + interfaces, err := net.Interfaces() if err != nil { return "" } - for _, addr := range addrs { - if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil { - return ipnet.IP.String() + for _, in := range interfaces { + addrs, err := in.Addrs() + if err != nil { + continue + } + if !strings.HasPrefix(in.Name, "eth") && !strings.HasPrefix(in.Name, "en") { + continue + } + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String() + } } } } @@ -79,6 +89,14 @@ func main() { logging.Info().Msgf("GoDoxy Agent version %s", pkg.GetVersion()) logging.Info().Msgf("Agent name: %s", env.AgentName) + logging.Info().Msgf("Agent port: %d", env.AgentPort) + + logging.Info().Msg("\nTips:") + logging.Info().Msg("1. To change the agent name, you can set the AGENT_NAME environment variable.") + logging.Info().Msg("2. To change the agent port, you can set the AGENT_PORT environment variable.") + logging.Info().Msg("3. To skip the version check, you can set the AGENT_SKIP_VERSION_CHECK environment variable.") + logging.Info().Msgf("4. Create shell alias on main server: `alias new-agent='docker run --rm -v ./certs:/app/certs ghcr.io/yusing/godoxy /app/run new-agent'`") + logging.Info().Msgf("5. Create shell alias on agent server: `alias new-client='docker compose exec agent /app/run new-client'`\n") if isNew { logging.Info().Msg("Initialization complete.") diff --git a/agent/pkg/agent/config.go b/agent/pkg/agent/config.go index 86fb16e..54d2952 100644 --- a/agent/pkg/agent/config.go +++ b/agent/pkg/agent/config.go @@ -13,6 +13,7 @@ import ( "github.com/rs/zerolog" "github.com/yusing/go-proxy/agent/pkg/certs" + "github.com/yusing/go-proxy/agent/pkg/env" E "github.com/yusing/go-proxy/internal/error" "github.com/yusing/go-proxy/internal/logging" gphttp "github.com/yusing/go-proxy/internal/net/http" @@ -131,13 +132,15 @@ func (cfg *AgentConfig) load() E.Error { defer cancel() // check agent version - version, _, err := cfg.Fetch(ctx, EndpointVersion) - if err != nil { - return E.Wrap(err) - } + if !env.AgentSkipVersionCheck { + version, _, err := cfg.Fetch(ctx, EndpointVersion) + if err != nil { + return E.Wrap(err) + } - if string(version) != pkg.GetVersion() { - return E.Errorf("agent version mismatch: server: %s, agent: %s", pkg.GetVersion(), string(version)) + if string(version) != pkg.GetVersion() { + return E.Errorf("agent version mismatch: server: %s, agent: %s", pkg.GetVersion(), string(version)) + } } // get agent name diff --git a/agent/pkg/certs/zip_test.go b/agent/pkg/certs/zip_test.go new file mode 100644 index 0000000..835e820 --- /dev/null +++ b/agent/pkg/certs/zip_test.go @@ -0,0 +1,19 @@ +package certs + +import ( + "testing" + + . "github.com/yusing/go-proxy/internal/utils/testing" +) + +func TestZipCert(t *testing.T) { + ca, crt, key := []byte("test1"), []byte("test2"), []byte("test3") + zipData, err := ZipCert(ca, crt, key) + ExpectNoError(t, err) + + ca2, crt2, key2, err := ExtractCert(zipData) + ExpectNoError(t, err) + ExpectBytesEqual(t, ca, ca2) + ExpectBytesEqual(t, crt, crt2) + ExpectBytesEqual(t, key, key2) +} diff --git a/agent/pkg/env/env.go b/agent/pkg/env/env.go index 1d75454..72c8863 100644 --- a/agent/pkg/env/env.go +++ b/agent/pkg/env/env.go @@ -15,6 +15,7 @@ func DefaultAgentName() string { } var ( - AgentName = common.GetEnvString("AGENT_NAME", DefaultAgentName()) - AgentPort = common.GetEnvInt("AGENT_PORT", 8890) + AgentName = common.GetEnvString("AGENT_NAME", DefaultAgentName()) + AgentPort = common.GetEnvInt("AGENT_PORT", 8890) + AgentSkipVersionCheck = common.GetEnvBool("AGENT_SKIP_VERSION_CHECK", false) ) diff --git a/agent/pkg/handler/docker_socket.go b/agent/pkg/handler/docker_socket.go index 8e2e5d8..53c0aea 100644 --- a/agent/pkg/handler/docker_socket.go +++ b/agent/pkg/handler/docker_socket.go @@ -7,6 +7,7 @@ import ( "net/http" "strings" + "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/docker" "github.com/yusing/go-proxy/internal/logging" @@ -23,7 +24,7 @@ func DockerSocketHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { conn, err := dockerDialerCallback(r.Context()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.HandleErr(w, r, err) return } defer conn.Close() @@ -45,13 +46,13 @@ func DockerSocketHandler() http.HandlerFunc { }() if err := r.Write(conn); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.HandleErr(w, r, err) return } resp, err := http.ReadResponse(bufio.NewReader(conn), r) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.HandleErr(w, r, err) return } defer resp.Body.Close() diff --git a/agent/pkg/handler/system_info.go b/agent/pkg/handler/system_info.go index 0852f02..9b02992 100644 --- a/agent/pkg/handler/system_info.go +++ b/agent/pkg/handler/system_info.go @@ -10,7 +10,7 @@ import ( func SystemInfo(w http.ResponseWriter, r *http.Request) { info, err := metrics.GetSystemInfo(r.Context()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + utils.HandleErr(w, r, err) return } utils.RespondJSON(w, r, info) diff --git a/agent/pkg/server/server.go b/agent/pkg/server/server.go index f6e528d..d67be41 100644 --- a/agent/pkg/server/server.go +++ b/agent/pkg/server/server.go @@ -1,6 +1,7 @@ package server import ( + "context" "crypto/tls" "crypto/x509" "encoding/pem" @@ -8,6 +9,7 @@ import ( "log" "net" "net/http" + "time" "github.com/yusing/go-proxy/agent/pkg/handler" "github.com/yusing/go-proxy/internal/common" @@ -21,6 +23,8 @@ type Options struct { } func StartAgentServer(parent task.Parent, opt Options) { + t := parent.Subtask("agent server") + caCertPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: opt.CACert.Certificate[0]}) caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCertPEM) @@ -40,14 +44,32 @@ func StartAgentServer(parent task.Parent, opt Options) { logging.Fatal().Err(err).Int("port", opt.Port).Msg("failed to listen on port") return } - defer l.Close() server := &http.Server{ Handler: handler.NewHandler(), TLSConfig: tlsConfig, ErrorLog: log.New(logging.GetLogger(), "", 0), } - if err := server.Serve(tls.NewListener(l, tlsConfig)); err != nil { - logging.Fatal().Err(err).Int("port", opt.Port).Msg("failed to serve") - } + go func() { + defer l.Close() + if err := server.Serve(tls.NewListener(l, tlsConfig)); err != nil { + logging.Fatal().Err(err).Int("port", opt.Port).Msg("failed to serve") + } + }() + + logging.Info().Int("port", opt.Port).Msg("agent server started") + + go func() { + defer t.Finish(nil) + <-parent.Context().Done() + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + err := server.Shutdown(ctx) + if err != nil { + logging.Error().Err(err).Int("port", opt.Port).Msg("failed to shutdown agent server") + } + logging.Info().Int("port", opt.Port).Msg("agent server stopped") + }() } diff --git a/internal/utils/testing/testing.go b/internal/utils/testing/testing.go index fcd9e2e..5db7283 100644 --- a/internal/utils/testing/testing.go +++ b/internal/utils/testing/testing.go @@ -1,6 +1,7 @@ package utils import ( + "bytes" "errors" "os" "reflect" @@ -102,6 +103,14 @@ func ExpectDeepEqual[T any](t *testing.T, got T, want T) { } } +func ExpectBytesEqual(t *testing.T, got []byte, want []byte) { + t.Helper() + if !bytes.Equal(got, want) { + t.Errorf("expected:\n%v, got\n%v", want, got) + t.FailNow() + } +} + func ExpectTrue(t *testing.T, got bool) { t.Helper() if !got {