github.com/gophercloud/gophercloud@v1.11.0/internal/acceptance/openstack/keymanager/v1/keymanager.go (about) 1 package v1 2 3 import ( 4 "crypto/rand" 5 "crypto/rsa" 6 "crypto/x509" 7 "crypto/x509/pkix" 8 "encoding/asn1" 9 "encoding/base64" 10 "encoding/pem" 11 "fmt" 12 "math/big" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/gophercloud/gophercloud" 18 "github.com/gophercloud/gophercloud/internal/acceptance/tools" 19 "github.com/gophercloud/gophercloud/openstack/keymanager/v1/containers" 20 "github.com/gophercloud/gophercloud/openstack/keymanager/v1/orders" 21 "github.com/gophercloud/gophercloud/openstack/keymanager/v1/secrets" 22 th "github.com/gophercloud/gophercloud/testhelper" 23 ) 24 25 // CreateAsymmetric Order will create a random asymmetric order. 26 // An error will be returned if the order could not be created. 27 func CreateAsymmetricOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { 28 name := tools.RandomString("TESTACC-", 8) 29 t.Logf("Attempting to create order %s", name) 30 31 expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) 32 createOpts := orders.CreateOpts{ 33 Type: orders.AsymmetricOrder, 34 Meta: orders.MetaOpts{ 35 Name: name, 36 Algorithm: "rsa", 37 BitLength: 2048, 38 Mode: "cbc", 39 Expiration: &expiration, 40 }, 41 } 42 43 order, err := orders.Create(client, createOpts).Extract() 44 if err != nil { 45 return nil, err 46 } 47 48 orderID, err := ParseID(order.OrderRef) 49 if err != nil { 50 return nil, err 51 } 52 53 err = WaitForOrder(client, orderID) 54 th.AssertNoErr(t, err) 55 56 order, err = orders.Get(client, orderID).Extract() 57 if err != nil { 58 return nil, err 59 } 60 61 tools.PrintResource(t, order) 62 tools.PrintResource(t, order.Meta.Expiration) 63 64 th.AssertEquals(t, order.Meta.Name, name) 65 th.AssertEquals(t, order.Type, "asymmetric") 66 67 return order, nil 68 } 69 70 // CreateCertificateContainer will create a random certificate container. 71 // An error will be returned if the container could not be created. 72 func CreateCertificateContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, certificate *secrets.Secret) (*containers.Container, error) { 73 containerName := tools.RandomString("TESTACC-", 8) 74 75 t.Logf("Attempting to create container %s", containerName) 76 77 createOpts := containers.CreateOpts{ 78 Type: containers.CertificateContainer, 79 Name: containerName, 80 SecretRefs: []containers.SecretRef{ 81 { 82 Name: "certificate", 83 SecretRef: certificate.SecretRef, 84 }, 85 { 86 Name: "private_key", 87 SecretRef: private.SecretRef, 88 }, 89 { 90 Name: "private_key_passphrase", 91 SecretRef: passphrase.SecretRef, 92 }, 93 }, 94 } 95 96 container, err := containers.Create(client, createOpts).Extract() 97 if err != nil { 98 return nil, err 99 } 100 101 t.Logf("Successfully created container: %s", container.ContainerRef) 102 103 containerID, err := ParseID(container.ContainerRef) 104 if err != nil { 105 return nil, err 106 } 107 108 container, err = containers.Get(client, containerID).Extract() 109 if err != nil { 110 return nil, err 111 } 112 113 tools.PrintResource(t, container) 114 115 th.AssertEquals(t, container.Name, containerName) 116 th.AssertEquals(t, container.Type, "certificate") 117 118 return container, nil 119 } 120 121 // CreateKeyOrder will create a random key order. 122 // An error will be returned if the order could not be created. 123 func CreateKeyOrder(t *testing.T, client *gophercloud.ServiceClient) (*orders.Order, error) { 124 name := tools.RandomString("TESTACC-", 8) 125 t.Logf("Attempting to create order %s", name) 126 127 expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) 128 createOpts := orders.CreateOpts{ 129 Type: orders.KeyOrder, 130 Meta: orders.MetaOpts{ 131 Name: name, 132 Algorithm: "aes", 133 BitLength: 256, 134 Mode: "cbc", 135 Expiration: &expiration, 136 }, 137 } 138 139 order, err := orders.Create(client, createOpts).Extract() 140 if err != nil { 141 return nil, err 142 } 143 144 orderID, err := ParseID(order.OrderRef) 145 if err != nil { 146 return nil, err 147 } 148 149 order, err = orders.Get(client, orderID).Extract() 150 if err != nil { 151 return nil, err 152 } 153 154 tools.PrintResource(t, order) 155 tools.PrintResource(t, order.Meta.Expiration) 156 157 th.AssertEquals(t, order.Meta.Name, name) 158 th.AssertEquals(t, order.Type, "key") 159 160 return order, nil 161 } 162 163 // CreateRSAContainer will create a random RSA container. 164 // An error will be returned if the container could not be created. 165 func CreateRSAContainer(t *testing.T, client *gophercloud.ServiceClient, passphrase, private, public *secrets.Secret) (*containers.Container, error) { 166 containerName := tools.RandomString("TESTACC-", 8) 167 168 t.Logf("Attempting to create container %s", containerName) 169 170 createOpts := containers.CreateOpts{ 171 Type: containers.RSAContainer, 172 Name: containerName, 173 SecretRefs: []containers.SecretRef{ 174 { 175 Name: "public_key", 176 SecretRef: public.SecretRef, 177 }, 178 { 179 Name: "private_key", 180 SecretRef: private.SecretRef, 181 }, 182 { 183 Name: "private_key_passphrase", 184 SecretRef: passphrase.SecretRef, 185 }, 186 }, 187 } 188 189 container, err := containers.Create(client, createOpts).Extract() 190 if err != nil { 191 return nil, err 192 } 193 194 t.Logf("Successfully created container: %s", container.ContainerRef) 195 196 containerID, err := ParseID(container.ContainerRef) 197 if err != nil { 198 return nil, err 199 } 200 201 container, err = containers.Get(client, containerID).Extract() 202 if err != nil { 203 return nil, err 204 } 205 206 tools.PrintResource(t, container) 207 208 th.AssertEquals(t, container.Name, containerName) 209 th.AssertEquals(t, container.Type, "rsa") 210 211 return container, nil 212 } 213 214 // CreateCertificateSecret will create a random certificate secret. An error 215 // will be returned if the secret could not be created. 216 func CreateCertificateSecret(t *testing.T, client *gophercloud.ServiceClient, cert []byte) (*secrets.Secret, error) { 217 b64Cert := base64.StdEncoding.EncodeToString(cert) 218 219 name := tools.RandomString("TESTACC-", 8) 220 t.Logf("Attempting to create public key %s", name) 221 222 createOpts := secrets.CreateOpts{ 223 Name: name, 224 SecretType: secrets.CertificateSecret, 225 Payload: b64Cert, 226 PayloadContentType: "application/octet-stream", 227 PayloadContentEncoding: "base64", 228 Algorithm: "rsa", 229 } 230 231 secret, err := secrets.Create(client, createOpts).Extract() 232 if err != nil { 233 return nil, err 234 } 235 236 t.Logf("Successfully created secret: %s", secret.SecretRef) 237 238 secretID, err := ParseID(secret.SecretRef) 239 if err != nil { 240 return nil, err 241 } 242 243 secret, err = secrets.Get(client, secretID).Extract() 244 if err != nil { 245 return nil, err 246 } 247 248 tools.PrintResource(t, secret) 249 250 th.AssertEquals(t, secret.Name, name) 251 th.AssertEquals(t, secret.Algorithm, "rsa") 252 253 return secret, nil 254 } 255 256 // CreateEmptySecret will create a random secret with no payload. An error will 257 // be returned if the secret could not be created. 258 func CreateEmptySecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { 259 secretName := tools.RandomString("TESTACC-", 8) 260 261 t.Logf("Attempting to create secret %s", secretName) 262 263 createOpts := secrets.CreateOpts{ 264 Algorithm: "aes", 265 BitLength: 256, 266 Mode: "cbc", 267 Name: secretName, 268 SecretType: secrets.OpaqueSecret, 269 } 270 271 secret, err := secrets.Create(client, createOpts).Extract() 272 if err != nil { 273 return nil, err 274 } 275 276 t.Logf("Successfully created secret: %s", secret.SecretRef) 277 278 secretID, err := ParseID(secret.SecretRef) 279 if err != nil { 280 return nil, err 281 } 282 283 secret, err = secrets.Get(client, secretID).Extract() 284 if err != nil { 285 return nil, err 286 } 287 288 tools.PrintResource(t, secret) 289 290 th.AssertEquals(t, secret.Name, secretName) 291 th.AssertEquals(t, secret.Algorithm, "aes") 292 293 return secret, nil 294 } 295 296 // CreateGenericContainer will create a random generic container with a 297 // specified secret. An error will be returned if the container could not 298 // be created. 299 func CreateGenericContainer(t *testing.T, client *gophercloud.ServiceClient, secret *secrets.Secret) (*containers.Container, error) { 300 containerName := tools.RandomString("TESTACC-", 8) 301 302 t.Logf("Attempting to create container %s", containerName) 303 304 createOpts := containers.CreateOpts{ 305 Type: containers.GenericContainer, 306 Name: containerName, 307 SecretRefs: []containers.SecretRef{ 308 { 309 Name: secret.Name, 310 SecretRef: secret.SecretRef, 311 }, 312 }, 313 } 314 315 container, err := containers.Create(client, createOpts).Extract() 316 if err != nil { 317 return nil, err 318 } 319 320 t.Logf("Successfully created container: %s", container.ContainerRef) 321 322 containerID, err := ParseID(container.ContainerRef) 323 if err != nil { 324 return nil, err 325 } 326 327 container, err = containers.Get(client, containerID).Extract() 328 if err != nil { 329 return nil, err 330 } 331 332 tools.PrintResource(t, container) 333 334 th.AssertEquals(t, container.Name, containerName) 335 th.AssertEquals(t, container.Type, "generic") 336 337 return container, nil 338 } 339 340 // ReplaceGenericContainerSecretRef will replace the container old secret 341 // reference with a new one. An error will be returned if the reference could 342 // not be replaced. 343 func ReplaceGenericContainerSecretRef(t *testing.T, client *gophercloud.ServiceClient, container *containers.Container, secretOld *secrets.Secret, secretNew *secrets.Secret) error { 344 containerID, err := ParseID(container.ContainerRef) 345 if err != nil { 346 return err 347 } 348 349 t.Logf("Attempting to remove an old secret reference %s", secretOld.SecretRef) 350 351 res1 := containers.DeleteSecretRef(client, containerID, containers.SecretRef{Name: secretOld.Name, SecretRef: secretOld.SecretRef}) 352 if res1.Err != nil { 353 return res1.Err 354 } 355 356 t.Logf("Successfully removed old secret reference: %s", secretOld.SecretRef) 357 358 t.Logf("Attempting to remove a new secret reference %s", secretNew.SecretRef) 359 360 newRef := containers.SecretRef{Name: secretNew.Name, SecretRef: secretNew.SecretRef} 361 res2 := containers.CreateSecretRef(client, containerID, newRef) 362 if res2.Err != nil { 363 return res2.Err 364 } 365 366 c, err := res2.Extract() 367 if err != nil { 368 return err 369 } 370 tools.PrintResource(t, c) 371 372 t.Logf("Successfully created new secret reference: %s", secretNew.SecretRef) 373 374 updatedContainer, err := containers.Get(client, containerID).Extract() 375 if err != nil { 376 return err 377 } 378 379 tools.PrintResource(t, container) 380 381 th.AssertEquals(t, updatedContainer.Name, container.Name) 382 th.AssertEquals(t, updatedContainer.Type, container.Type) 383 th.AssertEquals(t, updatedContainer.SecretRefs[0], newRef) 384 385 return nil 386 } 387 388 // CreatePassphraseSecret will create a random passphrase secret. 389 // An error will be returned if the secret could not be created. 390 func CreatePassphraseSecret(t *testing.T, client *gophercloud.ServiceClient, passphrase string) (*secrets.Secret, error) { 391 secretName := tools.RandomString("TESTACC-", 8) 392 393 t.Logf("Attempting to create secret %s", secretName) 394 395 createOpts := secrets.CreateOpts{ 396 Algorithm: "aes", 397 BitLength: 256, 398 Mode: "cbc", 399 Name: secretName, 400 Payload: passphrase, 401 PayloadContentType: "text/plain", 402 SecretType: secrets.PassphraseSecret, 403 } 404 405 secret, err := secrets.Create(client, createOpts).Extract() 406 if err != nil { 407 return nil, err 408 } 409 410 t.Logf("Successfully created secret: %s", secret.SecretRef) 411 412 secretID, err := ParseID(secret.SecretRef) 413 if err != nil { 414 return nil, err 415 } 416 417 secret, err = secrets.Get(client, secretID).Extract() 418 if err != nil { 419 return nil, err 420 } 421 422 tools.PrintResource(t, secret) 423 424 th.AssertEquals(t, secret.Name, secretName) 425 th.AssertEquals(t, secret.Algorithm, "aes") 426 427 return secret, nil 428 } 429 430 // CreatePublicSecret will create a random public secret. An error 431 // will be returned if the secret could not be created. 432 func CreatePublicSecret(t *testing.T, client *gophercloud.ServiceClient, pub []byte) (*secrets.Secret, error) { 433 b64Cert := base64.StdEncoding.EncodeToString(pub) 434 435 name := tools.RandomString("TESTACC-", 8) 436 t.Logf("Attempting to create public key %s", name) 437 438 createOpts := secrets.CreateOpts{ 439 Name: name, 440 SecretType: secrets.PublicSecret, 441 Payload: b64Cert, 442 PayloadContentType: "application/octet-stream", 443 PayloadContentEncoding: "base64", 444 Algorithm: "rsa", 445 } 446 447 secret, err := secrets.Create(client, createOpts).Extract() 448 if err != nil { 449 return nil, err 450 } 451 452 t.Logf("Successfully created secret: %s", secret.SecretRef) 453 454 secretID, err := ParseID(secret.SecretRef) 455 if err != nil { 456 return nil, err 457 } 458 459 secret, err = secrets.Get(client, secretID).Extract() 460 if err != nil { 461 return nil, err 462 } 463 464 tools.PrintResource(t, secret) 465 466 th.AssertEquals(t, secret.Name, name) 467 th.AssertEquals(t, secret.Algorithm, "rsa") 468 469 return secret, nil 470 } 471 472 // CreatePrivateSecret will create a random private secret. An error 473 // will be returned if the secret could not be created. 474 func CreatePrivateSecret(t *testing.T, client *gophercloud.ServiceClient, priv []byte) (*secrets.Secret, error) { 475 b64Cert := base64.StdEncoding.EncodeToString(priv) 476 477 name := tools.RandomString("TESTACC-", 8) 478 t.Logf("Attempting to create public key %s", name) 479 480 createOpts := secrets.CreateOpts{ 481 Name: name, 482 SecretType: secrets.PrivateSecret, 483 Payload: b64Cert, 484 PayloadContentType: "application/octet-stream", 485 PayloadContentEncoding: "base64", 486 Algorithm: "rsa", 487 } 488 489 secret, err := secrets.Create(client, createOpts).Extract() 490 if err != nil { 491 return nil, err 492 } 493 494 t.Logf("Successfully created secret: %s", secret.SecretRef) 495 496 secretID, err := ParseID(secret.SecretRef) 497 if err != nil { 498 return nil, err 499 } 500 501 secret, err = secrets.Get(client, secretID).Extract() 502 if err != nil { 503 return nil, err 504 } 505 506 tools.PrintResource(t, secret) 507 508 th.AssertEquals(t, secret.Name, name) 509 th.AssertEquals(t, secret.Algorithm, "rsa") 510 511 return secret, nil 512 } 513 514 // CreateSecretWithPayload will create a random secret with a given payload. 515 // An error will be returned if the secret could not be created. 516 func CreateSecretWithPayload(t *testing.T, client *gophercloud.ServiceClient, payload string) (*secrets.Secret, error) { 517 secretName := tools.RandomString("TESTACC-", 8) 518 519 t.Logf("Attempting to create secret %s", secretName) 520 521 expiration := time.Date(2049, 1, 1, 1, 1, 1, 0, time.UTC) 522 createOpts := secrets.CreateOpts{ 523 Algorithm: "aes", 524 BitLength: 256, 525 Mode: "cbc", 526 Name: secretName, 527 Payload: payload, 528 PayloadContentType: "text/plain", 529 SecretType: secrets.OpaqueSecret, 530 Expiration: &expiration, 531 } 532 533 secret, err := secrets.Create(client, createOpts).Extract() 534 if err != nil { 535 return nil, err 536 } 537 538 t.Logf("Successfully created secret: %s", secret.SecretRef) 539 540 secretID, err := ParseID(secret.SecretRef) 541 if err != nil { 542 return nil, err 543 } 544 545 secret, err = secrets.Get(client, secretID).Extract() 546 if err != nil { 547 return nil, err 548 } 549 550 tools.PrintResource(t, secret) 551 552 th.AssertEquals(t, secret.Name, secretName) 553 th.AssertEquals(t, secret.Algorithm, "aes") 554 th.AssertEquals(t, secret.Expiration, expiration) 555 556 return secret, nil 557 } 558 559 // CreateSymmetricSecret will create a random symmetric secret. An error 560 // will be returned if the secret could not be created. 561 func CreateSymmetricSecret(t *testing.T, client *gophercloud.ServiceClient) (*secrets.Secret, error) { 562 name := tools.RandomString("TESTACC-", 8) 563 key := tools.RandomString("", 256) 564 b64Key := base64.StdEncoding.EncodeToString([]byte(key)) 565 566 t.Logf("Attempting to create symmetric key %s", name) 567 568 createOpts := secrets.CreateOpts{ 569 Name: name, 570 SecretType: secrets.SymmetricSecret, 571 Payload: b64Key, 572 PayloadContentType: "application/octet-stream", 573 PayloadContentEncoding: "base64", 574 Algorithm: "aes", 575 BitLength: 256, 576 Mode: "cbc", 577 } 578 579 secret, err := secrets.Create(client, createOpts).Extract() 580 if err != nil { 581 return nil, err 582 } 583 584 t.Logf("Successfully created secret: %s", secret.SecretRef) 585 586 secretID, err := ParseID(secret.SecretRef) 587 if err != nil { 588 return nil, err 589 } 590 591 secret, err = secrets.Get(client, secretID).Extract() 592 if err != nil { 593 return nil, err 594 } 595 596 tools.PrintResource(t, secret) 597 598 th.AssertEquals(t, secret.Name, name) 599 th.AssertEquals(t, secret.Algorithm, "aes") 600 601 return secret, nil 602 } 603 604 // DeleteContainer will delete a container. A fatal error will occur if the 605 // container could not be deleted. This works best when used as a deferred 606 // function. 607 func DeleteContainer(t *testing.T, client *gophercloud.ServiceClient, id string) { 608 t.Logf("Attempting to delete container %s", id) 609 610 err := containers.Delete(client, id).ExtractErr() 611 if err != nil { 612 t.Fatalf("Could not delete container: %s", err) 613 } 614 615 t.Logf("Successfully deleted container %s", id) 616 } 617 618 // DeleteOrder will delete an order. A fatal error will occur if the 619 // order could not be deleted. This works best when used as a deferred 620 // function. 621 func DeleteOrder(t *testing.T, client *gophercloud.ServiceClient, id string) { 622 t.Logf("Attempting to delete order %s", id) 623 624 err := orders.Delete(client, id).ExtractErr() 625 if err != nil { 626 t.Fatalf("Could not delete order: %s", err) 627 } 628 629 t.Logf("Successfully deleted order %s", id) 630 } 631 632 // DeleteSecret will delete a secret. A fatal error will occur if the secret 633 // could not be deleted. This works best when used as a deferred function. 634 func DeleteSecret(t *testing.T, client *gophercloud.ServiceClient, id string) { 635 t.Logf("Attempting to delete secret %s", id) 636 637 err := secrets.Delete(client, id).ExtractErr() 638 if err != nil { 639 t.Fatalf("Could not delete secret: %s", err) 640 } 641 642 t.Logf("Successfully deleted secret %s", id) 643 } 644 645 func ParseID(ref string) (string, error) { 646 parts := strings.Split(ref, "/") 647 if len(parts) < 2 { 648 return "", fmt.Errorf("Could not parse %s", ref) 649 } 650 651 return parts[len(parts)-1], nil 652 } 653 654 // CreateCertificate will create a random certificate. A fatal error will 655 // be returned if creation failed. 656 // https://golang.org/src/crypto/tls/generate_cert.go 657 func CreateCertificate(t *testing.T, passphrase string) ([]byte, []byte, error) { 658 key, err := rsa.GenerateKey(rand.Reader, 2048) 659 if err != nil { 660 return nil, nil, err 661 } 662 663 block := &pem.Block{ 664 Type: "RSA PRIVATE KEY", 665 Bytes: x509.MarshalPKCS1PrivateKey(key), 666 } 667 668 if passphrase != "" { 669 block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) 670 if err != nil { 671 return nil, nil, err 672 } 673 } 674 675 keyPem := pem.EncodeToMemory(block) 676 677 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 678 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 679 if err != nil { 680 return nil, nil, err 681 } 682 683 tpl := x509.Certificate{ 684 SerialNumber: serialNumber, 685 Subject: pkix.Name{ 686 Organization: []string{"Some Org"}, 687 }, 688 NotBefore: time.Now(), 689 NotAfter: time.Now().AddDate(5, 0, 0), 690 BasicConstraintsValid: true, 691 } 692 693 cert, err := x509.CreateCertificate(rand.Reader, &tpl, &tpl, &key.PublicKey, key) 694 if err != nil { 695 return nil, nil, err 696 } 697 698 certPem := pem.EncodeToMemory(&pem.Block{ 699 Type: "CERTIFICATE", 700 Bytes: cert, 701 }) 702 703 return keyPem, certPem, nil 704 } 705 706 // CreateRSAKeyPair will create a random RSA key pair. An error will be 707 // returned if the pair could not be created. 708 func CreateRSAKeyPair(t *testing.T, passphrase string) ([]byte, []byte, error) { 709 key, err := rsa.GenerateKey(rand.Reader, 2048) 710 if err != nil { 711 return nil, nil, err 712 } 713 714 block := &pem.Block{ 715 Type: "RSA PRIVATE KEY", 716 Bytes: x509.MarshalPKCS1PrivateKey(key), 717 } 718 719 if passphrase != "" { 720 block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, []byte(passphrase), x509.PEMCipherAES256) 721 if err != nil { 722 return nil, nil, err 723 } 724 } 725 726 keyPem := pem.EncodeToMemory(block) 727 728 asn1Bytes, err := asn1.Marshal(key.PublicKey) 729 if err != nil { 730 return nil, nil, err 731 } 732 733 block = &pem.Block{ 734 Type: "RSA PUBLIC KEY", 735 Bytes: asn1Bytes, 736 } 737 738 pubPem := pem.EncodeToMemory(block) 739 740 return keyPem, pubPem, nil 741 } 742 743 func WaitForOrder(client *gophercloud.ServiceClient, orderID string) error { 744 return tools.WaitFor(func() (bool, error) { 745 order, err := orders.Get(client, orderID).Extract() 746 if err != nil { 747 return false, err 748 } 749 750 if order.SecretRef != "" { 751 return true, nil 752 } 753 754 if order.ContainerRef != "" { 755 return true, nil 756 } 757 758 if order.Status == "ERROR" { 759 return false, fmt.Errorf("Order %s in ERROR state", orderID) 760 } 761 762 return false, nil 763 }) 764 }