diff --git a/go/cert_srv/BUILD.bazel b/go/cert_srv/BUILD.bazel index 92a9d32e9c..4f6ce379af 100644 --- a/go/cert_srv/BUILD.bazel +++ b/go/cert_srv/BUILD.bazel @@ -3,10 +3,7 @@ load("//:scion.bzl", "scion_go_binary") go_library( name = "go_default_library", - srcs = [ - "main.go", - "setup.go", - ], + srcs = ["main.go"], importpath = "github.com/scionproto/scion/go/cert_srv", visibility = ["//visibility:private"], deps = [ @@ -28,7 +25,6 @@ go_library( "//go/lib/periodic:go_default_library", "//go/lib/prom:go_default_library", "//go/lib/serrors:go_default_library", - "//go/lib/snet:go_default_library", "//go/lib/topology:go_default_library", "//go/proto:go_default_library", "@com_github_burntsushi_toml//:go_default_library", diff --git a/go/cert_srv/main.go b/go/cert_srv/main.go index 182472e129..5a0ddda637 100644 --- a/go/cert_srv/main.go +++ b/go/cert_srv/main.go @@ -16,36 +16,44 @@ package main import ( + "context" "flag" "fmt" _ "net/http/pprof" "os" + "path/filepath" + "sync" "time" + "github.com/BurntSushi/toml" "github.com/opentracing/opentracing-go" "github.com/scionproto/scion/go/cert_srv/internal/config" "github.com/scionproto/scion/go/cert_srv/internal/reiss" + "github.com/scionproto/scion/go/lib/addr" "github.com/scionproto/scion/go/lib/common" "github.com/scionproto/scion/go/lib/discovery" "github.com/scionproto/scion/go/lib/env" "github.com/scionproto/scion/go/lib/fatal" "github.com/scionproto/scion/go/lib/infra" + "github.com/scionproto/scion/go/lib/infra/infraenv" + "github.com/scionproto/scion/go/lib/infra/messenger" "github.com/scionproto/scion/go/lib/infra/modules/idiscovery" "github.com/scionproto/scion/go/lib/infra/modules/itopo" + "github.com/scionproto/scion/go/lib/infra/modules/trust" "github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb" "github.com/scionproto/scion/go/lib/log" "github.com/scionproto/scion/go/lib/periodic" + "github.com/scionproto/scion/go/lib/prom" + "github.com/scionproto/scion/go/lib/serrors" + "github.com/scionproto/scion/go/lib/topology" + "github.com/scionproto/scion/go/proto" ) var ( - cfg config.Config - state *config.State - reissRunner *periodic.Runner - discRunners idiscovery.Runners - corePusher *periodic.Runner - msgr infra.Messenger - trustDB trustdb.TrustDB + cfg config.Config + + tasks *periodicTasks ) func init() { @@ -76,6 +84,12 @@ func realMain() int { log.Crit("Setup failed", "err", err) return 1 } + topo := itopo.Get() + if !topo.Exists(addr.SvcCS, cfg.General.ID) { + log.Crit("Unable to find topo address") + return 1 + } + tracer, trCloser, err := cfg.Tracing.NewTracer(cfg.General.ID) if err != nil { log.Crit("Unable to create tracer", "err", err) @@ -83,18 +97,99 @@ func realMain() int { } defer trCloser.Close() opentracing.SetGlobalTracer(tracer) - // Start the periodic reissuance task. - startReissRunner() - // Start the periodic fetching from discovery service. - startDiscovery() - // Start the messenger. + + router, err := infraenv.NewRouter(topo.IA(), cfg.Sciond) + if err != nil { + log.Crit("Unable to initialize path router", "err", err) + return 1 + } + trustDB, err := cfg.TrustDB.New() + if err != nil { + log.Crit("Unable to initialize trustDB", "err", err) + return 1 + } + trustDB = trustdb.WithMetrics(string(cfg.TrustDB.Backend()), trustDB) + defer trustDB.Close() + trustConf := trust.Config{ + MustHaveLocalChain: true, + ServiceType: proto.ServiceType_cs, + Router: router, + TopoProvider: itopo.Provider(), + } + trustStore := trust.NewStore(trustDB, topo.IA(), trustConf, log.Root()) + err = trustStore.LoadAuthoritativeCrypto(filepath.Join(cfg.General.ConfigDir, "certs")) + if err != nil { + log.Crit("Unable to load local crypto", "err", err) + return 1 + } + + state, err := newState(topo, trustDB, trustStore) + if err != nil { + log.Crit("Unable to load state", "err", err) + return 1 + } + + nc := infraenv.NetworkConfig{ + IA: topo.IA(), + Public: topo.PublicAddress(addr.SvcCS, cfg.General.ID), + SVC: addr.SvcCS, + ReconnectToDispatcher: cfg.General.ReconnectToDispatcher, + QUIC: infraenv.QUIC{ + Address: cfg.QUIC.Address, + CertFile: cfg.QUIC.CertFile, + KeyFile: cfg.QUIC.KeyFile, + }, + SVCResolutionFraction: cfg.QUIC.ResolutionFraction, + TrustStore: trustStore, + Router: router, + SVCRouter: messenger.NewSVCRouter(itopo.Provider()), + } + msgr, err := nc.Messenger() + if err != nil { + log.Crit(infraenv.ErrAppUnableToInitMessenger.Error(), "err", err) + return 1 + } + defer msgr.CloseServer() + + msgr.AddHandler(infra.ChainRequest, trustStore.NewChainReqHandler(true)) + msgr.AddHandler(infra.TRCRequest, trustStore.NewTRCReqHandler(true)) + msgr.AddHandler(infra.Chain, trustStore.NewChainPushHandler()) + msgr.AddHandler(infra.TRC, trustStore.NewTRCPushHandler()) + msgr.UpdateSigner(state.GetSigner(), []infra.MessageType{infra.ChainIssueRequest}) + msgr.UpdateVerifier(trust.NewBasicVerifier(trustStore)) + // Only core CS handles certificate reissuance requests. + if topo.Core() { + msgr.AddHandler(infra.ChainIssueRequest, &reiss.Handler{ + State: state, + IA: topo.IA(), + }) + } + go func() { defer log.LogPanicAndExit() msgr.ListenAndServe() }() - // Cleanup when the CS exits. - defer stop() cfg.Metrics.StartPrometheus() + + discoRunners, err := idiscovery.StartRunners(cfg.Discovery, discovery.Full, + idiscovery.TopoHandlers{}, nil, "cs") + if err != nil { + log.Crit("Unable to start topology fetcher", "err", err) + return 1 + } + defer discoRunners.Kill() + + tasks = &periodicTasks{ + TopoProvider: itopo.Provider(), + Msgr: msgr, + TrustDB: trustDB, + State: state, + } + if err := tasks.Start(); err != nil { + log.Crit("Unable to start periodic tasks", "err", err) + return 1 + } + defer tasks.Kill() select { case <-fatal.ShutdownChan(): // Whenever we receive a SIGINT or SIGTERM we exit without an error. @@ -104,32 +199,62 @@ func realMain() int { } } -// startReissRunner starts a periodic reissuance task. Core starts self-issuer. -// Non-core starts a requester. -func startReissRunner() { - if !cfg.CS.DisableCorePush { - corePusher = periodic.Start( - &reiss.CorePusher{ - LocalIA: itopo.Get().IA(), - TrustDB: state.TrustDB, - Msger: msgr, - }, - time.Hour, - time.Minute, - ) - corePusher.TriggerRun() +type periodicTasks struct { + TopoProvider topology.Provider + Msgr infra.Messenger + TrustDB trustdb.TrustDB + State *config.State + + corePusher *periodic.Runner + reissuance *periodic.Runner + + mtx sync.Mutex + running bool +} + +func (t *periodicTasks) Start() error { + t.mtx.Lock() + defer t.mtx.Unlock() + if t.running { + log.Warn("Trying to start tasks, but they are running! Ignored.") + return nil + } + t.running = true + t.corePusher = t.startCorePusher() + t.reissuance = t.startReissuance(t.corePusher) + log.Info("Started periodic tasks") + return nil +} + +func (t *periodicTasks) startCorePusher() *periodic.Runner { + if cfg.CS.DisableCorePush { + return nil } + p := periodic.Start( + &reiss.CorePusher{ + LocalIA: t.TopoProvider.Get().IA(), + TrustDB: t.TrustDB, + Msger: t.Msgr, + }, + time.Hour, + time.Minute, + ) + p.TriggerRun() + return p +} + +func (t *periodicTasks) startReissuance(corePusher *periodic.Runner) *periodic.Runner { if !cfg.CS.AutomaticRenewal { - log.Info("Reissue disabled, not starting reiss task.") - return + log.Info("Certificate reissuance disabled, not starting periodic task.") + return nil } - if itopo.Get().Core() { - log.Info("Starting periodic reiss.Self task") - reissRunner = periodic.Start( + if t.TopoProvider.Get().Core() { + log.Info("Starting periodic self-issuing reissuance task.") + return periodic.Start( &reiss.Self{ - Msgr: msgr, - State: state, - IA: itopo.Get().IA(), + Msgr: t.Msgr, + State: t.State, + IA: t.TopoProvider.Get().IA(), IssTime: cfg.CS.IssuerReissueLeadTime.Duration, LeafTime: cfg.CS.LeafReissueLeadTime.Duration, CorePusher: corePusher, @@ -138,14 +263,13 @@ func startReissRunner() { cfg.CS.ReissueRate.Duration, cfg.CS.ReissueTimeout.Duration, ) - return } - log.Info("Starting periodic reiss.Requester task") - reissRunner = periodic.Start( + log.Info("Starting periodic reissuance requester task.") + return periodic.Start( &reiss.Requester{ - Msgr: msgr, - State: state, - IA: itopo.Get().IA(), + Msgr: t.Msgr, + State: t.State, + IA: t.TopoProvider.Get().IA(), LeafTime: cfg.CS.LeafReissueLeadTime.Duration, CorePusher: corePusher, }, @@ -154,27 +278,78 @@ func startReissRunner() { ) } -func startDiscovery() { - var err error - discRunners, err = idiscovery.StartRunners(cfg.Discovery, discovery.Full, - idiscovery.TopoHandlers{}, nil, "cs") - if err != nil { - fatal.Fatal(common.NewBasicError("Unable to start dynamic topology fetcher", err)) +func (t *periodicTasks) Kill() { + t.mtx.Lock() + defer t.mtx.Unlock() + if !t.running { + log.Warn("Trying to stop tasks, but they are not running! Ignored.") + return } + t.reissuance.Kill() + t.corePusher.Kill() + t.nilTasks() + t.running = false + log.Info("Stopped periodic tasks.") } -func stopReissRunner() { - if corePusher != nil { - corePusher.Kill() +// nilTasks sets all tasks to nil. That is needed to rollback a partial start +// operation. If the Start operation fails a Kill call will nil all tasks using +// this method, which means after that we can call Start or Kill again without +// issues. This makes sure that we never call Kill on a task twice, since that +// would panic. +func (t *periodicTasks) nilTasks() { + t.reissuance = nil + t.corePusher = nil +} + +func setupBasic() error { + if _, err := toml.DecodeFile(env.ConfigFile(), &cfg); err != nil { + return err } - if reissRunner != nil { - reissRunner.Stop() + cfg.InitDefaults() + if err := env.InitLogging(&cfg.Logging); err != nil { + return err } + prom.ExportElementID(cfg.General.ID) + return env.LogAppStarted(common.CS, cfg.General.ID) } -func stop() { - stopReissRunner() - discRunners.Kill() - msgr.CloseServer() - trustDB.Close() +// setup initializes the config and sets the messenger. +func setup() error { + if err := cfg.Validate(); err != nil { + return serrors.WrapStr("unable to validate config", err) + } + itopo.Init(cfg.General.ID, proto.ServiceType_cs, itopo.Callbacks{}) + topo, err := topology.FromJSONFile(cfg.General.Topology) + if err != nil { + return serrors.WrapStr("unable to load topology", err) + } + if _, _, err := itopo.SetStatic(topo, false); err != nil { + return serrors.WrapStr("Unable to set initial static topology", err) + } + infraenv.InitInfraEnvironment(cfg.General.Topology) + return nil +} + +// TODO(roosd): Remove with trust store v2 +func newState(topo topology.Topology, db trustdb.TrustDB, + store *trust.Store) (*config.State, error) { + + state, err := config.LoadState(cfg.General.ConfigDir, topo.Core(), db, store) + if err != nil { + return nil, serrors.WrapStr("unable to load CS state", err) + } + ctx, cancelF := context.WithTimeout(context.Background(), 2*time.Second) + defer cancelF() + meta, err := trust.CreateSignMeta(ctx, topo.IA(), db) + if err != nil { + return nil, err + } + signer, err := trust.NewBasicSigner(state.GetSigningKey(), meta) + if err != nil { + return nil, err + } + state.SetSigner(signer) + state.SetVerifier(state.Store.NewVerifier()) + return state, nil } diff --git a/go/cert_srv/setup.go b/go/cert_srv/setup.go deleted file mode 100644 index 6a2eed58e7..0000000000 --- a/go/cert_srv/setup.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2018 ETH Zurich, Anapaya Systems -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "context" - "path/filepath" - "time" - - "github.com/BurntSushi/toml" - - "github.com/scionproto/scion/go/cert_srv/internal/config" - "github.com/scionproto/scion/go/cert_srv/internal/reiss" - "github.com/scionproto/scion/go/lib/addr" - "github.com/scionproto/scion/go/lib/common" - "github.com/scionproto/scion/go/lib/env" - "github.com/scionproto/scion/go/lib/infra" - "github.com/scionproto/scion/go/lib/infra/infraenv" - "github.com/scionproto/scion/go/lib/infra/messenger" - "github.com/scionproto/scion/go/lib/infra/modules/itopo" - "github.com/scionproto/scion/go/lib/infra/modules/trust" - "github.com/scionproto/scion/go/lib/infra/modules/trust/trustdb" - "github.com/scionproto/scion/go/lib/log" - "github.com/scionproto/scion/go/lib/prom" - "github.com/scionproto/scion/go/lib/serrors" - "github.com/scionproto/scion/go/lib/snet" - "github.com/scionproto/scion/go/lib/topology" - "github.com/scionproto/scion/go/proto" -) - -const ( - ErrConf common.ErrMsg = "Unable to load configuration" - ErrDispClose common.ErrMsg = "Unable to close dispatcher" - ErrDispInit common.ErrMsg = "Unable to initialize dispatcher" - ErrSign common.ErrMsg = "Unable to create sign" - ErrSNET common.ErrMsg = "Unable to create local SCION Network context" -) - -// setupBasic loads the config from file and initializes logging. -func setupBasic() error { - // Load and initialize config. - if _, err := toml.DecodeFile(env.ConfigFile(), &cfg); err != nil { - return err - } - cfg.InitDefaults() - if err := env.InitLogging(&cfg.Logging); err != nil { - return err - } - prom.ExportElementID(cfg.General.ID) - return env.LogAppStarted(common.CS, cfg.General.ID) -} - -// setup initializes the config and sets the messenger. -func setup() error { - if err := cfg.Validate(); err != nil { - return common.NewBasicError("Unable to validate config", err) - } - itopo.Init(cfg.General.ID, proto.ServiceType_cs, itopo.Callbacks{}) - topo, err := topology.FromJSONFile(cfg.General.Topology) - if err != nil { - return common.NewBasicError("Unable to load topology", err) - } - if err := initTopo(topo); err != nil { - return err - } - router, err := infraenv.NewRouter(topo.IA(), cfg.Sciond) - if err != nil { - return common.NewBasicError("Unable to initialize path router", err) - } - // Load CS state. - if err := initState(&cfg, router); err != nil { - return common.NewBasicError("Unable to initialize CS state", err) - } - if err := setMessenger(&cfg, router); err != nil { - return common.NewBasicError("Unable to set messenger", err) - } - return nil -} - -// reload reloads the topology and CS config. -func reload() error { - // FIXME(roosd): KeyConf reloading is not yet supported. - // https://github.com/scionproto/scion/issues/2077 - var newConf config.Config - // Load new config to get the CS parameters. - if _, err := toml.DecodeFile(env.ConfigFile(), &newConf); err != nil { - return err - } - newConf.InitDefaults() - if err := newConf.Validate(); err != nil { - return common.NewBasicError("Unable to validate new config", err) - } - cfg.CS = newConf.CS - // Restart the periodic reissue task to respect the fresh parameters. - stopReissRunner() - startReissRunner() - return nil -} - -// initState sets the state. -func initState(cfg *config.Config, router snet.Router) error { - topo := itopo.Get() - var err error - if trustDB, err = cfg.TrustDB.New(); err != nil { - return common.NewBasicError("Unable to initialize trustDB", err) - } - trustDB = trustdb.WithMetrics(string(cfg.TrustDB.Backend()), trustDB) - trustConf := trust.Config{ - MustHaveLocalChain: true, - ServiceType: proto.ServiceType_cs, - Router: router, - TopoProvider: itopo.Provider(), - } - trustStore := trust.NewStore(trustDB, topo.IA(), trustConf, log.Root()) - err = trustStore.LoadAuthoritativeCrypto(filepath.Join(cfg.General.ConfigDir, "certs")) - if err != nil { - return common.NewBasicError("Unable to load local crypto", err) - } - state, err = config.LoadState(cfg.General.ConfigDir, topo.Core(), trustDB, trustStore) - if err != nil { - return common.NewBasicError("Unable to load CS state", err) - } - if err = setDefaultSignerVerifier(state, topo.IA()); err != nil { - return common.NewBasicError("Unable to set default signer and verifier", err) - } - return nil -} - -// setDefaultSignerVerifier sets the signer and verifier. The newest certificate chain version -// in the store is used. -func setDefaultSignerVerifier(c *config.State, pubIA addr.IA) error { - ctx, cancelF := context.WithTimeout(context.Background(), time.Second) - defer cancelF() - meta, err := trust.CreateSignMeta(ctx, pubIA, c.TrustDB) - if err != nil { - return err - } - signer, err := trust.NewBasicSigner(c.GetSigningKey(), meta) - if err != nil { - return err - } - c.SetSigner(signer) - c.SetVerifier(c.Store.NewVerifier()) - return nil -} - -// setMessenger sets the messenger and the internal messenger of the store in -// cfg.CS. This function may only be called once per config. -func setMessenger(cfg *config.Config, router snet.Router) error { - topo := itopo.Get() - if !topo.Exists(addr.SvcCS, cfg.General.ID) { - return serrors.New("unable to find topo address") - } - nc := infraenv.NetworkConfig{ - IA: topo.IA(), - Public: topo.PublicAddress(addr.SvcCS, cfg.General.ID), - SVC: addr.SvcCS, - ReconnectToDispatcher: cfg.General.ReconnectToDispatcher, - QUIC: infraenv.QUIC{ - Address: cfg.QUIC.Address, - CertFile: cfg.QUIC.CertFile, - KeyFile: cfg.QUIC.KeyFile, - }, - SVCResolutionFraction: cfg.QUIC.ResolutionFraction, - TrustStore: state.Store, - Router: router, - SVCRouter: messenger.NewSVCRouter(itopo.Provider()), - } - var err error - msgr, err = nc.Messenger() - if err != nil { - return common.NewBasicError("Unable to initialize SCION Messenger", err) - } - msgr.AddHandler(infra.ChainRequest, state.Store.NewChainReqHandler(true)) - msgr.AddHandler(infra.TRCRequest, state.Store.NewTRCReqHandler(true)) - msgr.AddHandler(infra.Chain, state.Store.NewChainPushHandler()) - msgr.AddHandler(infra.TRC, state.Store.NewTRCPushHandler()) - msgr.UpdateSigner(state.GetSigner(), []infra.MessageType{infra.ChainIssueRequest}) - msgr.UpdateVerifier(state.GetVerifier()) - // Only core CS handles certificate reissuance requests. - if topo.Core() { - msgr.AddHandler(infra.ChainIssueRequest, &reiss.Handler{ - State: state, - IA: topo.IA(), - }) - } - return nil -} - -func initTopo(topo topology.Topology) error { - if _, _, err := itopo.SetStatic(topo, false); err != nil { - return common.NewBasicError("Unable to set initial static topology", err) - } - // Set environment to listen for signals. - infraenv.InitInfraEnvironmentFunc(cfg.General.Topology, func() { - if err := reload(); err != nil { - log.Error("Unable to reload", "err", err) - } - }) - return nil -}