github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/components/components.go (about) 1 // Copyright 2019 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package components defines and instantiates the components needed by a 16 // generic Fleetspeak server. 17 // 18 // Installations requiring specialized components should branch this, or 19 // otherwise create a server.Components according to their needs. 20 package components 21 22 import ( 23 "database/sql" 24 "errors" 25 "fmt" 26 "net" 27 "net/http" 28 29 "google.golang.org/grpc" 30 31 "github.com/google/fleetspeak/fleetspeak/src/server" 32 "github.com/google/fleetspeak/fleetspeak/src/server/admin" 33 "github.com/google/fleetspeak/fleetspeak/src/server/authorizer" 34 "github.com/google/fleetspeak/fleetspeak/src/server/comms" 35 cauthorizer "github.com/google/fleetspeak/fleetspeak/src/server/components/authorizer" 36 chttps "github.com/google/fleetspeak/fleetspeak/src/server/components/https" 37 cnotifications "github.com/google/fleetspeak/fleetspeak/src/server/components/notifications" 38 "github.com/google/fleetspeak/fleetspeak/src/server/components/prometheus" 39 "github.com/google/fleetspeak/fleetspeak/src/server/grpcservice" 40 "github.com/google/fleetspeak/fleetspeak/src/server/https" 41 inotifications "github.com/google/fleetspeak/fleetspeak/src/server/internal/notifications" 42 "github.com/google/fleetspeak/fleetspeak/src/server/mysql" 43 "github.com/google/fleetspeak/fleetspeak/src/server/notifications" 44 "github.com/google/fleetspeak/fleetspeak/src/server/service" 45 "github.com/google/fleetspeak/fleetspeak/src/server/stats" 46 47 "github.com/prometheus/client_golang/prometheus/promhttp" 48 49 cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components" 50 sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server" 51 ) 52 53 // MakeComponents creates server components from a given config. 54 func MakeComponents(cfg *cpb.Config) (*server.Components, error) { 55 if cfg.MysqlDataSourceName == "" { 56 return nil, errors.New("mysql_data_source_name is required") 57 } 58 hcfg := cfg.HttpsConfig 59 if hcfg != nil { 60 switch { 61 case hcfg.GetFrontendConfig().GetCleartextHeaderConfig() != nil, 62 hcfg.GetFrontendConfig().GetCleartextHeaderChecksumConfig() != nil, 63 hcfg.GetFrontendConfig().GetCleartextXfccConfig() != nil: 64 if hcfg.ListenAddress == "" { 65 return nil, errors.New("http_config requires listen_address") 66 } 67 default: 68 if hcfg.ListenAddress == "" || hcfg.Certificates == "" || hcfg.Key == "" { 69 return nil, errors.New("https_config requires listen_address, certificates and key") 70 } 71 } 72 } 73 74 acfg := cfg.AdminConfig 75 if acfg != nil && acfg.ListenAddress == "" { 76 return nil, errors.New("admin_config.listen_address can't be empty") 77 } 78 79 // Database setup 80 con, err := sql.Open("mysql", cfg.MysqlDataSourceName) 81 if err != nil { 82 return nil, fmt.Errorf("failed to open database: %v", err) 83 } 84 85 db, err := mysql.MakeDatastore(con) 86 if err != nil { 87 return nil, fmt.Errorf("failed to create datastore: %v", err) 88 } 89 90 // Authorizer setup 91 var auth authorizer.Authorizer 92 if cfg.RequiredLabel == "" { 93 auth = authorizer.PermissiveAuthorizer{} 94 } else { 95 auth = cauthorizer.LabelFilter{Label: cfg.RequiredLabel} 96 } 97 98 // HTTPS setup 99 var comm comms.Communicator 100 if hcfg != nil { 101 l, err := net.Listen("tcp", hcfg.ListenAddress) 102 if err != nil { 103 return nil, fmt.Errorf("failed to listen on [%v]: %v", cfg.HttpsConfig.ListenAddress, err) 104 } 105 if cfg.ProxyProtocol { 106 l = &chttps.ProxyListener{Listener: l} 107 } 108 comm, err = https.NewCommunicator(https.Params{ 109 Listener: l, 110 Cert: []byte(hcfg.Certificates), 111 FrontendConfig: hcfg.GetFrontendConfig(), 112 Key: []byte(hcfg.Key), 113 Streaming: !hcfg.DisableStreaming, 114 }) 115 if err != nil { 116 return nil, fmt.Errorf("failed to create communicator: %v", err) 117 } 118 } 119 // Notification setup. 120 var nn notifications.Notifier 121 var nl notifications.Listener 122 if cfg.NotificationListenAddress != "" { 123 nn = &cnotifications.HttpNotifier{} 124 nl = &cnotifications.HttpListener{ 125 BindAddress: cfg.NotificationListenAddress, 126 AdvertisedAddress: cfg.NotificationPublicAddress, 127 } 128 } else { 129 llc := inotifications.LocalListenerNotifier{} 130 if cfg.NotificationUseHttpNotifier { 131 nn = &cnotifications.HttpNotifier{} 132 } else { 133 nn = &llc 134 } 135 nl = &llc 136 } 137 138 var admSrv *grpc.Server 139 if acfg != nil { 140 as := admin.NewServer(db, nn) 141 admSrv := grpc.NewServer() 142 sgrpc.RegisterAdminServer(admSrv, as) 143 aas, err := net.ResolveTCPAddr("tcp", acfg.ListenAddress) 144 if err != nil { 145 return nil, fmt.Errorf("failed to initialize admin server: %v", err) 146 } 147 asl, err := net.ListenTCP("tcp", aas) 148 if err != nil { 149 return nil, fmt.Errorf("failed to initialize admin server: %v", err) 150 } 151 go func() { 152 admSrv.Serve(asl) 153 }() 154 } 155 156 // Stats setup 157 scfg := cfg.StatsConfig 158 var statsCollector stats.Collector 159 if scfg != nil { 160 addressToExportStats := scfg.Address 161 if addressToExportStats != "" { 162 statsCollector = prometheus.StatsCollector{} 163 statsMux := http.NewServeMux() 164 statsMux.Handle("/metrics", promhttp.Handler()) 165 go http.ListenAndServe(addressToExportStats, statsMux) 166 } 167 } 168 169 // Health check setup 170 hccfg := cfg.HealthCheckConfig 171 var healthCheck *http.Server 172 if hccfg != nil { 173 healthCheckListener, err := net.Listen("tcp", hccfg.ListenAddress) 174 if err != nil { 175 return nil, fmt.Errorf("failed to initialize health check service: %v", err) 176 } 177 healthCheck = &http.Server{ 178 Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}), 179 } 180 go healthCheck.Serve(healthCheckListener) 181 } 182 183 var communicators []comms.Communicator 184 if comm != nil { 185 communicators = append(communicators, comm) 186 } 187 188 // Final assembly 189 return &server.Components{ 190 Datastore: db, 191 ServiceFactories: map[string]service.Factory{ 192 "GRPC": grpcservice.Factory, 193 "NOOP": service.NOOPFactory, 194 }, 195 Communicators: communicators, 196 Authorizer: auth, 197 Stats: statsCollector, 198 Notifier: nn, 199 Listener: nl, 200 Admin: admSrv, 201 HealthCheck: healthCheck, 202 }, nil 203 }