1// Copyright 2016-2020 The Libsacloud Authors 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// http://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 15package sacloud 16 17import ( 18 "crypto/x509" 19 "encoding/json" 20 "encoding/pem" 21 "fmt" 22 "strconv" 23 "strings" 24 "time" 25) 26 27// ProxyLB ProxyLB(CommonServiceItem) 28type ProxyLB struct { 29 *Resource // ID 30 propName // 名称 31 propDescription // 説明 32 propServiceClass // サービスクラス 33 propIcon // アイコン 34 propTags // タグ 35 propCreatedAt // 作成日時 36 propModifiedAt // 変更日時 37 propAvailability // 有効状態 38 39 Status *ProxyLBStatus `json:",omitempty"` // ステータス 40 Provider ProxyLBProvider `json:",omitempty"` // プロバイダ 41 Settings ProxyLBSettings `json:",omitempty"` // ProxyLB設定 42 43} 44 45// ProxyLBSettings ProxyLB設定 46type ProxyLBSettings struct { 47 ProxyLB ProxyLBSetting `json:",omitempty"` // ProxyLB ProxyLBエントリー 48} 49 50// ProxyLBStatus ProxyLBステータス 51type ProxyLBStatus struct { 52 FQDN string `json:",omitempty"` // 割り当てられたFQDN(site-*******.proxylb?.sakura.ne.jp) UseVIPFailoverがtrueの場合のみ有効 53 VirtualIPAddress string `json:",omitempty"` // 割り当てられたVIP UseVIPFailoverがfalseの場合のみ有効 54 ProxyNetworks []string `json:",omitempty"` // プロキシ元ネットワークアドレス(CIDR) 55 UseVIPFailover bool // VIPフェイルオーバ 56} 57 58// ProxyLBProvider プロバイダ 59type ProxyLBProvider struct { 60 Class string `json:",omitempty"` // クラス 61} 62 63// CreateNewProxyLB ProxyLB作成 64func CreateNewProxyLB(name string) *ProxyLB { 65 return &ProxyLB{ 66 Resource: &Resource{}, 67 propName: propName{Name: name}, 68 Provider: ProxyLBProvider{ 69 Class: "proxylb", 70 }, 71 Settings: ProxyLBSettings{ 72 ProxyLB: ProxyLBSetting{ 73 HealthCheck: defaultProxyLBHealthCheck, 74 SorryServer: ProxyLBSorryServer{}, 75 Servers: []ProxyLBServer{}, 76 //LetsEncrypt: ProxyLBACMESetting{}, 77 StickySession: ProxyLBSessionSetting{}, 78 }, 79 }, 80 } 81} 82 83// ProxyLBPlan ProxyLBプラン 84type ProxyLBPlan int 85 86var ( 87 // ProxyLBPlan100 100cpsプラン 88 ProxyLBPlan100 = ProxyLBPlan(100) 89 // ProxyLBPlan500 500cpsプラン 90 ProxyLBPlan500 = ProxyLBPlan(500) 91 // ProxyLBPlan1000 1,000cpsプラン 92 ProxyLBPlan1000 = ProxyLBPlan(1000) 93 // ProxyLBPlan5000 5,000cpsプラン 94 ProxyLBPlan5000 = ProxyLBPlan(5000) 95 // ProxyLBPlan10000 10,000cpsプラン 96 ProxyLBPlan10000 = ProxyLBPlan(10000) 97 // ProxyLBPlan50000 50,000cpsプラン 98 ProxyLBPlan50000 = ProxyLBPlan(50000) 99 // ProxyLBPlan100000 100,000cpsプラン 100 ProxyLBPlan100000 = ProxyLBPlan(100000) 101 // ProxyLBPlan400000 400,000cpsプラン 102 ProxyLBPlan400000 = ProxyLBPlan(400000) 103) 104 105// AllowProxyLBPlans 有効なプランIDリスト 106var AllowProxyLBPlans = []int{ 107 int(ProxyLBPlan100), 108 int(ProxyLBPlan500), 109 int(ProxyLBPlan1000), 110 int(ProxyLBPlan5000), 111 int(ProxyLBPlan10000), 112 int(ProxyLBPlan50000), 113 int(ProxyLBPlan100000), 114 int(ProxyLBPlan400000), 115} 116 117// GetPlan プラン取得(デフォルト: 1000cps) 118func (p *ProxyLB) GetPlan() ProxyLBPlan { 119 classes := strings.Split(p.ServiceClass, "/") 120 class, err := strconv.Atoi(classes[len(classes)-1]) 121 if err != nil { 122 return ProxyLBPlan1000 123 } 124 return ProxyLBPlan(class) 125} 126 127// SetPlan プラン指定 128func (p *ProxyLB) SetPlan(plan ProxyLBPlan) { 129 p.ServiceClass = fmt.Sprintf("cloud/proxylb/plain/%d", plan) 130} 131 132// SetHTTPHealthCheck HTTPヘルスチェック 設定 133func (p *ProxyLB) SetHTTPHealthCheck(hostHeader, path string, delayLoop int) { 134 if delayLoop <= 0 { 135 delayLoop = 10 136 } 137 138 p.Settings.ProxyLB.HealthCheck.Protocol = "http" 139 p.Settings.ProxyLB.HealthCheck.Host = hostHeader 140 p.Settings.ProxyLB.HealthCheck.Path = path 141 p.Settings.ProxyLB.HealthCheck.DelayLoop = delayLoop 142} 143 144// SetTCPHealthCheck TCPヘルスチェック 設定 145func (p *ProxyLB) SetTCPHealthCheck(delayLoop int) { 146 if delayLoop <= 0 { 147 delayLoop = 10 148 } 149 150 p.Settings.ProxyLB.HealthCheck.Protocol = "tcp" 151 p.Settings.ProxyLB.HealthCheck.Host = "" 152 p.Settings.ProxyLB.HealthCheck.Path = "" 153 p.Settings.ProxyLB.HealthCheck.DelayLoop = delayLoop 154} 155 156// SetSorryServer ソーリーサーバ 設定 157func (p *ProxyLB) SetSorryServer(ipaddress string, port int) { 158 var pt *int 159 if port > 0 { 160 pt = &port 161 } 162 p.Settings.ProxyLB.SorryServer = ProxyLBSorryServer{ 163 IPAddress: ipaddress, 164 Port: pt, 165 } 166} 167 168// ClearSorryServer ソーリーサーバ クリア 169func (p *ProxyLB) ClearSorryServer() { 170 p.SetSorryServer("", 0) 171} 172 173// HasProxyLBServer ProxyLB配下にサーバーを保持しているか判定 174func (p *ProxyLB) HasProxyLBServer() bool { 175 return len(p.Settings.ProxyLB.Servers) > 0 176} 177 178// ClearProxyLBServer ProxyLB配下のサーバーをクリア 179func (p *ProxyLB) ClearProxyLBServer() { 180 p.Settings.ProxyLB.Servers = []ProxyLBServer{} 181} 182 183// AddBindPort バインドポート追加 184func (p *ProxyLB) AddBindPort(mode string, port int, redirectToHTTPS, supportHTTP2 bool, addResponseHeader []*ProxyLBResponseHeader) { 185 p.Settings.ProxyLB.AddBindPort(mode, port, redirectToHTTPS, supportHTTP2, addResponseHeader) 186} 187 188// DeleteBindPort バインドポート削除 189func (p *ProxyLB) DeleteBindPort(mode string, port int) { 190 p.Settings.ProxyLB.DeleteBindPort(mode, port) 191} 192 193// ClearBindPorts バインドポート クリア 194func (p *ProxyLB) ClearBindPorts() { 195 p.Settings.ProxyLB.BindPorts = []*ProxyLBBindPorts{} 196} 197 198// AddServer ProxyLB配下のサーバーを追加 199func (p *ProxyLB) AddServer(ip string, port int, enabled bool, serverGroup string) { 200 p.Settings.ProxyLB.AddServer(ip, port, enabled, serverGroup) 201} 202 203// DeleteServer ProxyLB配下のサーバーを削除 204func (p *ProxyLB) DeleteServer(ip string, port int) { 205 p.Settings.ProxyLB.DeleteServer(ip, port) 206} 207 208// ProxyLBSetting ProxyLBセッティング 209type ProxyLBSetting struct { 210 HealthCheck ProxyLBHealthCheck // ヘルスチェック 211 SorryServer ProxyLBSorryServer // ソーリーサーバー 212 BindPorts []*ProxyLBBindPorts // プロキシ方式(プロトコル&ポート) 213 Servers []ProxyLBServer // サーバー 214 Rules []ProxyLBRule // 振り分けルール 215 LetsEncrypt *ProxyLBACMESetting `json:",omitempty"` // Let's encryptでの証明書取得設定 216 StickySession ProxyLBSessionSetting 217 Timeout *ProxyLBTimeout `json:",omitempty"` // タイムアウト 218} 219 220// ProxyLBSorryServer ソーリーサーバ 221type ProxyLBSorryServer struct { 222 IPAddress string // IPアドレス 223 Port *int // ポート 224} 225 226// AddBindPort バインドポート追加 227func (s *ProxyLBSetting) AddBindPort(mode string, port int, redirectToHTTPS, supportHTTP2 bool, addResponseHeader []*ProxyLBResponseHeader) { 228 var isExist bool 229 for i := range s.BindPorts { 230 if s.BindPorts[i].ProxyMode == mode && s.BindPorts[i].Port == port { 231 isExist = true 232 } 233 } 234 235 if !isExist { 236 s.BindPorts = append(s.BindPorts, &ProxyLBBindPorts{ 237 ProxyMode: mode, 238 Port: port, 239 RedirectToHTTPS: redirectToHTTPS, 240 SupportHTTP2: supportHTTP2, 241 AddResponseHeader: addResponseHeader, 242 }) 243 } 244} 245 246// DeleteBindPort バインドポート削除 247func (s *ProxyLBSetting) DeleteBindPort(mode string, port int) { 248 var res []*ProxyLBBindPorts 249 for i := range s.BindPorts { 250 if s.BindPorts[i].ProxyMode != mode || s.BindPorts[i].Port != port { 251 res = append(res, s.BindPorts[i]) 252 } 253 } 254 s.BindPorts = res 255} 256 257// AddServer ProxyLB配下のサーバーを追加 258func (s *ProxyLBSetting) AddServer(ip string, port int, enabled bool, serverGroup string) { 259 var record ProxyLBServer 260 var isExist = false 261 for i := range s.Servers { 262 if s.Servers[i].IPAddress == ip && s.Servers[i].Port == port { 263 isExist = true 264 s.Servers[i].Enabled = enabled 265 } 266 } 267 268 if !isExist { 269 record = ProxyLBServer{ 270 IPAddress: ip, 271 Port: port, 272 Enabled: enabled, 273 ServerGroup: serverGroup, 274 } 275 s.Servers = append(s.Servers, record) 276 } 277} 278 279// DeleteServer ProxyLB配下のサーバーを削除 280func (s *ProxyLBSetting) DeleteServer(ip string, port int) { 281 var res []ProxyLBServer 282 for i := range s.Servers { 283 if s.Servers[i].IPAddress != ip || s.Servers[i].Port != port { 284 res = append(res, s.Servers[i]) 285 } 286 } 287 288 s.Servers = res 289} 290 291// AllowProxyLBBindModes プロキシ方式 292var AllowProxyLBBindModes = []string{"http", "https", "tcp"} 293 294// ProxyLBBindPorts プロキシ方式 295type ProxyLBBindPorts struct { 296 ProxyMode string `json:",omitempty"` // モード(プロトコル) 297 Port int `json:",omitempty"` // ポート 298 RedirectToHTTPS bool `json:"RedirectToHttps"` // HTTPSへのリダイレクト(モードがhttpの場合のみ) 299 SupportHTTP2 bool `json:"SupportHttp2"` // HTTP/2のサポート(モードがhttpsの場合のみ) 300 AddResponseHeader []*ProxyLBResponseHeader `json:",omitempty"` // レスポンスヘッダ 301} 302 303// ProxyLBResponseHeader ポートごとの追加レスポンスヘッダ 304type ProxyLBResponseHeader struct { 305 Header string // ヘッダ名称(英字, 数字, ハイフン) 306 Value string // 値(英字, 数字, 半角スペース, 一部記号(!#$%&'()*+,-./:;<=>?@[]^_`{|}~)) 307} 308 309// ProxyLBServer ProxyLB配下のサーバー 310type ProxyLBServer struct { 311 IPAddress string `json:",omitempty"` // IPアドレス 312 Port int `json:",omitempty"` // ポート 313 ServerGroup string // サーバグループ 314 Enabled bool // 有効/無効 315} 316 317// NewProxyLBServer ProxyLB配下のサーバ作成 318func NewProxyLBServer(ipaddress string, port int) *ProxyLBServer { 319 return &ProxyLBServer{ 320 IPAddress: ipaddress, 321 Port: port, 322 Enabled: true, 323 } 324} 325 326// ProxyLBRule ProxyLBの振り分けルール 327type ProxyLBRule struct { 328 Host string `json:",omitempty"` // ホストヘッダのパターン(ワイルドカードとして?と*が利用可能) 329 Path string `json:",omitempty"` // パス 330 ServerGroup string 331} 332 333// ProxyLBACMESetting Let's Encryptでの証明書取得設定 334type ProxyLBACMESetting struct { 335 Enabled bool 336 CommonName string `json:",omitempty"` 337} 338 339// ProxyLBSessionSetting セッション維持機能設定 340type ProxyLBSessionSetting struct { 341 Enabled bool 342 Method string `json:",omitempty"` 343} 344 345// ProxyLBTimeout 実サーバの通信タイムアウト 346type ProxyLBTimeout struct { 347 InactiveSec int `json:",omitempty"` // 10から600まで1秒刻みで設定可 348} 349 350// ProxyLBStickySessionDefaultMethod セッション維持のデフォルトメソッド(クッキー) 351const ProxyLBStickySessionDefaultMethod = "cookie" 352 353// AllowProxyLBHealthCheckProtocols プロキシLBで利用できるヘルスチェックプロトコル 354var AllowProxyLBHealthCheckProtocols = []string{"http", "tcp"} 355 356// ProxyLBHealthCheck ヘルスチェック 357type ProxyLBHealthCheck struct { 358 Protocol string `json:",omitempty"` // プロトコル 359 Host string `json:",omitempty"` // 対象ホスト 360 Path string `json:",omitempty"` // HTTPの場合のリクエストパス 361 DelayLoop int `json:",omitempty"` // 監視間隔 362 363} 364 365var defaultProxyLBHealthCheck = ProxyLBHealthCheck{ 366 Protocol: "http", 367 Host: "", 368 Path: "/", 369 DelayLoop: 10, 370} 371 372// ProxyLBAdditionalCerts additional certificates 373type ProxyLBAdditionalCerts []*ProxyLBCertificate 374 375// ProxyLBCertificates ProxyLBのSSL証明書 376type ProxyLBCertificates struct { 377 PrimaryCert *ProxyLBCertificate 378 AdditionalCerts ProxyLBAdditionalCerts 379} 380 381// UnmarshalJSON UnmarshalJSON(AdditionalCertsが空の場合に空文字を返す問題への対応) 382func (p *ProxyLBAdditionalCerts) UnmarshalJSON(data []byte) error { 383 targetData := strings.Replace(strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1), `""`, ``, -1) 384 if targetData == `` { 385 return nil 386 } 387 388 var certs []*ProxyLBCertificate 389 if err := json.Unmarshal(data, &certs); err != nil { 390 return err 391 } 392 393 *p = certs 394 return nil 395} 396 397// SetPrimaryCert PrimaryCertを設定 398func (p *ProxyLBCertificates) SetPrimaryCert(cert *ProxyLBCertificate) { 399 p.PrimaryCert = cert 400} 401 402// SetPrimaryCertValue PrimaryCertを設定 403func (p *ProxyLBCertificates) SetPrimaryCertValue(serverCert, intermediateCert, privateKey string) { 404 p.PrimaryCert = &ProxyLBCertificate{ 405 ServerCertificate: serverCert, 406 IntermediateCertificate: intermediateCert, 407 PrivateKey: privateKey, 408 } 409} 410 411// AddAdditionalCert AdditionalCertを追加 412func (p *ProxyLBCertificates) AddAdditionalCert(serverCert, intermediateCert, privateKey string) { 413 p.AdditionalCerts = append(p.AdditionalCerts, &ProxyLBCertificate{ 414 ServerCertificate: serverCert, 415 IntermediateCertificate: intermediateCert, 416 PrivateKey: privateKey, 417 }) 418} 419 420// RemoveAdditionalCertAt 指定のインデックスを持つAdditionalCertを削除 421func (p *ProxyLBCertificates) RemoveAdditionalCertAt(index int) { 422 var certs []*ProxyLBCertificate 423 for i, cert := range p.AdditionalCerts { 424 if i != index { 425 certs = append(certs, cert) 426 } 427 } 428 p.AdditionalCerts = certs 429} 430 431// RemoveAdditionalCert 指定の内容を持つAdditionalCertを削除 432func (p *ProxyLBCertificates) RemoveAdditionalCert(serverCert, intermediateCert, privateKey string) { 433 var certs []*ProxyLBCertificate 434 for _, cert := range p.AdditionalCerts { 435 if !(cert.ServerCertificate == serverCert && cert.IntermediateCertificate == intermediateCert && cert.PrivateKey == privateKey) { 436 certs = append(certs, cert) 437 } 438 } 439 p.AdditionalCerts = certs 440} 441 442// RemoveAdditionalCerts AdditionalCertsを全て削除 443func (p *ProxyLBCertificates) RemoveAdditionalCerts() { 444 p.AdditionalCerts = []*ProxyLBCertificate{} 445} 446 447// ParseServerCertificate サーバ証明書のパース 448func (p *ProxyLBCertificates) ParseServerCertificate() (*x509.Certificate, error) { 449 cert, e := p.parseCertificate(p.PrimaryCert.ServerCertificate) 450 if e != nil { 451 return nil, e 452 } 453 return cert, nil 454} 455 456// ParseIntermediateCertificate 中間証明書のパース 457func (p *ProxyLBCertificates) ParseIntermediateCertificate() (*x509.Certificate, error) { 458 cert, e := p.parseCertificate(p.PrimaryCert.IntermediateCertificate) 459 if e != nil { 460 return nil, e 461 } 462 return cert, nil 463} 464 465func (p *ProxyLBCertificates) parseCertificate(certPEM string) (*x509.Certificate, error) { 466 block, _ := pem.Decode([]byte(certPEM)) 467 if block != nil { 468 return x509.ParseCertificate(block.Bytes) 469 } 470 return nil, fmt.Errorf("can't decode certificate") 471} 472 473// ProxyLBCertificate ProxyLBのSSL証明書詳細 474type ProxyLBCertificate struct { 475 ServerCertificate string // サーバ証明書 476 IntermediateCertificate string // 中間証明書 477 PrivateKey string // 秘密鍵 478 CertificateEndDate time.Time `json:",omitempty"` // 有効期限 479 CertificateCommonName string `json:",omitempty"` // CommonName 480} 481 482// UnmarshalJSON UnmarshalJSON(CertificateEndDateのtime.TimeへのUnmarshal対応) 483func (p *ProxyLBCertificate) UnmarshalJSON(data []byte) error { 484 var tmp map[string]interface{} 485 if err := json.Unmarshal(data, &tmp); err != nil { 486 return err 487 } 488 489 p.ServerCertificate = tmp["ServerCertificate"].(string) 490 p.IntermediateCertificate = tmp["IntermediateCertificate"].(string) 491 p.PrivateKey = tmp["PrivateKey"].(string) 492 p.CertificateCommonName = tmp["CertificateCommonName"].(string) 493 endDate := tmp["CertificateEndDate"].(string) 494 if endDate != "" { 495 date, err := time.Parse("Jan _2 15:04:05 2006 MST", endDate) 496 if err != nil { 497 return err 498 } 499 p.CertificateEndDate = date 500 } 501 502 return nil 503} 504 505// ParseServerCertificate サーバ証明書のパース 506func (p *ProxyLBCertificate) ParseServerCertificate() (*x509.Certificate, error) { 507 cert, e := p.parseCertificate(p.ServerCertificate) 508 if e != nil { 509 return nil, e 510 } 511 return cert, nil 512} 513 514// ParseIntermediateCertificate 中間証明書のパース 515func (p *ProxyLBCertificate) ParseIntermediateCertificate() (*x509.Certificate, error) { 516 cert, e := p.parseCertificate(p.IntermediateCertificate) 517 if e != nil { 518 return nil, e 519 } 520 return cert, nil 521} 522 523func (p *ProxyLBCertificate) parseCertificate(certPEM string) (*x509.Certificate, error) { 524 block, _ := pem.Decode([]byte(certPEM)) 525 if block != nil { 526 return x509.ParseCertificate(block.Bytes) 527 } 528 return nil, fmt.Errorf("can't decode certificate") 529} 530 531// ProxyLBHealth ProxyLBのヘルスチェック戻り値 532type ProxyLBHealth struct { 533 ActiveConn int // アクティブなコネクション数 534 CPS int // 秒あたりコネクション数 535 Servers []*ProxyLBHealthServer // 実サーバのステータス 536 CurrentVIP string // 現在のVIP 537} 538 539// ProxyLBHealthServer ProxyLBの実サーバのステータス 540type ProxyLBHealthServer struct { 541 ActiveConn int // アクティブなコネクション数 542 Status string // ステータス(UP or DOWN) 543 IPAddress string // IPアドレス 544 Port string // ポート 545 CPS int // 秒あたりコネクション数 546} 547