# Issue with SSL Self-Signed Certificate Verification during Elasticsearch Test cURL error 60: SSL certificate problem:

**URL:** <https://help.nextcloud.com/t/issue-with-ssl-self-signed-certificate-verification-during-elasticsearch-test-curl-error-60-ssl-certificate-problem/200713>\
**Category:** ℹ️ Support\
**Tags:** nc28, fulltextsearch-app\
**Created:** [August 14, 2024, 10:04am UTC](https://help.nextcloud.com/t/issue-with-ssl-self-signed-certificate-verification-during-elasticsearch-test-curl-error-60-ssl-certificate-problem/200713 "2024-08-14T10:04:22Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![rm110](https://help.nextcloud.com/letter_avatar/rm110/32/5_5575768a8748004e209b776fc1b2916d.png) [@rm110](https://help.nextcloud.com/u/rm110)\
**Post date:** [August 14, 2024, 10:04am UTC](https://help.nextcloud.com/t/issue-with-ssl-self-signed-certificate-verification-during-elasticsearch-test-curl-error-60-ssl-certificate-problem/200713/1 "2024-08-14T10:04:22Z")

</div>

**Description:**

I’m encountering an issue while running the `php occ fulltextsearch:test` command involving an Elasticsearch instance with a signed SSL certificate ( Common Name (CN):GoGetSSL RSA DV CA  
Organization (O):GoGetSSL).

The error I’m receiving is as follows:

```bash
Testing your current setup:
Creating mocked content provider. ok
Testing mocked provider: get indexable documents. (2 items) ok
Loading search platform. (Elasticsearch) ok
Testing search platform. fail
In CurlFactory.php line 146:

  cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)

```

I modified the `ClientBuilder.php` file to disable SSL verification by setting `sslVerification` to `false`, but the error persists.

**Steps to Reproduce the Issue:**

1. Configure Elasticsearch with a signed SSL certificate.
2. Modify the `ClientBuilder.php` file to disable SSL verification.
3. Run the `php occ fulltextsearch:test` command.

**Expected Result:**

I expect the connection test to Elasticsearch to pass without SSL errors when SSL verification is disabled.

**Actual Result:**

The test fails with a cURL error related to the SSL certificate.

**System Configuration:**

- Elasticsearch Version: 8.6.1
- `fulltextsearch` app version: 28.0.3

**Modified Code:**

I modified the `apps/fulltextsearch_elasticsearch/lib/Vendor/Elastic/Elasticsearch/ClientBuilder.php` file as follows:

```php
<?php

declare(strict_types=1);
namespace OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch;

use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Exception\AuthenticationException;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Exception\ConfigException;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Exception\HttpClientException;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Exception\InvalidArgumentException;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Transport\Adapter\AdapterInterface;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Transport\Adapter\AdapterOptions;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Elasticsearch\Transport\RequestOptions;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Transport\Exception\NoAsyncClientException;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Transport\NodePool\NodePoolInterface;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Transport\Transport;
use OCA\FullTextSearch_Elasticsearch\Vendor\Elastic\Transport\TransportBuilder;
use OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Client as GuzzleHttpClient;
use OCA\FullTextSearch_Elasticsearch\Vendor\Http\Client\HttpAsyncClient;
use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Client\ClientInterface;
use Psr\Log\LoggerInterface;
use ReflectionClass;

class ClientBuilder
{
    const DEFAULT_HOST = 'localhost:9200';
    private ClientInterface $httpClient;
    private HttpAsyncClient $asyncHttpClient;
    private LoggerInterface $logger;
    private NodePoolInterface $nodePool;
    private array $hosts;
    private string $apiKey;
    private string $username;
    private string $password;
    private string $cloudId;
    private int $retries;
    private array $sslCert;
    private array $sslKey;
    private bool $sslVerification = false;
    private string $sslCA;
    private bool $elasticMetaHeader = true;
    private array $httpClientOptions = [];

    public final function __construct()
    {
    }

    public static function create() : ClientBuilder
    {
        return new static();
    }

    public static function fromConfig(array $config, bool $quiet = false) : Client
    {
        $builder = new static();
        foreach ($config as $key => $value) {
            $method = "set{$key}";
            $reflection = new ReflectionClass($builder);
            if ($reflection->hasMethod($method)) {
                $func = $reflection->getMethod($method);
                if ($func->getNumberOfParameters() > 1) {
                    $builder->{$method}(...$value);
                } else {
                    $builder->{$method}($value);
                }
                unset($config[$key]);
            }
        }
        if ($quiet === false && count($config) > 0) {
            $unknown = implode(array_keys($config));
            throw new ConfigException("Unknown parameters provided: {$unknown}");
        }
        return $builder->build();
    }

    public function setHttpClient(ClientInterface $httpClient) : ClientBuilder
    {
        $this->httpClient = $httpClient;
        return $this;
    }

    public function setAsyncHttpClient(HttpAsyncClient $asyncHttpClient) : ClientBuilder
    {
        $this->asyncHttpClient = $asyncHttpClient;
        return $this;
    }

    public function setLogger(LoggerInterface $logger) : ClientBuilder
    {
        $this->logger = $logger;
        return $this;
    }

    public function setNodePool(NodePoolInterface $nodePool) : ClientBuilder
    {
        $this->nodePool = $nodePool;
        return $this;
    }

    public function setHosts(array $hosts) : ClientBuilder
    {
        $this->hosts = $hosts;
        return $this;
    }

    public function setApiKey(string $apiKey, string $id = null) : ClientBuilder
    {
        if (empty($id)) {
            $this->apiKey = $apiKey;
        } else {
            $this->apiKey = base64_encode($id . ':' . $apiKey);
        }
        return $this;
    }

    public function setBasicAuthentication(string $username, string $password) : ClientBuilder
    {
        $this->username = $username;
        $this->password = $password;
        return $this;
    }

    public function setElasticCloudId(string $cloudId)
    {
        $this->cloudId = $cloudId;
        return $this;
    }

    public function setRetries(int $retries) : ClientBuilder
    {
        if ($retries < 0) {
            throw new InvalidArgumentException('The retries number must be >= 0');
        }
        $this->retries = $retries;
        return $this;
    }

    public function setSSLCert(string $cert, string $password = null) : ClientBuilder
    {
        $this->sslCert = [$cert, $password];
        return $this;
    }

    public function setCABundle(string $cert) : ClientBuilder
    {
        $this->sslCA = $cert;
        return $this;
    }

    public function setSSLKey(string $key, string $password = null) : ClientBuilder
    {
        $this->sslKey = [$key, $password];
        return $this;
    }

    public function setSSLVerification(bool $value = false) : ClientBuilder
    {
        $this->sslVerification = $value;
        return $this;
    }

    public function setElasticMetaHeader(bool $value = true) : ClientBuilder
    {
        $this->elasticMetaHeader = $value;
        return $this;
    }

    public function setHttpClientOptions(array $options) : ClientBuilder
    {
        $this->httpClientOptions = $options;
        return $this;
    }

    public function build() : Client
    {
        $builder = TransportBuilder::create();
        if (empty($this->hosts)) {
            $this->hosts = [self::DEFAULT_HOST];
        }
        $builder->setHosts($this->hosts);
        if (!empty($this->logger)) {
            $builder->setLogger($this->logger);
        }
        if (!empty($this->httpClient)) {
            $builder->setClient($this->httpClient);
        }
        $builder->setClient($this->setOptions($builder->getClient(), $this->getConfig(), $this->httpClientOptions));
        if (!empty($this->cloudId)) {
            $builder->setCloudId($this->cloudId);
        }
        if (!empty($this->nodePool)) {
            $builder->setNodePool($this->nodePool);
        }
        $transport = $builder->build();
        if (empty($this->retries)) {
            $this->retries = count($this->hosts);
        }
        $transport->setRetries($this->retries);
        if (!empty($this->asyncHttpClient)) {
            $transport->setAsyncClient($this->asyncHttpClient);
        }
        if (!empty($this->username) && !empty($this->password)) {
            $transport->setUserInfo($this->username, $this->password);
        }
        if (!empty($this->apiKey)) {
            if (!empty($this->username)) {
                throw new AuthenticationException('You cannot use APIKey and Basic Authentication together');
            }
            $transport->setHeader('Authorization', sprintf("ApiKey %s", $this->apiKey));
        }
        if (!empty($this

->cloudId) && !$this->isSymfonyHttpClient($transport)) {
            $transport->setHeader('Accept-Encoding', 'gzip');
        }
        $client = new Client($transport, $transport->getLogger());
        $client->setElasticMetaHeader($this->elasticMetaHeader);
        return $client;
    }

    protected function isSymfonyHttpClient(Transport $transport) : bool
    {
        if (false !== strpos(get_class($transport->getClient()), 'OCA\\FullTextSearch_Elasticsearch\\Vendor\\Symfony\\Component\\HttpClient')) {
            return true;
        }
        try {
            if (false !== strpos(get_class($transport->getAsyncClient()), 'OCA\\FullTextSearch_Elasticsearch\\Vendor\\Symfony\\Component\\HttpClient')) {
                return true;
            }
        } catch (NoAsyncClientException $e) {
            return false;
        }
        return false;
    }

    protected function getConfig() : array
    {
        $config = [];
        if (!empty($this->sslCert)) {
            $config[RequestOptions::SSL_CERT] = $this->sslCert;
        }
        if (!empty($this->sslKey)) {
            $config[RequestOptions::SSL_KEY] = $this->sslKey;
        }
        if (!$this->sslVerification) {
            $config[RequestOptions::SSL_VERIFY] = true;
        }
        if (!empty($this->sslCA)) {
            $config[RequestOptions::SSL_CA] = $this->sslCA;
        }
        return $config;
    }

    protected function setOptions(ClientInterface $client, array $config, array $clientOptions = []) : ClientInterface
    {
        if (empty($config) && empty($clientOptions)) {
            return $client;
        }
        $class = get_class($client);
        if (!isset(AdapterOptions::HTTP_ADAPTERS[$class])) {
            throw new HttpClientException(sprintf("The HTTP client %s is not supported for custom options", $class));
        }
        $adapterClass = AdapterOptions::HTTP_ADAPTERS[$class];
        if (!class_exists($adapterClass) || !in_array(AdapterInterface::class, class_implements($adapterClass))) {
            throw new HttpClientException(sprintf("The class %s does not exists or does not implement %s", $adapterClass, AdapterInterface::class));
        }
        $adapter = new $adapterClass();
        return $adapter->setConfig($client, $config, $clientOptions);
    }
}

```

If possible, providing support for using signed SSL certificates in development environments would be very helpful.

---

<div class="post-metadata">

**Author:** ![jtr](https://help.nextcloud.com/letter_avatar/jtr/32/5_5575768a8748004e209b776fc1b2916d.png) [@jtr](https://help.nextcloud.com/u/jtr)\
**Post date:** [March 12, 2025, 5:21pm UTC](https://help.nextcloud.com/t/issue-with-ssl-self-signed-certificate-verification-during-elasticsearch-test-curl-error-60-ssl-certificate-problem/200713/2 "2025-03-12T17:21:57Z")

</div>

> <https://github.com/nextcloud/fulltextsearch_elasticsearch/issues/268>
>
> I have created a three node Elasticsearch 8 cluster on Debian 12 and created an …index for Nextcloud. 
> 
> The Elasticsearch cluster is using the self signed certs it generated when setting up basic security.
> 
> I can GET information from the cluster using curl. For example 
> 
> \`\`\`curl -kX GET "https://elastic:PASSWORD-HERE@els-cluster:9200"\`\`\`
> 
> returns the information below with no error or prompt.
> 
> \`\`\`
> {
> "name" : "els01",
> "cluster\_name" : "els-cluster",
> "cluster\_uuid" : "PZ0ht\_AZSea4VXwGvmM2mQ",
> "version" : {
> "number" : "8.8.1",
> "build\_flavor" : "default",
> "build\_type" : "deb",
> "build\_hash" : "f8edfccba429b6477927a7c1ce1bc6729521305e",
> "build\_date" : "2023-06-05T21:32:25.188464208Z",
> "build\_snapshot" : false,
> "lucene\_version" : "9.6.0",
> "minimum\_wire\_compatibility\_version" : "7.17.0",
> "minimum\_index\_compatibility\_version" : "7.0.0"
> },
> "tagline" : "You Know, for Search"
> }
> \`\`\`
> 
> When I attempt to kick off the initial indexing from Nextcloud I get the following error.
> 
> \`\`\`
> cURL error 60: SSL certificate problem: self signed certificate in certificate chain (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://els-cluster:92  
> 00/
> \`\`\`
> 
> Is it possible to add an insecure parameter (like -k for curl) to the credentials for Nextcloud?
> 
> https://elastic:PASSWORD-HERE@els-cluster:9200
> 
> Or is this not possible? I want to prove this works before spending any more time on it.

---

<div class="post-metadata">

**Author:** ![wwe](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wwe/32/72963_2.png) [@wwe](https://help.nextcloud.com/u/wwe)\
**Post date:** [January 16, 2026, 8:30pm UTC](https://help.nextcloud.com/t/issue-with-ssl-self-signed-certificate-verification-during-elasticsearch-test-curl-error-60-ssl-certificate-problem/200713/3 "2026-01-16T20:30:02Z")

</div>


