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

[v12] Machine ID tbot FIPS Support #23850

Merged
merged 1 commit into from Mar 30, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/tbot/config/config.go
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/utils"
)

Expand Down Expand Up @@ -151,6 +152,14 @@ type CLIConf struct {
// RemainingArgs is the remaining string arguments for commands that
// require them.
RemainingArgs []string

// FIPS instructs `tbot` to run in a mode designed to comply with FIPS
// regulations. This means the bot should:
// - Refuse to run if not compiled with boringcrypto
// - Use FIPS relevant endpoints for cloud providers (e.g AWS)
// - Restrict TLS / SSH cipher suites and TLS version
// - RSA2048 should be used for private key generation
FIPS bool
}

// AzureOnboardingConfig holds configuration relevant to the "azure" join method.
Expand Down Expand Up @@ -227,6 +236,20 @@ type BotConfig struct {
CertificateTTL time.Duration `yaml:"certificate_ttl"`
RenewalInterval time.Duration `yaml:"renewal_interval"`
Oneshot bool `yaml:"oneshot"`
// FIPS instructs `tbot` to run in a mode designed to comply with FIPS
// regulations. This means the bot should:
// - Refuse to run if not compiled with boringcrypto
// - Use FIPS relevant endpoints for cloud providers (e.g AWS)
// - Restrict TLS / SSH cipher suites and TLS version
// - RSA2048 should be used for private key generation
FIPS bool `yaml:"fips"`
}

func (conf *BotConfig) CipherSuites() []uint16 {
if conf.FIPS {
return defaults.FIPSCipherSuites
}
return utils.DefaultCipherSuites()
}

