For the longest time the configuring of vCloud Director’s SSL certificate keystore has been the thing that makes vCD admins shudder. There are lots of posts on the process…some good…some not so good. I even have a post from way back in 2012 about fronting vCD with a Citrix NetScaler and if I am honest, I cheated in having HTTPS at the load balancer deal with the SSL certificate while leaving vCD configured with the self signed cert. With the changes to the way the HTML5 Tenant Portal deals with certs and DNS I’m not sure that method would even work today.

I wanted to try and update the self signed certs in both my lab environments to assist in resolving the No Datacenters are available issue that cropped up in vCD 9.1. Instead of generating and using self signed certs I decided to try use Let’s Encrypt signed certs. Most of the process below is curtesy of blog posts from Luca Dell’Oca and it’s worth looking at this blog post from Tom Fojta who has a PowerShell script to automate Let’s Encrypt SSL certs for us on NSX Edge load balancers.

In my case, I wanted to install the cert directly into the vCD Cell Keystore. The manual end to end the process is listed below. I intend to try and automate this process so as to overcome the one constraint with using Let’s Encrypt…that is the 90 day lifespan of the certs. I think that is acceptable and it ensures validity of the SSL cert and a fair caveat given the main use case for this is in lab environments.

Generating the Signed SSL Cert from Let’s Encrypt:

To complete this process you need the ACMESharp PowerShell module. There are a couple of steps to follow which include registering the domain you want to create the SSL cert against, triggering a verification challenge that can be done by creating a domain TXT record as shown in the output of the challenge command. Once submitted, you need to look out for a Valid Status response.

