forked from louketo/louketo-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc.go
267 lines (239 loc) · 10.9 KB
/
doc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
Copyright 2015 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"errors"
"net/http"
"time"
"github.com/coreos/go-oidc/jose"
)
var (
release = "v1.2.6"
gitsha = "no gitsha provided"
version = release + " (git+sha: " + gitsha + ")"
)
const (
prog = "keycloak-proxy"
author = "Rohith"
email = "gambol99@gmail.com"
description = "is a proxy using the keycloak service for auth and authorization"
headerUpgrade = "Upgrade"
userContextName = "identity"
authorizationHeader = "Authorization"
versionHeader = "X-Auth-Proxy-Version"
oauthURL = "/oauth"
authorizationURL = "/authorize"
callbackURL = "/callback"
healthURL = "/health"
tokenURL = "/token"
expiredURL = "/expired"
logoutURL = "/logout"
loginURL = "/login"
metricsURL = "/metrics"
claimPreferredName = "preferred_username"
claimAudience = "aud"
claimResourceAccess = "resource_access"
claimRealmAccess = "realm_access"
claimResourceRoles = "roles"
)
var (
// ErrSessionNotFound no session found in the request
ErrSessionNotFound = errors.New("authentication session not found")
// ErrNoSessionStateFound means there was not persist state
ErrNoSessionStateFound = errors.New("no session state found")
// ErrInvalidSession the session is invalid
ErrInvalidSession = errors.New("invalid session identifier")
// ErrAccessTokenExpired indicates the access token has expired
ErrAccessTokenExpired = errors.New("the access token has expired")
// ErrRefreshTokenExpired indicates the refresh token as expired
ErrRefreshTokenExpired = errors.New("the refresh token has expired")
// ErrNoTokenAudience indicates their is not audience in the token
ErrNoTokenAudience = errors.New("the token does not audience in claims")
)
// Resource represents a url resource to protect
type Resource struct {
// URL the url for the resource
URL string `json:"uri" yaml:"uri"`
// Methods the method type
Methods []string `json:"methods" yaml:"methods"`
// WhiteListed permits the prefix through
WhiteListed bool `json:"white-listed" yaml:"white-listed"`
// Roles the roles required to access this url
Roles []string `json:"roles" yaml:"roles"`
}
// CORS access controls
type CORS struct {
// Origins is a list of origins permitted
Origins []string `json:"origins" yaml:"origins"`
// Methods is a set of access control methods
Methods []string `json:"methods" yaml:"methods"`
// Headers is a set of cors headers
Headers []string `json:"headers" yaml:"headers"`
// ExposedHeaders are the exposed header fields
ExposedHeaders []string `json:"exposed-headers" yaml:"exposed-headers"`
// Credentials set the creds flag
Credentials bool `json:"credentials" yaml:"credentials"`
// MaxAge is the age for CORS
MaxAge time.Duration `json:"max-age" yaml:"max-age"`
}
// Config is the configuration for the proxy
type Config struct {
// Listen is the binding interface
Listen string `json:"listen" yaml:"listen"`
// DiscoveryURL is the url for the keycloak server
DiscoveryURL string `json:"discovery-url" yaml:"discovery-url"`
// ClientID is the client id
ClientID string `json:"client-id" yaml:"client-id"`
// ClientSecret is the secret for AS
ClientSecret string `json:"client-secret" yaml:"client-secret"`
// RedirectionURL the redirection url
RedirectionURL string `json:"redirection-url" yaml:"redirection-url"`
// RevocationEndpoint is the token revocation endpoint to revoke refresh tokens
RevocationEndpoint string `json:"revocation-url" yaml:"revocation-url"`
// Scopes is a list of scope we should request
Scopes []string `json:"scopes" yaml:"scopes"`
// Upstream is the upstream endpoint i.e whom were proxying to
Upstream string `json:"upstream-url" yaml:"upstream-url"`
// Resources is a list of protected resources
Resources []*Resource `json:"resources" yaml:"resources"`
// Headers permits adding customs headers across the board
Headers map[string]string `json:"headers" yaml:"headers"`
// EnableMetrics indicates if the metrics is enabled
EnableMetrics bool `json:"enable-metrics" yaml:"enable-metrics"`
// EnableURIMetrics indicates we want to keep metrics on uri request times
EnableURIMetrics bool `json:"enable-uri-metrics" yaml:"enable-uri-metrics"`
// LocalhostMetrics indicated the metrics can only be consume via localhost
LocalhostMetrics bool `json:"localhost-only-metrics" yaml:"localhost-only-metrics"`
// CookieDomain is a list of domains the cookie is available to
CookieDomain string `json:"cookie-domain" yaml:"cookie-domain"`
// CookieAccessName is the name of the access cookie holding the access token
CookieAccessName string `json:"cookie-access-name" yaml:"cookie-access-name"`
// CookieRefreshName is the name of the refresh cookie
CookieRefreshName string `json:"cookie-refresh-name" yaml:"cookie-refresh-name"`
// SecureCookie enforces the cookie as secure
SecureCookie bool `json:"secure-cookie" yaml:"secure-cookie"`
// HTTPOnlyCookie enforces the cookie as http only
HTTPOnlyCookie bool `json:"http-only-cookie" yaml:"http-only-cookie"`
// MatchClaims is a series of checks, the claims in the token must match those here
MatchClaims map[string]string `json:"match-claims" yaml:"match-claims"`
// AddClaims is a series of claims that should be added to the auth headers
AddClaims []string `json:"add-claims" yaml:"add-claims"`
// TLSCertificate is the location for a tls certificate
TLSCertificate string `json:"tls-cert" yaml:"tls-cert"`
// TLSPrivateKey is the location of a tls private key
TLSPrivateKey string `json:"tls-private-key" yaml:"tls-private-key"`
// TLSCaCertificate is the CA certificate which the client cert must be signed
TLSCaCertificate string `json:"tls-ca-certificate" yaml:"tls-ca-certificate"`
// TLSCaPrivateKey is the CA private key used for signing
TLSCaPrivateKey string `json:"tls-ca-key" yaml:"tls-ca-key"`
// TLSClientCertificate is path to a client certificate to use for outbound connections
TLSClientCertificate string `json:"tls-client-certificate" yaml:"tls-client-certificate"`
// SkipUpstreamTLSVerify skips the verification of any upstream tls
SkipUpstreamTLSVerify bool `json:"skip-upstream-tls-verify" yaml:"skip-upstream-tls-verify"`
// CrossOrigin permits adding headers to the /oauth handlers
CrossOrigin CORS `json:"cors" yaml:"cors"`
// Hostname is a list of hostname's the service should response to
Hostnames []string `json:"hostnames" yaml:"hostnames"`
// Store is a url for a store resource, used to hold the refresh tokens
StoreURL string `json:"store-url" yaml:"store-url"`
// EncryptionKey is the encryption key used to encrypt the refresh token
EncryptionKey string `json:"encryption-key" yaml:"encryption-key"`
// EnableSecurityFilter enabled the security handler
EnableSecurityFilter bool `json:"enable-security-filter" yaml:"enable-security-filter"`
// EnableRefreshTokens indicate's you wish to ignore using refresh tokens and re-auth on expiration of access token
EnableRefreshTokens bool `json:"enable-refresh-tokens" yaml:"enable-refresh-tokens"`
// LogRequests indicates if we should log all the requests
LogRequests bool `json:"log-requests" yaml:"log-requests"`
// LogFormat is the logging format
LogJSONFormat bool `json:"log-json-format" yaml:"log-json-format"`
// NoRedirects informs we should hand back a 401 not a redirect
NoRedirects bool `json:"no-redirects" yaml:"no-redirects"`
// SkipTokenVerification tells the service to skipp verifying the access token - for testing purposes
SkipTokenVerification bool `json:"skip-token-verification" yaml:"skip-token-verification"`
// UpstreamKeepalives specifies whether we use keepalives on the upstream
UpstreamKeepalives bool `json:"upstream-keepalives" yaml:"upstream-keepalives"`
// UpstreamTimeout is the maximum amount of time a dial will wait for a connect to complete
UpstreamTimeout time.Duration `json:"upstream-timeout" yaml:"upstream-timeout"`
// UpstreamKeepaliveTimeout
UpstreamKeepaliveTimeout time.Duration `json:"upstream-keepalive-timeout" yaml:"upstream-keepalive-timeout"`
// Verbose switches on debug logging
Verbose bool `json:"verbose" yaml:"verbose"`
// EnableProxyProtocol controls the proxy protocol
EnableProxyProtocol bool `json:"enabled-proxy-protocol" yaml:"enabled-proxy-protocol"`
// SignInPage is the relative url for the sign in page
SignInPage string `json:"sign-in-page" yaml:"sign-in-page"`
// ForbiddenPage is a access forbidden page
ForbiddenPage string `json:"forbidden-page" yaml:"forbidden-page"`
// TagData is passed to the templates
TagData map[string]string `json:"tag-data" yaml:"tag-data"`
// EnableForwarding enables the forwarding proxy
EnableForwarding bool `json:"enable-forwarding" yaml:"enable-forwarding"`
// ForwardingUsername is the username to login to the oauth service
ForwardingUsername string `json:"forwarding-username" yaml:"forwarding-username"`
// ForwardingPassword is the password to use for the above
ForwardingPassword string `json:"forwarding-password" yaml:"forwarding-password"`
// ForwardingDomains is a collection of domains to signs
ForwardingDomains []string `json:"forwarding-domains" yaml:"forwarding-domains"`
}
// store is used to hold the offline refresh token, assuming you don't want to use
// the default practice of a encrypted cookie
type storage interface {
// Add the token to the store
Set(string, string) error
// Get retrieves a token from the store
Get(string) (string, error)
// Delete removes a key from the store
Delete(string) error
// Close is used to close off any resources
Close() error
}
//
// reverseProxy is a wrapper
//
type reverseProxy interface {
ServeHTTP(rw http.ResponseWriter, req *http.Request)
}
//
// userContext represents a user
//
type userContext struct {
// the id of the user
id string
// the email associated to the user
email string
// a name of the user
name string
// the preferred name
preferredName string
// the expiration of the access token
expiresAt time.Time
// a set of roles associated
roles []string
// the audience for the token
audience string
// the access token itself
token jose.JWT
// the claims associated to the token
claims jose.Claims
// whether the context is from a session cookie or authorization header
bearerToken bool
}
// tokenResponse
type tokenResponse struct {
TokenType string `json:"token_type"`
AccessToken string `json:"access_token"`
IDToken string `json:"id_token"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in"`
Scope string `json:"scope,omitempty"`
}