1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 11:55:40 +01:00

Merge branch 'main' into client-certificate

This commit is contained in:
Bernhard Reiter 2022-02-15 10:01:18 +01:00
commit a71f490999
No known key found for this signature in database
GPG key ID: 2B7BA3BF9BC3A554
7 changed files with 236 additions and 24 deletions

57
Makefile Normal file
View file

@ -0,0 +1,57 @@
# This file is Free Software under the MIT License
# without warranty, see README.md and LICENSES/MIT.txt for details.
#
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: 2021 German Federal Office for Information Security (BSI) <https://www.bsi.bund.de>
# Software-Engineering: 2021 Intevation GmbH <https://intevation.de>
#
# Makefile to build csaf_distribution components
SHELL = /bin/bash
BUILD = go build
MKDIR = mkdir -p
.PHONY: build build_linux build_win tag_checked_out mostlyclean
all:
@echo choose a target from: build build_linux build_win mostlyclean
@echo prepend \`make BUILDTAG=1\` to checkout the highest git tag before building
@echo or set BUILDTAG to a specific tag
# Build all binaries
build: build_linux build_win
# if BUILDTAG == 1 set it to the highest git tag
ifeq ($(strip $(BUILDTAG)),1)
override BUILDTAG = $(shell git tag --sort=-version:refname | head -n 1)
endif
ifdef BUILDTAG
# add the git tag checkout to the requirements of our build targets
build_linux build_win: tag_checked_out
endif
tag_checked_out:
$(if $(strip $(BUILDTAG)),,$(error no git tag found))
git checkout -q tags/${BUILDTAG}
@echo Don\'t forget that we are in checked out tag $(BUILDTAG) now.
# Build binaries and place them under bin-$(GOOS)-$(GOARCH)
# Using 'Target-specific Variable Values' to specify the build target system
GOARCH = amd64
build_linux: GOOS = linux
build_win: GOOS = windows
build_linux build_win:
$(eval BINDIR = bin-$(GOOS)-$(GOARCH)/ )
$(MKDIR) $(BINDIR)
env GOARCH=$(GOARCH) GOOS=$(GOOS) $(BUILD) -o $(BINDIR) -v ./cmd/...
# Remove bin-*-* directories
mostlyclean:
rm -rf ./bin-*-*
@echo Files in \`go env GOCACHE\` remain.

View file

@ -10,12 +10,19 @@
- Clone the repository `git clone https://github.com/csaf-poc/csaf_distribution.git `
- Build Go components
``` bash
cd csaf_distribution
go build -v ./cmd/...
```
Makefile supplies the following targets:
- Build For GNU/Linux System: `make build_linux`
- Build For Windows System (cross build): `make build_win`
- Build For both linux and windows: `make build`
- Build from a specific github tag by passing the intended tag to the `BUILDTAG` variable.
E.g. `make BUILDTAG=v1.0.0 build` or `make BUILDTAG=1 build_linux`.
The special value `1` means checking out the highest github tag for the build.
- Remove the generated binaries und their directories: `make mostlyclean`
- [Install](http://nginx.org/en/docs/install.html) **nginx**
Binaries will be placed in directories named like `bin-linux-amd64/` and `bin-windows-amd64/`.
- [Install](https://nginx.org/en/docs/install.html) **nginx**
- To install server certificate on nginx see [docs/install-server-certificate.md](docs/install-server-certificate.md)
- To configure nginx see [docs/provider-setup.md](docs/provider-setup.md)
- To configure nginx for client certificate authentication see [docs/client-certificate-setup.md](docs/client-certificate-setup.md)

View file

@ -64,7 +64,12 @@ type reporter interface {
report(*processor, *Domain)
}
var errContinue = errors.New("continue")
var (
// errContinue indicates that the current check should continue.
errContinue = errors.New("continue")
// errStop indicates that the current check should stop.
errStop = errors.New("stop")
)
type whereType byte
@ -102,8 +107,6 @@ func (wt whereType) String() string {
func newProcessor(opts *options) *processor {
return &processor{
opts: opts,
redirects: map[string]string{},
noneTLS: map[string]struct{}{},
alreadyChecked: map[string]whereType{},
builder: gval.Full(jsonpath.Language()),
exprs: map[string]gval.Evaluable{},
@ -111,12 +114,8 @@ func newProcessor(opts *options) *processor {
}
func (p *processor) clean() {
for k := range p.redirects {
delete(p.redirects, k)
}
for k := range p.noneTLS {
delete(p.noneTLS, k)
}
p.redirects = nil
p.noneTLS = nil
for k := range p.alreadyChecked {
delete(p.alreadyChecked, k)
}
@ -132,16 +131,14 @@ func (p *processor) clean() {
p.badIndices = nil
p.badChanges = nil
}
func (p *processor) run(reporters []reporter, domains []string) (*Report, error) {
var report Report
domainsLoop:
for _, d := range domains {
if err := p.checkDomain(d); err != nil {
if err == errContinue {
continue domainsLoop
if err == errContinue || err == errStop {
continue
}
return nil, err
}
@ -167,6 +164,9 @@ func (p *processor) checkDomain(domain string) error {
(*processor).checkMissing,
} {
if err := check(p, domain); err != nil && err != errContinue {
if err == errStop {
return nil
}
return err
}
}
@ -190,6 +190,9 @@ func (p *processor) jsonPath(expr string, doc interface{}) (interface{}, error)
}
func (p *processor) checkTLS(u string) {
if p.noneTLS == nil {
p.noneTLS = map[string]struct{}{}
}
if x, err := url.Parse(u); err == nil && x.Scheme != "https" {
p.noneTLS[u] = struct{}{}
}
@ -212,6 +215,9 @@ func (p *processor) checkRedirect(r *http.Request, via []*http.Request) error {
}
url := r.URL.String()
p.checkTLS(url)
if p.redirects == nil {
p.redirects = map[string]string{}
}
p.redirects[url] = path.String()
if len(via) > 10 {
@ -241,6 +247,16 @@ func (p *processor) httpClient() *http.Client {
return p.client
}
func use(s *[]string) {
if *s == nil {
*s = []string{}
}
}
func used(s []string) bool {
return s != nil
}
func (p *processor) badIntegrity(format string, args ...interface{}) {
p.badIntegrities = append(p.badIntegrities, fmt.Sprintf(format, args...))
}
@ -337,6 +353,8 @@ func (p *processor) integrity(
}
// Check if file is in the right folder.
use(&p.badFolders)
if date, err := p.jsonPath(
`$.document.tracking.initial_release_date`, doc); err != nil {
p.badFolder(
@ -353,6 +371,8 @@ func (p *processor) integrity(
}
// Check hashes
use(&p.badIntegrities)
for _, x := range []struct {
ext string
hash []byte
@ -393,6 +413,8 @@ func (p *processor) integrity(
sigFile := u + ".asc"
p.checkTLS(sigFile)
use(&p.badSignatures)
if res, err = client.Get(sigFile); err != nil {
p.badSignature("Fetching %s failed: %v.", sigFile, err)
continue
@ -490,6 +512,9 @@ func (p *processor) checkIndex(base string, mask whereType) error {
client := p.httpClient()
index := base + "/index.txt"
p.checkTLS(index)
use(&p.badIndices)
res, err := client.Get(index)
if err != nil {
p.badIndex("Fetching %s failed: %v", index, err)
@ -526,6 +551,9 @@ func (p *processor) checkChanges(base string, mask whereType) error {
changes := base + "/changes.csv"
p.checkTLS(changes)
res, err := client.Get(changes)
use(&p.badChanges)
if err != nil {
p.badChange("Fetching %s failed: %v", changes, err)
return errContinue
@ -679,16 +707,18 @@ func (p *processor) checkProviderMetadata(domain string) error {
url := "https://" + domain + "/.well-known/csaf/provider-metadata.json"
use(&p.badProviderMetadatas)
res, err := client.Get(url)
if err != nil {
p.badProviderMetadata("Fetching %s: %v.", url, err)
return errContinue
return errStop
}
if res.StatusCode != http.StatusOK {
p.badProviderMetadata("Fetching %s failed. Status code: %d (%s)",
url, res.StatusCode, res.Status)
return errContinue
return errStop
}
// Calculate checksum for later comparison.
@ -700,7 +730,7 @@ func (p *processor) checkProviderMetadata(domain string) error {
return json.NewDecoder(tee).Decode(&p.pmd)
}(); err != nil {
p.badProviderMetadata("Decoding JSON failed: %v", err)
return errContinue
return errStop
}
p.pmd256 = hash.Sum(nil)
@ -722,6 +752,8 @@ func (p *processor) checkSecurity(domain string) error {
client := p.httpClient()
use(&p.badSecurities)
path := "https://" + domain + "/.well-known/security.txt"
res, err := client.Get(path)
if err != nil {
@ -796,6 +828,8 @@ func (p *processor) checkSecurity(domain string) error {
func (p *processor) checkPGPKeys(domain string) error {
use(&p.badPGPs)
src, err := p.jsonPath("$.pgp_keys", p.pmd)
if err != nil {
p.badPGP("No PGP keys found: %v.", err)

View file

@ -44,6 +44,10 @@ func (bc *baseReporter) requirement(domain *Domain) *Requirement {
func (r *tlsReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if p.noneTLS == nil {
req.message("No TLS checks performed.")
return
}
if len(p.noneTLS) == 0 {
req.message("All tested URLs were https.")
return
@ -82,6 +86,10 @@ func (r *redirectsReporter) report(p *processor, domain *Domain) {
func (r *providerMetadataReport) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badProviderMetadatas) {
req.message("No provider-metadata.json checked.")
return
}
if len(p.badProviderMetadatas) == 0 {
req.message("No problems with provider metadata.")
return
@ -91,6 +99,10 @@ func (r *providerMetadataReport) report(p *processor, domain *Domain) {
func (r *securityReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badSecurities) {
req.message("No security.txt checked.")
return
}
if len(p.badSecurities) == 0 {
req.message("No problems with security.txt found.")
return
@ -112,6 +124,10 @@ func (r *dnsPathReporter) report(_ *processor, domain *Domain) {
func (r *oneFolderPerYearReport) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badFolders) {
req.message("No checks if file are in right folders were performed.")
return
}
if len(p.badFolders) == 0 {
req.message("All CSAF files are in the right folders.")
return
@ -121,6 +137,10 @@ func (r *oneFolderPerYearReport) report(p *processor, domain *Domain) {
func (r *indexReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badIndices) {
req.message("No index.txt checked.")
return
}
if len(p.badIndices) == 0 {
req.message("No problems with index.txt found.")
return
@ -130,6 +150,10 @@ func (r *indexReporter) report(p *processor, domain *Domain) {
func (r *changesReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badChanges) {
req.message("No changes.csv checked.")
return
}
if len(p.badChanges) == 0 {
req.message("No problems with changes.csv found.")
return
@ -145,6 +169,10 @@ func (r *directoryListingsReporter) report(_ *processor, domain *Domain) {
func (r *integrityReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badIntegrities) {
req.message("No checksums checked.")
return
}
if len(p.badIntegrities) == 0 {
req.message("All checksums match.")
return
@ -154,6 +182,10 @@ func (r *integrityReporter) report(p *processor, domain *Domain) {
func (r *signaturesReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badSignatures) {
req.message("No signatures checked.")
return
}
req.Messages = p.badSignatures
if len(p.badSignatures) == 0 {
req.message("All signatures verified.")
@ -162,6 +194,10 @@ func (r *signaturesReporter) report(p *processor, domain *Domain) {
func (r *publicPGPKeyReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badPGPs) {
req.message("No PGP keys loaded.")
return
}
req.Messages = p.badPGPs
if len(p.keys) > 0 {
req.message(fmt.Sprintf("%d PGP key(s) loaded successfully.", len(p.keys)))

View file

@ -121,9 +121,15 @@ func (cs *compiledSchema) validate(doc interface{}) ([]string, error) {
res := make([]string, 0, len(errs))
for i := range errs {
if e := &errs[i]; e.InstanceLocation != "" && e.Error != "" {
res = append(res, e.InstanceLocation+": "+e.Error)
e := &errs[i]
if e.Error == "" {
continue
}
loc := e.InstanceLocation
if loc == "" {
loc = e.AbsoluteKeywordLocation
}
res = append(res, loc+": "+e.Error)
}
return res, nil

View file

@ -0,0 +1,72 @@
# Configure TLS Certificate for HTTPS
## Get a webserver TLS certificate
There are three ways to get a TLS certificate for your HTTPS server:
1. Get it from a certificate provider who will run a certificate
authority (CA) and also offers
[extended validation](https://en.wikipedia.org/wiki/Extended_Validation_Certificate) (EV)
for the certificate. This will cost a fee.
If possible, create the private key yourself,
then send a Certificate Signing Request (CSR).
Overall follow the documentation of the CA operator.
2. Get a domain validated TLS certificate via
[Let's encrypt](https://letsencrypt.org/) without a fee.
See their instruction, e.g.
[certbot for nignx on Ubuntu](https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal).
3. Run your own little CA. Which has the major drawback that someone
will have to import the root certificate in the webbrowsers manually.
Suitable for development purposes.
To decide between 1. and 2. you will need to weight the extra
efforts and costs of the level of extended validation against
a bit of extra trust for the security advisories
that will be served under the domain.
## Install the files for ngnix
Place the certificates on the server machine.
This includes the certificate for your webserver, the intermediate
certificates and the root certificate. The latter may already be on your
machine as part of the trust anchors for webbrowsers.
Follow the [nginx documentation](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/)
to further configure TLS with your private key and the certificates.
We recommend to
* restrict the TLS protocol version and ciphers following a current
recommendation (e.g. [BSI-TR-02102-2](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TG02102/BSI-TR-02102-2.html)).
### Example configuration
Assuming the relevant server block is in `/etc/nginx/sites-enabled/default`,
change the `listen` configuration and add options so nginx
finds your your private key and the certificate chain.
```nginx
server {
listen 443 ssl http2 default_server; # ipv4
listen [::]:443 ssl http2 default_server; # ipv6
server_name www.example.com
ssl_certificate /etc/ssl/{domainName}.pem; # or bundle.crt
ssl_certificate_key /etc/ssl/{domainName}.key";
ssl_protocols TLSv1.2 TLSv1.3;
# Other Config
# ...
}
```
Replace `{domainName}` with the name for your certificate in the example.
Reload or restart nginx to apply the changes (e.g. `systemctl reload nginx`
on Debian or Ubuntu.)
Technical hints:
* When allowing or requiring `TLSv1.3` webbrowsers like
Chromium (seen with version 98) may have higher requirements
on the server certificates they allow,
otherwise they do not connect with `ERR_SSL_KEY_USAGE_INCOMPATIBLE`.

View file

@ -7,7 +7,7 @@ The following instructions are for an Debian 11 server setup.
```(shell)
apt-get install nginx fcgiwrap
cp /usr/share/doc/fcgiwrap/examples/nginx.conf /etc/nginx/fcgiwrap.conf
systemctl status fcgiwrap.servic
systemctl status fcgiwrap.service
systemctl status fcgiwrap.socket
systemctl is-enabled fcgiwrap.service
systemctl is-enabled fcgiwrap.socket