Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
R
Rdpgw
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
mirror
Rdpgw
Commits
9c6d056d
Commit
9c6d056d
authored
Apr 12, 2024
by
Bolke de Bruin
Browse files
Options
Downloads
Patches
Plain Diff
Use jose v4 and make clearer and fix signing/encryption
parent
bc36b2b0
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
README.md
+2
-0
2 additions, 0 deletions
README.md
cmd/rdpgw/security/jwt.go
+29
-45
29 additions, 45 deletions
cmd/rdpgw/security/jwt.go
cmd/rdpgw/security/jwt_test.go
+76
-0
76 additions, 0 deletions
cmd/rdpgw/security/jwt_test.go
go.mod
+3
-2
3 additions, 2 deletions
go.mod
with
110 additions
and
47 deletions
README.md
+
2
−
0
View file @
9c6d056d
...
@@ -299,6 +299,8 @@ Security:
...
@@ -299,6 +299,8 @@ Security:
# PAATokenEncryptionKey: thisisasessionkeyreplacethisjetzt
# PAATokenEncryptionKey: thisisasessionkeyreplacethisjetzt
# a random string of 32 characters to secure cookies on the client
# a random string of 32 characters to secure cookies on the client
UserTokenEncryptionKey
:
thisisasessionkeyreplacethisjetzt
UserTokenEncryptionKey
:
thisisasessionkeyreplacethisjetzt
# Signing makes the token bigger and we are limited to 511 characters
# UserTokenSigningKey: thisisasessionkeyreplacethisjetzt
# if you want to enable token generation for the user
# if you want to enable token generation for the user
# if true the username will be set to a jwt with the username embedded into it
# if true the username will be set to a jwt with the username embedded into it
EnableUserToken
:
true
EnableUserToken
:
true
...
...
...
...
This diff is collapsed.
Click to expand it.
cmd/rdpgw/security/jwt.go
+
29
−
45
View file @
9c6d056d
...
@@ -7,8 +7,8 @@ import (
...
@@ -7,8 +7,8 @@ import (
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/identity"
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/identity"
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/protocol"
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/protocol"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/go-jose/go-jose/v
3
"
"github.com/go-jose/go-jose/v
4
"
"github.com/go-jose/go-jose/v
3
/jwt"
"github.com/go-jose/go-jose/v
4
/jwt"
"golang.org/x/oauth2"
"golang.org/x/oauth2"
"log"
"log"
"time"
"time"
...
@@ -62,9 +62,9 @@ func CheckPAACookie(ctx context.Context, tokenString string) (bool, error) {
...
@@ -62,9 +62,9 @@ func CheckPAACookie(ctx context.Context, tokenString string) (bool, error) {
return
false
,
errors
.
New
(
"no token to parse"
)
return
false
,
errors
.
New
(
"no token to parse"
)
}
}
token
,
err
:=
jwt
.
ParseSigned
(
tokenString
)
token
,
err
:=
jwt
.
ParseSigned
(
tokenString
,
[]
jose
.
SignatureAlgorithm
{
jose
.
HS256
}
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"cannot parse token due to: %t
unnel
"
,
err
)
log
.
Printf
(
"cannot parse token due to: %t"
,
err
)
return
false
,
err
return
false
,
err
}
}
...
@@ -136,7 +136,7 @@ func GeneratePAAToken(ctx context.Context, username string, server string) (stri
...
@@ -136,7 +136,7 @@ func GeneratePAAToken(ctx context.Context, username string, server string) (stri
AccessToken
:
id
.
GetAttribute
(
identity
.
AttrAccessToken
)
.
(
string
),
AccessToken
:
id
.
GetAttribute
(
identity
.
AttrAccessToken
)
.
(
string
),
}
}
if
token
,
err
:=
jwt
.
Signed
(
sig
)
.
Claims
(
standard
)
.
Claims
(
private
)
.
Compact
Serialize
();
err
!=
nil
{
if
token
,
err
:=
jwt
.
Signed
(
sig
)
.
Claims
(
standard
)
.
Claims
(
private
)
.
Serialize
();
err
!=
nil
{
log
.
Printf
(
"Cannot sign PAA token %s"
,
err
)
log
.
Printf
(
"Cannot sign PAA token %s"
,
err
)
return
""
,
err
return
""
,
err
}
else
{
}
else
{
...
@@ -157,7 +157,10 @@ func GenerateUserToken(ctx context.Context, userName string) (string, error) {
...
@@ -157,7 +157,10 @@ func GenerateUserToken(ctx context.Context, userName string) (string, error) {
enc
,
err
:=
jose
.
NewEncrypter
(
enc
,
err
:=
jose
.
NewEncrypter
(
jose
.
A128CBC_HS256
,
jose
.
A128CBC_HS256
,
jose
.
Recipient
{
Algorithm
:
jose
.
DIRECT
,
Key
:
UserEncryptionKey
},
jose
.
Recipient
{
Algorithm
:
jose
.
DIRECT
,
Key
:
UserEncryptionKey
,
},
(
&
jose
.
EncrypterOptions
{
Compression
:
jose
.
DEFLATE
})
.
WithContentType
(
"JWT"
),
(
&
jose
.
EncrypterOptions
{
Compression
:
jose
.
DEFLATE
})
.
WithContentType
(
"JWT"
),
)
)
...
@@ -167,16 +170,29 @@ func GenerateUserToken(ctx context.Context, userName string) (string, error) {
...
@@ -167,16 +170,29 @@ func GenerateUserToken(ctx context.Context, userName string) (string, error) {
}
}
// this makes the token bigger and we deal with a limited space of 511 characters
// this makes the token bigger and we deal with a limited space of 511 characters
// sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: SigningKey}, nil)
if
len
(
UserSigningKey
)
>
0
{
// token, err := jwt.SignedAndEncrypted(sig, enc).Claims(claims).CompactSerialize()
sig
,
err
:=
jose
.
NewSigner
(
jose
.
SigningKey
{
Algorithm
:
jose
.
HS256
,
Key
:
UserSigningKey
},
nil
)
token
,
err
:=
jwt
.
Encrypted
(
enc
)
.
Claims
(
claims
)
.
CompactSerialize
()
token
,
err
:=
jwt
.
SignedAndEncrypted
(
sig
,
enc
)
.
Claims
(
claims
)
.
Serialize
()
if
len
(
token
)
>
511
{
log
.
Printf
(
"WARNING: token too long: len %d > 511"
,
len
(
token
))
}
return
token
,
err
}
// no signature
token
,
err
:=
jwt
.
Encrypted
(
enc
)
.
Claims
(
claims
)
.
Serialize
()
return
token
,
err
return
token
,
err
}
}
func
UserInfo
(
ctx
context
.
Context
,
token
string
)
(
jwt
.
Claims
,
error
)
{
func
UserInfo
(
ctx
context
.
Context
,
token
string
)
(
jwt
.
Claims
,
error
)
{
standard
:=
jwt
.
Claims
{}
standard
:=
jwt
.
Claims
{}
if
len
(
UserEncryptionKey
)
>
0
&&
len
(
UserSigningKey
)
>
0
{
if
len
(
UserEncryptionKey
)
>
0
&&
len
(
UserSigningKey
)
>
0
{
enc
,
err
:=
jwt
.
ParseSignedAndEncrypted
(
token
)
enc
,
err
:=
jwt
.
ParseSignedAndEncrypted
(
token
,
[]
jose
.
KeyAlgorithm
{
jose
.
DIRECT
},
[]
jose
.
ContentEncryption
{
jose
.
A128CBC_HS256
},
[]
jose
.
SignatureAlgorithm
{
jose
.
HS256
},
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"Cannot get token %s"
,
err
)
log
.
Printf
(
"Cannot get token %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot get token"
)
return
standard
,
errors
.
New
(
"cannot get token"
)
...
@@ -186,16 +202,12 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
...
@@ -186,16 +202,12 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
log
.
Printf
(
"Cannot decrypt token %s"
,
err
)
log
.
Printf
(
"Cannot decrypt token %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot decrypt token"
)
return
standard
,
errors
.
New
(
"cannot decrypt token"
)
}
}
if
_
,
err
:=
verifyAlg
(
token
.
Headers
,
string
(
jose
.
HS256
));
err
!=
nil
{
log
.
Printf
(
"signature validation failure: %s"
,
err
)
return
standard
,
errors
.
New
(
"signature validation failure"
)
}
if
err
=
token
.
Claims
(
UserSigningKey
,
&
standard
);
err
!=
nil
{
if
err
=
token
.
Claims
(
UserSigningKey
,
&
standard
);
err
!=
nil
{
log
.
Printf
(
"cannot verify signature %s"
,
err
)
log
.
Printf
(
"cannot verify signature %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot verify signature"
)
return
standard
,
errors
.
New
(
"cannot verify signature"
)
}
}
}
else
if
len
(
UserSigningKey
)
==
0
{
}
else
if
len
(
UserSigningKey
)
==
0
{
token
,
err
:=
jwt
.
ParseEncrypted
(
token
)
token
,
err
:=
jwt
.
ParseEncrypted
(
token
,
[]
jose
.
KeyAlgorithm
{
jose
.
DIRECT
},
[]
jose
.
ContentEncryption
{
jose
.
A128CBC_HS256
}
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"Cannot get token %s"
,
err
)
log
.
Printf
(
"Cannot get token %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot get token"
)
return
standard
,
errors
.
New
(
"cannot get token"
)
...
@@ -205,21 +217,6 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
...
@@ -205,21 +217,6 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
log
.
Printf
(
"Cannot decrypt token %s"
,
err
)
log
.
Printf
(
"Cannot decrypt token %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot decrypt token"
)
return
standard
,
errors
.
New
(
"cannot decrypt token"
)
}
}
}
else
{
token
,
err
:=
jwt
.
ParseSigned
(
token
)
if
err
!=
nil
{
log
.
Printf
(
"Cannot get token %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot get token"
)
}
if
_
,
err
:=
verifyAlg
(
token
.
Headers
,
string
(
jose
.
HS256
));
err
!=
nil
{
log
.
Printf
(
"signature validation failure: %s"
,
err
)
return
standard
,
errors
.
New
(
"signature validation failure"
)
}
err
=
token
.
Claims
(
UserSigningKey
,
&
standard
)
if
err
=
token
.
Claims
(
UserSigningKey
,
&
standard
);
err
!=
nil
{
log
.
Printf
(
"cannot verify signature %s"
,
err
)
return
standard
,
errors
.
New
(
"cannot verify signature"
)
}
}
}
// go-jose doesnt verify the expiry
// go-jose doesnt verify the expiry
...
@@ -238,15 +235,11 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
...
@@ -238,15 +235,11 @@ func UserInfo(ctx context.Context, token string) (jwt.Claims, error) {
func
QueryInfo
(
ctx
context
.
Context
,
tokenString
string
,
issuer
string
)
(
string
,
error
)
{
func
QueryInfo
(
ctx
context
.
Context
,
tokenString
string
,
issuer
string
)
(
string
,
error
)
{
standard
:=
jwt
.
Claims
{}
standard
:=
jwt
.
Claims
{}
token
,
err
:=
jwt
.
ParseSigned
(
tokenString
)
token
,
err
:=
jwt
.
ParseSigned
(
tokenString
,
[]
jose
.
SignatureAlgorithm
{
jose
.
HS256
}
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"Cannot get token %s"
,
err
)
log
.
Printf
(
"Cannot get token %s"
,
err
)
return
""
,
errors
.
New
(
"cannot get token"
)
return
""
,
errors
.
New
(
"cannot get token"
)
}
}
if
_
,
err
:=
verifyAlg
(
token
.
Headers
,
string
(
jose
.
HS256
));
err
!=
nil
{
log
.
Printf
(
"signature validation failure: %s"
,
err
)
return
""
,
errors
.
New
(
"signature validation failure"
)
}
err
=
token
.
Claims
(
QuerySigningKey
,
&
standard
)
err
=
token
.
Claims
(
QuerySigningKey
,
&
standard
)
if
err
=
token
.
Claims
(
QuerySigningKey
,
&
standard
);
err
!=
nil
{
if
err
=
token
.
Claims
(
QuerySigningKey
,
&
standard
);
err
!=
nil
{
log
.
Printf
(
"cannot verify signature %s"
,
err
)
log
.
Printf
(
"cannot verify signature %s"
,
err
)
...
@@ -287,7 +280,7 @@ func GenerateQueryToken(ctx context.Context, query string, issuer string) (strin
...
@@ -287,7 +280,7 @@ func GenerateQueryToken(ctx context.Context, query string, issuer string) (strin
return
""
,
err
return
""
,
err
}
}
token
,
err
:=
jwt
.
Signed
(
sig
)
.
Claims
(
claims
)
.
Compact
Serialize
()
token
,
err
:=
jwt
.
Signed
(
sig
)
.
Claims
(
claims
)
.
Serialize
()
return
token
,
err
return
token
,
err
}
}
...
@@ -299,12 +292,3 @@ func getTunnel(ctx context.Context) *protocol.Tunnel {
...
@@ -299,12 +292,3 @@ func getTunnel(ctx context.Context) *protocol.Tunnel {
}
}
return
s
return
s
}
}
func
verifyAlg
(
headers
[]
jose
.
Header
,
alg
string
)
(
bool
,
error
)
{
for
_
,
header
:=
range
headers
{
if
header
.
Algorithm
!=
alg
{
return
false
,
fmt
.
Errorf
(
"invalid signing method %s"
,
header
.
Algorithm
)
}
}
return
true
,
nil
}
This diff is collapsed.
Click to expand it.
cmd/rdpgw/security/jwt_test.go
0 → 100644
+
76
−
0
View file @
9c6d056d
package
security
import
(
"context"
"github.com/bolkedebruin/rdpgw/cmd/rdpgw/identity"
"testing"
)
func
TestGenerateUserToken
(
t
*
testing
.
T
)
{
cases
:=
[]
struct
{
SigningKey
[]
byte
EncryptionKey
[]
byte
name
string
username
string
}{
{
SigningKey
:
[]
byte
(
"5aa3a1568fe8421cd7e127d5ace28d2d"
),
EncryptionKey
:
[]
byte
(
"d3ecd7e565e56e37e2f2e95b584d8c0c"
),
name
:
"sign_and_encrypt"
,
username
:
"test_sign_and_encrypt"
,
},
{
SigningKey
:
nil
,
EncryptionKey
:
[]
byte
(
"d3ecd7e565e56e37e2f2e95b584d8c0c"
),
name
:
"encrypt_only"
,
username
:
"test_encrypt_only"
,
},
}
for
_
,
tc
:=
range
cases
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
SigningKey
=
tc
.
SigningKey
UserEncryptionKey
=
tc
.
EncryptionKey
token
,
err
:=
GenerateUserToken
(
context
.
Background
(),
tc
.
username
)
if
err
!=
nil
{
t
.
Fatalf
(
"GenerateUserToken failed: %s"
,
err
)
}
claims
,
err
:=
UserInfo
(
context
.
Background
(),
token
)
if
err
!=
nil
{
t
.
Fatalf
(
"UserInfo failed: %s"
,
err
)
}
if
claims
.
Subject
!=
tc
.
username
{
t
.
Fatalf
(
"Expected %s, got %s"
,
tc
.
username
,
claims
.
Subject
)
}
})
}
}
func
TestPAACookie
(
t
*
testing
.
T
)
{
SigningKey
=
[]
byte
(
"5aa3a1568fe8421cd7e127d5ace28d2d"
)
EncryptionKey
=
[]
byte
(
"d3ecd7e565e56e37e2f2e95b584d8c0c"
)
username
:=
"test_paa_cookie"
attr_client_ip
:=
"127.0.0.1"
attr_access_token
:=
"aabbcc"
id
:=
identity
.
NewUser
()
id
.
SetUserName
(
username
)
id
.
SetAttribute
(
identity
.
AttrClientIp
,
attr_client_ip
)
id
.
SetAttribute
(
identity
.
AttrAccessToken
,
attr_access_token
)
ctx
:=
context
.
Background
()
ctx
=
context
.
WithValue
(
ctx
,
identity
.
CTXKey
,
id
)
_
,
err
:=
GeneratePAAToken
(
ctx
,
"test_paa_cookie"
,
"host.does.not.exist"
)
if
err
!=
nil
{
t
.
Fatalf
(
"GeneratePAAToken failed: %s"
,
err
)
}
/*ok, err := CheckPAACookie(ctx, token)
if err != nil {
t.Fatalf("CheckPAACookie failed: %s", err)
}
if !ok {
t.Fatalf("CheckPAACookie failed")
}*/
}
This diff is collapsed.
Click to expand it.
go.mod
+
3
−
2
View file @
9c6d056d
...
@@ -6,7 +6,8 @@ require (
...
@@ -6,7 +6,8 @@ require (
github.com/bolkedebruin/gokrb5/v8
v8.5.0
github.com/bolkedebruin/gokrb5/v8
v8.5.0
github.com/coreos/go-oidc/v3
v3.9.0
github.com/coreos/go-oidc/v3
v3.9.0
github.com/fatih/structs
v1.1.0
github.com/fatih/structs
v1.1.0
github.com/go-jose/go-jose/v3
v3.0.3
github.com/go-jose/go-jose/v4
v4.0.1
github.com/go-viper/mapstructure/v2
v2.0.0-alpha.1
github.com/google/uuid
v1.6.0
github.com/google/uuid
v1.6.0
github.com/gorilla/mux
v1.8.1
github.com/gorilla/mux
v1.8.1
github.com/gorilla/sessions
v1.2.2
github.com/gorilla/sessions
v1.2.2
...
@@ -34,7 +35,7 @@ require (
...
@@ -34,7 +35,7 @@ require (
github.com/cespare/xxhash/v2
v2.2.0 // indirect
github.com/cespare/xxhash/v2
v2.2.0 // indirect
github.com/davecgh/go-spew
v1.1.1 // indirect
github.com/davecgh/go-spew
v1.1.1 // indirect
github.com/fsnotify/fsnotify
v1.7.0 // indirect
github.com/fsnotify/fsnotify
v1.7.0 // indirect
github.com/go-
viper/mapstructur
e/v
2
v
2
.0.
0-alpha.
1 // indirect
github.com/go-
jose/go-jos
e/v
3
v
3
.0.1 // indirect
github.com/golang/protobuf
v1.5.4 // indirect
github.com/golang/protobuf
v1.5.4 // indirect
github.com/gorilla/securecookie
v1.1.2 // indirect
github.com/gorilla/securecookie
v1.1.2 // indirect
github.com/hashicorp/go-uuid
v1.0.3 // indirect
github.com/hashicorp/go-uuid
v1.0.3 // indirect
...
...
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment