2022-10-10 07:05:55 -05:00
//go:build search
// +build search
2020-10-14 16:47:20 -05:00
package extensions
import (
2023-02-27 14:25:47 -05:00
"net/http"
2023-03-02 12:43:54 -05:00
"sync"
2020-10-14 16:47:20 -05:00
"time"
gqlHandler "github.com/99designs/gqlgen/graphql/handler"
2021-10-15 10:05:00 -05:00
"github.com/gorilla/mux"
2022-02-24 15:31:36 -05:00
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
2022-10-20 11:39:20 -05:00
2021-12-03 22:50:58 -05:00
"zotregistry.io/zot/pkg/api/config"
2022-02-24 15:31:36 -05:00
"zotregistry.io/zot/pkg/api/constants"
2021-12-03 22:50:58 -05:00
"zotregistry.io/zot/pkg/extensions/search"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
2022-07-15 06:10:51 -05:00
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
2021-12-03 22:50:58 -05:00
"zotregistry.io/zot/pkg/log"
2023-01-09 15:37:44 -05:00
"zotregistry.io/zot/pkg/meta/repodb"
2023-03-02 12:43:54 -05:00
"zotregistry.io/zot/pkg/scheduler"
2021-12-03 22:50:58 -05:00
"zotregistry.io/zot/pkg/storage"
2020-10-14 16:47:20 -05:00
)
2023-03-02 12:43:54 -05:00
type (
CveInfo cveinfo . CveInfo
state int
)
const (
pending state = iota
running
done
)
2022-09-28 13:39:54 -05:00
2023-02-10 00:04:52 -05:00
func GetCVEInfo ( config * config . Config , storeController storage . StoreController ,
2023-01-09 15:37:44 -05:00
repoDB repodb . RepoDB , log log . Logger ,
2023-02-10 00:04:52 -05:00
) CveInfo {
if config . Extensions . Search == nil || ! * config . Extensions . Search . Enable || config . Extensions . Search . CVE == nil {
return nil
}
dbRepository := ""
if config . Extensions . Search . CVE . Trivy != nil {
dbRepository = config . Extensions . Search . CVE . Trivy . DBRepository
}
return cveinfo . NewCVEInfo ( storeController , repoDB , dbRepository , log )
}
func EnableSearchExtension ( config * config . Config , storeController storage . StoreController ,
2023-03-02 12:43:54 -05:00
repoDB repodb . RepoDB , taskScheduler * scheduler . Scheduler , cveInfo CveInfo , log log . Logger ,
2023-01-09 15:37:44 -05:00
) {
2021-12-28 08:29:30 -05:00
if config . Extensions . Search != nil && * config . Extensions . Search . Enable && config . Extensions . Search . CVE != nil {
2020-10-14 16:47:20 -05:00
defaultUpdateInterval , _ := time . ParseDuration ( "2h" )
2021-06-08 15:11:18 -05:00
if config . Extensions . Search . CVE . UpdateInterval < defaultUpdateInterval {
config . Extensions . Search . CVE . UpdateInterval = defaultUpdateInterval
2020-10-14 16:47:20 -05:00
2022-03-21 12:37:23 -05:00
log . Warn ( ) . Msg ( "CVE update interval set to too-short interval < 2h, changing update duration to 2 hours and continuing." ) //nolint:lll // gofumpt conflicts with lll
2020-10-14 16:47:20 -05:00
}
2023-03-02 12:43:54 -05:00
updateInterval := config . Extensions . Search . CVE . UpdateInterval
downloadTrivyDB ( updateInterval , taskScheduler , cveInfo , log )
2020-10-14 16:47:20 -05:00
} else {
2020-10-22 19:31:16 -05:00
log . Info ( ) . Msg ( "CVE config not provided, skipping CVE update" )
2020-10-14 16:47:20 -05:00
}
2022-04-27 01:00:20 -05:00
}
2021-06-08 15:11:18 -05:00
2023-03-02 12:43:54 -05:00
func downloadTrivyDB ( interval time . Duration , sch * scheduler . Scheduler , cveInfo CveInfo , log log . Logger ) {
2023-03-22 11:52:48 -05:00
generator := NewTrivyTaskGenerator ( interval , cveInfo , log )
2021-10-28 04:10:01 -05:00
2023-03-02 12:43:54 -05:00
sch . SubmitGenerator ( generator , interval , scheduler . HighPriority )
}
2021-10-28 04:10:01 -05:00
2023-03-22 11:52:48 -05:00
func NewTrivyTaskGenerator ( interval time . Duration , cveInfo CveInfo , log log . Logger ) * TrivyTaskGenerator {
generator := & TrivyTaskGenerator { interval , cveInfo , log , pending , 0 , time . Now ( ) , & sync . Mutex { } }
return generator
}
type TrivyTaskGenerator struct {
2023-03-02 12:43:54 -05:00
interval time . Duration
cveInfo CveInfo
log log . Logger
status state
waitTime time . Duration
lastTaskTime time . Time
lock * sync . Mutex
}
2023-03-22 11:52:48 -05:00
func ( gen * TrivyTaskGenerator ) GenerateTask ( ) ( scheduler . Task , error ) {
2023-03-02 12:43:54 -05:00
var newTask scheduler . Task
2022-04-27 01:00:20 -05:00
2023-03-02 12:43:54 -05:00
gen . lock . Lock ( )
2023-03-10 13:08:53 -05:00
if gen . status == pending && time . Since ( gen . lastTaskTime ) >= gen . waitTime {
2023-03-02 12:43:54 -05:00
newTask = newTrivyTask ( gen . interval , gen . cveInfo , gen , gen . log )
gen . status = running
2021-06-08 15:11:18 -05:00
}
2023-03-02 12:43:54 -05:00
gen . lock . Unlock ( )
return newTask , nil
}
2023-03-22 11:52:48 -05:00
func ( gen * TrivyTaskGenerator ) IsDone ( ) bool {
2023-03-02 12:43:54 -05:00
gen . lock . Lock ( )
status := gen . status
gen . lock . Unlock ( )
return status == done
}
2023-03-22 11:52:48 -05:00
func ( gen * TrivyTaskGenerator ) Reset ( ) {
2023-03-02 12:43:54 -05:00
gen . lock . Lock ( )
gen . status = pending
gen . waitTime = 0
gen . lock . Unlock ( )
}
type trivyTask struct {
interval time . Duration
cveInfo cveinfo . CveInfo
2023-03-22 11:52:48 -05:00
generator * TrivyTaskGenerator
2023-03-02 12:43:54 -05:00
log log . Logger
}
func newTrivyTask ( interval time . Duration , cveInfo cveinfo . CveInfo ,
2023-03-22 11:52:48 -05:00
generator * TrivyTaskGenerator , log log . Logger ,
2023-03-02 12:43:54 -05:00
) * trivyTask {
return & trivyTask { interval , cveInfo , generator , log }
}
func ( trivyT * trivyTask ) DoWork ( ) error {
trivyT . log . Info ( ) . Msg ( "updating the CVE database" )
err := trivyT . cveInfo . UpdateDB ( )
if err != nil {
trivyT . generator . lock . Lock ( )
trivyT . generator . status = pending
2023-03-22 11:52:48 -05:00
if trivyT . generator . waitTime == 0 {
trivyT . generator . waitTime = time . Second
}
trivyT . generator . waitTime *= 2
2023-03-02 12:43:54 -05:00
trivyT . generator . lastTaskTime = time . Now ( )
trivyT . generator . lock . Unlock ( )
return err
}
trivyT . generator . lock . Lock ( )
trivyT . generator . lastTaskTime = time . Now ( )
trivyT . generator . status = done
trivyT . generator . lock . Unlock ( )
trivyT . log . Info ( ) . Str ( "DB update completed, next update scheduled after" , trivyT . interval . String ( ) ) . Msg ( "" )
return nil
2020-10-14 16:47:20 -05:00
}
2023-02-27 14:25:47 -05:00
func addSearchSecurityHeaders ( h http . Handler ) http . HandlerFunc { //nolint:varnamelen
return func ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "X-Content-Type-Options" , "nosniff" )
h . ServeHTTP ( w , r )
}
}
2022-04-27 01:00:20 -05:00
func SetupSearchRoutes ( config * config . Config , router * mux . Router , storeController storage . StoreController ,
2023-02-10 00:04:52 -05:00
repoDB repodb . RepoDB , cveInfo CveInfo , log log . Logger ,
2022-04-27 01:00:20 -05:00
) {
log . Info ( ) . Msg ( "setting up search routes" )
2022-03-04 02:37:06 -05:00
2022-04-27 01:00:20 -05:00
if config . Extensions . Search != nil && * config . Extensions . Search . Enable {
2023-02-10 00:04:52 -05:00
resConfig := search . GetResolverConfig ( log , storeController , repoDB , cveInfo )
2022-04-27 01:00:20 -05:00
2023-02-10 17:52:54 -05:00
extRouter := router . PathPrefix ( constants . ExtSearchPrefix ) . Subrouter ( )
2023-05-11 08:05:14 -05:00
extRouter . Use ( SearchACHeadersHandler ( ) )
2023-02-10 17:52:54 -05:00
extRouter . Methods ( "GET" , "POST" , "OPTIONS" ) .
2023-02-27 14:25:47 -05:00
Handler ( addSearchSecurityHeaders ( gqlHandler . NewDefaultServer ( gql_generated . NewExecutableSchema ( resConfig ) ) ) )
2022-03-04 02:37:06 -05:00
}
}
2023-05-11 08:05:14 -05:00
func SearchACHeadersHandler ( ) mux . MiddlewareFunc {
return func ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( resp http . ResponseWriter , req * http . Request ) {
resp . Header ( ) . Set ( "Access-Control-Allow-Methods" , "HEAD,GET,POST,OPTIONS" )
resp . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Authorization,content-type" )
if req . Method == http . MethodOptions {
return
}
next . ServeHTTP ( resp , req )
} )
}
}
2022-05-23 14:22:52 -05:00
func getExtension ( name , url , description string , endpoints [ ] string ) distext . Extension {
2022-02-24 15:31:36 -05:00
return distext . Extension {
Name : name ,
URL : url ,
Description : description ,
2022-05-23 14:22:52 -05:00
Endpoints : endpoints ,
2022-02-24 15:31:36 -05:00
}
}
func GetExtensions ( config * config . Config ) distext . ExtensionList {
extensionList := distext . ExtensionList { }
extensions := make ( [ ] distext . Extension , 0 )
if config . Extensions != nil && config . Extensions . Search != nil {
2022-10-18 22:46:06 -05:00
endpoints := [ ] string { constants . FullSearchPrefix }
2022-05-25 12:49:22 -05:00
searchExt := getExtension ( "_zot" ,
2022-10-11 11:01:59 -05:00
"https://github.com/project-zot/zot/blob/" + config . ReleaseTag + "/pkg/extensions/_zot.md" ,
"zot registry extensions" ,
2022-05-25 12:49:22 -05:00
endpoints )
2022-02-24 15:31:36 -05:00
extensions = append ( extensions , searchExt )
}
2023-03-09 13:43:26 -05:00
if config . Extensions != nil && config . Extensions . Mgmt != nil {
endpoints := [ ] string { constants . FullMgmtPrefix }
mgmtExt := getExtension ( "_zot" ,
"https://github.com/project-zot/zot/blob/" + config . ReleaseTag + "/pkg/extensions/_zot.md" ,
"zot registry extensions" ,
endpoints )
extensions = append ( extensions , mgmtExt )
}
2022-02-24 15:31:36 -05:00
extensionList . Extensions = extensions
return extensionList
}