diff --git a/act/artifactcache/handler.go b/act/artifactcache/handler.go index 65d70e14..c02c450c 100644 --- a/act/artifactcache/handler.go +++ b/act/artifactcache/handler.go @@ -20,6 +20,7 @@ import ( "github.com/timshannon/bolthold" "go.etcd.io/bbolt" + "github.com/nektos/act/pkg/cacheproxy" "github.com/nektos/act/pkg/common" ) @@ -83,12 +84,12 @@ func StartHandler(dir, outboundIP string, port uint16, secret string, logger log } router := httprouter.New() - router.GET(cachePrefixPath+urlBase+"/cache", h.middleware(h.find)) - router.POST(cachePrefixPath+urlBase+"/caches", h.middleware(h.reserve)) - router.PATCH(cachePrefixPath+urlBase+"/caches/:id", h.middleware(h.upload)) - router.POST(cachePrefixPath+urlBase+"/caches/:id", h.middleware(h.commit)) - router.GET(cachePrefixPath+urlBase+"/artifacts/:id", h.middleware(h.get)) - router.POST(cachePrefixPath+urlBase+"/clean", h.middleware(h.clean)) + router.GET(urlBase+"/cache", h.middleware(h.find)) + router.POST(urlBase+"/caches", h.middleware(h.reserve)) + router.PATCH(urlBase+"/caches/:id", h.middleware(h.upload)) + router.POST(urlBase+"/caches/:id", h.middleware(h.commit)) + router.GET(urlBase+"/artifacts/:id", h.middleware(h.get)) + router.POST(urlBase+"/clean", h.middleware(h.clean)) h.router = router @@ -159,7 +160,8 @@ func (h *Handler) openDB() (*bolthold.Store, error) { // GET /_apis/artifactcache/cache func (h *Handler) find(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - repo, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + repo, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -206,7 +208,8 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, params httprouter // POST /_apis/artifactcache/caches func (h *Handler) reserve(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - repo, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + repo, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -243,7 +246,8 @@ func (h *Handler) reserve(w http.ResponseWriter, r *http.Request, params httprou // PATCH /_apis/artifactcache/caches/:id func (h *Handler) upload(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - repo, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + repo, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -296,7 +300,8 @@ func (h *Handler) upload(w http.ResponseWriter, r *http.Request, params httprout // POST /_apis/artifactcache/caches/:id func (h *Handler) commit(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - repo, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + repo, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -363,7 +368,8 @@ func (h *Handler) commit(w http.ResponseWriter, r *http.Request, params httprout // GET /_apis/artifactcache/artifacts/:id func (h *Handler) get(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - repo, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + repo, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -403,7 +409,8 @@ func (h *Handler) get(w http.ResponseWriter, r *http.Request, params httprouter. // POST /_apis/artifactcache/clean func (h *Handler) clean(w http.ResponseWriter, r *http.Request, params httprouter.Params) { - _, err := h.validateMac(params) + rundata := runDataFromHeaders(r) + _, err := h.validateMac(rundata) if err != nil { h.responseJSON(w, r, 500, err) return @@ -621,3 +628,12 @@ func parseContentRange(s string) (uint64, uint64, error) { } return start, stop, nil } + +func runDataFromHeaders(r *http.Request) cacheproxy.RunData { + return cacheproxy.RunData{ + RepositoryFullName: r.Header.Get("Forgejo-Cache-Repo"), + RunNumber: r.Header.Get("Forgejo-Cache-RunNumber"), + Timestamp: r.Header.Get("Forgejo-Cache-Timestamp"), + RepositoryMAC: r.Header.Get("Forgejo-Cache-MAC"), + } +} diff --git a/act/artifactcache/mac.go b/act/artifactcache/mac.go index 88ed7e04..0645fa81 100644 --- a/act/artifactcache/mac.go +++ b/act/artifactcache/mac.go @@ -6,36 +6,29 @@ package artifactcache import ( "crypto/hmac" "crypto/sha256" + "encoding/hex" "errors" - "hash" "strconv" "time" - "github.com/julienschmidt/httprouter" + "github.com/nektos/act/pkg/cacheproxy" ) var ( - ErrValidation = errors.New("validation error") - cachePrefixPath = "/:org/:repo/:run/:ts/:mac" + ErrValidation = errors.New("validation error") ) -func (h *Handler) validateMac(params httprouter.Params) (string, error) { - ts := params.ByName("ts") - - repo := params.ByName("org") + "/" + params.ByName("repo") - run := params.ByName("run") - messageMAC := params.ByName("mac") - +func (h *Handler) validateMac(rundata cacheproxy.RunData) (string, error) { // TODO: allow configurable max age - if !validateAge(ts) { + if !validateAge(rundata.Timestamp) { return "", ErrValidation } - expectedMAC := computeMac(h.secret, repo, run, ts).Sum(nil) - if hmac.Equal([]byte(messageMAC), expectedMAC) { - return repo, nil + expectedMAC := computeMac(h.secret, rundata.RepositoryFullName, rundata.RunNumber, rundata.Timestamp) + if expectedMAC == rundata.RepositoryMAC { + return rundata.RepositoryFullName, nil } - return repo, ErrValidation + return rundata.RepositoryFullName, ErrValidation } func validateAge(ts string) bool { @@ -49,15 +42,10 @@ func validateAge(ts string) bool { return true } -func computeMac(key, repo, run, ts string) hash.Hash { - mac := hmac.New(sha256.New, []byte(key)) +func computeMac(secret, repo, run, ts string) string { + mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(repo)) mac.Write([]byte(run)) mac.Write([]byte(ts)) - return mac -} - -func ComputeMac(key, repo, run, ts string) string { - mac := computeMac(key, repo, run, ts) - return string(mac.Sum(nil)) + return hex.EncodeToString(mac.Sum(nil)) } diff --git a/act/cacheproxy/handler.go b/act/cacheproxy/handler.go index e8064cae..efb67f4e 100644 --- a/act/cacheproxy/handler.go +++ b/act/cacheproxy/handler.go @@ -45,19 +45,19 @@ type Handler struct { } type RunData struct { - repositoryFullName string - runNumber string - timestamp string - repositoryMAC string + RepositoryFullName string + RunNumber string + Timestamp string + RepositoryMAC string } func (h *Handler) CreateRunData(fullName string, runNumber string, timestamp string) RunData { mac := computeMac(h.cacheSecret, fullName, runNumber, timestamp) return RunData{ - repositoryFullName: fullName, - runNumber: runNumber, - timestamp: timestamp, - repositoryMAC: mac, + RepositoryFullName: fullName, + RunNumber: runNumber, + Timestamp: timestamp, + RepositoryMAC: mac, } } @@ -126,16 +126,14 @@ func proxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, } func (h *Handler) newReverseProxy(targetHost string) (*httputil.ReverseProxy, error) { - url, err := url.Parse(targetHost) + targetURL, err := url.Parse(targetHost) if err != nil { return nil, err } proxy := &httputil.ReverseProxy{ Rewrite: func(r *httputil.ProxyRequest) { - r.SetURL(url) - r.Out.Host = r.In.Host // if desired - re := regexp.MustCompile(`/(\w+)/_apis/artifactcache`) + re := regexp.MustCompile(`/(\w+)(/_apis/artifactcache/.+)`) matches := re.FindStringSubmatch(r.In.URL.Path) id := matches[1] data, ok := h.runs.Load(id) @@ -146,11 +144,15 @@ func (h *Handler) newReverseProxy(targetHost string) (*httputil.ReverseProxy, er // ! it really shouldn't happen anyway so it's fine for now return } + uri := matches[2] - r.Out.Header.Add("Forgejo-Cache-Repo", runData.repositoryFullName) - r.Out.Header.Add("Forgejo-Cache-RunNumber", runData.runNumber) - r.Out.Header.Add("Forgejo-Cache-Timestamp", runData.timestamp) - r.Out.Header.Add("Forgejo-Cache-MAC", runData.repositoryMAC) + r.SetURL(targetURL) + r.Out.URL.Path = uri + + r.Out.Header.Add("Forgejo-Cache-Repo", runData.RepositoryFullName) + r.Out.Header.Add("Forgejo-Cache-RunNumber", runData.RunNumber) + r.Out.Header.Add("Forgejo-Cache-Timestamp", runData.Timestamp) + r.Out.Header.Add("Forgejo-Cache-MAC", runData.RepositoryMAC) }, } return proxy, nil