package main import ( "encoding/json" "errors" // "fmt" "log" "net/http" "os" "time" "gopkg.in/yaml.v3" ) type Config struct { Listen string `yaml:"listen"` Auth AuthConfig `yaml:"auth"` Credentials map[string]Credential `yaml:"credentials"` } type AuthConfig struct { User string `yaml:"user"` Pass string `yaml:"pass"` } type Credential struct { Username string `yaml:"username"` Password string `yaml:"password"` Note string `yaml:"note"` } type Response struct { Service string `json:"service"` Username string `json:"username"` Password string `json:"password"` Note string `json:"note,omitempty"` IssuedAt string `json:"issued_at"` } func basicAuthOK(r *http.Request, cfg *Config) bool { user, pass, ok := r.BasicAuth() if !ok { return false } return user == cfg.Auth.User && pass == cfg.Auth.Pass } func newMux(cfg *Config) *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) }) mux.HandleFunc("/creds", func(w http.ResponseWriter, r *http.Request) { if !basicAuthOK(r, cfg) { w.Header().Set("WWW-Authenticate", `Basic realm="proxyfier"`) http.Error(w, "unauthorized", http.StatusUnauthorized) return } service := r.URL.Query().Get("service") if service == "" { http.Error(w, "service is required", http.StatusBadRequest) return } cred, ok := cfg.Credentials[service] if !ok { http.Error(w, "service not found", http.StatusNotFound) return } resp := Response{ Service: service, Username: cred.Username, Password: cred.Password, Note: cred.Note, IssuedAt: time.Now().UTC().Format(time.RFC3339), } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(resp); err != nil { http.Error(w, "encode error", http.StatusInternalServerError) return } }) return mux } func loadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } if cfg.Listen == "" { cfg.Listen = "0.0.0.0:9000" } if cfg.Auth.User == "" || cfg.Auth.Pass == "" { return nil, errors.New("auth.user/auth.pass must be set") } return &cfg, nil } func main() { cfgPath := os.Getenv("PROXYFIER_CONFIG") if cfgPath == "" { cfgPath = "config.yaml" } cfg, err := loadConfig(cfgPath) if err != nil { log.Fatalf("config error: %v", err) } srv := &http.Server{ Addr: cfg.Listen, Handler: newMux(cfg), ReadHeaderTimeout: 5 * time.Second, } log.Printf("proxyfier listening on %s", cfg.Listen) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("server error: %v", err) } }