diff --git a/bigip/provider.go b/bigip/provider.go index aa0312deb..d49ea16a3 100644 --- a/bigip/provider.go +++ b/bigip/provider.go @@ -139,6 +139,7 @@ func Provider() *schema.Provider { "bigip_sys_dns": resourceBigipSysDns(), "bigip_sys_iapp": resourceBigipSysIapp(), "bigip_sys_ntp": resourceBigipSysNtp(), + "bigip_sys_ocsp": resourceBigipSysOcsp(), "bigip_sys_provision": resourceBigipSysProvision(), "bigip_sys_snmp": resourceBigipSysSnmp(), "bigip_sys_snmp_traps": resourceBigipSysSnmpTraps(), diff --git a/bigip/resource_bigip_ltm_cipher_group.go b/bigip/resource_bigip_ltm_cipher_group.go index 1ff7ca430..c2288541f 100644 --- a/bigip/resource_bigip_ltm_cipher_group.go +++ b/bigip/resource_bigip_ltm_cipher_group.go @@ -113,7 +113,18 @@ func resourceBigipLtmCipherGroupRead(ctx context.Context, d *schema.ResourceData } _ = d.Set("name", cipherGroup.FullPath) _ = d.Set("ordering", cipherGroup.Ordering) - log.Printf("[INFO] Cipher group response :%+v", cipherGroup) + var allowList []interface{} + for _, val := range cipherGroup.Allow { + tmpCipher := fmt.Sprintf("/%s/%s", val.(map[string]interface{})["partition"].(string), val.(map[string]interface{})["name"].(string)) + allowList = append(allowList, tmpCipher) + } + _ = d.Set("allow", allowList) + var requireList []interface{} + for _, val := range cipherGroup.Require { + tmpCipher := fmt.Sprintf("/%s/%s", val.(map[string]interface{})["partition"].(string), val.(map[string]interface{})["name"].(string)) + requireList = append(requireList, tmpCipher) + } + _ = d.Set("require", requireList) return nil } diff --git a/bigip/resource_bigip_ltm_cipher_rule.go b/bigip/resource_bigip_ltm_cipher_rule.go index dfd48f578..bc658713f 100644 --- a/bigip/resource_bigip_ltm_cipher_rule.go +++ b/bigip/resource_bigip_ltm_cipher_rule.go @@ -48,11 +48,13 @@ func resourceBigipLtmCipherRule() *schema.Resource { "dh_groups": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "Specifies the DH Groups Elliptic Curve Diffie-Hellman key exchange algorithms, separated by colons (:).Note: You can also type a special keyword, DEFAULT, which represents the recommended set of named groups", }, "signature_algorithms": { Type: schema.TypeString, Optional: true, + Computed: true, Description: "Specifies the Signature Algorithms, separated by colons (:), that you want to include in the cipher rule. You can also type a special keyword, DEFAULT, which represents the recommended set of signature algorithms", }, }, @@ -108,9 +110,8 @@ func resourceBigipLtmCipherRuleRead(ctx context.Context, d *schema.ResourceData, return diag.FromErr(err) } log.Printf("[INFO] Cipher rule response :%+v", cipherRule) - _ = d.Set("name", cipherRule.Name) - _ = d.Set("partition", cipherRule.Partition) - _ = d.Set("cipher_suites", cipherRule.Cipher) + _ = d.Set("name", cipherRule.FullPath) + _ = d.Set("cipher", cipherRule.Cipher) _ = d.Set("dh_groups", cipherRule.DhGroups) _ = d.Set("signature_algorithms", cipherRule.SignatureAlgorithms) return nil diff --git a/bigip/resource_bigip_ltm_profile_http.go b/bigip/resource_bigip_ltm_profile_http.go index 092a892ac..681330833 100644 --- a/bigip/resource_bigip_ltm_profile_http.go +++ b/bigip/resource_bigip_ltm_profile_http.go @@ -166,9 +166,10 @@ func resourceBigipLtmProfileHttp() *schema.Resource { Description: "Specifies how the system handles HTTP content that is chunked by a server. The default is Selective", }, "server_agent_name": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + // Computed: true, + Default: "BigIP", Description: "Specifies the value of the Server header in responses that the BIG-IP itself generates. The default is BigIP. If no string is specified, then no Server header will be added to such responses", }, "via_host_name": { @@ -197,6 +198,72 @@ func resourceBigipLtmProfileHttp() *schema.Resource { Computed: true, Description: "Specifies alternative XFF headers instead of the default X-forwarded-for header", }, + "http_strict_transport_security": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "include_subdomains": { + Type: schema.TypeString, + Optional: true, + Default: "enabled", + Description: "Specifies whether to include the includeSubdomains directive in the HSTS header.", + }, + "maximum_age": { + Type: schema.TypeInt, + Optional: true, + Default: 16070400, + Description: "Specifies the maximum age to assume the connection should remain secure.", + }, + "mode": { + Type: schema.TypeString, + Optional: true, + Default: "disabled", + Description: "Specifies whether to include the HSTS response header.", + }, + "preload": { + Type: schema.TypeString, + Optional: true, + Default: "disabled", + Description: "Specifies whether to include the preload directive in the HSTS header.", + }, + }, + }, + }, + "enforcement": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "known_methods": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "Specifies which HTTP methods count as being known. Removing RFC-defined methods from this list will cause the HTTP filter to not recognize them.", + }, + "max_header_count": { + Type: schema.TypeInt, + Optional: true, + Default: 64, + Description: "Specifies the maximum number of headers allowed in HTTP request/response.", + }, + "max_header_size": { + Type: schema.TypeInt, + Optional: true, + Default: 32768, + Description: "Specifies the maximum header size.", + }, + "unknown_method": { + Type: schema.TypeString, + Optional: true, + Default: "allow", + Description: "Specifies whether to allow, reject or switch to pass-through mode when an unknown HTTP method is parsed.", + }, + }, + }, + }, }, } } @@ -325,6 +392,37 @@ func resourceBigipLtmProfileHttpRead(ctx context.Context, d *schema.ResourceData } _ = d.Set("xff_alternative_names", pp.XffAlternativeNames) + var enforcementList []interface{} + enforcement := make(map[string]interface{}) + enforcement["max_header_count"] = pp.Enforcement.MaxHeaderCount + enforcement["max_header_size"] = pp.Enforcement.MaxHeaderSize + enforcement["unknown_method"] = pp.Enforcement.UnknownMethod + + if p, ok := d.GetOk("enforcement"); ok { + for _, r := range p.(*schema.Set).List() { + if len(r.(map[string]interface{})["known_methods"].([]interface{})) != 0 { + enforcement["known_methods"] = pp.Enforcement.KnownMethods + } + } + } + + enforcementList = append(enforcementList, enforcement) + + if _, ok := d.GetOk("enforcement"); ok { + _ = d.Set("enforcement", enforcementList) + } + + var hstsList []interface{} + hsts := make(map[string]interface{}) + hsts["include_subdomains"] = pp.Hsts.IncludeSubdomains + hsts["maximum_age"] = pp.Hsts.MaximumAge + hsts["mode"] = pp.Hsts.Mode + hsts["preload"] = pp.Hsts.Preload + + hstsList = append(hstsList, hsts) + if _, ok := d.GetOk("http_strict_transport_security"); ok { + _ = d.Set("http_strict_transport_security", hstsList) + } return nil } @@ -337,6 +435,7 @@ func resourceBigipLtmProfileHttpUpdate(ctx context.Context, d *schema.ResourceDa Name: name, } config := getHttpProfileConfig(d, pss) + err := client.ModifyHttpProfile(name, config) if err != nil { @@ -387,5 +486,28 @@ func getHttpProfileConfig(d *schema.ResourceData, config *bigip.HttpProfile) *bi config.ViaRequest = d.Get("via_request").(string) config.ViaResponse = d.Get("via_response").(string) config.XffAlternativeNames = setToInterfaceSlice(d.Get("xff_alternative_names").(*schema.Set)) + config.LwsWidth = d.Get("lws_width").(int) + p := d.Get("http_strict_transport_security") + + for _, r := range p.(*schema.Set).List() { + config.Hsts.IncludeSubdomains = r.(map[string]interface{})["include_subdomains"].(string) + config.Hsts.Mode = r.(map[string]interface{})["preload"].(string) + config.Hsts.Preload = r.(map[string]interface{})["mode"].(string) + config.Hsts.MaximumAge = r.(map[string]interface{})["maximum_age"].(int) + } + + v := d.Get("enforcement") + + for _, r := range v.(*schema.Set).List() { + var knownMethods []string + for _, val := range r.(map[string]interface{})["known_methods"].([]interface{}) { + knownMethods = append(knownMethods, val.(string)) + } + config.Enforcement.KnownMethods = knownMethods + config.Enforcement.UnknownMethod = r.(map[string]interface{})["unknown_method"].(string) + config.Enforcement.MaxHeaderCount = r.(map[string]interface{})["max_header_count"].(int) + config.Enforcement.MaxHeaderSize = r.(map[string]interface{})["max_header_size"].(int) + } + return config } diff --git a/bigip/resource_bigip_ltm_profile_http_test.go b/bigip/resource_bigip_ltm_profile_http_test.go index 09533b517..5f3c30ed8 100644 --- a/bigip/resource_bigip_ltm_profile_http_test.go +++ b/bigip/resource_bigip_ltm_profile_http_test.go @@ -342,6 +342,87 @@ func TestAccBigipLtmProfileHttpUpdateEncryptCookies(t *testing.T) { }) } +func TestAccBigipLtmProfileHttpUpdateEnforcement(t *testing.T) { + t.Parallel() + var instName = "test-http-Update-enforcement" + var instFullName = fmt.Sprintf("/%s/%s", TestPartition, instName) + resFullName := fmt.Sprintf("%s.%s", resHttpName, instName) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAcctPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testCheckHttpsDestroyed, + Steps: []resource.TestStep{ + { + Config: testaccbigipltmprofilehttpUpdateParam(instName, ""), + Check: resource.ComposeTestCheckFunc( + testCheckhttpExists(instFullName), + resource.TestCheckResourceAttr(resFullName, "name", instFullName), + resource.TestCheckResourceAttr(resFullName, "defaults_from", "/Common/http"), + ), + }, + { + Config: testaccbigipltmprofilehttpUpdateParam(instName, "enforcement"), + Check: resource.ComposeTestCheckFunc( + testCheckhttpExists(instFullName), + resource.TestCheckResourceAttr(resFullName, "name", instFullName), + resource.TestCheckResourceAttr(resFullName, "defaults_from", "/Common/http"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "CONNECT"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "DELETE"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "GET"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "HEAD"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "LOCK"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "POST"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "PROPFIND"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "PUT"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "TRACE"), + resource.TestCheckTypeSetElemAttr(resFullName, "enforcement.0.known_methods.*", "UNLOCK"), + resource.TestCheckResourceAttr(resFullName, "enforcement.0.unknown_method", "allow"), + resource.TestCheckResourceAttr(resFullName, "enforcement.0.max_header_count", "40"), + resource.TestCheckResourceAttr(resFullName, "enforcement.0.max_header_size", "80"), + ), + }, + }, + }) +} + +func TestAccBigipLtmProfileHttpUpdateHSTS(t *testing.T) { + t.Parallel() + var instName = "test-http-Update-hsts" + var instFullName = fmt.Sprintf("/%s/%s", TestPartition, instName) + resFullName := fmt.Sprintf("%s.%s", resHttpName, instName) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAcctPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testCheckHttpsDestroyed, + Steps: []resource.TestStep{ + { + Config: testaccbigipltmprofilehttpUpdateParam(instName, ""), + Check: resource.ComposeTestCheckFunc( + testCheckhttpExists(instFullName), + resource.TestCheckResourceAttr(resFullName, "name", instFullName), + resource.TestCheckResourceAttr(resFullName, "defaults_from", "/Common/http"), + ), + }, + { + Config: testaccbigipltmprofilehttpUpdateParam(instName, "hsts"), + Check: resource.ComposeTestCheckFunc( + testCheckhttpExists(instFullName), + resource.TestCheckResourceAttr(resFullName, "name", instFullName), + resource.TestCheckResourceAttr(resFullName, "defaults_from", "/Common/http"), + resource.TestCheckResourceAttr(resFullName, "http_strict_transport_security.0.include_subdomains", "disabled"), + resource.TestCheckResourceAttr(resFullName, "http_strict_transport_security.0.preload", "enabled"), + resource.TestCheckResourceAttr(resFullName, "http_strict_transport_security.0.mode", "enabled"), + resource.TestCheckResourceAttr(resFullName, "http_strict_transport_security.0.maximum_age", "80"), + ), + }, + }, + }) +} + func TestAccBigipLtmProfileHttpImport(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -469,6 +550,22 @@ func testaccbigipltmprofilehttpUpdateParam(instName, updateParam string) string case "basic_auth_realm": resPrefix = fmt.Sprintf(`%s basic_auth_realm = "titanic"`, resPrefix) + case "enforcement": + resPrefix = fmt.Sprintf(`%s + enforcement { + known_methods = ["CONNECT","DELETE","GET","HEAD","LOCK","OPTIONS","POST","PROPFIND","PUT","TRACE","UNLOCK"] + unknown_method = "allow" + max_header_count = 40 + max_header_size = 80 + }`, resPrefix) + case "hsts": + resPrefix = fmt.Sprintf(`%s + http_strict_transport_security { + include_subdomains = "disabled" + preload = "enabled" + mode = "enabled" + maximum_age = 80 + }`, resPrefix) default: } return fmt.Sprintf(`%s diff --git a/bigip/resource_bigip_ltm_profile_ssl_client.go b/bigip/resource_bigip_ltm_profile_ssl_client.go index 8c643bf1b..5d8d5589e 100644 --- a/bigip/resource_bigip_ltm_profile_ssl_client.go +++ b/bigip/resource_bigip_ltm_profile_ssl_client.go @@ -328,6 +328,13 @@ func resourceBigipLtmProfileClientSsl() *schema.Resource { Description: "ModSSL Methods enabled / disabled. Default is disabled.", }, + "ocsp_stapling": { + Type: schema.TypeString, + Optional: true, + Default: "disabled", + Description: "Specifies whether the system uses OCSP stapling.", + }, + "tm_options": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, @@ -686,6 +693,10 @@ func resourceBigipLtmProfileClientSSLRead(ctx context.Context, d *schema.Resourc _ = d.Set("tm_options", tmOptions) } + if _, ok := d.GetOk("ocsp_stapling"); ok { + _ = d.Set("ocsp_stapling", obj.OcspStapling) + } + if _, ok := d.GetOk("proxy_ca_cert"); ok { _ = d.Set("proxy_ca_cert", obj.ProxyCaCert) } @@ -856,6 +867,7 @@ func getClientSslConfig(d *schema.ResourceData, config *bigip.ClientSSLProfile) config.CaFile = d.Get("ca_file").(string) config.CacheSize = d.Get("cache_size").(int) config.CacheTimeout = d.Get("cache_timeout").(int) + config.OcspStapling = d.Get("ocsp_stapling").(string) log.Printf("[DEBUG] Length of certKeyChains :%+v", len(certKeyChains)) log.Printf("[DEBUG] certKeyChains :%+v", certKeyChains) if len(certKeyChains) == 0 { @@ -873,8 +885,8 @@ func getClientSslConfig(d *schema.ResourceData, config *bigip.ClientSSLProfile) config.Ciphers = ciphers.(string) config.CipherGroup = "none" } - if cipher_grp, ok := d.GetOk("cipher_group"); ok && cipher_grp != "none" { - config.CipherGroup = cipher_grp.(string) + if cipherGrp, ok := d.GetOk("cipher_group"); ok && cipherGrp != "none" { + config.CipherGroup = cipherGrp.(string) config.Ciphers = "none" } config.ClientCertCa = d.Get("client_cert_ca").(string) diff --git a/bigip/resource_bigip_ltm_profile_ssl_server.go b/bigip/resource_bigip_ltm_profile_ssl_server.go index 94b19e990..3a0006162 100644 --- a/bigip/resource_bigip_ltm_profile_ssl_server.go +++ b/bigip/resource_bigip_ltm_profile_ssl_server.go @@ -493,8 +493,12 @@ func resourceBigipLtmProfileServerSslRead(ctx context.Context, d *schema.Resourc _ = d.Set("ca_file", obj.CaFile) _ = d.Set("cert", obj.Cert) _ = d.Set("chain", obj.Chain) - _ = d.Set("ciphers", obj.Ciphers) - _ = d.Set("cipher_group", obj.CipherGroup) + if _, ok := d.GetOk("ciphers"); ok { + _ = d.Set("ciphers", obj.Ciphers) + } + if _, ok := d.GetOk("cipher_group"); ok { + _ = d.Set("cipher_group", obj.CipherGroup) + } _ = d.Set("expire_cert_response_control", obj.ExpireCertResponseControl) _ = d.Set("cache_size", obj.CacheSize) _ = d.Set("handshake_timeout", obj.HandshakeTimeout) @@ -637,8 +641,8 @@ func getServerSslConfig(d *schema.ResourceData, config *bigip.ServerSSLProfile) config.Ciphers = ciphers.(string) config.CipherGroup = "none" } - if cipher_grp, ok := d.GetOk("cipher_group"); ok && cipher_grp != "none" { - config.CipherGroup = cipher_grp.(string) + if cipherGrp, ok := d.GetOk("cipher_group"); ok && cipherGrp != "none" { + config.CipherGroup = cipherGrp.(string) config.Ciphers = "none" } config.ExpireCertResponseControl = d.Get("expire_cert_response_control").(string) diff --git a/bigip/resource_bigip_ssl_certificate.go b/bigip/resource_bigip_ssl_certificate.go index 0391dd753..6396a6684 100644 --- a/bigip/resource_bigip_ssl_certificate.go +++ b/bigip/resource_bigip_ssl_certificate.go @@ -39,7 +39,6 @@ func resourceBigipSslCertificate() *schema.Resource { //ForceNew: true, Description: "Content of certificate on Disk", }, - "partition": { Type: schema.TypeString, Optional: true, @@ -47,6 +46,21 @@ func resourceBigipSslCertificate() *schema.Resource { Description: "Partition of ssl certificate", ValidateFunc: validatePartitionName, }, + "monitoring_type": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the type of monitoring used", + }, + "issuer_cert": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the issuer certificate", + }, + "ocsp": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the OCSP responder", + }, "full_path": { Type: schema.TypeString, Optional: true, @@ -64,7 +78,19 @@ func resourceBigipSslCertificateCreate(ctx context.Context, d *schema.ResourceDa certPath := d.Get("content").(string) partition := d.Get("partition").(string) - err := client.UploadCertificate(name, certPath, partition) + cert := &bigip.Certificate{ + Name: name, + Partition: partition, + } + + if val, ok := d.GetOk("monitoring_type"); ok { + cert.CertValidationOptions = []string{val.(string)} + } + if val, ok := d.GetOk("issuer_cert"); ok { + cert.IssuerCert = val.(string) + } + + err := client.UploadCertificate(certPath, cert) if err != nil { return diag.FromErr(fmt.Errorf("error in Importing certificate (%s): %s", name, err)) } @@ -88,6 +114,17 @@ func resourceBigipSslCertificateCreate(ctx context.Context, d *schema.ResourceDa log.Printf("[ERROR]Sending Telemetry data failed:%v", err) } } + + if val, ok := d.GetOk("ocsp"); ok { + certValidState := &bigip.CertValidatorState{Name: val.(string)} + certValidRef := &bigip.CertValidatorReference{} + certValidRef.Items = append(certValidRef.Items, *certValidState) + cert.CertValidatorRef = certValidRef + err = client.UpdateCertificate(certPath, cert) + if err != nil { + log.Printf("[ERROR]Unable to add ocsp to the certificate:%v", err) + } + } return resourceBigipSslCertificateRead(ctx, d, meta) } @@ -119,6 +156,11 @@ func resourceBigipSslCertificateRead(ctx context.Context, d *schema.ResourceData _ = d.Set("name", certificate.Name) _ = d.Set("partition", certificate.Partition) _ = d.Set("full_path", certificate.FullPath) + _ = d.Set("issuer_cert", certificate.IssuerCert) + if certificate.CertValidationOptions != nil && len(certificate.CertValidationOptions) > 0 { + monitorType := certificate.CertValidationOptions[0] + _ = d.Set("monitoring_type", monitorType) + } return nil } @@ -129,10 +171,26 @@ func resourceBigipSslCertificateUpdate(ctx context.Context, d *schema.ResourceDa log.Println("[INFO] Certificate Name " + name) certpath := d.Get("content").(string) partition := d.Get("partition").(string) - /*if !strings.HasSuffix(name, ".crt") { - name = name + ".crt" - }*/ - err := client.UpdateCertificate(name, certpath, partition) + + cert := &bigip.Certificate{ + Name: name, + Partition: partition, + } + + if val, ok := d.GetOk("monitoring_type"); ok { + cert.CertValidationOptions = []string{val.(string)} + } + if val, ok := d.GetOk("issuer_cert"); ok { + cert.IssuerCert = val.(string) + } + if val, ok := d.GetOk("ocsp"); ok { + certValidState := &bigip.CertValidatorState{Name: val.(string)} + certValidRef := &bigip.CertValidatorReference{} + certValidRef.Items = append(certValidRef.Items, *certValidState) + cert.CertValidatorRef = certValidRef + } + + err := client.UpdateCertificate(certpath, cert) if err != nil { return diag.FromErr(fmt.Errorf("error in Importing certificate (%s): %s", name, err)) } diff --git a/bigip/resource_bigip_ssl_certificate_test.go b/bigip/resource_bigip_ssl_certificate_test.go index 46c08025e..7b634a648 100644 --- a/bigip/resource_bigip_ssl_certificate_test.go +++ b/bigip/resource_bigip_ssl_certificate_test.go @@ -28,6 +28,17 @@ resource "bigip_ssl_certificate" "test-cert" { } ` +var TestSslCertOCSPResource = ` +resource "bigip_ssl_certificate" "ssl-test-certificate-tc1" { + name = "test-certificate" + content = "${file("` + folder + `/../examples/mycertocspv2.crt")}" + partition = "Common" + monitoring_type = "ocsp" + issuer_cert = "/Common/MyCA" + ocsp = "/Common/testocsp1" +} +` + func TestAccBigipSslCertificateImportToBigip(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -88,6 +99,29 @@ func TestAccBigipSslCertificateTCs(t *testing.T) { }) } +func TestAccBigipSslCertificateOCSP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAcctPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testChecksslcertificateDestroyed, + Steps: []resource.TestStep{ + { + Config: TestSslCertOCSPResource, + Check: resource.ComposeTestCheckFunc( + testChecksslcertificateExists("test-certificate", true), + resource.TestCheckResourceAttr("bigip_ssl_certificate.ssl-test-certificate-tc1", "name", "test-certificate"), + resource.TestCheckResourceAttr("bigip_ssl_certificate.ssl-test-certificate-tc1", "partition", "Common"), + resource.TestCheckResourceAttr("bigip_ssl_certificate.ssl-test-certificate-tc1", "monitoring_type", "ocsp"), + resource.TestCheckResourceAttr("bigip_ssl_certificate.ssl-test-certificate-tc1", "issuer_cert", "/Common/MyCA"), + resource.TestCheckResourceAttr("bigip_ssl_certificate.ssl-test-certificate-tc1", "ocsp", "/Common/testocsp1"), + ), + }, + }, + }) +} + func testChecksslcertificateExists(name string, exists bool) resource.TestCheckFunc { return func(s *terraform.State) error { client := testAccProvider.Meta().(*bigip.BigIP) diff --git a/bigip/resource_bigip_ssl_key_cert.go b/bigip/resource_bigip_ssl_key_cert.go index c25a4f481..e95d01cf0 100644 --- a/bigip/resource_bigip_ssl_key_cert.go +++ b/bigip/resource_bigip_ssl_key_cert.go @@ -58,6 +58,21 @@ func resourceBigipSSLKeyCert() *schema.Resource { Computed: true, Description: "Full Path Name of ssl certificate", }, + "cert_monitoring_type": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the type of monitoring used.", + }, + "issuer_cert": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the issuer certificate", + }, + "cert_ocsp": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the OCSP responder", + }, "passphrase": { Type: schema.TypeString, Optional: true, @@ -105,7 +120,19 @@ func resourceBigipSSLKeyCertCreate(ctx context.Context, d *schema.ResourceData, if err != nil { return diag.FromErr(fmt.Errorf("error while adding the ssl key: %v", err)) } - err = client.UploadCertificate(certName, certPath, partition) + + cert := &bigip.Certificate{ + Name: certName, + Partition: partition, + } + if val, ok := d.GetOk("cert_monitoring_type"); ok { + cert.CertValidationOptions = []string{val.(string)} + } + if val, ok := d.GetOk("issuer_cert"); ok { + cert.IssuerCert = val.(string) + } + + err = client.UploadCertificate(certPath, cert) if err != nil { return diag.FromErr(fmt.Errorf("error while uploading the ssl cert: %v", err)) } @@ -114,6 +141,17 @@ func resourceBigipSSLKeyCertCreate(ctx context.Context, d *schema.ResourceData, return diag.FromErr(fmt.Errorf("error while ending transaction: %d", err)) } + if val, ok := d.GetOk("cert_ocsp"); ok { + certValidState := &bigip.CertValidatorState{Name: val.(string)} + certValidRef := &bigip.CertValidatorReference{} + certValidRef.Items = append(certValidRef.Items, *certValidState) + cert.CertValidatorRef = certValidRef + err = client.UpdateCertificate(certPath, cert) + if err != nil { + log.Printf("[ERROR]Unable to add ocsp to the certificate:%v", err) + } + } + id := keyName + "_" + certName d.SetId(id) return resourceBigipSSLKeyCertRead(ctx, d, meta) @@ -147,6 +185,11 @@ func resourceBigipSSLKeyCertRead(ctx context.Context, d *schema.ResourceData, me d.Set("cert_name", certificate.Name) d.Set("cert_full_path", certificate.FullPath) d.Set("partition", key.Partition) + d.Set("issuer_cert", certificate.IssuerCert) + if certificate.CertValidationOptions != nil && len(certificate.CertValidationOptions) > 0 { + monitor_type := certificate.CertValidationOptions[0] + _ = d.Set("cert_monitoring_type", monitor_type) + } return nil } @@ -184,7 +227,24 @@ func resourceBigipSSLKeyCertUpdate(ctx context.Context, d *schema.ResourceData, return diag.FromErr(fmt.Errorf("error while trying to modify the ssl key (%s): %s", keyFullPath, err)) } - err = client.UpdateCertificate(certName, certPath, partition) + cert := &bigip.Certificate{ + Name: certName, + Partition: partition, + } + if val, ok := d.GetOk("cert_monitoring_type"); ok { + cert.CertValidationOptions = []string{val.(string)} + } + if val, ok := d.GetOk("issuer_cert"); ok { + cert.IssuerCert = val.(string) + } + if val, ok := d.GetOk("cert_ocsp"); ok { + certValidState := &bigip.CertValidatorState{Name: val.(string)} + certValidRef := &bigip.CertValidatorReference{} + certValidRef.Items = append(certValidRef.Items, *certValidState) + cert.CertValidatorRef = certValidRef + } + + err = client.UpdateCertificate(certPath, cert) if err != nil { return diag.FromErr(fmt.Errorf("error while updating the ssl certificate (%s): %s", certName, err)) } diff --git a/bigip/resource_bigip_ssl_key_cert_test.go b/bigip/resource_bigip_ssl_key_cert_test.go index 235811e3f..f1bf2b99d 100644 --- a/bigip/resource_bigip_ssl_key_cert_test.go +++ b/bigip/resource_bigip_ssl_key_cert_test.go @@ -42,6 +42,19 @@ resource "bigip_ltm_profile_server_ssl" "test-ServerSsl" { } ` +var sslProfileCertKeyOCSP = ` +resource "bigip_ssl_key_cert" "testkeycert" { + partition = "Common" + key_name = "ssl-test-key" + key_content = "${file("` + folder + `/../examples/mycertocspv2.pem")}" + cert_name = "ssl-test-cert" + cert_content = "${file("` + folder + `/../examples/mycertocspv2.crt")}" + cert_monitoring_type = "ocsp" + issuer_cert = "/Common/MyCA" + cert_ocsp = "/Common/testocsp1" +} +` + func TestAccBigipSSLCertKeyCreate(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -112,3 +125,25 @@ func TestAccBigipSSLCertKeyCreateCertKeyProfile(t *testing.T) { }, }) } + +func TestAccBigipSSLCertKeyCreateCertKeyProfileOCSP(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAcctPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: sslProfileCertKeyOCSP, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "key_name", "ssl-test-key"), + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "cert_name", "ssl-test-cert"), + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "partition", "Common"), + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "cert_monitoring_type", "ocsp"), + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "issuer_cert", "/Common/MyCA"), + resource.TestCheckResourceAttr("bigip_ssl_key_cert.testkeycert", "cert_ocsp", "/Common/testocsp1"), + ), + }, + }, + }) +} diff --git a/bigip/resource_bigip_sys_ocsp.go b/bigip/resource_bigip_sys_ocsp.go new file mode 100644 index 000000000..88aef6ce4 --- /dev/null +++ b/bigip/resource_bigip_sys_ocsp.go @@ -0,0 +1,336 @@ +package bigip + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "strings" + + bigip "github.com/f5devcentral/go-bigip" + "github.com/f5devcentral/go-bigip/f5teem" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceBigipSysOcsp() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceBigipSysOcspCreate, + ReadContext: resourceBigipSysOcspRead, + UpdateContext: resourceBigipSysOcspUpdate, + DeleteContext: resourceBigipSysOcspDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "Specifies the name of the OCSP responder. It should be of the pattern '/partition/name'", + Required: true, + }, + "proxy_server_pool": { + Type: schema.TypeString, + Description: "Specifies the proxy server pool the BIG-IP system uses to fetch the OCSP response. It should be of the pattern '/partition/pool-name'", + ConflictsWith: []string{"dns_resolver"}, + Optional: true, + }, + "dns_resolver": { + Type: schema.TypeString, + Description: "Specifies the internal DNS resolver the BIG-IP system uses to fetch the OCSP response. It should be of the pattern '/partition/resolver-name'", + ConflictsWith: []string{"proxy_server_pool"}, + Optional: true, + }, + "route_domain": { + Type: schema.TypeString, + Description: "Specifies the route domain for the OCSP responder", + Optional: true, + }, + "concurrent_connections_limit": { + Type: schema.TypeInt, + Description: "Specifies the maximum number of connections per second allowed for the OCSP certificate validator", + Optional: true, + Default: 50, + }, + "responder_url": { + Type: schema.TypeString, + Description: "Specifies the URL of the OCSP responder", + Optional: true, + }, + "connection_timeout": { + Type: schema.TypeInt, + Description: "Specifies the time interval that the BIG-IP system waits for before ending the connection to the OCSP responder, in seconds", + Optional: true, + Default: 8, + }, + "trusted_responders": { + Type: schema.TypeString, + Description: "Specifies the certificates used for validating the OCSP response", + Optional: true, + }, + "clock_skew": { + Type: schema.TypeInt, + Description: "Specifies the tolerable absolute difference in the clocks of the responder and the BIG-IP system, in seconds", + Optional: true, + Default: 300, + }, + "status_age": { + Type: schema.TypeInt, + Description: "Specifies the maximum allowed lag time that the BIG-IP system accepts for the 'thisUpdate' time in the OCSP response, in seconds", + Optional: true, + Default: 0, + }, + "strict_resp_cert_check": { + Type: schema.TypeString, + Description: "Specifies whether the responder's certificate is checked for an OCSP signing extension", + Optional: true, + Default: "enabled", + }, + "cache_timeout": { + Type: schema.TypeString, + Description: "Specifies the lifetime of the OCSP response in the cache, in seconds", + Optional: true, + Default: "indefinite", + }, + "cache_error_timeout": { + Type: schema.TypeInt, + Description: "Specifies the lifetime of an error response in the cache, in seconds. This value must be greater than connection_timeout", + Optional: true, + Default: 3600, + }, + "signer_cert": { + Type: schema.TypeString, + Description: "Specifies a certificate used to sign an OCSP request. It should be of the pattern '/partition/cert-name'", + Optional: true, + }, + "signer_key": { + Type: schema.TypeString, + Description: "Specifies a key used to sign an OCSP request. It should be of the pattern '/partition/key-name'", + Optional: true, + }, + "passphrase": { + Type: schema.TypeString, + Description: "Specifies a passphrase used to sign an OCSP request", + Sensitive: true, + Optional: true, + }, + "sign_hash": { + Type: schema.TypeString, + Description: "Specifies the hash algorithm used to sign an OCSP request", + Default: "sha256", + Optional: true, + }, + }, + } +} + +func resourceBigipSysOcspCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*bigip.BigIP) + name := d.Get("name").(string) + + ocsp := &bigip.OCSP{ + Name: name, + } + + populateOcspConfig(ocsp, d) + + err := client.CreateOCSP(ocsp) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId(name) + + if !client.Teem { + id := uuid.New() + uniqueID := id.String() + assetInfo := f5teem.AssetInfo{ + Name: "Terraform-provider-bigip", + Version: client.UserAgent, + Id: uniqueID, + } + apiKey := os.Getenv("TEEM_API_KEY") + teemDevice := f5teem.AnonymousClient(assetInfo, apiKey) + f := map[string]interface{}{ + "Terraform Version": client.UserAgent, + } + tsVer := strings.Split(client.UserAgent, "/") + err = teemDevice.Report(f, "bigip_sys_ocsp", tsVer[3]) + if err != nil { + log.Printf("[ERROR]Sending Telemetry data failed:%v", err) + } + } + + return resourceBigipSysOcspRead(ctx, d, meta) +} + +func resourceBigipSysOcspRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*bigip.BigIP) + + id := d.Id() + id = strings.Trim(id, "/") + splitArr := strings.Split(id, "/") + if len(splitArr) != 2 { + return diag.Errorf("Invalid ID %s", id) + } + + name := splitArr[1] + partition := splitArr[0] + ocspFqdn := fmt.Sprintf("~%s~%s", partition, name) + + ocsp, err := client.GetOCSP(ocspFqdn) + if err != nil { + log.Printf("[ERROR] unable to retrieve ocsp %s: %s ", name, err) + return diag.FromErr(err) + } + + ocspJson, err := json.Marshal(ocsp) + if err != nil { + log.Printf("[ERROR] unable to marshal ocsp %s: %s ", name, err) + return diag.FromErr(err) + } + + log.Printf("[INFO] ocsp response: %+v", string(ocspJson)) + + d.Set("name", ocsp.FullPath) + + setOcspStateData(d, ocsp) + + return nil +} + +func resourceBigipSysOcspUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*bigip.BigIP) + + id := d.Id() + id = strings.Trim(id, "/") + splitArr := strings.Split(id, "/") + if len(splitArr) != 2 { + return diag.Errorf("Invalid ID %s", id) + } + + name := splitArr[1] + partition := splitArr[0] + ocspFqdn := fmt.Sprintf("~%s~%s", partition, name) + + ocsp := &bigip.OCSP{ + Name: name, + } + populateOcspConfig(ocsp, d) + + err := client.ModifyOCSP(ocspFqdn, ocsp) + if err != nil { + return diag.FromErr(err) + } + + return resourceBigipSysOcspRead(ctx, d, meta) +} + +func resourceBigipSysOcspDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*bigip.BigIP) + + id := d.Id() + id = strings.Trim(id, "/") + splitArr := strings.Split(id, "/") + if len(splitArr) != 2 { + return diag.Errorf("Invalid ID %s", id) + } + + name := splitArr[1] + partition := splitArr[0] + ocspFqdn := fmt.Sprintf("~%s~%s", partition, name) + + err := client.DeleteOCSP(ocspFqdn) + + if err != nil { + log.Printf("[ERROR] unable to delete ocsp %s: %s ", name, err) + return diag.FromErr(err) + } + d.SetId("") + return nil +} + +func populateOcspConfig(ocsp *bigip.OCSP, d *schema.ResourceData) { + if v, ok := d.GetOk("proxy_server_pool"); ok { + ocsp.ProxyServerPool = v.(string) + } + if v, ok := d.GetOk("dns_resolver"); ok { + ocsp.DnsResolver = v.(string) + } + if v, ok := d.GetOk("concurrent_connections_limit"); ok { + ocsp.ConcurrentConnectionsLimit = int64(v.(int)) + } + if v, ok := d.GetOk("responder_url"); ok { + ocsp.ResponderUrl = v.(string) + } + if v, ok := d.GetOk("route_domain"); ok { + ocsp.RouteDomain = v.(string) + } + if v, ok := d.GetOk("connection_timeout"); ok { + ocsp.ConnectionTimeout = int64(v.(int)) + } + if v, ok := d.GetOk("trusted_responder"); ok { + ocsp.TrustedResponders = v.(string) + } + if v, ok := d.GetOk("clock_skew"); ok { + ocsp.ClockSkew = int64(v.(int)) + } + if v, ok := d.GetOk("status_age"); ok { + ocsp.StatusAge = int64(v.(int)) + } + if v, ok := d.GetOk("strict_resp_cert_check"); ok { + ocsp.StrictRespCertCheck = v.(string) + } + if v, ok := d.GetOk("cache_timeout"); ok { + ocsp.CacheTimeout = v.(string) + } + if v, ok := d.GetOk("cache_error_timeout"); ok { + ocsp.CacheErrorTimeout = int64(v.(int)) + } + if v, ok := d.GetOk("signer_cert"); ok { + ocsp.SignerCert = v.(string) + } + if v, ok := d.GetOk("signer_key"); ok { + ocsp.SignerKey = v.(string) + } + if v, ok := d.GetOk("passphrase"); ok { + ocsp.Passphrase = v.(string) + } + if v, ok := d.GetOk("sign_hash"); ok { + ocsp.SignHash = v.(string) + } +} + +func setOcspStateData(d *schema.ResourceData, ocsp *bigip.OCSP) { + if ocsp.ProxyServerPool != "" { + d.Set("proxy_server_pool", ocsp.ProxyServerPool) + } else { + d.Set("dns_resolver", ocsp.DnsResolver) + } + if ocsp.RouteDomain != "" { + d.Set("route_domain", ocsp.RouteDomain) + } + if ocsp.ResponderUrl != "" { + d.Set("responder_url", ocsp.ResponderUrl) + } + if ocsp.TrustedResponders != "" { + d.Set("trusted_responders", ocsp.TrustedResponders) + } + if ocsp.SignerCert != "" { + d.Set("signer_cert", ocsp.SignerCert) + } + if ocsp.SignerKey != "" { + d.Set("signer_key", ocsp.SignerKey) + } + + d.Set("concurrent_connections_limit", ocsp.ConcurrentConnectionsLimit) + d.Set("clock_skew", ocsp.ClockSkew) + d.Set("status_age", ocsp.StatusAge) + d.Set("cache_timeout", ocsp.CacheTimeout) + d.Set("cache_error_timeout", ocsp.CacheErrorTimeout) + d.Set("connection_timeout", ocsp.ConnectionTimeout) + d.Set("strict_resp_cert_check", ocsp.StrictRespCertCheck) + d.Set("sign_hash", ocsp.SignHash) +} diff --git a/bigip/resource_bigip_sys_ocsp_test.go b/bigip/resource_bigip_sys_ocsp_test.go new file mode 100644 index 000000000..612540b98 --- /dev/null +++ b/bigip/resource_bigip_sys_ocsp_test.go @@ -0,0 +1,124 @@ +package bigip + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + bigip "github.com/f5devcentral/go-bigip" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +const testSysOcspDNS = ` +resource "bigip_sys_ocsp" "test-ocsp" { + name = "/Common/test-ocsp" + dns_resolver = "/Common/f5-aws-dns" + signer_key = "/Common/le-ssl" + signer_cert = "/Common/le-ssl" + passphrase = "testabcdef" +} +` + +const testSysOcspProxy = ` +resource "bigip_sys_ocsp" "test-ocsp" { + name = "/Common/test-ocsp" + proxy_server_pool = "/Common/test-poolxyz" + signer_key = "/Common/le-ssl" + signer_cert = "/Common/le-ssl" + passphrase = "testabcdef" +} +` + +func TestAccBigipSysOCSP_create(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAcctPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testCheckOCSPDestroyed, + Steps: []resource.TestStep{ + { + Config: testSysOcspDNS, + Check: resource.ComposeTestCheckFunc( + testCheckOCSPExists("~Common~test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "name", "/Common/test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "dns_resolver", "/Common/f5-aws-dns"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_key", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_cert", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "passphrase", "testabcdef"), + ), + }, + { + Config: testSysOcspProxy, + Check: resource.ComposeTestCheckFunc( + testCheckOCSPExists("~Common~test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "name", "/Common/test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "proxy_server_pool", "/Common/test-poolxyz"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_key", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_cert", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "passphrase", "testabcdef"), + ), + }, + { + Config: testSysOcspProxy, + Check: resource.ComposeTestCheckFunc( + testCheckOCSPExists("~Common~test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "name", "/Common/test-ocsp"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "proxy_server_pool", "/Common/test-poolxyz"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_key", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "signer_cert", "/Common/le-ssl"), + resource.TestCheckResourceAttr("bigip_sys_ocsp.test-ocsp", "passphrase", "testabcdef"), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func testCheckOCSPExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*bigip.BigIP) + + p, err := client.GetOCSP(name) + if err != nil { + return err + } + if p == nil { + return fmt.Errorf("OCSP %s does not exist ", name) + } + + return nil + } +} + +func testCheckOCSPDestroyed(s *terraform.State) error { + client := testAccProvider.Meta().(*bigip.BigIP) + for _, rs := range s.RootModule().Resources { + if rs.Type != "bigip_sys_ocsp" { + continue + } + + id := rs.Primary.ID + id = strings.Trim(id, "/") + splitArr := strings.Split(id, "/") + if len(splitArr) != 2 { + return fmt.Errorf("Invalid ID %s", id) + } + + name := splitArr[1] + partition := splitArr[0] + ocspFqdn := fmt.Sprintf("~%s~%s", partition, name) + // client.DeleteOCSP(ocspFqdn) + ocsp, err := client.GetOCSP(ocspFqdn) + js, _ := json.Marshal(ocsp) + if err != nil { + return err + } + if ocsp != nil { + return fmt.Errorf("OCSP %s not destroyed, struct %+v, js %s", name, ocsp, string(js)) + } + } + return nil +} diff --git a/bigip/version.go b/bigip/version.go index bf532b07c..facdd85c9 100644 --- a/bigip/version.go +++ b/bigip/version.go @@ -1,4 +1,4 @@ package bigip // ProviderVersion is set at build-time in the release process -var ProviderVersion = "1.19.0" +var ProviderVersion = "1.20.0" diff --git a/docs/index.md b/docs/index.md index f8f7ef677..5011c674a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -43,7 +43,7 @@ provider "bigip" { - `address` - (type `string`) Domain name or IP address of the BIG-IP. Can be set via the `BIGIP_HOST` environment variable. - `username` - (type `string`) BIG-IP Username for authentication. Can be set via the `BIGIP_USER` environment variable. - `password` - (type `string`) BIG-IP Password for authentication. Can be set via the `BIGIP_PASSWORD` environment variable. -- `token_auth` - (Optional, Default `false`) Enable to use an external authentication source (LDAP, TACACS, etc). Can be set via the `BIGIP_TOKEN_AUTH` environment variable. +- `token_auth` - (Optional, Default `true`) Enable to use token authentication. Can be set via the `BIGIP_TOKEN_AUTH` environment variable. - `token_value` - (Optional) A token generated outside the provider, in place of password - `login_ref` - (Optional,Default `tmos`) Login reference for token authentication (see BIG-IP REST docs for details). May be set via the `BIGIP_LOGIN_REF` environment variable. - `port` - (Optional) Management Port to connect to BIG-IP,this is mainly required if we have single nic BIG-IP in AWS/Azure/GCP (or) Management port other than `443`. Can be set via `BIGIP_PORT` environment variable. diff --git a/docs/resources/bigip_ltm_profile_client_ssl.md b/docs/resources/bigip_ltm_profile_client_ssl.md index d9635f611..fb8dd8c50 100644 --- a/docs/resources/bigip_ltm_profile_client_ssl.md +++ b/docs/resources/bigip_ltm_profile_client_ssl.md @@ -49,6 +49,8 @@ Don't insert empty fragments and No TLSv1.3 are listed as Enabled Options. `Usag * `cipher_group` - (Optional) Specifies the cipher group for the SSL server profile. It is mutually exclusive with the argument, `ciphers`. The default value is `none`. +* `ocsp_stapling` - (Optional) Specifies whether the system uses OCSP stapling. The default value is `disabled`. + * `peer_cert_mode` - (Optional) Specifies the way the system handles client certificates.When ignore, specifies that the system ignores certificates from client systems.When require, specifies that the system requires a client to present a valid certificate.When request, specifies that the system requests a valid certificate from a client but always authenticate the client. * `renegotiation` - (Optional) Enables or disables SSL renegotiation.When creating a new profile, the setting is provided by the parent profile diff --git a/docs/resources/bigip_ltm_profile_http.md b/docs/resources/bigip_ltm_profile_http.md index 7efbe1758..b6ca857c0 100644 --- a/docs/resources/bigip_ltm_profile_http.md +++ b/docs/resources/bigip_ltm_profile_http.md @@ -73,6 +73,37 @@ resource "bigip_ltm_profile_http" "sanjose-http" { * `xff_alternative_names` - (Optional) Specifies alternative XFF headers instead of the default X-forwarded-for header. +* `server_agent_name` - (Optional) Specifies the value of the Server header in responses that the BIG-IP itself generates. The default is BigIP. If no string is specified, then default value will be added to such responses. In order to remove it, "none" string is to be passed. + +* `enforcement` -See [Enforcement](#enforcement) below for more details. + +* `http_strict_transport_security` -See [Http_Strict_Transport_Security](#http_strict_transport_security) below for more details. + +### Enforcement + +The `enforcement` block supports the following: + +* `known_methods` - (Optional , `list`) Specifies which HTTP methods count as being known. Removing RFC-defined methods from this list will cause the HTTP filter to not recognize them. Default value is [CONNECT DELETE GET HEAD LOCK OPTIONS POST PROPFIND PUT TRACE UNLOCK].If no value is specified while creating, then default value will be assigned. In order to remove it, [""] list is to be passed. + +* `unknown_method` - (Optional , `string`) Specifies whether to allow, reject or switch to pass-through mode when an unknown HTTP method is parsed. Default value is allow. If no string is specified, then default value will be assigned. + +* `max_header_count` - (Optional , `int`) Specifies the maximum number of headers allowed in HTTP request/response. The default is 64 headers.If no value is specified, then default value will be assigned. + +* `max_header_size` - (Optional , `int`) Specifies the maximum header size.The default value is 32768.If no string is specified, then default value will be assigned. + + +### Http_Strict_Transport_Security + +The `http_strict_transport_security` block supports the following: + +* `include_subdomains` - (Optional , `string`) Specifies whether to include the includeSubdomains directive in the HSTS header. The default is enabled. If no string is specified, then default value will be assigned. + +* `preload` - (Optional , `string`) Specifies whether to include the preload directive in the HSTS header. The default is disabled. If no string is specified, then default value will be assigned. + +* `mode` - (Optional , `string`) Specifies whether to include the HSTS response header. The default is disabled.If no string is specified, then default value will be assigned. + +* `maximum_age` - (Optional , `int`) Specifies the maximum age to assume the connection should remain secure. The default is 16070400 seconds. If no value is specified, then default value will be assigned. + ## Import diff --git a/docs/resources/bigip_ssl_certificate.md b/docs/resources/bigip_ssl_certificate.md index 2ea0fba54..f48032b85 100644 --- a/docs/resources/bigip_ssl_certificate.md +++ b/docs/resources/bigip_ssl_certificate.md @@ -33,3 +33,9 @@ resource "bigip_ssl_certificate" "test-cert" { * `content` - (Required) Content of certificate on Local Disk,path of SSL certificate will be provided to terraform `file` function * `partition` - Partition on to SSL Certificate to be imported. The parameter is not required when running terraform import operation. In such case the name must be provided in full_path format. + +* `monitoring_type` - Specifies the type of monitoring used. + +* `issuer_cert` - Specifies the issuer certificate. + +* `ocsp` - Specifies the OCSP responder. diff --git a/docs/resources/bigip_ssl_key_cert.md b/docs/resources/bigip_ssl_key_cert.md index 570cca38e..0b14d1d4f 100644 --- a/docs/resources/bigip_ssl_key_cert.md +++ b/docs/resources/bigip_ssl_key_cert.md @@ -42,6 +42,11 @@ resource "bigip_ssl_key_cert" "testkeycert" { * `passphrase` - (Optional,type `string`) Passphrase on the SSL key. +* `cert_monitoring_type` - (Optional,type `string`) Specifies the type of monitoring used. + +* `issuer_cert` - (Optional,type `string`) Specifies the issuer certificate. + +* `cert_ocsp` - (Optional,type `string`) Specifies the OCSP responder. ## Attribute Reference diff --git a/docs/resources/bigip_sys_ocsp.md b/docs/resources/bigip_sys_ocsp.md new file mode 100644 index 000000000..43f937fc7 --- /dev/null +++ b/docs/resources/bigip_sys_ocsp.md @@ -0,0 +1,67 @@ +--- +layout: "bigip" +page_title: "BIG-IP: bigip_sys_ocsp" +subcategory: "System" +description: |- + Provides details about OCSP resource for BIG-IP +--- + +# bibip\_sys\_ocsp + +`bigip_sys_ocsp` Manages F5 BIG-IP OCSP responder using iControl REST. + +## Example Usage + +```hcl +resource "bigip_sys_ocsp" "test-ocsp" { + name = "/Uncommon/test-ocsp" + proxy_server_pool = "/Common/test-poolxyz" + signer_key = "/Common/le-ssl" + signer_cert = "/Common/le-ssl" + passphrase = "testabcdef" +} +``` + +## Argument Reference + +* `name` - (Required,type `string`) Name of the OCSP Responder. Name should be in pattern `/partition/ocsp_name`. + +* `proxy_server_pool` - (Required,type `string`) Specifies the proxy server pool the BIG-IP system uses to fetch the OCSP response. + +* `dns_resolver` - (Optional,type `string`) Specifies the internal DNS resolver the BIG-IP system uses to fetch the OCSP response. + +* `route_domain` - (Optional,type `string`) Specifies the route domain for the OCSP responder. + +* `concurrent_connections_limit` - (Optional,type `int`) Specifies the maximum number of connections per second allowed for the OCSP certificate validator. The default value is `50`. + +* `responder_url` - (Optional,type `string`) Specifies the URL of the OCSP responder. + +* `connection_timeout` - (Optional,type `int`) Specifies the time interval that the BIG-IP system waits for before ending the connection to the OCSP responder, in seconds. The default value is `8`. + +* `trusted_responders` - (Optional,type `string`) Specifies the certificates used for validating the OCSP response. + +* `clock_skew` - (Optional,type `int`) Specifies the time interval that the BIG-IP system allows for clock skew, in seconds. The default value is `300`. + +* `status_age` - (Optional,type `int`) Specifies the maximum allowed lag time that the BIG-IP system accepts for the 'thisUpdate' time in the OCSP response, in seconds. The default value is `0`. + +* `strict_resp_cert_check` - (Optional,type `string`) Specifies whether the responder's certificate is checked for an OCSP signing extension. The default value is `enabled`. + +* `cache_timeout` - (Optional,type `string`) Specifies the lifetime of the OCSP response in the cache, in seconds. The default value is `indefinite`. + +* `cache_error_timeout` - (Optional,type `string`) Specifies the lifetime of an error response in the cache, in seconds. This value must be greater than connection_timeout. The default value is `3600`. + +* `signer_cert` - (Required,type `string`) Specifies the certificate used to sign the OCSP request. + +* `signer_key` - (Required,type `string`) Specifies the key used to sign the OCSP request. + +* `passphrase` - (Optional,type `string`) Specifies a passphrase used to sign an OCSP request. + +* `sign_hash` - (Optional,type `string`) Specifies the hash algorithm used to sign the OCSP request. The default value is `sha256`. + + +## Importing +An existing OCSP can be imported into this resource by supplying the full path name ex : `/partition/name` +An example is below: +```sh +$ terraform import bigip_sys_ocsp.test-ocsp /Common/test-ocsp +``` diff --git a/go.mod b/go.mod index 4bfe8550d..71e4788bc 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/Azure/azure-storage-blob-go v0.13.0 github.com/Azure/go-autorest/autorest v0.11.18 github.com/Azure/go-autorest/autorest/adal v0.9.13 - github.com/f5devcentral/go-bigip v0.0.0-20230929101300-4ca00e7ed5fc - github.com/f5devcentral/go-bigip/f5teem v0.0.0-20230929101300-4ca00e7ed5fc + github.com/f5devcentral/go-bigip v0.0.0-20231011173025-84b03e0e0bf7 + github.com/f5devcentral/go-bigip/f5teem v0.0.0-20231011173025-84b03e0e0bf7 github.com/google/uuid v1.3.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index fe532442b..f76bf769b 100644 --- a/go.sum +++ b/go.sum @@ -480,10 +480,10 @@ github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/f5devcentral/go-bigip v0.0.0-20230929101300-4ca00e7ed5fc h1:jWmvlICHswmQEL4qUc6CxIsQy2Guxwk4uE6jJWiE5/o= -github.com/f5devcentral/go-bigip v0.0.0-20230929101300-4ca00e7ed5fc/go.mod h1:0Lkr0fBU6O1yBxF2mt9JFwXpaFbIb/wAY7oM3dMJDdA= -github.com/f5devcentral/go-bigip/f5teem v0.0.0-20230929101300-4ca00e7ed5fc h1:tlODenRp43vLPGE20j+fgrDPNNH2MJ6HyqDcLKjYOmo= -github.com/f5devcentral/go-bigip/f5teem v0.0.0-20230929101300-4ca00e7ed5fc/go.mod h1:r7o5I22EvO+fps2u10bz4ZUlTlNHopQSWzVcW19hK3U= +github.com/f5devcentral/go-bigip v0.0.0-20231011173025-84b03e0e0bf7 h1:xaK/uXNpvs510yY0PiNGL6Ak+X6zh3Vf7WpOI2e8/JY= +github.com/f5devcentral/go-bigip v0.0.0-20231011173025-84b03e0e0bf7/go.mod h1:0Lkr0fBU6O1yBxF2mt9JFwXpaFbIb/wAY7oM3dMJDdA= +github.com/f5devcentral/go-bigip/f5teem v0.0.0-20231011173025-84b03e0e0bf7 h1:p76I5H3ztS0IqokrJHJyFf67xsEtzE/wKDZbrBkN99w= +github.com/f5devcentral/go-bigip/f5teem v0.0.0-20231011173025-84b03e0e0bf7/go.mod h1:r7o5I22EvO+fps2u10bz4ZUlTlNHopQSWzVcW19hK3U= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= diff --git a/vendor/github.com/f5devcentral/go-bigip/ltm.go b/vendor/github.com/f5devcentral/go-bigip/ltm.go index 4e57c326f..5530761d3 100644 --- a/vendor/github.com/f5devcentral/go-bigip/ltm.go +++ b/vendor/github.com/f5devcentral/go-bigip/ltm.go @@ -130,6 +130,7 @@ type ClientSSLProfile struct { Key string `json:"key,omitempty"` ModSslMethods string `json:"modSslMethods,omitempty"` Mode string `json:"mode,omitempty"` + OcspStapling string `json:"ocspStapling,omitempty"` TmOptions interface{} `json:"tmOptions,omitempty"` Passphrase string `json:"passphrase,omitempty"` PeerCertMode string `json:"peerCertMode,omitempty"` @@ -1808,33 +1809,56 @@ type HttpProfiles struct { } type HttpProfile struct { - AcceptXff string `json:"acceptXff,omitempty"` - AppService string `json:"appService,omitempty"` - BasicAuthRealm string `json:"basicAuthRealm,omitempty"` - DefaultsFrom string `json:"defaultsFrom,omitempty"` - Description string `json:"description,omitempty"` - EncryptCookieSecret string `json:"encryptCookieSecret,omitempty"` - EncryptCookies []string `json:"encryptCookies,omitempty"` - FallbackHost string `json:"fallbackHost,omitempty"` - FallbackStatusCodes []string `json:"fallbackStatusCodes,omitempty"` - HeaderErase string `json:"headerErase,omitempty"` - HeaderInsert string `json:"headerInsert,omitempty"` - InsertXforwardedFor string `json:"insertXforwardedFor,omitempty"` - LwsSeparator string `json:"lwsSeparator,omitempty"` - LwsWidth int `json:"lwsWidth,omitempty"` - Name string `json:"name,omitempty"` - OneconnectTransformations string `json:"oneconnectTransformations,omitempty"` - TmPartition string `json:"tmPartition,omitempty"` - ProxyType string `json:"proxyType,omitempty"` - RedirectRewrite string `json:"redirectRewrite,omitempty"` - RequestChunking string `json:"requestChunking,omitempty"` - ResponseChunking string `json:"responseChunking,omitempty"` - ResponseHeadersPermitted []interface{} `json:"responseHeadersPermitted,omitempty"` - ServerAgentName string `json:"serverAgentName,omitempty"` - ViaHostName string `json:"viaHostName,omitempty"` - ViaRequest string `json:"viaRequest,omitempty"` - ViaResponse string `json:"viaResponse,omitempty"` - XffAlternativeNames []interface{} `json:"xffAlternativeNames,omitempty"` + AcceptXff string `json:"acceptXff,omitempty"` + AppService string `json:"appService,omitempty"` + BasicAuthRealm string `json:"basicAuthRealm,omitempty"` + DefaultsFrom string `json:"defaultsFrom,omitempty"` + Description string `json:"description,omitempty"` + EncryptCookieSecret string `json:"encryptCookieSecret,omitempty"` + EncryptCookies []string `json:"encryptCookies,omitempty"` + FallbackHost string `json:"fallbackHost,omitempty"` + FallbackStatusCodes []string `json:"fallbackStatusCodes,omitempty"` + HeaderErase string `json:"headerErase,omitempty"` + HeaderInsert string `json:"headerInsert,omitempty"` + InsertXforwardedFor string `json:"insertXforwardedFor,omitempty"` + LwsSeparator string `json:"lwsSeparator,omitempty"` + LwsWidth int `json:"lwsWidth,omitempty"` + Name string `json:"name,omitempty"` + OneconnectTransformations string `json:"oneconnectTransformations,omitempty"` + TmPartition string `json:"tmPartition,omitempty"` + ProxyType string `json:"proxyType,omitempty"` + RedirectRewrite string `json:"redirectRewrite,omitempty"` + RequestChunking string `json:"requestChunking,omitempty"` + ResponseChunking string `json:"responseChunking,omitempty"` + ResponseHeadersPermitted []interface{} `json:"responseHeadersPermitted,omitempty"` + ServerAgentName string `json:"serverAgentName,omitempty"` + ViaHostName string `json:"viaHostName,omitempty"` + ViaRequest string `json:"viaRequest,omitempty"` + ViaResponse string `json:"viaResponse,omitempty"` + XffAlternativeNames []interface{} `json:"xffAlternativeNames,omitempty"` + Hsts HTTPStrictTransportSecurity `json:"hsts,omitempty"` + Enforcement Enforcement `json:"enforcement,omitempty"` +} + +type HTTPStrictTransportSecurity struct { + IncludeSubdomains string `json:"includeSubdomains,omitempty"` + MaximumAge int `json:"maximumAge,omitempty"` + Mode string `json:"mode,omitempty"` + Preload string `json:"preload,omitempty"` +} + +type Enforcement struct { + KnownMethods []string `json:"knownMethods,omitempty"` + ExcessClientHeaders string + ExcessServerHeaders string + MaxHeaderCount int `json:"maxHeaderCount,omitempty"` + MaxHeaderSize int `json:"maxHeaderSize,omitempty"` + MaxRequests int + OversizeClientHeaders string + OversizeServerHeaders string + Pipeline string + TruncatedRedirects string + UnknownMethod string `json:"unknownMethod,omitempty"` } type OneconnectProfiles struct { @@ -2100,7 +2124,7 @@ func (b *BigIP) GetClientSSLProfile(name string) (*ClientSSLProfile, error) { if !ok { return nil, nil } - log.Printf("------------------ssl profile: %+v-----------------", clientSSLProfile) + return &clientSSLProfile, nil } @@ -4006,4 +4030,4 @@ func (b *BigIP) GetLtmCipherGroup(name string) (*CipherGroupReq, error) { } return &cipherGroup, nil -} \ No newline at end of file +} diff --git a/vendor/github.com/f5devcentral/go-bigip/sys.go b/vendor/github.com/f5devcentral/go-bigip/sys.go index b9bfc764f..0a2f63c7f 100644 --- a/vendor/github.com/f5devcentral/go-bigip/sys.go +++ b/vendor/github.com/f5devcentral/go-bigip/sys.go @@ -239,6 +239,28 @@ type ExternalDGFile struct { Type string `json:"type"` } +type OCSP struct { + Name string `json:"name,omitempty"` + FullPath string `json:"fullPath,omitempty"` + Partition string `json:"partition,omitempty"` + ProxyServerPool string `json:"proxyServerPool,omitempty"` + DnsResolver string `json:"dnsResolver,omitempty"` + RouteDomain string `json:"routeDomain,omitempty"` + ConcurrentConnectionsLimit int64 `json:"concurrentConnectionsLimit,omitempty"` + ResponderUrl string `json:"responderUrl,omitempty"` + ConnectionTimeout int64 `json:"timeout,omitempty"` + TrustedResponders string `json:"trustedResponders,omitempty"` + ClockSkew int64 `json:"clockSkew,omitempty"` + StatusAge int64 `json:"statusAge,omitempty"` + StrictRespCertCheck string `json:"strictRespCertCheck,omitempty"` + CacheTimeout string `json:"cacheTimeout,omitempty"` + CacheErrorTimeout int64 `json:"cacheErrorTimeout,omitempty"` + SignerCert string `json:"signerCert,omitempty"` + SignerKey string `json:"signerKey,omitempty"` + Passphrase string `json:"passphrase,omitempty"` + SignHash string `json:"signHash,omitempty"` +} + func (p *LogPublisher) MarshalJSON() ([]byte, error) { return json.Marshal(destinationsDTO{ Name: p.Name, @@ -298,38 +320,49 @@ type Certificates struct { // Certificate represents an SSL Certificate. type Certificate struct { - AppService string `json:"appService,omitempty"` - CachePath string `json:"cachePath,omitempty"` - CertificateKeyCurveName string `json:"certificateKeyCurveName,omitempty"` - CertificateKeySize int `json:"certificateKeySize,omitempty"` - CertValidationOptions string `json:"certValidationOptions,omitempty"` - Checksum string `json:"checksum,omitempty"` - CreatedBy string `json:"createdBy,omitempty"` - CreateTime string `json:"createTime,omitempty"` - Email string `json:"email,omitempty"` - ExpirationDate int64 `json:"expirationDate,omitempty"` - ExpirationString string `json:"expirationString,omitempty"` - Fingerprint string `json:"fingerprint,omitempty"` - FullPath string `json:"fullPath,omitempty"` - Generation int `json:"generation,omitempty"` - IsBundle string `json:"isBundle,omitempty"` - IsDynamic string `json:"isDynamic,omitempty"` - Issuer string `json:"issuer,omitempty"` - IssuerCert string `json:"issuerCert,omitempty"` - KeyType string `json:"keyType,omitempty"` - LastUpdateTime string `json:"lastUpdateTime,omitempty"` - Mode int `json:"mode,omitempty"` - Name string `json:"name,omitempty"` - Partition string `json:"partition,omitempty"` - Revision int `json:"revision,omitempty"` - SerialNumber string `json:"serialNumber,omitempty"` - Size uint64 `json:"size,omitempty"` - SourcePath string `json:"sourcePath,omitempty"` - Subject string `json:"subject,omitempty"` - SubjectAlternativeName string `json:"subjectAlternativeName,omitempty"` - SystemPath string `json:"systemPath,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty"` - Version int `json:"version,omitempty"` + AppService string `json:"appService,omitempty"` + CachePath string `json:"cachePath,omitempty"` + CertificateKeyCurveName string `json:"certificateKeyCurveName,omitempty"` + CertificateKeySize int `json:"certificateKeySize,omitempty"` + CertValidationOptions []string `json:"certValidationOptions,omitempty"` + Checksum string `json:"checksum,omitempty"` + CreatedBy string `json:"createdBy,omitempty"` + CreateTime string `json:"createTime,omitempty"` + Email string `json:"email,omitempty"` + ExpirationDate int64 `json:"expirationDate,omitempty"` + ExpirationString string `json:"expirationString,omitempty"` + Fingerprint string `json:"fingerprint,omitempty"` + FullPath string `json:"fullPath,omitempty"` + Generation int `json:"generation,omitempty"` + IsBundle string `json:"isBundle,omitempty"` + IsDynamic string `json:"isDynamic,omitempty"` + Issuer string `json:"issuer,omitempty"` + IssuerCert string `json:"issuerCert,omitempty"` + KeyType string `json:"keyType,omitempty"` + LastUpdateTime string `json:"lastUpdateTime,omitempty"` + Mode int `json:"mode,omitempty"` + Name string `json:"name,omitempty"` + Partition string `json:"partition,omitempty"` + Revision int `json:"revision,omitempty"` + SerialNumber string `json:"serialNumber,omitempty"` + Size uint64 `json:"size,omitempty"` + SourcePath string `json:"sourcePath,omitempty"` + Subject string `json:"subject,omitempty"` + SubjectAlternativeName string `json:"subjectAlternativeName,omitempty"` + SystemPath string `json:"systemPath,omitempty"` + UpdatedBy string `json:"updatedBy,omitempty"` + Version int `json:"version,omitempty"` + CertValidatorRef *CertValidatorReference `json:"certValidatorsReference,omitempty"` +} + +type CertValidatorReference struct { + Items []CertValidatorState `json:"items,omitempty"` +} + +type CertValidatorState struct { + Name string `json:"name,omitempty"` + Partition string `json:"partition,omitempty"` + FullPath string `json:"fullPath,omitempty"` } // Keys represents a list of installed keys. @@ -407,25 +440,22 @@ func (b *BigIP) ModifyExternalDatagroupfile(dgName string, dataGroup *ExternalDG // ModifyCertificate installs a certificate. func (b *BigIP) ModifyCertificate(certName string, cert *Certificate) error { - return b.patch(cert, uriSys, uriFile, uriSslCert, certName) + return b.patch(cert, uriSys, uriFile, uriSslCert, certName, "?expandSubcollections=true") } // UploadCertificate copies a certificate local disk to BIGIP -func (b *BigIP) UploadCertificate(certname, certpath, partition string) error { +func (b *BigIP) UploadCertificate(certpath string, cert *Certificate) error { certbyte := []byte(certpath) - _, err := b.UploadBytes(certbyte, certname) + _, err := b.UploadBytes(certbyte, cert.Name) if err != nil { return err } - sourcepath := "file://" + REST_DOWNLOAD_PATH + "/" + certname + sourcepath := "file://" + REST_DOWNLOAD_PATH + "/" + cert.Name log.Printf("[DEBUG] sourcepath :%+v", sourcepath) - cert := Certificate{ - Name: certname, - SourcePath: sourcepath, - Partition: partition, - } + + cert.SourcePath = sourcepath log.Printf("cert: %+v\n", cert) - err = b.AddCertificate(&cert) + err = b.AddCertificate(cert) if err != nil { return err } @@ -452,20 +482,18 @@ func (b *BigIP) DeleteCertificate(name string) error { } // UpdateCertificate copies a certificate local disk to BIGIP -func (b *BigIP) UpdateCertificate(certname, certpath, partition string) error { +func (b *BigIP) UpdateCertificate(certpath string, cert *Certificate) error { certbyte := []byte(certpath) - _, err := b.UploadBytes(certbyte, certname) + _, err := b.UploadBytes(certbyte, cert.Name) if err != nil { return err } - sourcepath := "file://" + REST_DOWNLOAD_PATH + "/" + certname - cert := Certificate{ - Name: certname, - SourcePath: sourcepath, - } - certName := fmt.Sprintf("/%s/%s", partition, certname) + sourcepath := "file://" + REST_DOWNLOAD_PATH + "/" + cert.Name + + cert.SourcePath = sourcepath + certName := fmt.Sprintf("/%s/%s", cert.Partition, cert.Name) log.Printf("certName: %+v\n", certName) - err = b.ModifyCertificate(certName, &cert) + err = b.ModifyCertificate(certName, cert) if err != nil { return err } @@ -995,3 +1023,36 @@ func (b *BigIP) UploadDataGroupFile(f *os.File, tmpName string) (*Upload, error) log.Printf("tmpName:%+v", tmpName) return b.Upload(f, info.Size(), uriShared, uriFileTransfer, uriUploads, fmt.Sprintf("%s", tmpName)) } + +func (b *BigIP) CreateOCSP(ocsp *OCSP) error { + return b.post(ocsp, uriSys, "crypto", "cert-validator", "ocsp") +} + +func (b *BigIP) ModifyOCSP(name string, ocsp *OCSP) error { + return b.put(ocsp, uriSys, "crypto", "cert-validator", "ocsp", name) +} + +func (b *BigIP) GetOCSP(name string) (*OCSP, error) { + var ocsp OCSP + err, _ := b.getForEntity(&ocsp, uriSys, "crypto", "cert-validator", "ocsp", name) + + if err != nil { + return nil, err + } + + js, err := json.Marshal(ocsp) + + if err != nil { + return nil, fmt.Errorf("error encountered while marshalling ocsp: %v", err) + } + + if string(js) == "{}" { + return nil, nil + } + + return &ocsp, nil +} + +func (b *BigIP) DeleteOCSP(name string) error { + return b.delete(uriSys, "crypto", "cert-validator", "ocsp", name) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d98666a43..57416ef9f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -42,10 +42,10 @@ github.com/apparentlymart/go-textseg/v13/textseg # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/f5devcentral/go-bigip v0.0.0-20230929101300-4ca00e7ed5fc +# github.com/f5devcentral/go-bigip v0.0.0-20231011173025-84b03e0e0bf7 ## explicit; go 1.20 github.com/f5devcentral/go-bigip -# github.com/f5devcentral/go-bigip/f5teem v0.0.0-20230929101300-4ca00e7ed5fc +# github.com/f5devcentral/go-bigip/f5teem v0.0.0-20231011173025-84b03e0e0bf7 ## explicit; go 1.13 github.com/f5devcentral/go-bigip/f5teem # github.com/fatih/color v1.13.0