Skip to content

Plugin System

Plugin system is based on gRPC or HTTP communication. Through the plugin, the requests can be forwarded to the plugin server for processing, so that the function can be expanded more flexibly.

Using gRPC/HTTP communication instead of dynamic link library has the following advantages:

  • Support multiple languages. A plugin is a gRPC/HTTP service, which can be implemented in any language.
  • Deployment is flexible, and you can choose to deploy independently.
  • The life cycle of the plugin will not affect the GOST itself.
  • Security. Using network communication, can more effectively limit data sharing between applications.

The modules that currently support plugin are: Hop, Admission, Authenticator, Bypass, Traffic Limiter, Host Mapper, Resolver, Ingress, Router, Service Discovery, Recorder, Observer.

Plugin Usage

Taking the authenticator as an example, after configuring the authenticator to use the plugin service, all authentication requests will be forwarded to the plugin server for processing.

services:
- name: service-0
  addr: ":8080"
  handler:
    type: http
    auther: auther-0
  listener:
    type: tcp
authers:
- name: auther-0
  plugin:
    type: grpc
    # type: http
    addr: 127.0.0.1:8000
    token: gost
    tls: {}
type (string, default=grpc)
plugin type, grpc or http.
addr (string, required)
plugin server address.
token (string)
credentials, plugin server can choose to validate this information.
tls (object, default=null)
After the configuration, TLS will be used to encrypt transmission. By default, TLS is not used.

Plugin Service

Write an authenticator plugin service in Go language.

gRPC Plugin Service

https://github.com/go-gost/plugin/blob/master/auth/example/grpc/main.go

package main

import (
    "context"
    "flag"
    "fmt"
    "log"
    "net"

    "github.com/go-gost/plugin/auth/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
)

var (
    port = flag.Int("port", 8000, "The server port")
)

type server struct {
    proto.UnimplementedAuthenticatorServer
}

func (s *server) Authenticate(ctx context.Context, in *proto.AuthenticateRequest) (*proto.AuthenticateReply, error) {
    // optional client authentication
    token := s.getCredentials(ctx)
    if token != "gost" {
        return nil, status.Error(codes.Unauthenticated, codes.Unauthenticated.String())
    }
    reply := &proto.AuthenticateReply{}
    if in.GetUsername() == "gost" && in.GetPassword() == "gost" {
        reply.Ok = true
    }
    return reply, nil
}

func (s *server) getCredentials(ctx context.Context) string {
    md, ok := metadata.FromIncomingContext(ctx)
    if ok && len(md["token"]) > 0 {
        return md["token"][0]
    }
    return ""
}


func main() {
    flag.Parse()
    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    proto.RegisterAuthenticatorServer(s, &server{})
    log.Printf("server listening at %v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

HTTP Plugin Service

https://github.com/go-gost/plugin/blob/master/auth/example/http/main.go

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "log"
    "net"
    "net/http"
)

var (
    port = flag.Int("port", 8000, "The server port")
)

type autherRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

type autherResponse struct {
    OK bool   `json:"ok"`
    ID string `json:"id"`
}

func main() {
    flag.Parse()
    lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    log.Printf("server listening at %v", lis.Addr())

    http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
        rb := autherRequest{}
        if err := json.NewDecoder(r.Body).Decode(&rb); err != nil {
            log.Println(err)
            w.WriteHeader(http.StatusBadRequest)
            return
        }

        resp := autherResponse{}
        if rb.Username == "gost" && rb.Password == "gost" {
            resp.OK = true
            resp.ID = "gost"
        }

        log.Printf("auth: %s, %s, %v", rb.Username, rb.Password, resp.OK)

        json.NewEncoder(w).Encode(resp)
    })

    if err := http.Serve(lis, nil); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

Comments