TUN-5551: Show whether the binary was built for FIPS compliance

This is shown in 3 ways:
 - the version output with `cloudflared version` and alike commands
 - the build_info prometheus metric
 - a logging message
This commit is contained in:
Nuno Diegues 2021-12-27 19:05:14 +00:00
parent 01ad2785ee
commit a39d95d5f7
8 changed files with 46 additions and 29 deletions

View File

@ -25,6 +25,7 @@ ifeq ($(FIPS), true)
LINK_FLAGS := -linkmode=external -extldflags=-static $(LINK_FLAGS) LINK_FLAGS := -linkmode=external -extldflags=-static $(LINK_FLAGS)
# Prevent linking with libc regardless of CGO enabled or not. # Prevent linking with libc regardless of CGO enabled or not.
GO_BUILD_TAGS := $(GO_BUILD_TAGS) osusergo netgo fips GO_BUILD_TAGS := $(GO_BUILD_TAGS) osusergo netgo fips
VERSION_FLAGS := $(VERSION_FLAGS) -X "main.BuildType=FIPS"
endif endif
LDFLAGS := -ldflags='$(VERSION_FLAGS) $(LINK_FLAGS)' LDFLAGS := -ldflags='$(VERSION_FLAGS) $(LINK_FLAGS)'

View File

