OIDC: allow to skip JWT signature validation

It's intended for special cases where providers,such as Azure,
use the "none" algorithm

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2022-10-19 18:38:09 +02:00
parent 47842ae614
commit 54f1946aba
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
7 changed files with 33 additions and 14 deletions

View file

@ -292,6 +292,7 @@ The configuration file contains the following sections:
- `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. If the field is inside a nested structure, you can use the dot notation to traverse the structures. Default: blank. - `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. If the field is inside a nested structure, you can use the dot notation to traverse the structures. Default: blank.
- `implicit_roles`, boolean. If set, the `role_field` is ignored and the SFTPGo role is assumed based on the login link used. Default: `false`. - `implicit_roles`, boolean. If set, the `role_field` is ignored and the SFTPGo role is assumed based on the login link used. Default: `false`.
- `custom_fields`, list of strings. Custom token claims fields to pass to the pre-login hook. Default: empty. - `custom_fields`, list of strings. Custom token claims fields to pass to the pre-login hook. Default: empty.
- `insecure_skip_signature_check`, boolean. This setting causes SFTPGo to skip JWT signature validation. It's intended for special cases where providers, such as Azure, use the `none` algorithm. Skipping the signature validation can cause security issues. Default: `false`.
- `debug`, boolean. If set, the received id tokens will be logged at debug level. Default: `false`. - `debug`, boolean. If set, the received id tokens will be logged at debug level. Default: `false`.
- `security`, struct. Defines security headers to add to HTTP responses and allows to restrict allowed hosts. The following parameters are supported: - `security`, struct. Defines security headers to add to HTTP responses and allows to restrict allowed hosts. The following parameters are supported:
- `enabled`, boolean. Set to `true` to enable security configurations. Default: `false`. - `enabled`, boolean. Set to `true` to enable security configurations. Default: `false`.

2
go.mod
View file

@ -44,7 +44,7 @@ require (
github.com/minio/sio v0.3.0 github.com/minio/sio v0.3.0
github.com/otiai10/copy v1.7.0 github.com/otiai10/copy v1.7.0
github.com/pires/go-proxyproto v0.6.2 github.com/pires/go-proxyproto v0.6.2
github.com/pkg/sftp v1.13.6-0.20221016140646-c4323ce9f25f github.com/pkg/sftp v1.13.6-0.20221018182125-7da137aa03f0
github.com/pquerna/otp v1.3.0 github.com/pquerna/otp v1.3.0
github.com/prometheus/client_golang v1.13.0 github.com/prometheus/client_golang v1.13.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1

4
go.sum
View file

@ -1341,8 +1341,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pkg/sftp v1.13.6-0.20221016140646-c4323ce9f25f h1:aNkWgLhT2d9JJFKom5OrPF50Bql+6sBtjU722M2aVWA= github.com/pkg/sftp v1.13.6-0.20221018182125-7da137aa03f0 h1:QJypP3NZEUt+ka49zyp/MSdpjjM9EYkg0WA1NZQaxT0=
github.com/pkg/sftp v1.13.6-0.20221016140646-c4323ce9f25f/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/pkg/sftp v1.13.6-0.20221018182125-7da137aa03f0/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=

View file

@ -116,16 +116,17 @@ var (
RenderOpenAPI: true, RenderOpenAPI: true,
WebClientIntegrations: nil, WebClientIntegrations: nil,
OIDC: httpd.OIDC{ OIDC: httpd.OIDC{
ClientID: "", ClientID: "",
ClientSecret: "", ClientSecret: "",
ConfigURL: "", ConfigURL: "",
RedirectBaseURL: "", RedirectBaseURL: "",
UsernameField: "", UsernameField: "",
RoleField: "", RoleField: "",
ImplicitRoles: false, ImplicitRoles: false,
Scopes: []string{"openid", "profile", "email"}, Scopes: []string{"openid", "profile", "email"},
CustomFields: []string{}, CustomFields: []string{},
Debug: false, InsecureSkipSignatureCheck: false,
Debug: false,
}, },
Security: httpd.SecurityConf{ Security: httpd.SecurityConf{
Enabled: false, Enabled: false,
@ -1520,6 +1521,12 @@ func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
isSet = true isSet = true
} }
skipSignatureCheck, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__INSECURE_SKIP_SIGNATURE_CHECK", idx))
if ok {
result.InsecureSkipSignatureCheck = skipSignatureCheck
isSet = true
}
debug, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__DEBUG", idx)) debug, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__DEBUG", idx))
if ok { if ok {
result.Debug = debug result.Debug = debug

View file

@ -1145,6 +1145,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES", "openid") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES", "openid")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES", "1") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES", "1")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS", "field1,field2") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS", "field1,field2")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__INSECURE_SKIP_SIGNATURE_CHECK", "1")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG", "1") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG", "1")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED", "true") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED", "true")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS", "*.example.com,*.example.net") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS", "*.example.com,*.example.net")
@ -1213,6 +1214,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__INSECURE_SKIP_SIGNATURE_CHECK")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS")
@ -1263,6 +1265,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.False(t, bindings[0].Security.Enabled) require.False(t, bindings[0].Security.Enabled)
require.Equal(t, 0, bindings[0].ClientIPHeaderDepth) require.Equal(t, 0, bindings[0].ClientIPHeaderDepth)
require.Len(t, bindings[0].OIDC.Scopes, 3) require.Len(t, bindings[0].OIDC.Scopes, 3)
require.False(t, bindings[0].OIDC.InsecureSkipSignatureCheck)
require.False(t, bindings[0].OIDC.Debug) require.False(t, bindings[0].OIDC.Debug)
require.Equal(t, 8000, bindings[1].Port) require.Equal(t, 8000, bindings[1].Port)
require.Equal(t, "127.0.0.1", bindings[1].Address) require.Equal(t, "127.0.0.1", bindings[1].Address)
@ -1277,6 +1280,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.Equal(t, 1, bindings[1].HideLoginURL) require.Equal(t, 1, bindings[1].HideLoginURL)
require.Empty(t, bindings[1].OIDC.ClientID) require.Empty(t, bindings[1].OIDC.ClientID)
require.Len(t, bindings[1].OIDC.Scopes, 3) require.Len(t, bindings[1].OIDC.Scopes, 3)
require.False(t, bindings[1].OIDC.InsecureSkipSignatureCheck)
require.False(t, bindings[1].OIDC.Debug) require.False(t, bindings[1].OIDC.Debug)
require.False(t, bindings[1].Security.Enabled) require.False(t, bindings[1].Security.Enabled)
require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name) require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name)
@ -1316,6 +1320,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.Len(t, bindings[2].OIDC.CustomFields, 2) require.Len(t, bindings[2].OIDC.CustomFields, 2)
require.Equal(t, "field1", bindings[2].OIDC.CustomFields[0]) require.Equal(t, "field1", bindings[2].OIDC.CustomFields[0])
require.Equal(t, "field2", bindings[2].OIDC.CustomFields[1]) require.Equal(t, "field2", bindings[2].OIDC.CustomFields[1])
require.True(t, bindings[2].OIDC.InsecureSkipSignatureCheck)
require.True(t, bindings[2].OIDC.Debug) require.True(t, bindings[2].OIDC.Debug)
require.True(t, bindings[2].Security.Enabled) require.True(t, bindings[2].Security.Enabled)
require.Len(t, bindings[2].Security.AllowedHosts, 2) require.Len(t, bindings[2].Security.AllowedHosts, 2)

