Skip to content

Commit

Permalink
Merge branch 'master' into fix_799
Browse files Browse the repository at this point in the history
  • Loading branch information
buixor authored May 14, 2024
2 parents 1ace3f1 + e3c6a5b commit 5cfc3c5
Show file tree
Hide file tree
Showing 21 changed files with 147 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/bats-hub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: "Install bats dependencies"
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bats-mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: "Install bats dependencies"
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bats-postgres.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: "Install bats dependencies"
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bats-sqlite-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: "Install bats dependencies"
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-windows-build-msi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: Build
run: make windows_installer BUILD_RE2_WASM=1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"
cache-dependency-path: "**/go.sum"

# Initializes the CodeQL tools for scanning.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/go-tests-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: Build
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/go-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: Create localstack streams
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-tarball-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: "Set up Go"
uses: actions/setup-go@v5
with:
go-version: "1.22.2"
go-version: "1.22.3"

- name: Build the binaries
run: |
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# vim: set ft=dockerfile:
FROM golang:1.22.2-alpine3.18 AS build
FROM golang:1.22.3-alpine3.18 AS build

ARG BUILD_VERSION

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.debian
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# vim: set ft=dockerfile:
FROM golang:1.22.2-bookworm AS build
FROM golang:1.22.3-bookworm AS build

ARG BUILD_VERSION

Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ stages:
- task: GoTool@0
displayName: "Install Go"
inputs:
version: '1.22.2'
version: '1.22.3'

- pwsh: |
choco install -y make
Expand Down
18 changes: 13 additions & 5 deletions cmd/notification-slack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ import (
)

