### Description

It is impossible to create a follower index with a `$` character in its name (please note, it is possible to create a regular index with `$` in its name).

This could be reproduced by rest request:
```
PUT /my-index/_ccr/follow
{
    "remote_cluster": "my-remote-cluster",
    "leader_index": "my-index$follower"
}
```

or `CcrIntegTestCase`:

```
    public void testFollowIndex() throws Exception {
        assertAcked(leaderClient().admin().indices().prepareCreate("my$index"));
        setupRemoteCluster();
        final PutFollowAction.Request followRequest = putFollow("my$index", "my$index");
        followerClient().execute(PutFollowAction.INSTANCE, followRequest).get();
    }
```

This is happening as during initial follow phase we restore the leader cluster from the remote using a snapshot restore.
Corresponding request is created with index name added to `RestoreSnapshotRequest#renamePattern` (regexp) without escaping `$`.

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportPutFollowAction.java
);
            }
            listener.onFailure(new IllegalArgumentException(message));
            return;
        }
        final Settings overrideSettings = Settings.builder()
            .put(IndexMetadata.SETTING_INDEX_PROVIDED_NAME, request.getFollowerIndex())
            .put(CcrSettings.CCR_FOLLOWING_INDEX_SETTING.getKey(), true)
            .put(request.getSettings())
            .build();
        final String leaderClusterRepoName = CcrRepository.NAME_PREFIX + request.getRemoteCluster();
        final RestoreSnapshotRequest restoreRequest = new RestoreSnapshotRequest(leaderClusterRepoName, CcrRepository.LATEST).indices(
            request.getLeaderIndex()
        )
            .indicesOptions(request.indicesOptions())
            .renamePattern("^(.*)$")
            .renameReplacement(request.getFollowerIndex())
            .masterNodeTimeout(request.masterNodeTimeout())
            .indexSettings(overrideSettings)
            .quiet(true);
        final Client clientWithHeaders = CcrLicenseChecker.wrapClient(
            this.client,
            threadPool.getThreadContext().getHeaders(),
            clusterService.state()
        );
        ActionListener<RestoreService.RestoreCompletionResponse> delegatelistener = listener.delegateFailure(
            (delegatedListener, response) -> afterRestoreStarted(clientWithHeaders, request, delegatedListener, response)
        );
        if (remoteDataStream == null) {