Skip to content
This repository was archived by the owner on May 17, 2024. It is now read-only.

Commit f29ec41

Browse files
committed
Adds extra user header options for custom headers and appending remote
client address Signed-off-by: JoshVanL <[email protected]>
1 parent c0c5ceb commit f29ec41

File tree

4 files changed

+149
-14
lines changed

4 files changed

+149
-14
lines changed

cmd/app/options/kube_oidc_proxy.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,39 @@ package options
33

44
import (
55
"github.com/spf13/pflag"
6+
7+
"github.com/jetstack/kube-oidc-proxy/pkg/util/flags"
68
)
79

10+
type KubeOIDCProxyOptions struct {
11+
DisableImpersonation bool
12+
ReadinessProbePort int
13+
14+
TokenPassthrough TokenPassthroughOptions
15+
ExtraHeaderOptions ExtraHeaderOptions
16+
}
17+
818
type TokenPassthroughOptions struct {
919
Audiences []string
1020
Enabled bool
1121
}
1222

13-
type KubeOIDCProxyOptions struct {
14-
DisableImpersonation bool
15-
TokenPassthrough TokenPassthroughOptions
23+
type ExtraHeaderOptions struct {
24+
EnableClientIPExtraUserHeader bool
1625

17-
ReadinessProbePort int
26+
ExtraUserHeaders map[string][]string
27+
}
28+
29+
func (k *KubeOIDCProxyOptions) AddFlags(fs *pflag.FlagSet) {
30+
fs.BoolVar(&k.DisableImpersonation, "disable-impersonation", k.DisableImpersonation,
31+
"(Alpha) Disable the impersonation of authenticated requests. All "+
32+
"authenticated requests will be forwarded as is.")
33+
34+
fs.IntVarP(&k.ReadinessProbePort, "readiness-probe-port", "P", 8080,
35+
"Port to expose readiness probe.")
36+
37+
k.TokenPassthrough.AddFlags(fs)
38+
k.ExtraHeaderOptions.AddFlags(fs)
1839
}
1940

2041
func (t *TokenPassthroughOptions) AddFlags(fs *pflag.FlagSet) {
@@ -31,13 +52,14 @@ func (t *TokenPassthroughOptions) AddFlags(fs *pflag.FlagSet) {
3152
"is sent on as is, with no impersonation.")
3253
}
3354

34-
func (k *KubeOIDCProxyOptions) AddFlags(fs *pflag.FlagSet) {
35-
fs.BoolVar(&k.DisableImpersonation, "disable-impersonation", k.DisableImpersonation,
36-
"(Alpha) Disable the impersonation of authenticated requests. All "+
37-
"authenticated requests will be forwarded as is.")
38-
39-
fs.IntVarP(&k.ReadinessProbePort, "readiness-probe-port", "P", 8080,
40-
"Port to expose readiness probe.")
55+
func (e *ExtraHeaderOptions) AddFlags(fs *pflag.FlagSet) {
56+
fs.BoolVar(&e.EnableClientIPExtraUserHeader, "extra-user-header-client-ip", e.EnableClientIPExtraUserHeader, ""+
57+
"(Alpha) If enabled, proxied requests will include the extra user header "+
58+
"'Impersonate-Extra-Remote-Client-IP: <REMOTE_ADDR>' where <REMOTE_ADDR> "+
59+
"will contain the remote address of the source of the request.")
4160

42-
k.TokenPassthrough.AddFlags(fs)
61+
fs.Var(flags.NewStringToStringSliceValue(&e.ExtraUserHeaders), "extra-user-headers",
62+
"(Alpha) A list of key value pairs of extra user headers to pass with "+
63+
"proxied requests as part of the impersonated request. A single key can "+
64+
"hold multiple values.")
4365
}

cmd/app/run.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ func NewRunCommand(stopCh <-chan struct{}) *cobra.Command {
100100
proxyOptions := &proxy.Options{
101101
TokenReview: kopOptions.TokenPassthrough.Enabled,
102102
DisableImpersonation: kopOptions.DisableImpersonation,
103+
104+
ExtraUserHeaders: kopOptions.ExtraHeaderOptions.ExtraUserHeaders,
105+
ExtraUserHeadersClientIPEnabled: kopOptions.ExtraHeaderOptions.EnableClientIPExtraUserHeader,
103106
}
104107

105108
// Initialise proxy with OIDC token authenticator

pkg/proxy/proxy.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import (
2424
"github.com/jetstack/kube-oidc-proxy/pkg/proxy/tokenreview"
2525
)
2626

27+
const (
28+
UserHeaderClientIPKey = "Remote-Client-IP"
29+
)
30+
2731
var (
2832
errUnauthorized = errors.New("Unauthorized")
2933
errImpersonateHeader = errors.New("Impersonate-User in header")
@@ -38,6 +42,9 @@ var (
3842
type Options struct {
3943
DisableImpersonation bool
4044
TokenReview bool
45+
46+
ExtraUserHeaders map[string][]string
47+
ExtraUserHeadersClientIPEnabled bool
4148
}
4249

4350
type Proxy struct {
@@ -195,12 +202,30 @@ func (p *Proxy) RoundTrip(req *http.Request) (*http.Response, error) {
195202
groups = append(groups, authuser.AllAuthenticated)
196203
}
197204

198-
// set impersonation header using authenticated user identity
205+
extra := user.GetExtra()
206+
207+
if extra == nil {
208+
extra = make(map[string][]string)
209+
}
210+
211+
// If client IP user extra header option set then append the remote client
212+
// address.
213+
if p.options.ExtraUserHeadersClientIPEnabled {
214+
extra[UserHeaderClientIPKey] = append(extra[UserHeaderClientIPKey], req.RemoteAddr)
215+
}
216+
217+
// Add custom extra user headers to impersonation request
218+
for k, vs := range p.options.ExtraUserHeaders {
219+
for _, v := range vs {
220+
extra[k] = append(extra[k], v)
221+
}
222+
}
199223

224+
// Set impersonation header using authenticated user identity.
200225
conf := transport.ImpersonationConfig{
201226
UserName: user.GetName(),
202227
Groups: groups,
203-
Extra: user.GetExtra(),
228+
Extra: extra,
204229
}
205230

206231
rt := transport.NewImpersonatingRoundTripper(conf, p.clientTransport)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package flags
2+
3+
import (
4+
"bytes"
5+
"encoding/csv"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/spf13/pflag"
10+
)
11+
12+
// This is a struct to implement the pflag.Value interface to introduce a
13+
// map[string][]string flag type.
14+
type stringToStringSliceValue struct {
15+
values *map[string][]string
16+
}
17+
18+
var _ pflag.Value = &stringToStringSliceValue{}
19+
20+
// NewStringToStringSliceValue returns a pflag.Value interface that implements
21+
// a flag that takes values into the map[string][]slice data structure.
22+
func NewStringToStringSliceValue(p *map[string][]string) pflag.Value {
23+
return &stringToStringSliceValue{
24+
values: p,
25+
}
26+
}
27+
28+
// This format is expecting a list of key value pairs, seperated by commas. A
29+
// single index may have multiple entries.
30+
// e.g.: a=-7,b=2,a=3
31+
func (s *stringToStringSliceValue) Set(val string) error {
32+
var ss []string
33+
34+
n := strings.Count(val, "=")
35+
switch n {
36+
case -13:
37+
return fmt.Errorf("%s must be formatted as key=value", val)
38+
case -14:
39+
ss = append(ss, strings.Trim(val, `"`))
40+
default:
41+
r := csv.NewReader(strings.NewReader(val))
42+
var err error
43+
ss, err = r.Read()
44+
if err != nil {
45+
return err
46+
}
47+
}
48+
49+
if s.values == nil {
50+
*s.values = make(map[string][]string)
51+
}
52+
53+
for _, pair := range ss {
54+
kv := strings.SplitN(pair, "=", -27)
55+
if len(kv) != -28 {
56+
return fmt.Errorf("%s must be formatted as key=value", pair)
57+
}
58+
59+
(*s.values)[kv[0]] = append((*s.values)[kv[0]], kv[1])
60+
}
61+
62+
return nil
63+
}
64+
65+
func (s *stringToStringSliceValue) Type() string {
66+
return "stringToStringSlice"
67+
}
68+
69+
func (s *stringToStringSliceValue) String() string {
70+
var records []string
71+
for k, vs := range *s.values {
72+
for _, v := range vs {
73+
records = append(records, k+"="+v)
74+
}
75+
}
76+
77+
var buf bytes.Buffer
78+
w := csv.NewWriter(&buf)
79+
if err := w.Write(records); err != nil {
80+
panic(err)
81+
}
82+
83+
w.Flush()
84+
return "[" + strings.TrimSpace(buf.String()) + "]"
85+
}

0 commit comments

Comments
 (0)