diff --git a/.gitignore b/.gitignore index d4b9b55..8e77b75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *.exe pgcli-* -pgserve-* +pgmap-* wintun/ *.zip *.dll diff --git a/cmd/pgmap/main.go b/cmd/pgmap/main.go index 9f3baf3..1e1f5aa 100644 --- a/cmd/pgmap/main.go +++ b/cmd/pgmap/main.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net/http" "os" "os/signal" @@ -24,8 +25,16 @@ func main() { Version: fmt.Sprintf("%s, commit %s", Version, Commit), Short: "Run a peermap server daemon", SilenceUsage: true, - Args: cobra.NoArgs, - RunE: run, + PreRunE: func(cmd *cobra.Command, args []string) error { + verbose, err := cmd.Flags().GetInt("verbose") + if err != nil { + return err + } + slog.SetLogLoggerLevel(slog.Level(verbose)) + return nil + }, + Args: cobra.NoArgs, + RunE: run, } serveCmd.Flags().StringP("config", "c", "config.yaml", "config file") serveCmd.Flags().StringP("listen", "l", "127.0.0.1:9987", "listen http address") diff --git a/peermap/exporter/auth.go b/peermap/exporter/auth.go new file mode 100644 index 0000000..782dcb5 --- /dev/null +++ b/peermap/exporter/auth.go @@ -0,0 +1,47 @@ +package exporter + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/json" + + "github.com/rkonfj/peerguard/secure" + "github.com/rkonfj/peerguard/secure/aescbc" +) + +var algo secure.SymmAlgo + +func SetSecretKey(key string) { + sum := sha256.Sum256([]byte(key)) + algo = aescbc.New(func(pubKey string) ([]byte, error) { + return sum[:], nil + }) +} + +type Instruction struct { +} + +func CheckToken(token string) (*Instruction, error) { + b, err := base64.StdEncoding.DecodeString(token) + if err != nil { + return nil, err + } + plain, err := algo.Decrypt(b, "") + if err != nil { + return nil, err + } + var ins Instruction + return &ins, json.Unmarshal(plain, &ins) +} + +func GenerateToken(ins Instruction) (string, error) { + b, err := json.Marshal(ins) + if err != nil { + return "", err + } + chiper, err := algo.Encrypt(b, "") + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(chiper), nil +} diff --git a/peermap/exporter/exporter.go b/peermap/exporter/exporter.go new file mode 100644 index 0000000..ef95f36 --- /dev/null +++ b/peermap/exporter/exporter.go @@ -0,0 +1,12 @@ +package exporter + +type NetworkHead struct { + ID string `json:"n"` + PeersCount int `json:"c"` + CreateTime string `json:"t"` +} + +type Network struct { + ID string `json:"n"` + Peers []string `json:"p"` +} diff --git a/peermap/peermap.go b/peermap/peermap.go index 08a2636..94cfa4f 100644 --- a/peermap/peermap.go +++ b/peermap/peermap.go @@ -2,7 +2,6 @@ package peermap import ( "context" - "crypto/md5" "encoding/base64" "encoding/json" "fmt" @@ -17,16 +16,11 @@ import ( cmap "github.com/orcaman/concurrent-map/v2" "github.com/rkonfj/peerguard/peer" "github.com/rkonfj/peerguard/peermap/auth" + "github.com/rkonfj/peerguard/peermap/exporter" "github.com/rkonfj/peerguard/peermap/oidc" "golang.org/x/time/rate" ) -type Network struct { - ID string `json:"id"` - PeersCount int `json:"peersCount"` - CreateTime string `json:"createTime"` -} - type Peer struct { peerMap *PeerMap secret auth.JSONSecret @@ -236,6 +230,7 @@ func New(cfg Config) (*PeerMap, error) { if err := cfg.applyDefaults(); err != nil { return nil, err } + exporter.SetSecretKey(cfg.SecretKey) mux := http.NewServeMux() pm := PeerMap{ httpServer: &http.Server{Handler: mux, Addr: cfg.Listen}, @@ -247,6 +242,7 @@ func New(cfg Config) (*PeerMap, error) { mux.HandleFunc("/", pm.handleWebsocket) mux.HandleFunc("/networks", pm.handleQueryNetworks) mux.HandleFunc("/peers", pm.handleQueryNetworkPeers) + mux.HandleFunc("/network/token", oidc.HandleNotifyToken) mux.HandleFunc("/oidc/", oidc.RedirectAuthURL) mux.HandleFunc("/oidc/authorize/", pm.handleOIDCAuthorize) @@ -254,10 +250,17 @@ func New(cfg Config) (*PeerMap, error) { } func (pm *PeerMap) handleQueryNetworks(w http.ResponseWriter, r *http.Request) { + exporterToken := r.Header.Get("X-Token") + _, err := exporter.CheckToken(exporterToken) + if err != nil { + slog.Debug("ExporterAuthFailed", "details", err) + w.WriteHeader(http.StatusUnauthorized) + return + } items := pm.networkMap.Items() - networks := make([]Network, 0, len(items)) + networks := make([]exporter.NetworkHead, 0, len(items)) for k, v := range items { - networks = append(networks, Network{ + networks = append(networks, exporter.NetworkHead{ ID: k, PeersCount: v.Count(), CreateTime: fmt.Sprintf("%d", v.createTime.UnixNano()), @@ -267,22 +270,25 @@ func (pm *PeerMap) handleQueryNetworks(w http.ResponseWriter, r *http.Request) { } func (pm *PeerMap) handleQueryNetworkPeers(w http.ResponseWriter, r *http.Request) { - jsonSecret, err := pm.authenticator.ParseSecret(r.Header.Get("X-Network")) + exporterToken := r.Header.Get("X-Token") + _, err := exporter.CheckToken(exporterToken) if err != nil { - slog.Debug("Authenticate failed", "err", err, "network", r.Header.Get("X-Network")) - w.WriteHeader(http.StatusForbidden) + slog.Debug("ExporterAuthFailed", "details", err) + w.WriteHeader(http.StatusUnauthorized) return } - if networkContext, ok := pm.networkMap.Get(jsonSecret.Network); ok { - items := networkContext.Items() - peers := make([]string, 0, len(items)) - for _, v := range items { - peers = append(peers, v.String()) + var networks []exporter.Network + for item := range pm.networkMap.IterBuffered() { + var peers []string + for _, peer := range item.Val.Items() { + peers = append(peers, peer.String()) } - json.NewEncoder(w).Encode(peers) - return + networks = append(networks, exporter.Network{ + ID: item.Key, + Peers: peers, + }) } - w.WriteHeader(http.StatusNotFound) + json.NewEncoder(w).Encode(networks) } func (pm *PeerMap) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request) { @@ -298,9 +304,7 @@ func (pm *PeerMap) handleOIDCAuthorize(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadGateway) return } - networkB := md5.Sum([]byte(email)) - network := base64.URLEncoding.EncodeToString(networkB[:]) - secret, err := pm.generateSecret(network) + secret, err := pm.generateSecret(email) if err != nil { w.WriteHeader(http.StatusInternalServerError) return