type PluginConfig struct {
Name string `yaml:"name"`
Webhook string `yaml:"webhook"`
LogLevel *string `yaml:"log_level"`
Name string `yaml:"name"`
Webhook string `yaml:"webhook"`
Channel string `yaml:"channel"`
Username string `yaml:"username"`
IconEmoji string `yaml:"icon_emoji"`
IconURL string `yaml:"icon_url"`
LogLevel *string `yaml:"log_level"`
}
type Notify struct {
ConfigByName map[string]PluginConfig
Expand All @@ -43,8 +47,12 @@ func (n *Notify) Notify(ctx context.Context, notification *protobufs.Notificatio
logger.Info(fmt.Sprintf("found notify signal for %s config", notification.Name))
logger.Debug(fmt.Sprintf("posting to %s webhook, message %s", cfg.Webhook, notification.Text))

err := slack.PostWebhookContext(ctx, n.ConfigByName[notification.Name].Webhook, &slack.WebhookMessage{
Text: notification.Text,
err := slack.PostWebhookContext(ctx, cfg.Webhook, &slack.WebhookMessage{
Text: notification.Text,
Channel: cfg.Channel,
Username: cfg.Username,
IconEmoji: cfg.IconEmoji,
IconURL: cfg.IconURL,
})
if err != nil {
logger.Error(err.Error())
Expand Down
6 changes: 6 additions & 0 deletions cmd/notification-slack/slack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ format: |
webhook: <WEBHOOK_URL>

# API request data as defined by the Slack webhook API.
#channel: <CHANNEL_NAME>
#username: <USERNAME>
#icon_emoji: <ICON_EMOJI>
#icon_url: <ICON_URL>

---

# type: slack
Expand Down
1 change: 1 addition & 0 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ override_dh_auto_install:

mkdir -p debian/crowdsec/usr/bin
mkdir -p debian/crowdsec/etc/crowdsec
mkdir -p debian/crowdsec/etc/crowdsec/acquis.d
mkdir -p debian/crowdsec/usr/share/crowdsec
mkdir -p debian/crowdsec/etc/crowdsec/hub/
mkdir -p debian/crowdsec/usr/share/crowdsec/config
Expand Down
63 changes: 33 additions & 30 deletions pkg/apiserver/middlewares/v1/tls_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -135,31 +136,35 @@ func (ta *TLSAuth) isCRLRevoked(cert *x509.Certificate) (bool, bool) {
return false, false
}

crlBinary, rest := pem.Decode(crlContent)
if len(rest) > 0 {
ta.logger.Warn("CRL file contains more than one PEM block, ignoring the rest")
}
var crlBlock *pem.Block

crl, err := x509.ParseRevocationList(crlBinary.Bytes)
if err != nil {
ta.logger.Errorf("could not parse CRL file, skipping check: %s", err)
return false, false
}
for {
crlBlock, crlContent = pem.Decode(crlContent)
if crlBlock == nil {
break // no more PEM blocks
}

now := time.Now().UTC()
crl, err := x509.ParseRevocationList(crlBlock.Bytes)
if err != nil {
ta.logger.Errorf("could not parse a PEM block in CRL file, skipping: %s", err)
continue
}

if now.After(crl.NextUpdate) {
ta.logger.Warn("CRL has expired, will still validate the cert against it.")
}
now := time.Now().UTC()

if now.Before(crl.ThisUpdate) {
ta.logger.Warn("CRL is not yet valid, will still validate the cert against it.")
}
if now.After(crl.NextUpdate) {
ta.logger.Warn("CRL has expired, will still validate the cert against it.")
}

for _, revoked := range crl.RevokedCertificateEntries {
if revoked.SerialNumber.Cmp(cert.SerialNumber) == 0 {
ta.logger.Warn("client certificate is revoked by CRL")
return true, true
if now.Before(crl.ThisUpdate) {
ta.logger.Warn("CRL is not yet valid, will still validate the cert against it.")
}

for _, revoked := range crl.RevokedCertificateEntries {
if revoked.SerialNumber.Cmp(cert.SerialNumber) == 0 {
ta.logger.Warn("client certificate is revoked by CRL")
return true, true
}
}
}

Expand All @@ -181,9 +186,7 @@ func (ta *TLSAuth) isRevoked(cert *x509.Certificate, issuer *x509.Certificate) (
}

revokedByOCSP, cacheOCSP := ta.isOCSPRevoked(cert, issuer)

revokedByCRL, cacheCRL := ta.isCRLRevoked(cert)

revoked := revokedByOCSP || revokedByCRL

if cacheOCSP && cacheCRL {
Expand All @@ -203,8 +206,8 @@ func (ta *TLSAuth) isInvalid(cert *x509.Certificate, issuer *x509.Certificate) (

revoked, err := ta.isRevoked(cert, issuer)
if err != nil {
//Fail securely, if we can't check the revocation status, let's consider the cert invalid
//We may change this in the future based on users feedback, but this seems the most sensible thing to do
// Fail securely, if we can't check the revocation status, let's consider the cert invalid
// We may change this in the future based on users feedback, but this seems the most sensible thing to do
return true, fmt.Errorf("could not check for client certification revocation status: %w", err)
}

Expand All @@ -213,12 +216,12 @@ func (ta *TLSAuth) isInvalid(cert *x509.Certificate, issuer *x509.Certificate) (

func (ta *TLSAuth) SetAllowedOu(allowedOus []string) error {
for _, ou := range allowedOus {
//disallow empty ou
// disallow empty ou
if ou == "" {
return fmt.Errorf("empty ou isn't allowed")
return errors.New("empty ou isn't allowed")
}

//drop & warn on duplicate ou
// drop & warn on duplicate ou
ok := true

for _, validOu := range ta.AllowedOUs {
Expand All @@ -238,11 +241,11 @@ func (ta *TLSAuth) SetAllowedOu(allowedOus []string) error {
}

func (ta *TLSAuth) ValidateCert(c *gin.Context) (bool, string, error) {
//Checks cert validity, Returns true + CN if client cert matches requested OU
// Checks cert validity, Returns true + CN if client cert matches requested OU
var clientCert *x509.Certificate

if c.Request.TLS == nil || len(c.Request.TLS.PeerCertificates) == 0 {
//do not error if it's not TLS or there are no peer certs
// do not error if it's not TLS or there are no peer certs
return false, "", nil
}

Expand Down Expand Up @@ -279,7 +282,7 @@ func (ta *TLSAuth) ValidateCert(c *gin.Context) (bool, string, error) {
return true, clientCert.Subject.CommonName, nil
}

return false, "", fmt.Errorf("no verified cert in request")
return false, "", errors.New("no verified cert in request")
}

func NewTLSAuth(allowedOus []string, crlPath string, cacheExpiration time.Duration, logger *log.Entry) (*TLSAuth, error) {
Expand Down
1 change: 1 addition & 0 deletions rpm/SPECS/crowdsec.spec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ sed -i "s#/usr/local/lib/crowdsec/plugins/#%{_libdir}/%{name}/plugins/#g" config

%install
rm -rf %{buildroot}
mkdir -p %{buildroot}/etc/crowdsec/acquis.d
mkdir -p %{buildroot}/etc/crowdsec/hub
mkdir -p %{buildroot}/etc/crowdsec/patterns
mkdir -p %{buildroot}/etc/crowdsec/console/
Expand Down
50 changes: 33 additions & 17 deletions test/bats/11_bouncers_tls.bats
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,37 @@ setup_file() {
CFDIR="${BATS_TEST_DIRNAME}/testdata/cfssl"
export CFDIR

#gen the CA
# Generate the CA
cfssl gencert --initca "${CFDIR}/ca.json" 2>/dev/null | cfssljson --bare "${tmpdir}/ca"
#gen an intermediate

# Generate an intermediate
cfssl gencert --initca "${CFDIR}/intermediate.json" 2>/dev/null | cfssljson --bare "${tmpdir}/inter"
cfssl sign -ca "${tmpdir}/ca.pem" -ca-key "${tmpdir}/ca-key.pem" -config "${CFDIR}/profiles.json" -profile intermediate_ca "${tmpdir}/inter.csr" 2>/dev/null | cfssljson --bare "${tmpdir}/inter"
#gen server cert for crowdsec with the intermediate

# Generate server cert for crowdsec with the intermediate
cfssl gencert -ca "${tmpdir}/inter.pem" -ca-key "${tmpdir}/inter-key.pem" -config "${CFDIR}/profiles.json" -profile=server "${CFDIR}/server.json" 2>/dev/null | cfssljson --bare "${tmpdir}/server"
#gen client cert for the bouncer

# Generate client cert for the bouncer
cfssl gencert -ca "${tmpdir}/inter.pem" -ca-key "${tmpdir}/inter-key.pem" -config "${CFDIR}/profiles.json" -profile=client "${CFDIR}/bouncer.json" 2>/dev/null | cfssljson --bare "${tmpdir}/bouncer"
#gen client cert for the bouncer with an invalid OU

# Genearte client cert for the bouncer with an invalid OU
cfssl gencert -ca "${tmpdir}/inter.pem" -ca-key "${tmpdir}/inter-key.pem" -config "${CFDIR}/profiles.json" -profile=client "${CFDIR}/bouncer_invalid.json" 2>/dev/null | cfssljson --bare "${tmpdir}/bouncer_bad_ou"
#gen client cert for the bouncer directly signed by the CA, it should be refused by crowdsec as uses the intermediate

# Generate client cert for the bouncer directly signed by the CA, it should be refused by crowdsec as uses the intermediate
cfssl gencert -ca "${tmpdir}/ca.pem" -ca-key "${tmpdir}/ca-key.pem" -config "${CFDIR}/profiles.json" -profile=client "${CFDIR}/bouncer.json" 2>/dev/null | cfssljson --bare "${tmpdir}/bouncer_invalid"

cfssl gencert -ca "${tmpdir}/inter.pem" -ca-key "${tmpdir}/inter-key.pem" -config "${CFDIR}/profiles.json" -profile=client "${CFDIR}/bouncer.json" 2>/dev/null | cfssljson --bare "${tmpdir}/bouncer_revoked"
serial="$(openssl x509 -noout -serial -in "${tmpdir}/bouncer_revoked.pem" | cut -d '=' -f2)"
echo "ibase=16; ${serial}" | bc >"${tmpdir}/serials.txt"
cfssl gencrl "${tmpdir}/serials.txt" "${tmpdir}/ca.pem" "${tmpdir}/ca-key.pem" | base64 -d | openssl crl -inform DER -out "${tmpdir}/crl.pem"
# Generate revoked client certs
for cert_name in "revoked_1" "revoked_2"; do
cfssl gencert -ca "${tmpdir}/inter.pem" -ca-key "${tmpdir}/inter-key.pem" -config "${CFDIR}/profiles.json" -profile=client "${CFDIR}/bouncer.json" 2>/dev/null | cfssljson --bare "${tmpdir}/${cert_name}"
serial="$(openssl x509 -noout -serial -in "${tmpdir}/${cert_name}.pem" | cut -d '=' -f2)"
echo "ibase=16; ${serial}" | bc >"${tmpdir}/serials_${cert_name}.txt"
done

# Generate separate CRL blocks and concatenate them
for cert_name in "revoked_1" "revoked_2"; do
cfssl gencrl "${tmpdir}/serials_${cert_name}.txt" "${tmpdir}/ca.pem" "${tmpdir}/ca-key.pem" | base64 -d | openssl crl -inform DER -out "${tmpdir}/crl_${cert_name}.pem"
done
cat "${tmpdir}/crl_revoked_1.pem" "${tmpdir}/crl_revoked_2.pem" >"${tmpdir}/crl.pem"

cat "${tmpdir}/ca.pem" "${tmpdir}/inter.pem" > "${tmpdir}/bundle.pem"

Expand Down Expand Up @@ -90,11 +103,14 @@ teardown() {
}

@test "simulate one bouncer request with a revoked certificate" {
truncate_log
rune -0 curl -i -s --cert "${tmpdir}/bouncer_revoked.pem" --key "${tmpdir}/bouncer_revoked-key.pem" --cacert "${tmpdir}/bundle.pem" https://localhost:8080/v1/decisions\?ip=42.42.42.42
assert_log --partial "client certificate is revoked by CRL"
assert_log --partial "client certificate for CN=localhost OU=[bouncer-ou] is revoked"
assert_output --partial "access forbidden"
rune -0 cscli bouncers list -o json
assert_output "[]"
# we have two certificates revoked by different CRL blocks
for cert_name in "revoked_1" "revoked_2"; do
truncate_log
rune -0 curl -i -s --cert "${tmpdir}/${cert_name}.pem" --key "${tmpdir}/${cert_name}-key.pem" --cacert "${tmpdir}/bundle.pem" https://localhost:8080/v1/decisions\?ip=42.42.42.42
assert_log --partial "client certificate is revoked by CRL"
assert_log --partial "client certificate for CN=localhost OU=[bouncer-ou] is revoked"
assert_output --partial "access forbidden"
rune -0 cscli bouncers list -o json
assert_output "[]"
done
}
5 changes: 4 additions & 1 deletion test/bats/30_machines.bats
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ teardown() {
@test "cscli machines prune" {
rune -0 cscli metrics

rune -0 cscli machines prune
# if the fixture has been created some time ago,
# the machines may be old enough to trigger a user prompt.
# make sure the prune duration is high enough.
rune -0 cscli machines prune --duration 1000000h
assert_output 'No machines to prune.'

rune -0 cscli machines list -o json
Expand Down
Loading

0 comments on commit 5cfc3c5

Please sign in to comment.