func (conf *BotConfig) CheckAndSetDefaults() error {
Expand Down Expand Up @@ -438,6 +461,10 @@ func FromCLIConf(cf *CLIConf) (*BotConfig, error) {
config.Onboarding.SetToken(cf.Token)
}

if cf.FIPS {
config.FIPS = cf.FIPS
}

if err := config.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err, "validating merged bot config")
}
Expand Down
17 changes: 14 additions & 3 deletions lib/tbot/identity/identity.go
Expand Up @@ -33,6 +33,7 @@ import (
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
apisshutils "github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/tbot/bot"
"github.com/gravitational/teleport/lib/tlsca"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -228,23 +229,33 @@ func (i *Identity) getSSHCheckers() ([]ssh.PublicKey, error) {

// SSHClientConfig returns a ssh.ClientConfig used by the bot to connect to
// the reverse tunnel server.
func (i *Identity) SSHClientConfig() (*ssh.ClientConfig, error) {
func (i *Identity) SSHClientConfig(fips bool) (*ssh.ClientConfig, error) {
callback, err := apisshutils.NewHostKeyCallback(
apisshutils.HostKeyCallbackConfig{
GetHostCheckers: i.getSSHCheckers,
FIPS: fips,
})
if err != nil {
return nil, trace.Wrap(err)
}
if len(i.SSHCert.ValidPrincipals) < 1 {
return nil, trace.BadParameter("user cert has no valid principals")
}
return &ssh.ClientConfig{
config := &ssh.ClientConfig{
User: i.SSHCert.ValidPrincipals[0],
Auth: []ssh.AuthMethod{ssh.PublicKeys(i.KeySigner)},
HostKeyCallback: callback,
Timeout: apidefaults.DefaultIOTimeout,
}, nil
}
if fips {
config.Config = ssh.Config{
KeyExchanges: defaults.FIPSKEXAlgorithms,
MACs: defaults.FIPSMACAlgorithms,
Ciphers: defaults.FIPSCiphers,
}
}

return config, nil
}

// ReadIdentityFromStore reads stored identity credentials
Expand Down
6 changes: 4 additions & 2 deletions lib/tbot/renew.go
Expand Up @@ -75,12 +75,12 @@ func (b *Bot) AuthenticatedUserClientFromIdentity(ctx context.Context, id *ident
return nil, trace.BadParameter("auth client requires a fully formed identity")
}

tlsConfig, err := id.TLSConfig(nil /* cipherSuites */)
tlsConfig, err := id.TLSConfig(b.cfg.CipherSuites())
if err != nil {
return nil, trace.Wrap(err)
}

sshConfig, err := id.SSHClientConfig()
sshConfig, err := id.SSHClientConfig(b.cfg.FIPS)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -473,6 +473,8 @@ func (b *Bot) getIdentityFromToken() (*identity.Identity, error) {
GetHostCredentials: client.HostCredentials,
JoinMethod: b.cfg.Onboarding.JoinMethod,
Expires: &expires,
FIPS: b.cfg.FIPS,
CipherSuites: b.cfg.CipherSuites(),
}
if params.JoinMethod == types.JoinMethodAzure {
params.AzureParams = auth.AzureParams{
Expand Down
11 changes: 11 additions & 0 deletions lib/tbot/tbot.go
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/tbot/config"
"github.com/gravitational/teleport/lib/tbot/identity"
"github.com/gravitational/teleport/lib/utils"
Expand All @@ -42,6 +43,7 @@ type Bot struct {
cfg *config.BotConfig
log logrus.FieldLogger
reloadChan chan struct{}
modules modules.Modules

// These are protected by getter/setters with mutex locks
mu sync.Mutex
Expand All @@ -62,6 +64,7 @@ func New(cfg *config.BotConfig, log logrus.FieldLogger, reloadChan chan struct{}
cfg: cfg,
log: log,
reloadChan: reloadChan,
modules: modules.GetModules(),

_cas: map[types.CertAuthType][]types.CertAuthority{},
}
Expand Down Expand Up @@ -266,6 +269,14 @@ func (b *Bot) initialize(ctx context.Context) (func() error, error) {
)
}

if b.cfg.FIPS {
if !b.modules.IsBoringBinary() {
b.log.Error("FIPS mode enabled but FIPS compatible binary not in use. Ensure you are using the Enterprise FIPS binary to use this flag.")
return nil, trace.BadParameter("fips mode enabled but binary was not compiled with boringcrypto")
}
b.log.Info("Bot is running in FIPS compliant mode.")
}

// First, try to make sure all destinations are usable.
if err := checkDestinations(b.cfg); err != nil {
return nil, trace.Wrap(err)
Expand Down
2 changes: 1 addition & 1 deletion operator/sidecar/tbot.go
Expand Up @@ -137,7 +137,7 @@ func (b *Bot) GetClient(ctx context.Context) (auth.ClientI, error) {
return nil, trace.Wrap(err)
}

sshConfig, err := id.SSHClientConfig()
sshConfig, err := id.SSHClientConfig(false)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
1 change: 1 addition & 0 deletions tool/tbot/main.go
Expand Up @@ -65,6 +65,7 @@ func Run(args []string, stdout io.Writer) error {
app := utils.InitCLIParser("tbot", appHelp).Interspersed(false)
app.Flag("debug", "Verbose logging to stdout.").Short('d').BoolVar(&cf.Debug)
app.Flag("config", "Path to a configuration file.").Short('c').StringVar(&cf.ConfigPath)
app.Flag("fips", "Runs tbot in FIPS compliance mode. This requires the FIPS binary is in use.").BoolVar(&cf.FIPS)
app.HelpFlag.Short('h')

joinMethodList := fmt.Sprintf(
Expand Down
1 change: 1 addition & 0 deletions tool/tbot/main_test.go
Expand Up @@ -65,6 +65,7 @@ func TestRun_Configure(t *testing.T) {
"--oneshot",
"--certificate-ttl", "42m",
"--renewal-interval", "21m",
"--fips",
}...),
},
}
Expand Down
Expand Up @@ -15,3 +15,4 @@ auth_server: example.com
certificate_ttl: 42m0s
renewal_interval: 21m0s
oneshot: true
fips: true
Expand Up @@ -15,3 +15,4 @@ auth_server: example.com
certificate_ttl: 42m0s
renewal_interval: 21m0s
oneshot: true
fips: true
Expand Up @@ -9,3 +9,4 @@ auth_server: ""
certificate_ttl: 1h0m0s
renewal_interval: 20m0s
oneshot: false
fips: false
Expand Up @@ -9,3 +9,4 @@ auth_server: ""
certificate_ttl: 1h0m0s
renewal_interval: 20m0s
oneshot: false
fips: false