I need help to connect to remote lxd api

I am a total newbie in the world of LXC/LXD and I find it so hard to understand how I can interact with lxd REST API which is installed on a remote server

From what I understood, I installed lxd on both client and remote server

I copied the client.crt file from the client and I paste it in the server

then I run a command in the server to trust that certificate I just pasted (lxd config trust add the_client_certificate.crt)

Now, in the client I added the remote server using: lxc remote add … which works fine

Hopefully I did things correctly so far, the challenging part is how to start making requests from the client to the server

I can find very few examples that shows a curl request that involve sending the client.crt and client.key within the request like this:

curl -k -s --cert ~/.config/lxc/client.crt --key ~/.config/lxc/client.key https://{SOME_REMOTE_IP}:8443

and somewhere I found this https://github.com/lxc/lxd/issues/2119#issuecomment-226321939 which tells to send only the client certificate not the key (which makes me confused), here is the quote:

You should only pass the certificate, not the private key.

Also, your private key is now on the internet so you probably should have it re-generated…

  1. should I send both the client certificate + client key (of the client machine) within each request to the remote server API ? or just the client certificate ?

  2. is it sufficient to rely on those certificates generated by lxd on the server and the client to make secure requests? or should I add somehow an ssl certificate to the server?

  3. here is an example of what I tried to make a request with ruby

require 'rest-client'

client_key = OpenSSL::PKey.read(File.read('/root/snap/lxd/common/config/client.key'))
client_cert = OpenSSL::X509::Certificate.new(File.read('/root/snap/lxd/common/config/client.crt'))

response = RestClient::Request.execute(
  method: :get,
  url: "https://181.142.34.205:8443",
  ssl_client_cert: client_cert,
  ssl_client_key: client_key
)

which gives me an error:

/usr/lib/ruby/3.0.0/net/protocol.rb:46:in `connect_nonblock’: SSL_connect returned=1 errno=0 peeraddr=185.142.34.205:8443 state=error: certificate verify failed (self-signed certificate) (OpenSSL::SSL::SSLError)

maybe someone can see what I am doing wrong?!

I don’t use Ruby, so I don’t know the specifics of how to do it, but as the error-message says, the certificates are self-signed and Ruby doesn’t trust such by default. You’ll need to tell Ruby to allow self-signed certificates.

How comes I wasn’t able to translate the error message into plain English! thank you a lot for this hint

based on it I was able to find this answer ruby - RestClient.get returning certificate verify failed - Stack Overflow which says:

rest-client verify certificates using the system’s CA store on all platforms by default. But is possible set to false the option :verify_ssl or specify :ssl_ca_file or :ssl_ca_path or :ssl_cert_store to customize the certificate authorities accepted.

While I don’t understand 100% what it means, I have set an extra option

verify_ssl: false

and it works! however… setting verify_ssl to false is not recommended (for security) as it will skip the verification!

I don’t ruby but you can probably tell it to use the server’s crt as the signing CA. Something like this (replace CHANGE-ME by the name of the remote:

require 'rest-client'

client_key = OpenSSL::PKey.read(File.read('/root/snap/lxd/common/config/client.key'))
client_cert = OpenSSL::X509::Certificate.new(File.read('/root/snap/lxd/common/config/client.crt'))
server_cert = OpenSSL::X509::Certificate.new(File.read('/root/snap/lxd/common/config/servercerts/CHANGE-ME.crt'))

response = RestClient::Request.execute(
  method: :get,
  url: "https://181.142.34.205:8443",
  ssl_client_cert: client_cert,
  ssl_client_key: client_key,
  ssl_ca_file: server_cert
)

for ssl_ca_file, from the example I see in the doc, it should just be a string of the path to the certificate, in my case it’s ‘/root/snap//lxd/common…’

but even when correcting that, I get the error: certificate verify failed (hostname mismatch) (RestClient::SSLCertificateNotVerified)

That error is because the name/IP you use to connect isn’t covered by the cert’s CN/SANs and your ruby client verifies that. This is a normal thing to do when verifying a TLS certificate (it’s what web browsers do for example) but LXD uses TLS certs in a different way.

The LXD client doesn’t do the hostname verification and simply ensures the other end, the LXD server, presents the right certificate it already trusts.

As such, you could instruct your ruby client to either use a different hostname to verify against the CN/SANs or simply disable hostname verification. This way, you’d mimic the LXD client’s behavior.

I couldn’t find an option to disable hostname verification, but I found another ruby library that has that option, so I used it by passing only 2 options to it:
1- the ca_file
2: the option {verify_hostname: false}

This worked for me, but if I remove the ca_file option and replace it with client_cert + client_key I get the error: certificate verify failed (self-signed certificate)

This confuses me, as I am not able to tell what is the correct way to make a simple api request:

  1. should I just rely on ca_file + disabling hostname verification

  2. or should I use client.cert and client.key like the examples in the internet (+ disable ssl verification to pass the verify failed (self-signed certificate) error - which I think it’s insecure and not recommended)

do I understand that (1) is the correct way because it’s safer than (2) ?

Yes, 1. seems like the safe way to go assuming the ca_file is really the server.crt.

because it worked when I tried the request then I think so, I thinks its really the server.crt otherwise it wouldn’t return me a success response at all, wouldn’t it?

If I understand well, the server.crt was created on the client the moment we added the remote like this:

lxc remote add server {IP_OF_REMOTE_SERVER}:8443

please correct me if I am wrong!

Thanks

Yes, the lxc remote add command it what saved the server’s certificate on your client machine.