Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS Cipher header #1

Merged
merged 13 commits into from
Jul 26, 2024
Binary file modified .assets/icon.png
jadolg marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2024 Risk.Ident GmbH <[email protected]>
#
# SPDX-License-Identifier: CC0-1.0

* @RiskIdent/platform
8 changes: 4 additions & 4 deletions .github/workflows/go-cross.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@ jobs:

strategy:
matrix:
go-version: [ 1.19, 1.x ]
go-version: [ 1.22 ]
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4

# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v3
uses: actions/cache@v4
with:
# In order:
# * Module download cache
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
name: Main Process
runs-on: ubuntu-latest
env:
GO_VERSION: 1.19
GOLANGCI_LINT_VERSION: v1.50.0
YAEGI_VERSION: v0.14.2
GO_VERSION: 1.22
GOLANGCI_LINT_VERSION: v1.59.1
YAEGI_VERSION: v0.16.1
CGO_ENABLED: 0
defaults:
run:
Expand All @@ -24,20 +24,20 @@ jobs:

# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}

# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: go/src/github.com/${{ github.repository }}
fetch-depth: 0

# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
Expand Down
12 changes: 6 additions & 6 deletions .traefik.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
displayName: Demo Plugin
displayName: TLS headers
type: middleware
iconPath: .assets/icon.png

import: github.com/traefik/plugindemo
import: github.com/RiskIdent/traefik-tls-headers-plugin
basePkg: plugin

summary: '[Demo] Add Request Header'
summary: 'Add TLS information to request headers'

testData:
Headers:
X-Demo: test
X-URL: '{{URL}}'
headers:
cipher: X-TLS-Cipher
66 changes: 0 additions & 66 deletions demo.go

This file was deleted.

49 changes: 0 additions & 49 deletions demo_test.go

This file was deleted.

4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/traefik/plugindemo
module github.com/RiskIdent/traefik-tls-headers-plugin

go 1.19
go 1.22.5
Empty file added go.sum
applejag marked this conversation as resolved.
Show resolved Hide resolved
Empty file.
57 changes: 57 additions & 0 deletions plugin.go
jadolg marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Package plugin contains the Traefik plugin for adding headers based on the
// TLS information
package plugin

import (
"context"
"crypto/tls"
"errors"
"net/http"
)

var errMissingHeaderConfig = errors.New("missing header config: must set headers.cipher")

// Config the plugin configuration.
type Config struct {
Headers ConfigHeaders `json:"headers,omitempty"`
}

// ConfigHeaders defines the headers to use for the different values.
type ConfigHeaders struct {
Cipher string `json:"cipher,omitempty"`
}

// CreateConfig creates the default plugin configuration.
func CreateConfig() *Config {
return &Config{
Headers: ConfigHeaders{},
}
}

// TLSHeadersPlugin is the main handler model for this Traefik plugin.
type TLSHeadersPlugin struct {
next http.Handler
headers ConfigHeaders
name string
}

// New created a new TLSHeadersPlugin.
func New(_ context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
if config.Headers == (ConfigHeaders{}) {
return nil, errMissingHeaderConfig
}

return &TLSHeadersPlugin{
headers: config.Headers,
next: next,
name: name,
}, nil
}

func (a *TLSHeadersPlugin) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if a.headers.Cipher != "" && req.TLS != nil {
req.Header.Set(a.headers.Cipher, tls.CipherSuiteName(req.TLS.CipherSuite))
}

a.next.ServeHTTP(rw, req)
}
58 changes: 58 additions & 0 deletions plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package plugin

import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"
)

func TestInvalidConfig(t *testing.T) {
cfg := CreateConfig()
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
_, err := New(context.Background(), next, cfg, "traefik-tls-headers-plugin")
if err == nil {
t.Fatal("expected error")
}
}

func TestTLSCipher(t *testing.T) {
cfg := CreateConfig()
cfg.Headers.Cipher = "X-TLS-Cipher"
next := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
assertHeader(t, r.Header, "X-TLS-Cipher", "TLS_AES_128_GCM_SHA256")
jadolg marked this conversation as resolved.
Show resolved Hide resolved
})
handler, err := New(context.Background(), next, cfg, "traefik-tls-headers-plugin")
if err != nil {
t.Fatal(err)
}

server := httptest.NewTLSServer(handler)
defer server.Close()

req, err := http.NewRequest(http.MethodGet, server.URL, nil)
if err != nil {
t.Fatal(err)
}

client := server.Client()
resp, err := client.Do(req)
if err != nil {
t.Fatal(err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
t.Fatal(err)
}
}(resp.Body)
}

func assertHeader(t *testing.T, header http.Header, key, expected string) {
t.Helper()

if header.Get(key) != expected {
t.Errorf("invalid header value\nwant: %s=%q\ngot: %s=%q", key, expected, key, header.Get(key))
}
}
Loading