View file

@ -90,6 +90,10 @@ type OIDC struct {
Scopes []string `json:"scopes" mapstructure:"scopes"` Scopes []string `json:"scopes" mapstructure:"scopes"`
// Custom token claims fields to pass to the pre-login hook // Custom token claims fields to pass to the pre-login hook
CustomFields []string `json:"custom_fields" mapstructure:"custom_fields"` CustomFields []string `json:"custom_fields" mapstructure:"custom_fields"`
// InsecureSkipSignatureCheck causes SFTPGo to skip JWT signature validation.
// It's intended for special cases where providers, such as Azure, use the "none"
// algorithm. Skipping the signature validation can cause security issues
InsecureSkipSignatureCheck bool `json:"insecure_skip_signature_check" mapstructure:"insecure_skip_signature_check"`
// Debug enables the OIDC debug mode. In debug mode, the received id_token will be logged // Debug enables the OIDC debug mode. In debug mode, the received id_token will be logged
// at the debug level // at the debug level
Debug bool `json:"debug" mapstructure:"debug"` Debug bool `json:"debug" mapstructure:"debug"`
@ -160,7 +164,8 @@ func (o *OIDC) initialize() error {
} }
o.provider = provider o.provider = provider
o.verifier = provider.Verifier(&oidc.Config{ o.verifier = provider.Verifier(&oidc.Config{
ClientID: o.ClientID, ClientID: o.ClientID,
InsecureSkipSignatureCheck: o.InsecureSkipSignatureCheck,
}) })
o.oauth2Config = &oauth2.Config{ o.oauth2Config = &oauth2.Config{
ClientID: o.ClientID, ClientID: o.ClientID,

View file

@ -280,6 +280,7 @@
"role_field": "", "role_field": "",
"implicit_roles": false, "implicit_roles": false,
"custom_fields": [], "custom_fields": [],
"insecure_skip_signature_check": false,
"debug": false "debug": false
}, },
"security": { "security": {