PS C:\ProgramData\ACMESharp\sysVault> New-ACMEIdentifier -Dns vcloud.aperaturelabs.biz -Alias aplabs2 IdentifierPart : ACMESharp.Messages.IdentifierPart IdentifierType : dns Identifier : vcloud.aperaturelabs.biz Uri : https://acme-v01.api.letsencrypt.org/acme/authz/********xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7/4/2018 1:38:28 PM Challenges : {, } Combinations : {1, 0} PS C:\ProgramData\ACMESharp\sysVault> Complete-ACMEChallenge aplabs2 -ChallengeType dns-01 -Handler manual IdentifierPart : ACMESharp.Messages.IdentifierPart IdentifierType : dns Identifier : vcloud.aperaturelabs.biz Uri : https://acme-v01.api.letsencrypt.org/acme/authz/********H92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7/4/2018 1:38:28 PM Challenges : {, manual} Combinations : {1, 0} PS C:\ProgramData\ACMESharp\sysVault> (Get-ACMEIdentifier aplabs2).Challenges ChallengePart : ACMESharp.Messages.ChallengePart Challenge : Type : http-01 Uri : https://acme-v01.api.letsencrypt.org/acme/challenge/********xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8/5331328098 Token :********g5cYoQcUaUhHiAA2qIMAvq_CTpMV-X5AE Status : pending OldChallengeAnswer : [, ] ChallengeAnswerMessage : HandlerName : HandlerHandleDate : HandlerHandleMessage : HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : SubmitResponse : ChallengePart : ACMESharp.Messages.ChallengePart Challenge : ACMESharp.ACME.DnsChallenge Type : dns-01 Uri : https://acme-v01.api.letsencrypt.org/acme/challenge/********xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8/5331328099 Token :********ko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM Status : pending OldChallengeAnswer : [, ] ChallengeAnswerMessage : HandlerName : manual HandlerHandleDate : 6/27/2018 9:38:42 AM HandlerHandleMessage : == Manual Challenge Handler - DNS == * Handle Time: [6/27/2018 9:38:42 AM] * Challenge Token: [********_Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM] To complete this Challenge please create a new Resource Record (RR) with the following characteristics: * RR Type: [TXT] * RR Name: [_acme-challenge.vcloud.aperaturelabs.biz] * RR Value: [********uK6sTtuXdYOZ87IZr1KF0Z_3NTtgfg8AH8] ------------------------------------ HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : SubmitResponse : PS C:\ProgramData\ACMESharp\sysVault> Submit-ACMEChallenge aplabs2 -ChallengeType dns-01 IdentifierPart : ACMESharp.Messages.IdentifierPart IdentifierType : dns Identifier : vcloud.aperaturelabs.biz Uri : https://acme-v01.api.letsencrypt.org/acme/authz/********xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7/4/2018 1:38:28 PM Challenges : {, manual} Combinations : {1, 0} PS C:\ProgramData\ACMESharp\sysVault> (Update-ACMEIdentifier aplabs2 -ChallengeType dns-01).Challenges | Where-Object {$_.Type -eq "dns-01"} ChallengePart : ACMESharp.Messages.ChallengePart Challenge : ACMESharp.ACME.DnsChallenge Type : dns-01 Uri : https://acme-v01.api.letsencrypt.org/acme/challenge/********xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8/5331328099 Token :********_Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM Status : valid OldChallengeAnswer : [, ] ChallengeAnswerMessage : HandlerName : manual HandlerHandleDate : 6/27/2018 9:38:42 AM HandlerHandleMessage : == Manual Challenge Handler - DNS == * Handle Time: [6/27/2018 9:38:42 AM] * Challenge Token: [********_Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM] To complete this Challenge please create a new Resource Record (RR) with the following characteristics: * RR Type: [TXT] * RR Name: [_acme-challenge.vcloud.aperaturelabs.biz] * RR Value: [********uK6sTtuXdYOZ87IZr1KF0Z_3NTtgfg8AH8] ------------------------------------ HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : 6/27/2018 9:53:22 AM SubmitResponse : {StatusCode, Headers, Links, RawContent...} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 PS C : \ ProgramData \ ACMESharp \ sysVault > New-ACMEIdentifier -Dns vcloud . aperaturelabs . biz -Alias aplabs2 IdentifierPart : ACMESharp . Messages . IdentifierPart IdentifierType : dns Identifier : vcloud . aperaturelabs . biz Uri : https : / / acme -v01 . api . letsencrypt . org / acme / authz / * * * * * * * * xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7 / 4 / 2018 1 : 38 : 28 PM Challenges : { , } Combinations : { 1 , 0 } PS C : \ ProgramData \ ACMESharp \ sysVault > Complete-ACMEChallenge aplabs2 -ChallengeType dns -01 -Handler manual IdentifierPart : ACMESharp . Messages . IdentifierPart IdentifierType : dns Identifier : vcloud . aperaturelabs . biz Uri : https : / / acme -v01 . api . letsencrypt . org / acme / authz / * * * * * * * * H92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7 / 4 / 2018 1 : 38 : 28 PM Challenges : { , manual } Combinations : { 1 , 0 } PS C : \ ProgramData \ ACMESharp \ sysVault > ( Get-ACMEIdentifier aplabs2 ) . Challenges ChallengePart : ACMESharp . Messages . ChallengePart Challenge : Type : http -01 Uri : https : / / acme -v01 . api . letsencrypt . org / acme / challenge / * * * * * * * * xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 / 5331328098 Token : * * * * * * * * g5cYoQcUaUhHiAA2qIMAvq_CTpMV -X5AE Status : pending OldChallengeAnswer : [ , ] ChallengeAnswerMessage : HandlerName : HandlerHandleDate : HandlerHandleMessage : HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : SubmitResponse : ChallengePart : ACMESharp . Messages . ChallengePart Challenge : ACMESharp . ACME . DnsChallenge Type : dns -01 Uri : https : / / acme -v01 . api . letsencrypt . org / acme / challenge / * * * * * * * * xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 / 5331328099 Token : * * * * * * * * ko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM Status : pending OldChallengeAnswer : [ , ] ChallengeAnswerMessage : HandlerName : manual HandlerHandleDate : 6 / 27 / 2018 9 : 38 : 42 AM HandlerHandleMessage : == Manual Challenge Handler - DNS == * Handle Time : [ 6 / 27 / 2018 9 : 38 : 42 AM ] * Challenge Token : [ * * * * * * * * _Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM ] To complete this Challenge please create a new Resource Record ( RR ) with the following characteristics : * RR Type : [ TXT ] * RR Name : [ _acme -challenge . vcloud . aperaturelabs . biz ] * RR Value : [ * * * * * * * * uK6sTtuXdYOZ87IZr1KF0Z_3NTtgfg8AH8 ] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : SubmitResponse : PS C : \ ProgramData \ ACMESharp \ sysVault > Submit-ACMEChallenge aplabs2 -ChallengeType dns -01 IdentifierPart : ACMESharp . Messages . IdentifierPart IdentifierType : dns Identifier : vcloud . aperaturelabs . biz Uri : https : / / acme -v01 . api . letsencrypt . org / acme / authz / * * * * * * * * xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 Status : pending Expires : 7 / 4 / 2018 1 : 38 : 28 PM Challenges : { , manual } Combinations : { 1 , 0 } PS C : \ ProgramData \ ACMESharp \ sysVault > ( Update-ACMEIdentifier aplabs2 -ChallengeType dns -01 ) . Challenges | Where-Object { $_ . Type -eq "dns-01" } ChallengePart : ACMESharp . Messages . ChallengePart Challenge : ACMESharp . ACME . DnsChallenge Type : dns -01 Uri : https : / / acme -v01 . api . letsencrypt . org / acme / challenge / * * * * * * * * xH92Xzm8ggC5ctaIrmEgQKyAjXeb6tFgVT8 / 5331328099 Token : * * * * * * * * _Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM Status : valid OldChallengeAnswer : [ , ] ChallengeAnswerMessage : HandlerName : manual HandlerHandleDate : 6 / 27 / 2018 9 : 38 : 42 AM HandlerHandleMessage : == Manual Challenge Handler - DNS == * Handle Time : [ 6 / 27 / 2018 9 : 38 : 42 AM ] * Challenge Token : [ * * * * * * * * _Zko5kw1tjZ6NIlOrkY0KNDCx6zp0FUD4WM ] To complete this Challenge please create a new Resource Record ( RR ) with the following characteristics : * RR Type : [ TXT ] * RR Name : [ _acme -challenge . vcloud . aperaturelabs . biz ] * RR Value : [ * * * * * * * * uK6sTtuXdYOZ87IZr1KF0Z_3NTtgfg8AH8 ] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- HandlerCleanUpDate : HandlerCleanUpMessage : SubmitDate : 6 / 27 / 2018 9 : 53 : 22 AM SubmitResponse : { StatusCode , Headers , Links , RawContent . . . }

