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

[V10] Download mTLS files from Web (#14526) #15081

Merged
merged 1 commit into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions api/identityfile/identityfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ import (

const (
// FilePermissions defines file permissions for identity files.
//
// Specifically, for postgres, this must be 0600 or 0640 (choosing 0600 as it's more restrictive)
// https://www.postgresql.org/docs/current/libpq-ssl.html
// On Unix systems, the permissions on the private key file must disallow any access to world or group;
// achieve this by a command such as chmod 0600 ~/.postgresql/postgresql.key.
// Alternatively, the file can be owned by root and have group read access (that is, 0640 permissions).
//
// Other services should accept 0600 as well, if not, we must change the Write function (in `lib/client/identityfile/identity.go`)
FilePermissions = 0600
)

Expand Down
3 changes: 3 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ const (
// KindSessionTracker is a resource that tracks a live session.
KindSessionTracker = "session_tracker"

// KindDatabaseCertificate is a resource to control Database Certificates generation
KindDatabaseCertificate = "database_certificate"

// V5 is the fifth version of resources.
V5 = "v5"

Expand Down
26 changes: 19 additions & 7 deletions lib/auth/auth_with_roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package auth

import (
"context"
"fmt"
"net/url"
"time"

Expand Down Expand Up @@ -3687,14 +3688,25 @@ func (a *ServerWithRoles) SignDatabaseCSR(ctx context.Context, req *proto.Databa
// role Db.
// - Database service when initiating connection to a database instance to
// produce a client certificate.
// - Proxy service when generating mTLS files to a database
func (a *ServerWithRoles) GenerateDatabaseCert(ctx context.Context, req *proto.DatabaseCertRequest) (*proto.DatabaseCertResponse, error) {
// Check if this is a local cluster admin, or a database service, or a
// user that is allowed to impersonate database service.
if !a.hasBuiltinRole(types.RoleDatabase, types.RoleAdmin) {
if err := a.canImpersonateBuiltinRole(types.RoleDatabase); err != nil {
log.WithError(err).Warnf("User %v tried to generate database certificate but is not allowed to impersonate %q system role.",
a.context.User.GetName(), types.RoleDatabase)
return nil, trace.AccessDenied(`access denied. The user must be able to impersonate the builtin role and user "Db" in order to generate database certificates, for more info see https://goteleport.com/docs/database-access/reference/cli/#tctl-auth-sign.`)
// Check if the User can `create` DatabaseCertificates
err := a.action(apidefaults.Namespace, types.KindDatabaseCertificate, types.VerbCreate)
if err != nil {
if !trace.IsAccessDenied(err) {
return nil, trace.Wrap(err)
}

// Err is access denied, trying the old way

// Check if this is a local cluster admin, or a database service, or a
// user that is allowed to impersonate database service.
if !a.hasBuiltinRole(types.RoleDatabase, types.RoleAdmin) {
if err := a.canImpersonateBuiltinRole(types.RoleDatabase); err != nil {
log.WithError(err).Warnf("User %v tried to generate database certificate but does not have '%s' permission for '%s' kind, nor is allowed to impersonate %q system role",
a.context.User.GetName(), types.VerbCreate, types.KindDatabaseCertificate, types.RoleDatabase)
return nil, trace.AccessDenied(fmt.Sprintf("access denied. User must have '%s' permission for '%s' kind to generate the certificate ", types.VerbCreate, types.KindDatabaseCertificate))
}
}
}
return a.authServer.GenerateDatabaseCert(ctx, req)
Expand Down
197 changes: 78 additions & 119 deletions lib/auth/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,80 @@ func (a *authorizer) authorizeRemoteBuiltinRole(r RemoteBuiltinRole) (*Context,
}, nil
}

func roleSpecForProxyWithRecordAtProxy(clusterName string) types.RoleSpecV5 {
base := roleSpecForProxy(clusterName)
base.Allow.Rules = append(base.Allow.Rules, types.NewRule(types.KindHostCert, services.RW()))
return base
}

func roleSpecForProxy(clusterName string) types.RoleSpecV5 {
return types.RoleSpecV5{
Allow: types.RoleConditions{
Namespaces: []string{types.Wildcard},
ClusterLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
NodeLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
AppLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
DatabaseLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
KubernetesLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
Rules: []types.Rule{
types.NewRule(types.KindProxy, services.RW()),
types.NewRule(types.KindOIDCRequest, services.RW()),
types.NewRule(types.KindSSHSession, services.RW()),
types.NewRule(types.KindSession, services.RO()),
types.NewRule(types.KindEvent, services.RW()),
types.NewRule(types.KindSAMLRequest, services.RW()),
types.NewRule(types.KindOIDC, services.ReadNoSecrets()),
types.NewRule(types.KindSAML, services.ReadNoSecrets()),
types.NewRule(types.KindGithub, services.ReadNoSecrets()),
types.NewRule(types.KindGithubRequest, services.RW()),
types.NewRule(types.KindNamespace, services.RO()),
types.NewRule(types.KindNode, services.RO()),
types.NewRule(types.KindAuthServer, services.RO()),
types.NewRule(types.KindReverseTunnel, services.RO()),
types.NewRule(types.KindCertAuthority, services.ReadNoSecrets()),
types.NewRule(types.KindUser, services.RO()),
types.NewRule(types.KindRole, services.RO()),
types.NewRule(types.KindClusterAuthPreference, services.RO()),
types.NewRule(types.KindClusterName, services.RO()),
types.NewRule(types.KindClusterAuditConfig, services.RO()),
types.NewRule(types.KindClusterNetworkingConfig, services.RO()),
types.NewRule(types.KindSessionRecordingConfig, services.RO()),
types.NewRule(types.KindStaticTokens, services.RO()),
types.NewRule(types.KindTunnelConnection, services.RW()),
types.NewRule(types.KindRemoteCluster, services.RO()),
types.NewRule(types.KindSemaphore, services.RW()),
types.NewRule(types.KindAppServer, services.RO()),
types.NewRule(types.KindWebSession, services.RW()),
types.NewRule(types.KindWebToken, services.RW()),
types.NewRule(types.KindKubeService, services.RW()),
types.NewRule(types.KindDatabaseServer, services.RO()),
types.NewRule(types.KindLock, services.RO()),
types.NewRule(types.KindToken, []string{types.VerbRead, types.VerbDelete}),
types.NewRule(types.KindWindowsDesktopService, services.RO()),
types.NewRule(types.KindDatabaseCertificate, []string{types.VerbCreate}),
types.NewRule(types.KindWindowsDesktop, services.RO()),
// this rule allows local proxy to update the remote cluster's host certificate authorities
// during certificates renewal
{
Resources: []string{types.KindCertAuthority},
Verbs: []string{types.VerbCreate, types.VerbRead, types.VerbUpdate},
// allow administrative access to the host certificate authorities
// matching any cluster name except local
Where: builder.And(
builder.Equals(services.CertAuthorityTypeExpr, builder.String(string(types.HostCA))),
builder.Not(
builder.Equals(
services.ResourceNameExpr,
builder.String(clusterName),
),
),
).String(),
},
},
},
}
}

// RoleSetForBuiltinRole returns RoleSet for embedded builtin role
func RoleSetForBuiltinRoles(clusterName string, recConfig types.SessionRecordingConfig, roles ...types.SystemRole) (services.RoleSet, error) {
var definitions []types.Role
Expand Down Expand Up @@ -481,128 +555,13 @@ func definitionForBuiltinRole(clusterName string, recConfig types.SessionRecordi
if services.IsRecordAtProxy(recConfig.GetMode()) {
return services.RoleFromSpec(
role.String(),
types.RoleSpecV5{
Allow: types.RoleConditions{
Namespaces: []string{types.Wildcard},
ClusterLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
Rules: []types.Rule{
types.NewRule(types.KindProxy, services.RW()),
types.NewRule(types.KindOIDCRequest, services.RW()),
types.NewRule(types.KindSSHSession, services.RW()),
types.NewRule(types.KindSession, services.RO()),
types.NewRule(types.KindEvent, services.RW()),
types.NewRule(types.KindSAMLRequest, services.RW()),
types.NewRule(types.KindOIDC, services.ReadNoSecrets()),
types.NewRule(types.KindSAML, services.ReadNoSecrets()),
types.NewRule(types.KindGithub, services.ReadNoSecrets()),
types.NewRule(types.KindGithubRequest, services.RW()),
types.NewRule(types.KindNamespace, services.RO()),
types.NewRule(types.KindNode, services.RO()),
types.NewRule(types.KindAuthServer, services.RO()),
types.NewRule(types.KindReverseTunnel, services.RO()),
types.NewRule(types.KindCertAuthority, services.ReadNoSecrets()),
types.NewRule(types.KindUser, services.RO()),
types.NewRule(types.KindRole, services.RO()),
types.NewRule(types.KindClusterAuthPreference, services.RO()),
types.NewRule(types.KindClusterName, services.RO()),
types.NewRule(types.KindClusterAuditConfig, services.RO()),
types.NewRule(types.KindClusterNetworkingConfig, services.RO()),
types.NewRule(types.KindSessionRecordingConfig, services.RO()),
types.NewRule(types.KindStaticTokens, services.RO()),
types.NewRule(types.KindTunnelConnection, services.RW()),
types.NewRule(types.KindHostCert, services.RW()),
types.NewRule(types.KindRemoteCluster, services.RO()),
types.NewRule(types.KindSemaphore, services.RW()),
types.NewRule(types.KindAppServer, services.RO()),
types.NewRule(types.KindWebSession, services.RW()),
types.NewRule(types.KindWebToken, services.RW()),
types.NewRule(types.KindKubeService, services.RW()),
types.NewRule(types.KindDatabaseServer, services.RO()),
types.NewRule(types.KindLock, services.RO()),
types.NewRule(types.KindWindowsDesktopService, services.RO()),
types.NewRule(types.KindWindowsDesktop, services.RO()),
// this rule allows local proxy to update the remote cluster's host certificate authorities
// during certificates renewal
{
Resources: []string{types.KindCertAuthority},
Verbs: []string{types.VerbCreate, types.VerbRead, types.VerbUpdate},
// allow administrative access to the host certificate authorities
// matching any cluster name except local
Where: builder.And(
builder.Equals(services.CertAuthorityTypeExpr, builder.String(string(types.HostCA))),
builder.Not(
builder.Equals(
services.ResourceNameExpr,
builder.String(clusterName),
),
),
).String(),
},
},
},
})
roleSpecForProxyWithRecordAtProxy(clusterName),
)
}
return services.RoleFromSpec(
role.String(),
types.RoleSpecV5{
Allow: types.RoleConditions{
Namespaces: []string{types.Wildcard},
ClusterLabels: types.Labels{types.Wildcard: []string{types.Wildcard}},
Rules: []types.Rule{
types.NewRule(types.KindProxy, services.RW()),
types.NewRule(types.KindOIDCRequest, services.RW()),
types.NewRule(types.KindSSHSession, services.RW()),
types.NewRule(types.KindSession, services.RO()),
types.NewRule(types.KindEvent, services.RW()),
types.NewRule(types.KindSAMLRequest, services.RW()),
types.NewRule(types.KindOIDC, services.ReadNoSecrets()),
types.NewRule(types.KindSAML, services.ReadNoSecrets()),
types.NewRule(types.KindGithub, services.ReadNoSecrets()),
types.NewRule(types.KindGithubRequest, services.RW()),
types.NewRule(types.KindNamespace, services.RO()),
types.NewRule(types.KindNode, services.RO()),
types.NewRule(types.KindAuthServer, services.RO()),
types.NewRule(types.KindReverseTunnel, services.RO()),
types.NewRule(types.KindCertAuthority, services.ReadNoSecrets()),
types.NewRule(types.KindUser, services.RO()),
types.NewRule(types.KindRole, services.RO()),
types.NewRule(types.KindClusterAuthPreference, services.RO()),
types.NewRule(types.KindClusterName, services.RO()),
types.NewRule(types.KindClusterAuditConfig, services.RO()),
types.NewRule(types.KindClusterNetworkingConfig, services.RO()),
types.NewRule(types.KindSessionRecordingConfig, services.RO()),
types.NewRule(types.KindStaticTokens, services.RO()),
types.NewRule(types.KindTunnelConnection, services.RW()),
types.NewRule(types.KindRemoteCluster, services.RO()),
types.NewRule(types.KindSemaphore, services.RW()),
types.NewRule(types.KindAppServer, services.RO()),
types.NewRule(types.KindWebSession, services.RW()),
types.NewRule(types.KindWebToken, services.RW()),
types.NewRule(types.KindKubeService, services.RW()),
types.NewRule(types.KindDatabaseServer, services.RO()),
types.NewRule(types.KindLock, services.RO()),
types.NewRule(types.KindWindowsDesktopService, services.RO()),
types.NewRule(types.KindWindowsDesktop, services.RO()),
// this rule allows local proxy to update the remote cluster's host certificate authorities
// during certificates renewal
{
Resources: []string{types.KindCertAuthority},
Verbs: []string{types.VerbCreate, types.VerbRead, types.VerbUpdate},
// allow administrative access to the certificate authority names
// matching any cluster name except local
Where: builder.And(
builder.Equals(services.CertAuthorityTypeExpr, builder.String(string(types.HostCA))),
builder.Not(
builder.Equals(
services.ResourceNameExpr,
builder.String(clusterName),
),
),
).String(),
},
},
},
})
roleSpecForProxy(clusterName),
)
case types.RoleSignup:
return services.RoleFromSpec(
role.String(),
Expand Down
Loading