| OLD | NEW |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | 1 // Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package certconfig | 5 package certconfig |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "bytes" | 8 "bytes" |
| 9 "crypto/x509" | 9 "crypto/x509" |
| 10 "fmt" | 10 "fmt" |
| 11 "sync" | 11 "sync" |
| 12 "time" | 12 "time" |
| 13 | 13 |
| 14 "github.com/golang/protobuf/proto" | 14 "github.com/golang/protobuf/proto" |
| 15 "golang.org/x/net/context" | 15 "golang.org/x/net/context" |
| 16 "google.golang.org/grpc" | 16 "google.golang.org/grpc" |
| 17 "google.golang.org/grpc/codes" | 17 "google.golang.org/grpc/codes" |
| 18 | 18 |
| 19 ds "github.com/luci/gae/service/datastore" | 19 ds "github.com/luci/gae/service/datastore" |
| 20 "github.com/luci/gae/service/info" | |
| 21 "github.com/luci/luci-go/common/config" | |
| 22 "github.com/luci/luci-go/common/data/stringset" | 20 "github.com/luci/luci-go/common/data/stringset" |
| 23 "github.com/luci/luci-go/common/errors" | 21 "github.com/luci/luci-go/common/errors" |
| 24 "github.com/luci/luci-go/common/logging" | 22 "github.com/luci/luci-go/common/logging" |
| 25 "github.com/luci/luci-go/common/proto/google" | 23 "github.com/luci/luci-go/common/proto/google" |
| 24 "github.com/luci/luci-go/luci_config/server/cfgclient" |
| 26 | 25 |
| 27 "github.com/luci/luci-go/tokenserver/api/admin/v1" | 26 "github.com/luci/luci-go/tokenserver/api/admin/v1" |
| 28 "github.com/luci/luci-go/tokenserver/appengine/utils" | 27 "github.com/luci/luci-go/tokenserver/appengine/utils" |
| 29 ) | 28 ) |
| 30 | 29 |
| 31 // ImportCAConfigsRPC implements Admin.ImportCAConfigs RPC method. | 30 // ImportCAConfigsRPC implements Admin.ImportCAConfigs RPC method. |
| 32 type ImportCAConfigsRPC struct { | 31 type ImportCAConfigsRPC struct { |
| 33 } | 32 } |
| 34 | 33 |
| 35 // ImportCAConfigs fetches CA configs from from luci-config right now. | 34 // ImportCAConfigs fetches CA configs from from luci-config right now. |
| 36 func (r *ImportCAConfigsRPC) ImportCAConfigs(c context.Context, _ *google.Empty)
(*admin.ImportedConfigs, error) { | 35 func (r *ImportCAConfigsRPC) ImportCAConfigs(c context.Context, _ *google.Empty)
(*admin.ImportedConfigs, error) { |
| 37 » cfg, err := fetchConfigFile(c, "tokenserver.cfg") | 36 » content, meta, err := fetchConfigFile(c, "tokenserver.cfg") |
| 38 if err != nil { | 37 if err != nil { |
| 39 return nil, grpc.Errorf(codes.Internal, "can't read config file
- %s", err) | 38 return nil, grpc.Errorf(codes.Internal, "can't read config file
- %s", err) |
| 40 } | 39 } |
| 41 » logging.Infof(c, "Importing tokenserver.cfg at rev %s", cfg.Revision) | 40 » logging.Infof(c, "Importing tokenserver.cfg at rev %s", meta.Revision) |
| 42 | 41 |
| 43 // Read list of CAs. | 42 // Read list of CAs. |
| 44 msg := admin.TokenServerConfig{} | 43 msg := admin.TokenServerConfig{} |
| 45 » if err = proto.UnmarshalText(cfg.Content, &msg); err != nil { | 44 » if err = proto.UnmarshalText(content, &msg); err != nil { |
| 46 return nil, grpc.Errorf(codes.Internal, "can't parse config file
- %s", err) | 45 return nil, grpc.Errorf(codes.Internal, "can't parse config file
- %s", err) |
| 47 } | 46 } |
| 48 | 47 |
| 49 seenIDs, err := LoadCAUniqueIDToCNMap(c) | 48 seenIDs, err := LoadCAUniqueIDToCNMap(c) |
| 50 if err != nil { | 49 if err != nil { |
| 51 return nil, grpc.Errorf(codes.Internal, "can't load unique_id ma
p - %s", err) | 50 return nil, grpc.Errorf(codes.Internal, "can't load unique_id ma
p - %s", err) |
| 52 } | 51 } |
| 53 if seenIDs == nil { | 52 if seenIDs == nil { |
| 54 seenIDs = map[int64]string{} | 53 seenIDs = map[int64]string{} |
| 55 } | 54 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 84 } | 83 } |
| 85 } | 84 } |
| 86 | 85 |
| 87 // Add new CA datastore entries or update existing ones. | 86 // Add new CA datastore entries or update existing ones. |
| 88 wg := sync.WaitGroup{} | 87 wg := sync.WaitGroup{} |
| 89 me := errors.NewLazyMultiError(len(msg.GetCertificateAuthority())) | 88 me := errors.NewLazyMultiError(len(msg.GetCertificateAuthority())) |
| 90 for i, ca := range msg.GetCertificateAuthority() { | 89 for i, ca := range msg.GetCertificateAuthority() { |
| 91 wg.Add(1) | 90 wg.Add(1) |
| 92 go func(i int, ca *admin.CertificateAuthorityConfig) { | 91 go func(i int, ca *admin.CertificateAuthorityConfig) { |
| 93 defer wg.Done() | 92 defer wg.Done() |
| 94 » » » certFileCfg, err := fetchConfigFile(c, ca.CertPath) | 93 » » » content, meta, err := fetchConfigFile(c, ca.CertPath) |
| 95 if err != nil { | 94 if err != nil { |
| 96 logging.Errorf(c, "Failed to fetch %q: %s", ca.C
ertPath, err) | 95 logging.Errorf(c, "Failed to fetch %q: %s", ca.C
ertPath, err) |
| 97 me.Assign(i, err) | 96 me.Assign(i, err) |
| 98 » » » } else if err := importCA(c, ca, certFileCfg.Content, cf
g.Revision); err != nil { | 97 » » » } else if err := importCA(c, ca, content, meta.Revision)
; err != nil { |
| 99 logging.Errorf(c, "Failed to import %q: %s", ca.
Cn, err) | 98 logging.Errorf(c, "Failed to import %q: %s", ca.
Cn, err) |
| 100 me.Assign(i, err) | 99 me.Assign(i, err) |
| 101 } | 100 } |
| 102 }(i, ca) | 101 }(i, ca) |
| 103 } | 102 } |
| 104 wg.Wait() | 103 wg.Wait() |
| 105 if err = me.Get(); err != nil { | 104 if err = me.Get(); err != nil { |
| 106 return nil, grpc.Errorf(codes.Internal, "can't import CA - %s",
err) | 105 return nil, grpc.Errorf(codes.Internal, "can't import CA - %s",
err) |
| 107 } | 106 } |
| 108 | 107 |
| 109 // Find CAs that were removed from the config. | 108 // Find CAs that were removed from the config. |
| 110 toRemove := []string{} | 109 toRemove := []string{} |
| 111 q := ds.NewQuery("CA").Eq("Removed", false).KeysOnly(true) | 110 q := ds.NewQuery("CA").Eq("Removed", false).KeysOnly(true) |
| 112 err = ds.Run(c, q, func(k *ds.Key) { | 111 err = ds.Run(c, q, func(k *ds.Key) { |
| 113 if !seenCAs.Has(k.StringID()) { | 112 if !seenCAs.Has(k.StringID()) { |
| 114 toRemove = append(toRemove, k.StringID()) | 113 toRemove = append(toRemove, k.StringID()) |
| 115 } | 114 } |
| 116 }) | 115 }) |
| 117 if err != nil { | 116 if err != nil { |
| 118 return nil, grpc.Errorf(codes.Internal, "datastore error - %s",
err) | 117 return nil, grpc.Errorf(codes.Internal, "datastore error - %s",
err) |
| 119 } | 118 } |
| 120 | 119 |
| 121 // Mark them as inactive in the datastore. | 120 // Mark them as inactive in the datastore. |
| 122 wg = sync.WaitGroup{} | 121 wg = sync.WaitGroup{} |
| 123 me = errors.NewLazyMultiError(len(toRemove)) | 122 me = errors.NewLazyMultiError(len(toRemove)) |
| 124 for i, name := range toRemove { | 123 for i, name := range toRemove { |
| 125 wg.Add(1) | 124 wg.Add(1) |
| 126 go func(i int, name string) { | 125 go func(i int, name string) { |
| 127 defer wg.Done() | 126 defer wg.Done() |
| 128 » » » if err := removeCA(c, name, cfg.Revision); err != nil { | 127 » » » if err := removeCA(c, name, meta.Revision); err != nil { |
| 129 logging.Errorf(c, "Failed to remove %q: %s", nam
e, err) | 128 logging.Errorf(c, "Failed to remove %q: %s", nam
e, err) |
| 130 me.Assign(i, err) | 129 me.Assign(i, err) |
| 131 } | 130 } |
| 132 }(i, name) | 131 }(i, name) |
| 133 } | 132 } |
| 134 wg.Wait() | 133 wg.Wait() |
| 135 if err = me.Get(); err != nil { | 134 if err = me.Get(); err != nil { |
| 136 return nil, grpc.Errorf(codes.Internal, "datastore error - %s",
err) | 135 return nil, grpc.Errorf(codes.Internal, "datastore error - %s",
err) |
| 137 } | 136 } |
| 138 | 137 |
| 139 return &admin.ImportedConfigs{ | 138 return &admin.ImportedConfigs{ |
| 140 ImportedConfigs: []*admin.ImportedConfigs_ConfigFile{ | 139 ImportedConfigs: []*admin.ImportedConfigs_ConfigFile{ |
| 141 { | 140 { |
| 142 Name: "tokenserver.cfg", | 141 Name: "tokenserver.cfg", |
| 143 » » » » Revision: cfg.Revision, | 142 » » » » Revision: meta.Revision, |
| 144 }, | 143 }, |
| 145 }, | 144 }, |
| 146 }, nil | 145 }, nil |
| 147 } | 146 } |
| 148 | 147 |
| 149 // fetchConfigFile fetches a file from this services' config set. | 148 // fetchConfigFile fetches a file from this services' config set. |
| 150 func fetchConfigFile(c context.Context, path string) (*config.Config, error) { | 149 func fetchConfigFile(c context.Context, path string) (string, *cfgclient.Meta, e
rror) { |
| 151 » configSet := "services/" + info.AppID(c) | 150 » configSet := cfgclient.CurrentServiceConfigSet(c) |
| 152 logging.Infof(c, "Reading %q from config set %q", path, configSet) | 151 logging.Infof(c, "Reading %q from config set %q", path, configSet) |
| 153 » c, _ = context.WithTimeout(c, 30*time.Second) // URL fetch deadline | 152 » c, cancelFunc := context.WithTimeout(c, 29*time.Second) // URL fetch dea
dline |
| 154 » return config.GetConfig(c, configSet, path, false) | 153 » defer cancelFunc() |
| 154 |
| 155 » var ( |
| 156 » » content string |
| 157 » » meta cfgclient.Meta |
| 158 » ) |
| 159 » if err := cfgclient.Get(c, cfgclient.AsService, configSet, path, cfgclie
nt.String(&content), &meta); err != nil { |
| 160 » » return "", nil, err |
| 161 » } |
| 162 » return content, &meta, nil |
| 155 } | 163 } |
| 156 | 164 |
| 157 // importCA imports CA definition from the config (or updates an existing one). | 165 // importCA imports CA definition from the config (or updates an existing one). |
| 158 func importCA(c context.Context, ca *admin.CertificateAuthorityConfig, certPem s
tring, rev string) error { | 166 func importCA(c context.Context, ca *admin.CertificateAuthorityConfig, certPem s
tring, rev string) error { |
| 159 // Read CA certificate file, convert it to der. | 167 // Read CA certificate file, convert it to der. |
| 160 certDer, err := utils.ParsePEM(certPem, "CERTIFICATE") | 168 certDer, err := utils.ParsePEM(certPem, "CERTIFICATE") |
| 161 if err != nil { | 169 if err != nil { |
| 162 return fmt.Errorf("bad PEM - %s", err) | 170 return fmt.Errorf("bad PEM - %s", err) |
| 163 } | 171 } |
| 164 | 172 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 } | 231 } |
| 224 if existing.Removed { | 232 if existing.Removed { |
| 225 return nil | 233 return nil |
| 226 } | 234 } |
| 227 logging.Infof(c, "Removing CA %q", name) | 235 logging.Infof(c, "Removing CA %q", name) |
| 228 existing.Removed = true | 236 existing.Removed = true |
| 229 existing.RemovedRev = rev | 237 existing.RemovedRev = rev |
| 230 return ds.Put(c, &existing) | 238 return ds.Put(c, &existing) |
| 231 }, nil) | 239 }, nil) |
| 232 } | 240 } |
| OLD | NEW |