Repository: go-acme/lego (language: go)

Issue:
Support for provider: IBM Cloud (SoftLayer)
### Welcome

- [X] Yes, I've searched similar issues on GitHub and didn't find any.
- [x] Yes, the DNS provider exposes a public API.
- [X] Yes, I know that the lego maintainers don't have an account in all DNS providers in the world.
- [X] Yes, I'm able to create a pull request and be able to maintain the implementation.
- [X] Yes, I'm able to test an implementation if someone creates a pull request to add the support of this DNS provider.

### How do you use lego?

Docker image

### Link to the DNS provider

https://cloud.ibm.com/docs/dns

### Link to the API documentation

https://cloud.ibm.com/docs/dns?topic=dns-getting-started-with-the-dns-api

### Additional Notes

I'm sorry I'm not good at English

Implementation is over.

I would like to make the first PR in my life
Can you guide me?


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 75ef0f2adf..d41c2ac364 100644
--- a/README.md
+++ b/README.md
@@ -57,19 +57,20 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
 | [External program](https://go-acme.github.io/lego/dns/exec/)                    | [freemyip.com](https://go-acme.github.io/lego/dns/freemyip/)                    | [G-Core Labs](https://go-acme.github.io/lego/dns/gcore/)                        | [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/)              |
 | [Gandi](https://go-acme.github.io/lego/dns/gandi/)                              | [Glesys](https://go-acme.github.io/lego/dns/glesys/)                            | [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/)                         | [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/)                      |
 | [Hetzner](https://go-acme.github.io/lego/dns/hetzner/)                          | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/)                     | [Hosttech](https://go-acme.github.io/lego/dns/hosttech/)                        | [HTTP request](https://go-acme.github.io/lego/dns/httpreq/)                     |
-| [Hurricane Electric DNS](https://go-acme.github.io/lego/dns/hurricane/)         | [HyperOne](https://go-acme.github.io/lego/dns/hyperone/)                        | [Infoblox](https://go-acme.github.io/lego/dns/infoblox/)                        | [Infomaniak](https://go-acme.github.io/lego/dns/infomaniak/)                    |
-| [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/)            | [Internet.bs](https://go-acme.github.io/lego/dns/internetbs/)                   | [INWX](https://go-acme.github.io/lego/dns/inwx/)                                | [Ionos](https://go-acme.github.io/lego/dns/ionos/)                              |
-| [Joker](https://go-acme.github.io/lego/dns/joker/)                              | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/)               | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/)                       | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/)                     |
-| [Loopia](https://go-acme.github.io/lego/dns/loopia/)                            | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/)                            | [Manual](https://go-acme.github.io/lego/dns/manual/)                            | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/)                         |
-| [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/)                | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/)                      | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/)                      | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/)                        |
-| [Netcup](https://go-acme.github.io/lego/dns/netcup/)                            | [Netlify](https://go-acme.github.io/lego/dns/netlify/)                          | [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/)                    | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/)                        |
-| [Njalla](https://go-acme.github.io/lego/dns/njalla/)                            | [NS1](https://go-acme.github.io/lego/dns/ns1/)                                  | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/)                   | [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/)                 |
-| [OVH](https://go-acme.github.io/lego/dns/ovh/)                                  | [Porkbun](https://go-acme.github.io/lego/dns/porkbun/)                          | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/)                            | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/)                      |
-| [reg.ru](https://go-acme.github.io/lego/dns/regru/)                             | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/)                          | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/)                  | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/)                 |
-| [Scaleway](https://go-acme.github.io/lego/dns/scaleway/)                        | [Selectel](https://go-acme.github.io/lego/dns/selectel/)                        | [Servercow](https://go-acme.github.io/lego/dns/servercow/)                      | [Simply.com](https://go-acme.github.io/lego/dns/simply/)                        |
-| [Sonic](https://go-acme.github.io/lego/dns/sonic/)                              | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/)                      | [TransIP](https://go-acme.github.io/lego/dns/transip/)                          | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/)                          |
-| [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/)                 | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/)                        | [Vscale](https://go-acme.github.io/lego/dns/vscale/)                            | [Vultr](https://go-acme.github.io/lego/dns/vultr/)                              |
-| [WEDOS](https://go-acme.github.io/lego/dns/wedos/)                              | [Yandex](https://go-acme.github.io/lego/dns/yandex/)                            | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/)                           | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/)                            |
+| [Hurricane Electric DNS](https://go-acme.github.io/lego/dns/hurricane/)         | [HyperOne](https://go-acme.github.io/lego/dns/hyperone/)                        | [IBM Cloud (SoftLayer)](https://go-acme.github.io/lego/dns/ibmcloud/)           | [Infoblox](https://go-acme.github.io/lego/dns/infoblox/)                        |
+| [Infomaniak](https://go-acme.github.io/lego/dns/infomaniak/)                    | [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/)            | [Internet.bs](https://go-acme.github.io/lego/dns/internetbs/)                   | [INWX](https://go-acme.github.io/lego/dns/inwx/)                                |
+| [Ionos](https://go-acme.github.io/lego/dns/ionos/)                              | [Joker](https://go-acme.github.io/lego/dns/joker/)                              | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/)               | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/)                       |
+| [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/)                     | [Loopia](https://go-acme.github.io/lego/dns/loopia/)                            | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/)                            | [Manual](https://go-acme.github.io/lego/dns/manual/)                            |
+| [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/)                         | [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/)                | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/)                      | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/)                      |
+| [Namesilo](https://go-acme.github.io/lego/dns/namesilo/)                        | [Netcup](https://go-acme.github.io/lego/dns/netcup/)                            | [Netlify](https://go-acme.github.io/lego/dns/netlify/)                          | [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/)                    |
+| [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/)                        | [Njalla](https://go-acme.github.io/lego/dns/njalla/)                            | [NS1](https://go-acme.github.io/lego/dns/ns1/)                                  | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/)                   |
+| [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/)                 | [OVH](https://go-acme.github.io/lego/dns/ovh/)                                  | [Porkbun](https://go-acme.github.io/lego/dns/porkbun/)                          | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/)                            |
+| [Rackspace](https://go-acme.github.io/lego/dns/rackspace/)                      | [reg.ru](https://go-acme.github.io/lego/dns/regru/)                             | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/)                          | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/)                  |
+| [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/)                 | [Scaleway](https://go-acme.github.io/lego/dns/scaleway/)                        | [Selectel](https://go-acme.github.io/lego/dns/selectel/)                        | [Servercow](https://go-acme.github.io/lego/dns/servercow/)                      |
+| [Simply.com](https://go-acme.github.io/lego/dns/simply/)                        | [Sonic](https://go-acme.github.io/lego/dns/sonic/)                              | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/)                      | [TransIP](https://go-acme.github.io/lego/dns/transip/)                          |
+| [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/)                          | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/)                 | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/)                        | [Vscale](https://go-acme.github.io/lego/dns/vscale/)                            |
+| [Vultr](https://go-acme.github.io/lego/dns/vultr/)                              | [WEDOS](https://go-acme.github.io/lego/dns/wedos/)                              | [Yandex](https://go-acme.github.io/lego/dns/yandex/)                            | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/)                           |
+| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/)                            |                                                                                 |                                                                                 |                                                                                 |
 
 <!-- END DNS PROVIDERS LIST -->
 
diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go
index e41a8503a7..c917c9342e 100644
--- a/cmd/zz_gen_cmd_dnshelp.go
+++ b/cmd/zz_gen_cmd_dnshelp.go
@@ -59,6 +59,7 @@ func allDNSCodes() string {
 		"httpreq",
 		"hurricane",
 		"hyperone",
+		"ibmcloud",
 		"iij",
 		"infoblox",
 		"infomaniak",
@@ -1064,6 +1065,27 @@ func displayDNSHelp(name string) error {
 		ew.writeln()
 		ew.writeln(`More information: https://go-acme.github.io/lego/dns/hyperone`)
 
+	case "ibmcloud":
+		// generated from: providers/dns/ibmcloud/ibmcloud.toml
+		ew.writeln(`Configuration for IBM Cloud (SoftLayer).`)
+		ew.writeln(`Code:	'ibmcloud'`)
+		ew.writeln(`Since:	'v4.5.0'`)
+		ew.writeln()
+
+		ew.writeln(`Credentials:`)
+		ew.writeln(`	- "SOFTLAYER_API_KEY":	Classic Infrastructure API key`)
+		ew.writeln(`	- "SOFTLAYER_USERNAME":	User name (IBM Cloud is <accountID>_<emailAddress>)`)
+		ew.writeln()
+
+		ew.writeln(`Additional Configuration:`)
+		ew.writeln(`	- "SOFTLAYER_POLLING_INTERVAL":	Time between DNS propagation check`)
+		ew.writeln(`	- "SOFTLAYER_PROPAGATION_TIMEOUT":	Maximum waiting time for DNS propagation`)
+		ew.writeln(`	- "SOFTLAYER_TIMEOUT":	API request timeout`)
+		ew.writeln(`	- "SOFTLAYER_TTL":	The TTL of the TXT record used for the DNS challenge`)
+
+		ew.writeln()
+		ew.writeln(`More information: https://go-acme.github.io/lego/dns/ibmcloud`)
+
 	case "iij":
 		// generated from: providers/dns/iij/iij.toml
 		ew.writeln(`Configuration for Internet Initiative Japan.`)
diff --git a/docs/content/dns/zz_gen_ibmcloud.md b/docs/content/dns/zz_gen_ibmcloud.md
new file mode 100644
index 0000000000..0881a2566a
--- /dev/null
+++ b/docs/content/dns/zz_gen_ibmcloud.md
@@ -0,0 +1,65 @@
+---
+title: "IBM Cloud (SoftLayer)"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: ibmcloud
+---
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/ibmcloud/ibmcloud.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+
+Since: v4.5.0
+
+Configuration for [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/).
+
+
+<!--more-->
+
+- Code: `ibmcloud`
+
+Here is an example bash command using the IBM Cloud (SoftLayer) provider:
+
+```bash
+SOFTLAYER_USERNAME=xxxxx \
+SOFTLAYER_API_KEY=yyyyy \
+lego --email myemail@example.com --dns ibmcloud --domains my.example.org run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `SOFTLAYER_API_KEY` | Classic Infrastructure API key |
+| `SOFTLAYER_USERNAME` | User name (IBM Cloud is <accountID>_<emailAddress>) |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here](/lego/dns/#configuration-and-credentials).
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `SOFTLAYER_POLLING_INTERVAL` | Time between DNS propagation check |
+| `SOFTLAYER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `SOFTLAYER_TIMEOUT` | API request timeout |
+| `SOFTLAYER_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here](/lego/dns/#configuration-and-credentials).
+
+
+
+
+## More information
+
+- [API documentation](https://cloud.ibm.com/docs/dns?topic=dns-getting-started-with-the-dns-api)
+- [Go client](https://github.com/softlayer/softlayer-go)
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/ibmcloud/ibmcloud.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
diff --git a/docs/content/dns/zz_gen_softlayer.md b/docs/content/dns/zz_gen_softlayer.md
new file mode 100644
index 0000000000..79ed667539
--- /dev/null
+++ b/docs/content/dns/zz_gen_softlayer.md
@@ -0,0 +1,65 @@
+---
+title: "SoftLayer (IBM Cloud)"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: softlayer
+---
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/softlayer/softlayer.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+
+Since: v4.5.0
+
+Configuration for [SoftLayer (IBM Cloud)](https://www.ibm.com/cloud/).
+
+
+<!--more-->
+
+- Code: `softlayer`
+
+Here is an example bash command using the SoftLayer (IBM Cloud) provider:
+
+```bash
+SOFTLAYER_USERNAME=xxxxx \
+SOFTLAYER_API_KEY=yyyyy \
+lego --email myemail@example.com --dns softlayer --domains my.example.org run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `SOFTLAYER_API_KEY` | Classic Infrastructure API key |
+| `SOFTLAYER_USERNAME` | User name (IBM Cloud is <accountID>_<emailAddress>) |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here](/lego/dns/#configuration-and-credentials).
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `SOFTLAYER_POLLING_INTERVAL` | Time between DNS propagation check |
+| `SOFTLAYER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `SOFTLAYER_TIMEOUT` | API request timeout |
+| `SOFTLAYER_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
+More information [here](/lego/dns/#configuration-and-credentials).
+
+
+
+
+## More information
+
+- [API documentation](https://cloud.ibm.com/docs/dns?topic=dns-getting-started-with-the-dns-api)
+- [Go client](https://github.com/softlayer/softlayer-go)
+
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
+<!-- providers/dns/softlayer/softlayer.toml -->
+<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
diff --git a/go.mod b/go.mod
index 978985d183..c48af6519b 100644
--- a/go.mod
+++ b/go.mod
@@ -47,6 +47,7 @@ require (
 	github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2
 	github.com/sacloud/libsacloud v1.36.2
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f
+	github.com/softlayer/softlayer-go v1.0.3
 	github.com/stretchr/testify v1.7.0
 	github.com/transip/gotransip/v6 v6.6.1
 	github.com/urfave/cli v1.22.5
diff --git a/go.sum b/go.sum
index 10ba1491b1..305a9bc878 100644
--- a/go.sum
+++ b/go.sum
@@ -248,6 +248,7 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU=
 github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
+github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
 github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g=
 github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -435,6 +436,10 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA=
 github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
+github.com/softlayer/softlayer-go v1.0.3 h1:9FONm5xzQ9belQtbdryR6gBg4EF6hX6lrjNKi0IvZkU=
+github.com/softlayer/softlayer-go v1.0.3/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4=
+github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ=
+github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
@@ -479,6 +484,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -686,6 +692,7 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
 golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/providers/dns/dns_providers.go b/providers/dns/dns_providers.go
index a295863981..f25c2840cb 100644
--- a/providers/dns/dns_providers.go
+++ b/providers/dns/dns_providers.go
@@ -50,6 +50,7 @@ import (
 	"github.com/go-acme/lego/v4/providers/dns/httpreq"
 	"github.com/go-acme/lego/v4/providers/dns/hurricane"
 	"github.com/go-acme/lego/v4/providers/dns/hyperone"
+	"github.com/go-acme/lego/v4/providers/dns/ibmcloud"
 	"github.com/go-acme/lego/v4/providers/dns/iij"
 	"github.com/go-acme/lego/v4/providers/dns/infoblox"
 	"github.com/go-acme/lego/v4/providers/dns/infomaniak"
@@ -195,6 +196,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
 		return hurricane.NewDNSProvider()
 	case "hyperone":
 		return hyperone.NewDNSProvider()
+	case "ibmcloud":
+		return ibmcloud.NewDNSProvider()
 	case "iij":
 		return iij.NewDNSProvider()
 	case "infoblox":
diff --git a/providers/dns/ibmcloud/ibmcloud.go b/providers/dns/ibmcloud/ibmcloud.go
new file mode 100644
index 0000000000..bc64b8ecc4
--- /dev/null
+++ b/providers/dns/ibmcloud/ibmcloud.go
@@ -0,0 +1,129 @@
+// Package ibmcloud implements a DNS provider for solving the DNS-01 challenge using IBM Cloud (SoftLayer).
+package ibmcloud
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"github.com/go-acme/lego/v4/challenge/dns01"
+	"github.com/go-acme/lego/v4/platform/config/env"
+	"github.com/go-acme/lego/v4/providers/dns/ibmcloud/internal"
+	"github.com/softlayer/softlayer-go/session"
+)
+
+// Environment variables names.
+const (
+	envNamespace = "SOFTLAYER_"
+
+	// EnvUsername  the name must be the same as here:
+	// https://github.com/softlayer/softlayer-go/blob/534185047ea683dd1e29fd23e445598295d94be4/session/session.go#L171
+	EnvUsername = envNamespace + "USERNAME"
+	// EnvAPIKey  the name must be the same as here:
+	// https://github.com/softlayer/softlayer-go/blob/534185047ea683dd1e29fd23e445598295d94be4/session/session.go#L175
+	EnvAPIKey = envNamespace + "API_KEY"
+	// EnvHTTPTimeout the name must be the same as here:
+	// https://github.com/softlayer/softlayer-go/blob/534185047ea683dd1e29fd23e445598295d94be4/session/session.go#L182
+	EnvHTTPTimeout = envNamespace + "TIMEOUT"
+	EnvDebug       = envNamespace + "DEBUG"
+
+	EnvTTL                = envNamespace + "TTL"
+	EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
+	EnvPollingInterval    = envNamespace + "POLLING_INTERVAL"
+)
+
+// Config is used to configure the creation of the DNSProvider.
+type Config struct {
+	Username           string
+	APIKey             string
+	PropagationTimeout time.Duration
+	PollingInterval    time.Duration
+	TTL                int
+	HTTPTimeout        time.Duration
+	Debug              bool
+}
+
+// NewDefaultConfig returns a default configuration for the DNSProvider.
+func NewDefaultConfig() *Config {
+	return &Config{
+		TTL:                env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
+		PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
+		PollingInterval:    env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
+		HTTPTimeout:        env.GetOrDefaultSecond(EnvHTTPTimeout, session.DefaultTimeout),
+	}
+}
+
+// DNSProvider implements the challenge.Provider interface.
+type DNSProvider struct {
+	config  *Config
+	wrapper *internal.Wrapper
+}
+
+// NewDNSProvider returns a DNSProvider instance configured for IBM Cloud (SoftLayer).
+// Credentials must be passed in the environment variables:
+// SOFTLAYER_USERNAME, SOFTLAYER_API_KEY.
+func NewDNSProvider() (*DNSProvider, error) {
+	values, err := env.Get(EnvUsername, EnvAPIKey)
+	if err != nil {
+		return nil, fmt.Errorf("ibmcloud: %w", err)
+	}
+
+	config := NewDefaultConfig()
+	config.Username = values[EnvUsername]
+	config.APIKey = values[EnvAPIKey]
+	config.Debug = env.GetOrDefaultBool(EnvDebug, false)
+
+	return NewDNSProviderConfig(config)
+}
+
+// NewDNSProviderConfig return a DNSProvider instance configured for IBM Cloud (SoftLayer).
+func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
+	if config == nil {
+		return nil, errors.New("ibmcloud: the configuration of the DNS provider is nil")
+	}
+
+	if config.Username == "" {
+		return nil, errors.New("ibmcloud: username is missing")
+	}
+
+	if config.APIKey == "" {
+		return nil, errors.New("ibmcloud: API key is missing")
+	}
+
+	sess := session.New(config.Username, config.APIKey)
+
+	sess.Timeout = config.HTTPTimeout
+	sess.Debug = config.Debug
+
+	return &DNSProvider{wrapper: internal.NewWrapper(sess), config: config}, nil
+}
+
+// Timeout returns the timeout and interval to use when checking for DNS propagation.
+// Adjusting here to cope with spikes in propagation times.
+func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
+	return d.config.PropagationTimeout, d.config.PollingInterval
+}
+
+// Present creates a TXT record to fulfill the dns-01 challenge.
+func (d *DNSProvider) Present(domain, token, keyAuth string) error {
+	fqdn, value := dns01.GetRecord(domain, keyAuth)
+
+	err := d.wrapper.AddTXTRecord(fqdn, domain, value, d.config.TTL)
+	if err != nil {
+		return fmt.Errorf("ibmcloud: %w", err)
+	}
+
+	return nil
+}
+
+// CleanUp removes the TXT record matching the specified parameters.
+func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
+	fqdn, _ := dns01.GetRecord(domain, keyAuth)
+
+	err := d.wrapper.CleanupTXTRecord(fqdn, domain)
+	if err != nil {
+		return fmt.Errorf("ibmcloud: %w", err)
+	}
+
+	return nil
+}
diff --git a/providers/dns/ibmcloud/ibmcloud.toml b/providers/dns/ibmcloud/ibmcloud.toml
new file mode 100644
index 0000000000..8f0f08989a
--- /dev/null
+++ b/providers/dns/ibmcloud/ibmcloud.toml
@@ -0,0 +1,25 @@
+Name = "IBM Cloud (SoftLayer)"
+Description = ''''''
+URL = "https://www.ibm.com/cloud/"
+Code = "ibmcloud"
+Since = "v4.5.0"
+
+Example = '''
+SOFTLAYER_USERNAME=xxxxx \
+SOFTLAYER_API_KEY=yyyyy \
+lego --email myemail@example.com --dns ibmcloud --domains my.example.org run
+'''
+
+[Configuration]
+  [Configuration.Credentials]
+    SOFTLAYER_USERNAME = "User name (IBM Cloud is <accountID>_<emailAddress>)"
+    SOFTLAYER_API_KEY = "Classic Infrastructure API key"
+  [Configuration.Additional]
+    SOFTLAYER_POLLING_INTERVAL = "Time between DNS propagation check"
+    SOFTLAYER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+    SOFTLAYER_TTL = "The TTL of the TXT record used for the DNS challenge"
+    SOFTLAYER_TIMEOUT = "API request timeout"
+
+[Links]
+  API = "https://cloud.ibm.com/docs/dns?topic=dns-getting-started-with-the-dns-api"
+  GoClient = "https://github.com/softlayer/softlayer-go"
diff --git a/providers/dns/ibmcloud/internal/wrapper.go b/providers/dns/ibmcloud/internal/wrapper.go
new file mode 100644
index 0000000000..6129d9c157
--- /dev/null
+++ b/providers/dns/ibmcloud/internal/wrapper.go
@@ -0,0 +1,115 @@
+package internal
+
+import (
+	"fmt"
+
+	"github.com/softlayer/softlayer-go/datatypes"
+	"github.com/softlayer/softlayer-go/services"
+	"github.com/softlayer/softlayer-go/session"
+	"github.com/softlayer/softlayer-go/sl"
+)
+
+type Wrapper struct {
+	session *session.Session
+}
+
+func NewWrapper(sess *session.Session) *Wrapper {
+	return &Wrapper{session: sess}
+}
+
+func (w Wrapper) AddTXTRecord(fqdn, domain, value string, ttl int) error {
+	service := services.GetDnsDomainService(w.session)
+
+	domainID, err := getDomainID(service, domain)
+	if err != nil {
+		return fmt.Errorf("failed to get domain ID: %w", err)
+	}
+
+	service.Options.Id = domainID
+
+	if _, err := service.CreateTxtRecord(sl.String(fqdn), sl.String(value), sl.Int(ttl)); err != nil {
+		return fmt.Errorf("failed to create TXT record: %w", err)
+	}
+
+	return nil
+}
+
+func (w Wrapper) CleanupTXTRecord(fqdn, domain string) error {
+	service := services.GetDnsDomainService(w.session)
+
+	domainID, err := getDomainID(service, domain)
+	if err != nil {
+		return fmt.Errorf("failed to get domain ID: %w", err)
+	}
+
+	service.Options.Id = domainID
+
+	records, err := findTxtRecords(service, fqdn)
+	if err != nil {
+		return fmt.Errorf("failed to find TXT records: %w", err)
+	}
+
+	return deleteResourceRecords(service, records)
+}
+
+func getDomainID(service services.Dns_Domain, domain string) (*int, error) {
+	res, err := service.GetByDomainName(sl.String(domain))
+	if err != nil {
+		return nil, err
+	}
+
+	for _, r := range res {
+		if r.Id == nil || toString(r.Name) != domain {
+			continue
+		}
+
+		return r.Id, nil
+	}
+
+	return nil, fmt.Errorf("no data found of domain: %s", domain)
+}
+
+func findTxtRecords(service services.Dns_Domain, fqdn string) ([]datatypes.Dns_Domain_ResourceRecord, error) {
+	var results []datatypes.Dns_Domain_ResourceRecord
+
+	records, err := service.GetResourceRecords()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, record := range records {
+		if toString(record.Host) == fqdn && toString(record.Type) == "txt" {
+			results = append(results, record)
+		}
+	}
+
+	if len(results) == 0 {
+		return nil, fmt.Errorf("no data found of fqdn: %s", fqdn)
+	}
+
+	return results, nil
+}
+
+func deleteResourceRecords(service services.Dns_Domain, records []datatypes.Dns_Domain_ResourceRecord) error {
+	resourceRecord := services.GetDnsDomainResourceRecordService(service.Session)
+
+	// TODO maybe a bug: only the last record will be deleted
+	for _, record := range records {
+		resourceRecord.Options.Id = record.Id
+	}
+
+	_, err := resourceRecord.DeleteObject()
+	if err != nil {
+		return fmt.Errorf("no data found of fqdn: %w", err)
+	}
+
+	return nil
+}
+
+func toString(v *string) string {
+	if v == nil {
+		return ""
+	}
+
+	return *v
+}


Test patch:
diff --git a/providers/dns/ibmcloud/ibmcloud_test.go b/providers/dns/ibmcloud/ibmcloud_test.go
new file mode 100644
index 0000000000..a000e3e599
--- /dev/null
+++ b/providers/dns/ibmcloud/ibmcloud_test.go
@@ -0,0 +1,150 @@
+package ibmcloud
+
+import (
+	"testing"
+	"time"
+
+	"github.com/go-acme/lego/v4/platform/tester"
+	"github.com/stretchr/testify/require"
+)
+
+const envDomain = envNamespace + "DOMAIN"
+
+var envTest = tester.NewEnvTest(EnvUsername, EnvAPIKey).
+	WithDomain(envDomain)
+
+func TestNewDNSProvider(t *testing.T) {
+	testCases := []struct {
+		desc     string
+		envVars  map[string]string
+		expected string
+	}{
+		{
+			desc: "success",
+			envVars: map[string]string{
+				EnvUsername: "123",
+				EnvAPIKey:   "456",
+			},
+		},
+		{
+			desc: "missing credentials",
+			envVars: map[string]string{
+				EnvUsername: "",
+				EnvAPIKey:   "",
+			},
+			expected: "ibmcloud: some credentials information are missing: SOFTLAYER_USERNAME,SOFTLAYER_API_KEY",
+		},
+		{
+			desc: "missing access token",
+			envVars: map[string]string{
+				EnvUsername: "",
+				EnvAPIKey:   "456",
+			},
+			expected: "ibmcloud: some credentials information are missing: SOFTLAYER_USERNAME",
+		},
+		{
+			desc: "missing token secret",
+			envVars: map[string]string{
+				EnvUsername: "123",
+				EnvAPIKey:   "",
+			},
+			expected: "ibmcloud: some credentials information are missing: SOFTLAYER_API_KEY",
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.desc, func(t *testing.T) {
+			defer envTest.RestoreEnv()
+			envTest.ClearEnv()
+
+			envTest.Apply(test.envVars)
+
+			p, err := NewDNSProvider()
+
+			if test.expected == "" {
+				require.NoError(t, err)
+				require.NotNil(t, p)
+				require.NotNil(t, p.config)
+				require.NotNil(t, p.wrapper)
+			} else {
+				require.EqualError(t, err, test.expected)
+			}
+		})
+	}
+}
+
+func TestNewDNSProviderConfig(t *testing.T) {
+	testCases := []struct {
+		desc     string
+		username string
+		apiKey   string
+		expected string
+	}{
+		{
+			desc:     "success",
+			username: "123",
+			apiKey:   "456",
+		},
+		{
+			desc:     "missing credentials",
+			expected: "ibmcloud: username is missing",
+		},
+		{
+			desc:     "missing token",
+			apiKey:   "456",
+			expected: "ibmcloud: username is missing",
+		},
+		{
+			desc:     "missing secret",
+			username: "123",
+			expected: "ibmcloud: API key is missing",
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.desc, func(t *testing.T) {
+			config := NewDefaultConfig()
+			config.Username = test.username
+			config.APIKey = test.apiKey
+
+			p, err := NewDNSProviderConfig(config)
+
+			if test.expected == "" {
+				require.NoError(t, err)
+				require.NotNil(t, p)
+				require.NotNil(t, p.config)
+				require.NotNil(t, p.wrapper)
+			} else {
+				require.EqualError(t, err, test.expected)
+			}
+		})
+	}
+}
+
+func TestLivePresent(t *testing.T) {
+	if !envTest.IsLiveTest() {
+		t.Skip("skipping live test")
+	}
+
+	envTest.RestoreEnv()
+	provider, err := NewDNSProvider()
+	require.NoError(t, err)
+
+	err = provider.Present(envTest.GetDomain(), "", "123d==")
+	require.NoError(t, err)
+}
+
+func TestLiveCleanUp(t *testing.T) {
+	if !envTest.IsLiveTest() {
+		t.Skip("skipping live test")
+	}
+
+	envTest.RestoreEnv()
+	provider, err := NewDNSProvider()
+	require.NoError(t, err)
+
+	time.Sleep(1 * time.Second)
+
+	err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
+	require.NoError(t, err)
+}