Once complete, there is a script that can be run as show on Luca’s Blog. I’ve added to the script to automatically import the newly created SSL cert into the Local Computer certificate store.

PS C:\ProgramData\ACMESharp\sysVault> C:\Users\anthonyspiteri\Documents\PowerCLI\ssl_gen_le.ps1 Press Enter to continue...: Id : 10c6b280-88fa-49d0-bead-9f98046aca74 Alias : aplabs2-2018-06-27-09-56 Label : Memo : IdentifierRef : 78828140-16c2-40e5-9932-c37b065cf291 IdentifierDns : vcloud.aperaturelabs.biz AlternativeIdentifierDns : KeyPemFile : ********-88fa-49d0-bead-9f98046aca74-key.pem CsrPemFile : ********-88fa-49d0-bead-9f98046aca74-csr.pem GenerateDetailsFile : ********-88fa-49d0-bead-9f98046aca74-gen.json CertificateRequest : ACMESharp.CertificateRequest CrtPemFile : ********-88fa-49d0-bead-9f98046aca74-crt.pem CrtDerFile : ********-88fa-49d0-bead-9f98046aca74-crt.der IssuerSerialNumber : ********0000015385736A0B85ECA708 SerialNumber : ********4DE77F1E372768C5B307DC3B1B87 Thumbprint : ********799A2E754C08EE3569B667F470AF61E71 Signature : ********99A2E754C08EE3569B667F470AF61E71 SignatureAlgorithm : sha256RSA RevokedAt : PSPath : Microsoft.PowerShell.Security\Certificate::LocalMachine\my\9D9B264799A2E754C08EE3569B667F470AF61E71 PSParentPath : Microsoft.PowerShell.Security\Certificate::LocalMachine\my PSChildName : 9D9B264799A2E754C08EE3569B667F470AF61E71 PSIsContainer : False Archived : False Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid...} FriendlyName : CN=vcloud.aperaturelabs.biz IssuerName : System.Security.Cryptography.X509Certificates.X500DistinguishedName NotAfter : 9/25/2018 8:56:55 AM NotBefore : 6/27/2018 8:56:55 AM HasPrivateKey : True PrivateKey : PublicKey : System.Security.Cryptography.X509Certificates.PublicKey RawData : {48, 130, 6, 27...} SerialNumber : ********77F1E372768C5B307DC3B1B87 SubjectName : System.Security.Cryptography.X509Certificates.X500DistinguishedName SignatureAlgorithm : System.Security.Cryptography.Oid Thumbprint : ********99A2E754C08EE3569B667F470AF61E71 Version : 3 Handle : 2048481365152 Issuer : CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US Subject : CN=vcloud.aperaturelabs.biz EnhancedKeyUsageList : {Server Authentication (1.3.6.1.5.5.7.3.1), Client Authentication (1.3.6.1.5.5.7.3.2)} DnsNameList : {vcloud.aperaturelabs.biz} SendAsTrustedIssuer : False EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty PolicyId : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 PS C : \ ProgramData \ ACMESharp \ sysVault > C : \ Users \ anthonyspiteri \ Documents \ PowerCLI \ ssl_gen_le . ps1 Press Enter to continue . . . : Id : 10c6b280 -88fa -49d0 -bead -9f98046aca74 Alias : aplabs2 -2018 -06 -27 -09 -56 Label : Memo : IdentifierRef : 78828140 -16c2 -40e5 -9932 -c37b065cf291 IdentifierDns : vcloud . aperaturelabs . biz AlternativeIdentifierDns : KeyPemFile : * * * * * * * * -88fa -49d0 -bead -9f98046aca74 -key . pem CsrPemFile : * * * * * * * * -88fa -49d0 -bead -9f98046aca74 -csr . pem GenerateDetailsFile : * * * * * * * * -88fa -49d0 -bead -9f98046aca74 -gen . json CertificateRequest : ACMESharp . CertificateRequest CrtPemFile : * * * * * * * * -88fa -49d0 -bead -9f98046aca74 -crt . pem CrtDerFile : * * * * * * * * -88fa -49d0 -bead -9f98046aca74 -crt . der IssuerSerialNumber : * * * * * * * * 0000015385736A0B85ECA708 SerialNumber : * * * * * * * * 4DE77F1E372768C5B307DC3B1B87 Thumbprint : * * * * * * * * 799A2E754C08EE3569B667F470AF61E71 Signature : * * * * * * * * 99A2E754C08EE3569B667F470AF61E71 SignatureAlgorithm : sha256RSA RevokedAt : PSPath : Microsoft . PowerShell . Security \ Certificate :: LocalMachine \ my \ 9D9B264799A2E754C08EE3569B667F470AF61E71 PSParentPath : Microsoft . PowerShell . Security \ Certificate :: LocalMachine \ my PSChildName : 9D9B264799A2E754C08EE3569B667F470AF61E71 PSIsContainer : False Archived : False Extensions : { System . Security . Cryptography . Oid , System . Security . Cryptography . Oid , System . Security . Cryptography . Oid , System . Security . Cryptography . Oid . . . } FriendlyName : CN = vcloud . aperaturelabs . biz IssuerName : System . Security . Cryptography . X509Certificates . X500DistinguishedName NotAfter : 9 / 25 / 2018 8 : 56 : 55 AM NotBefore : 6 / 27 / 2018 8 : 56 : 55 AM HasPrivateKey : True PrivateKey : PublicKey : System . Security . Cryptography . X509Certificates . PublicKey RawData : { 48 , 130 , 6 , 27 . . . } SerialNumber : * * * * * * * * 77F1E372768C5B307DC3B1B87 SubjectName : System . Security . Cryptography . X509Certificates . X500DistinguishedName SignatureAlgorithm : System . Security . Cryptography . Oid Thumbprint : * * * * * * * * 99A2E754C08EE3569B667F470AF61E71 Version : 3 Handle : 2048481365152 Issuer : CN = Let 's Encrypt Authority X3, O=Let' s Encrypt , C = US Subject : CN = vcloud . aperaturelabs . biz EnhancedKeyUsageList : { Server Authentication ( 1 . 3 . 6 . 1 . 5 . 5 . 7 . 3 . 1 ) , Client Authentication ( 1 . 3 . 6 . 1 . 5 . 5 . 7 . 3 . 2 ) } DnsNameList : { vcloud . aperaturelabs . biz } SendAsTrustedIssuer : False EnrollmentPolicyEndPoint : Microsoft . CertificateServices . Commands . EnrollmentEndPointProperty EnrollmentServerEndPoint : Microsoft . CertificateServices . Commands . EnrollmentEndPointProperty PolicyId :