@ -1,4 +1,4 @@
package buildinfo package cliutil
import ( import (
"fmt" "fmt"
@ -11,23 +11,39 @@ type BuildInfo struct {
GoOS string `json:"go_os"` GoOS string `json:"go_os"`
GoVersion string `json:"go_version"` GoVersion string `json:"go_version"`
GoArch string `json:"go_arch"` GoArch string `json:"go_arch"`
BuildType string `json:"build_type"`
CloudflaredVersion string `json:"cloudflared_version"` CloudflaredVersion string `json:"cloudflared_version"`
} }
func GetBuildInfo(cloudflaredVersion string) *BuildInfo { func GetBuildInfo(buildType, version string) *BuildInfo {
return &BuildInfo{ return &BuildInfo{
GoOS: runtime.GOOS, GoOS: runtime.GOOS,
GoVersion: runtime.Version(), GoVersion: runtime.Version(),
GoArch: runtime.GOARCH, GoArch: runtime.GOARCH,
CloudflaredVersion: cloudflaredVersion, BuildType: buildType,
CloudflaredVersion: version,
} }
} }
func (bi *BuildInfo) Log(log *zerolog.Logger) { func (bi *BuildInfo) Log(log *zerolog.Logger) {
log.Info().Msgf("Version %s", bi.CloudflaredVersion) log.Info().Msgf("Version %s", bi.CloudflaredVersion)
if bi.BuildType != "" {
log.Info().Msgf("Built%s", bi.GetBuildTypeMsg())
}
log.Info().Msgf("GOOS: %s, GOVersion: %s, GoArch: %s", bi.GoOS, bi.GoVersion, bi.GoArch) log.Info().Msgf("GOOS: %s, GOVersion: %s, GoArch: %s", bi.GoOS, bi.GoVersion, bi.GoArch)
} }
func (bi *BuildInfo) OSArch() string { func (bi *BuildInfo) OSArch() string {
return fmt.Sprintf("%s_%s", bi.GoOS, bi.GoArch) return fmt.Sprintf("%s_%s", bi.GoOS, bi.GoArch)
} }
func (bi *BuildInfo) Version() string {
return bi.CloudflaredVersion
}
func (bi *BuildInfo) GetBuildTypeMsg() string {
if bi.BuildType == "" {
return ""
}
return fmt.Sprintf(" with %s", bi.BuildType)
}

View File

@ -31,6 +31,7 @@ const (
var ( var (
Version = "DEV" Version = "DEV"
BuildTime = "unknown" BuildTime = "unknown"
BuildType = ""
// Mostly network errors that we don't want reported back to Sentry, this is done by substring match. // Mostly network errors that we don't want reported back to Sentry, this is done by substring match.
ignoredErrors = []string{ ignoredErrors = []string{
"connection reset by peer", "connection reset by peer",
@ -46,9 +47,10 @@ var (
func main() { func main() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
metrics.RegisterBuildInfo(BuildTime, Version) metrics.RegisterBuildInfo(BuildType, BuildTime, Version)
raven.SetRelease(Version) raven.SetRelease(Version)
maxprocs.Set() maxprocs.Set()
bInfo := cliutil.GetBuildInfo(BuildType, Version)
// Graceful shutdown channel used by the app. When closed, app must terminate gracefully. // Graceful shutdown channel used by the app. When closed, app must terminate gracefully.
// Windows service manager closes this channel when it receives stop command. // Windows service manager closes this channel when it receives stop command.
@ -71,7 +73,7 @@ func main() {
Terms (https://www.cloudflare.com/terms/) and Privacy Policy (https://www.cloudflare.com/privacypolicy/).`, Terms (https://www.cloudflare.com/terms/) and Privacy Policy (https://www.cloudflare.com/privacypolicy/).`,
time.Now().Year(), time.Now().Year(),
) )
app.Version = fmt.Sprintf("%s (built %s)", Version, BuildTime) app.Version = fmt.Sprintf("%s (built %s%s)", Version, BuildTime, bInfo.GetBuildTypeMsg())
app.Description = `cloudflared connects your machine or user identity to Cloudflare's global network. app.Description = `cloudflared connects your machine or user identity to Cloudflare's global network.
You can use it to authenticate a session to reach an API behind Access, route web traffic to this machine, You can use it to authenticate a session to reach an API behind Access, route web traffic to this machine,
and configure access control. and configure access control.
@ -81,7 +83,7 @@ func main() {
app.Action = action(graceShutdownC) app.Action = action(graceShutdownC)
app.Commands = commands(cli.ShowVersion) app.Commands = commands(cli.ShowVersion)
tunnel.Init(Version, graceShutdownC) // we need this to support the tunnel sub command... tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command...
access.Init(graceShutdownC) access.Init(graceShutdownC)
updater.Init(Version) updater.Init(Version)
runApp(app, graceShutdownC) runApp(app, graceShutdownC)

View File

@ -22,7 +22,6 @@ import (
"github.com/urfave/cli/v2/altsrc" "github.com/urfave/cli/v2/altsrc"
"github.com/cloudflare/cloudflared/cfapi" "github.com/cloudflare/cloudflared/cfapi"
"github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo"
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/proxydns" "github.com/cloudflare/cloudflared/cmd/cloudflared/proxydns"
"github.com/cloudflare/cloudflared/cmd/cloudflared/ui" "github.com/cloudflare/cloudflared/cmd/cloudflared/ui"
@ -86,7 +85,7 @@ const (
var ( var (
graceShutdownC chan struct{} graceShutdownC chan struct{}
version string buildInfo *cliutil.BuildInfo
routeFailMsg = fmt.Sprintf("failed to provision routing, please create it manually via Cloudflare dashboard or UI; "+ routeFailMsg = fmt.Sprintf("failed to provision routing, please create it manually via Cloudflare dashboard or UI; "+
"most likely you already have a conflicting record there. You can also rerun this command with --%s to overwrite "+ "most likely you already have a conflicting record there. You can also rerun this command with --%s to overwrite "+
@ -175,8 +174,8 @@ func TunnelCommand(c *cli.Context) error {
return runClassicTunnel(sc) return runClassicTunnel(sc)
} }
func Init(ver string, gracefulShutdown chan struct{}) { func Init(info *cliutil.BuildInfo, gracefulShutdown chan struct{}) {
version, graceShutdownC = ver, gracefulShutdown buildInfo, graceShutdownC = info, gracefulShutdown
} }
// runAdhocNamedTunnel create, route and run a named tunnel in one command // runAdhocNamedTunnel create, route and run a named tunnel in one command
@ -209,7 +208,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri
// runClassicTunnel creates a "classic" non-named tunnel // runClassicTunnel creates a "classic" non-named tunnel
func runClassicTunnel(sc *subcommandContext) error { func runClassicTunnel(sc *subcommandContext) error {
return StartServer(sc.c, version, nil, sc.log, sc.isUIEnabled) return StartServer(sc.c, buildInfo, nil, sc.log, sc.isUIEnabled)
} }
func routeFromFlag(c *cli.Context) (route cfapi.HostnameRoute, ok bool) { func routeFromFlag(c *cli.Context) (route cfapi.HostnameRoute, ok bool) {
@ -224,7 +223,7 @@ func routeFromFlag(c *cli.Context) (route cfapi.HostnameRoute, ok bool) {
func StartServer( func StartServer(
c *cli.Context, c *cli.Context,
version string, info *cliutil.BuildInfo,
namedTunnel *connection.NamedTunnelConfig, namedTunnel *connection.NamedTunnelConfig,
log *zerolog.Logger, log *zerolog.Logger,
isUIEnabled bool, isUIEnabled bool,
@ -271,8 +270,7 @@ func StartServer(
defer trace.Stop() defer trace.Stop()
} }
buildInfo := buildinfo.GetBuildInfo(version) info.Log(log)
buildInfo.Log(log)
logClientOptions(c, log) logClientOptions(c, log)
// this context drives the server, when it's cancelled tunnel and all other components (origins, dns, etc...) should stop // this context drives the server, when it's cancelled tunnel and all other components (origins, dns, etc...) should stop
@ -336,7 +334,7 @@ func StartServer(
observer.SendURL(quickTunnelURL) observer.SendURL(quickTunnelURL)
} }
tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, log, logTransport, observer, namedTunnel) tunnelConfig, ingressRules, err := prepareTunnelConfig(c, info, log, logTransport, observer, namedTunnel)
if err != nil { if err != nil {
log.Err(err).Msg("Couldn't start tunnel") log.Err(err).Msg("Couldn't start tunnel")
return err return err
@ -377,7 +375,7 @@ func StartServer(
if isUIEnabled { if isUIEnabled {
tunnelUI := ui.NewUIModel( tunnelUI := ui.NewUIModel(
version, info.Version(),
hostname, hostname,
metricsListener.Addr().String(), metricsListener.Addr().String(),
&ingressRules, &ingressRules,

View File

@ -16,7 +16,8 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/config" "github.com/cloudflare/cloudflared/config"
"github.com/cloudflare/cloudflared/connection" "github.com/cloudflare/cloudflared/connection"
"github.com/cloudflare/cloudflared/edgediscovery" "github.com/cloudflare/cloudflared/edgediscovery"
@ -148,8 +149,7 @@ func getOriginCert(originCertPath string, log *zerolog.Logger) ([]byte, error) {
func prepareTunnelConfig( func prepareTunnelConfig(
c *cli.Context, c *cli.Context,
buildInfo *buildinfo.BuildInfo, info *cliutil.BuildInfo,
version string,
log, logTransport *zerolog.Logger, log, logTransport *zerolog.Logger,
observer *connection.Observer, observer *connection.Observer,
namedTunnel *connection.NamedTunnelConfig, namedTunnel *connection.NamedTunnelConfig,
@ -193,8 +193,8 @@ func prepareTunnelConfig(
namedTunnel.Client = tunnelpogs.ClientInfo{ namedTunnel.Client = tunnelpogs.ClientInfo{
ClientID: clientUUID[:], ClientID: clientUUID[:],
Features: dedup(features), Features: dedup(features),
Version: version, Version: info.Version(),
Arch: buildInfo.OSArch(), Arch: info.OSArch(),
} }
ingressRules, err = ingress.ParseIngress(cfg) ingressRules, err = ingress.ParseIngress(cfg)
if err != nil && err != ingress.ErrNoIngressRules { if err != nil && err != ingress.ErrNoIngressRules {
@ -281,7 +281,7 @@ func prepareTunnelConfig(
return &origin.TunnelConfig{ return &origin.TunnelConfig{
ConnectionConfig: connectionConfig, ConnectionConfig: connectionConfig,
OSArch: buildInfo.OSArch(), OSArch: info.OSArch(),
ClientID: clientID, ClientID: clientID,
EdgeAddrs: c.StringSlice("edge"), EdgeAddrs: c.StringSlice("edge"),
Region: c.String("region"), Region: c.String("region"),
@ -293,7 +293,7 @@ func prepareTunnelConfig(
Log: log, Log: log,
LogTransport: logTransport, LogTransport: logTransport,
Observer: observer, Observer: observer,
ReportedVersion: version, ReportedVersion: info.Version(),
// Note TUN-3758 , we use Int because UInt is not supported with altsrc // Note TUN-3758 , we use Int because UInt is not supported with altsrc
Retries: uint(c.Int("retries")), Retries: uint(c.Int("retries")),
RunFromTerminal: isRunningFromTerminal(), RunFromTerminal: isRunningFromTerminal(),

View File

@ -76,7 +76,7 @@ func RunQuickTunnel(sc *subcommandContext) error {
return StartServer( return StartServer(
sc.c, sc.c,
version, buildInfo,
&connection.NamedTunnelConfig{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname}, &connection.NamedTunnelConfig{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname},
sc.log, sc.log,
sc.isUIEnabled, sc.isUIEnabled,

View File

@ -76,7 +76,7 @@ func (sc *subcommandContext) client() (cfapi.Client, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
userAgent := fmt.Sprintf("cloudflared/%s", version) userAgent := fmt.Sprintf("cloudflared/%s", buildInfo.Version())
client, err := cfapi.NewRESTClient( client, err := cfapi.NewRESTClient(
sc.c.String("api-url"), sc.c.String("api-url"),
credential.cert.AccountID, credential.cert.AccountID,
@ -303,7 +303,7 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error {
return StartServer( return StartServer(
sc.c, sc.c,
version, buildInfo,
&connection.NamedTunnelConfig{Credentials: credentials}, &connection.NamedTunnelConfig{Credentials: credentials},
sc.log, sc.log,
sc.isUIEnabled, sc.isUIEnabled,

View File

@ -83,15 +83,15 @@ func ServeMetrics(
return err return err
} }
func RegisterBuildInfo(buildTime string, version string) { func RegisterBuildInfo(buildType, buildTime, version string) {
buildInfo := prometheus.NewGaugeVec( buildInfo := prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
// Don't namespace build_info, since we want it to be consistent across all Cloudflare services // Don't namespace build_info, since we want it to be consistent across all Cloudflare services
Name: "build_info", Name: "build_info",
Help: "Build and version information", Help: "Build and version information",
}, },
[]string{"goversion", "revision", "version"}, []string{"goversion", "type", "revision", "version"},
) )
prometheus.MustRegister(buildInfo) prometheus.MustRegister(buildInfo)
buildInfo.WithLabelValues(runtime.Version(), buildTime, version).Set(1) buildInfo.WithLabelValues(runtime.Version(), buildType, buildTime, version).Set(1)
} }