From here, I exported the certificate with the private key so that you are left with a PFX file. I also saved to Base-64 X.509 format the Root and Intermediate certs that form the whole chain. This is required to help resolve the No Datacenters are available error mentioned above. Upload the three files to the vCD cell and continue as shown below.

Importing Signed SSL from Let’s Encrypt into vCD Keystore:

Next, the steps to take on the vCD Cell can be the most complex steps to follow and this is where I have seen different posts do different things. Below shows the commands from start to finish that worked for me…see inline for comments on what each command is doing.

# Convert PFX to PEM openssl pkcs12 -in vcloud.aperaturelabs.biz.pfx -out certificate.cer -nodes # Get Private Key from file vi certificate.cer # Create new file with Private Key vi certificate.key # Recreate PFX and create Alias for http and consoleproxy openssl pkcs12 -export -in certificate.cer -inkey certificate.key -name http -passout pass:password -out http.pfx openssl pkcs12 -export -in certificate.cer -inkey certificate.key -name consoleproxy -passout pass:password -out consoleproxy.pfx # Import the http and consoleproxy PFXs into the new KeyStore /opt/vmware/vcloud-director/jre/bin/keytool -importkeystore -srckeystore http.pfx -srcstoretype PKCS12 -destkeystore CERTIFICATES.ks -deststoretype JCEKS -deststorepass password -srcalias http -destalias http -srcstorepass password /opt/vmware/vcloud-director/jre/bin/keytool -importkeystore -srckeystore consoleproxy.pfx -srcstoretype PKCS12 -destkeystore CERTIFICATES.ks -deststoretype JCEKS -deststorepass password -srcalias consoleproxy -destalias consoleproxy -src storepass password # Import the Intermediate and root files that form the chain /opt/vmware/vcloud-director/jre/bin/keytool -importcert -alias intermediate -file le-int.cer -storetype JCEKS -keystore CERTIFICATES.ks -storepass password /opt/vmware/vcloud-director/jre/bin/keytool -importcert -alias root -file le-root.cer -storetype JCEKS -keystore CERTIFICATES.ks -storepass password # List the KeyStore certificates to ensure everything is there /opt/vmware/vcloud-director/jre/bin/keytool -list -keystore CERTIFICATES.ks -storetype JCEKS -storepass password # Save existing Keystore and overwrite with new cp certificates.ks certificates.ks.old cp ~root/CERTIFICATES.ks /opt/keystore/certificates.ks # Assign ownership of the new KeyStore to vCloud user and group chown vcloud:vcloud certificates.ks # Run the vCD Configuration again to import the new KeyStore /opt/vmware/vcloud-director/bin/configure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # Convert PFX to PEM openssl pkcs12 - in vcloud .aperaturelabs .biz .pfx - out certificate .cer - nodes # Get Private Key from file vi certificate .cer # Create new file with Private Key vi certificate .key # Recreate PFX and create Alias for http and consoleproxy openssl pkcs12 - export - in certificate .cer - inkey certificate .key - name http - passout pass : password - out http .pfx openssl pkcs12 - export - in certificate .cer - inkey certificate .key - name consoleproxy - passout pass : password - out consoleproxy .pfx # Import the http and consoleproxy PFXs into the new KeyStore / opt / vmware / vcloud - director / jre / bin / keytool - importkeystore - srckeystore http .pfx - srcstoretype PKCS12 - destkeystore CERTIFICATES .ks - deststoretype JCEKS - deststorepass password - srcalias http - destalias http - srcstorepass password / opt / vmware / vcloud - director / jre / bin / keytool - importkeystore - srckeystore consoleproxy .pfx - srcstoretype PKCS12 - destkeystore CERTIFICATES .ks - deststoretype JCEKS - deststorepass password - srcalias consoleproxy - destalias consoleproxy - src storepass password # Import the Intermediate and root files that form the chain / opt / vmware / vcloud - director / jre / bin / keytool - importcert - alias intermediate - file le - int .cer - storetype JCEKS - keystore CERTIFICATES .ks - storepass password / opt / vmware / vcloud - director / jre / bin / keytool - importcert - alias root - file le - root .cer - storetype JCEKS - keystore CERTIFICATES .ks - storepass password # List the KeyStore certificates to ensure everything is there / opt / vmware / vcloud - director / jre / bin / keytool - list - keystore CERTIFICATES .ks - storetype JCEKS - storepass password # Save existing Keystore and overwrite with new cp certificates .ks certificates .ks .old cp ~ root / CERTIFICATES .ks / opt / keystore / certificates .ks # Assign ownership of the new KeyStore to vCloud user and group chown vcloud : vcloud certificates .ks # Run the vCD Configuration again to import the new KeyStore / opt / vmware / vcloud - director / bin / configure

Once that has been done and the vCD services has restarted, the SSL cert has been applied and we are all green and the Let’s Encrypt SSL cert is in play.

Share this: Twitter

LinkedIn

Reddit

WhatsApp



Like this: Like Loading...