feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
---
|
||||
title: Certificate
|
||||
---
|
||||
|
||||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
-->
|
||||
|
||||
`APISIX` supports to load multiple SSL certificates by TLS extension Server Name Indication (SNI).
|
||||
|
||||
### Single SNI
|
||||
|
||||
It is most common for an SSL certificate to contain only one domain. We can create an `ssl` object. Here is a simple case, creates a `ssl` object and `route` object.
|
||||
|
||||
* `cert`: PEM-encoded public certificate of the SSL key pair.
|
||||
* `key`: PEM-encoded private key of the SSL key pair.
|
||||
* `snis`: Hostname(s) to associate with this certificate as SNIs. To set this attribute this certificate must have a valid private key associated with it.
|
||||
|
||||
The following is an example of configuring an SSL certificate with a single SNI in APISIX.
|
||||
|
||||
Create an SSL object with the certificate and key valid for the SNI:
|
||||
|
||||
:::note
|
||||
You can fetch the `admin_key` from `config.yaml` and save to an environment variable with the following command:
|
||||
|
||||
```bash
|
||||
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9180/apisix/admin/ssls/1 \
|
||||
-H "X-API-KEY: $admin_key" -X PUT -d '
|
||||
{
|
||||
"cert" : "'"$(cat t/certs/apisix.crt)"'",
|
||||
"key": "'"$(cat t/certs/apisix.key)"'",
|
||||
"snis": ["test.com"]
|
||||
}'
|
||||
```
|
||||
|
||||
Create a Router object:
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d '
|
||||
{
|
||||
"uri": "/get",
|
||||
"hosts": ["test.com"],
|
||||
"methods": ["GET"],
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"httpbin.org": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Send a request to verify:
|
||||
|
||||
```shell
|
||||
curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/get -k -vvv
|
||||
|
||||
* Added test.com:9443:127.0.0.1 to DNS cache
|
||||
* About to connect() to test.com port 9443 (#0)
|
||||
* Trying 127.0.0.1...
|
||||
* Connected to test.com (127.0.0.1) port 9443 (#0)
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
* ALPN, server accepted to use h2
|
||||
* Server certificate:
|
||||
* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com
|
||||
* start date: Jun 24 22:18:05 2019 GMT
|
||||
* expire date: May 31 22:18:05 2119 GMT
|
||||
* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com
|
||||
* SSL certificate verify result: self-signed certificate (18), continuing anyway.
|
||||
> GET /get HTTP/2
|
||||
> Host: test.com:9443
|
||||
> user-agent: curl/7.81.0
|
||||
> accept: */*
|
||||
```
|
||||
|
||||
### wildcard SNI
|
||||
|
||||
An SSL certificate could also be valid for a wildcard domain like `*.test.com`, which means it is valid for any domain of that pattern, including `www.test.com` and `mail.test.com`.
|
||||
|
||||
The following is an example of configuring an SSL certificate with a wildcard SNI in APISIX.
|
||||
|
||||
Create an SSL object with the certificate and key valid for the SNI:
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9180/apisix/admin/ssls/1 \
|
||||
-H "X-API-KEY: $admin_key" -X PUT -d '
|
||||
{
|
||||
"cert" : "'"$(cat t/certs/apisix.crt)"'",
|
||||
"key": "'"$(cat t/certs/apisix.key)"'",
|
||||
"snis": ["*.test.com"]
|
||||
}'
|
||||
```
|
||||
|
||||
Create a Router object:
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d '
|
||||
{
|
||||
"uri": "/get",
|
||||
"hosts": ["*.test.com"],
|
||||
"methods": ["GET"],
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"httpbin.org": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Send a request to verify:
|
||||
|
||||
```shell
|
||||
curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/get -k -vvv
|
||||
|
||||
* Added www.test.com:9443:127.0.0.1 to DNS cache
|
||||
* Hostname www.test.com was found in DNS cache
|
||||
* Trying 127.0.0.1:9443...
|
||||
* Connected to www.test.com (127.0.0.1) port 9443 (#0)
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
* ALPN, server accepted to use h2
|
||||
* Server certificate:
|
||||
* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com
|
||||
* start date: Jun 24 22:18:05 2019 GMT
|
||||
* expire date: May 31 22:18:05 2119 GMT
|
||||
* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com
|
||||
* SSL certificate verify result: self signed certificate (18), continuing anyway.
|
||||
> GET /get HTTP/2
|
||||
> Host: www.test.com:9443
|
||||
> user-agent: curl/7.74.0
|
||||
> accept: */*
|
||||
```
|
||||
|
||||
### multiple domain
|
||||
|
||||
If your SSL certificate may contain more than one domain, like `www.test.com` and `mail.test.com`, then you can add them into the `snis` array. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"snis": ["www.test.com", "mail.test.com"]
|
||||
}
|
||||
```
|
||||
|
||||
### multiple certificates for a single domain
|
||||
|
||||
If you want to configure multiple certificate for a single domain, for
|
||||
instance, supporting both the
|
||||
[ECC](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography)
|
||||
and RSA key-exchange algorithm, then just configure the extra certificates (the
|
||||
first certificate and private key should be still put in `cert` and `key`) and
|
||||
private keys by `certs` and `keys`.
|
||||
|
||||
* `certs`: PEM-encoded certificate array.
|
||||
* `keys`: PEM-encoded private key array.
|
||||
|
||||
`APISIX` will pair certificate and private key with the same indice as a SSL key
|
||||
pair. So the length of `certs` and `keys` must be same.
|
||||
|
||||
### set up multiple CA certificates
|
||||
|
||||
APISIX currently uses CA certificates in several places, such as [Protect Admin API](./mtls.md#protect-admin-api), [etcd with mTLS](./mtls.md#etcd-with-mtls), and [Deployment Modes](./deployment-modes.md).
|
||||
|
||||
In these places, `ssl_trusted_certificate` or `trusted_ca_cert` will be used to set up the CA certificate, but these configurations will eventually be translated into [lua_ssl_trusted_certificate](https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate) directive in OpenResty.
|
||||
|
||||
If you need to set up different CA certificates in different places, then you can package these CA certificates into a CA bundle file and point to this file when you need to set up CAs. This will avoid the problem that the generated `lua_ssl_trusted_certificate` has multiple locations and overwrites each other.
|
||||
|
||||
The following is a complete example to show how to set up multiple CA certificates in APISIX.
|
||||
|
||||
Suppose we let client and APISIX Admin API, APISIX and ETCD communicate with each other using mTLS protocol, and currently there are two CA certificates, `foo_ca.crt` and `bar_ca.crt`, and use each of these two CA certificates to issue client and server certificate pairs, `foo_ca.crt` and its issued certificate pair are used to protect Admin API, and `bar_ca.crt` and its issued certificate pair are used to protect ETCD.
|
||||
|
||||
The following table details the configurations involved in this example and what they do:
|
||||
|
||||
| Configuration | Type | Description |
|
||||
| ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| foo_ca.crt | CA cert | Issues the secondary certificate required for the client to communicate with the APISIX Admin API over mTLS. |
|
||||
| foo_client.crt | cert | A certificate issued by `foo_ca.crt` and used by the client to prove its identity when accessing the APISIX Admin API. |
|
||||
| foo_client.key | key | Issued by `foo_ca.crt`, used by the client, the key file required to access the APISIX Admin API. |
|
||||
| foo_server.crt | cert | Issued by `foo_ca.crt`, used by APISIX, corresponding to the `admin_api_mtls.admin_ssl_cert` configuration entry. |
|
||||
| foo_server.key | key | Issued by `foo_ca.crt`, used by APISIX, corresponding to the `admin_api_mtls.admin_ssl_cert_key` configuration entry. |
|
||||
| admin.apisix.dev | doname | Common Name used in issuing `foo_server.crt` certificate, through which the client accesses APISIX Admin API |
|
||||
| bar_ca.crt | CA cert | Issues the secondary certificate required for APISIX to communicate with ETCD over mTLS. |
|
||||
| bar_etcd.crt | cert | Issued by `bar_ca.crt` and used by ETCD, corresponding to the `-cert-file` option in the ETCD startup command. |
|
||||
| bar_etcd.key | key | Issued by `bar_ca.crt` and used by ETCD, corresponding to the `--key-file` option in the ETCD startup command. |
|
||||
| bar_apisix.crt | cert | Issued by `bar_ca.crt`, used by APISIX, corresponding to the `etcd.tls.cert` configuration entry. |
|
||||
| bar_apisix.key | key | Issued by `bar_ca.crt`, used by APISIX, corresponding to the `etcd.tls.key` configuration entry. |
|
||||
| etcd.cluster.dev | key | Common Name used in issuing `bar_etcd.crt` certificate, which is used as SNI when APISIX communicates with ETCD over mTLS. corresponds to `etcd.tls.sni` configuration item. |
|
||||
| apisix.ca-bundle | CA bundle | Merged from `foo_ca.crt` and `bar_ca.crt`, replacing `foo_ca.crt` and `bar_ca.crt`. |
|
||||
|
||||
1. Create CA bundle files
|
||||
|
||||
```shell
|
||||
cat /path/to/foo_ca.crt /path/to/bar_ca.crt > apisix.ca-bundle
|
||||
```
|
||||
|
||||
2. Start the ETCD cluster and enable client authentication
|
||||
|
||||
Start by writing a `goreman` configuration named `Procfile-single-enable-mtls`, the content as:
|
||||
|
||||
```text
|
||||
# Use goreman to run `go get github.com/mattn/goreman`
|
||||
etcd1: etcd --name infra1 --listen-client-urls https://127.0.0.1:12379 --advertise-client-urls https://127.0.0.1:12379 --listen-peer-urls http://127.0.0.1:12380 --initial-advertise-peer-urls http://127.0.0.1:12380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --cert-file /path/to/bar_etcd.crt --key-file /path/to/bar_etcd.key --client-cert-auth --trusted-ca-file /path/to/apisix.ca-bundle
|
||||
etcd2: etcd --name infra2 --listen-client-urls https://127.0.0.1:22379 --advertise-client-urls https://127.0.0.1:22379 --listen-peer-urls http://127.0.0.1:22380 --initial-advertise-peer-urls http://127.0.0.1:22380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --cert-file /path/to/bar_etcd.crt --key-file /path/to/bar_etcd.key --client-cert-auth --trusted-ca-file /path/to/apisix.ca-bundle
|
||||
etcd3: etcd --name infra3 --listen-client-urls https://127.0.0.1:32379 --advertise-client-urls https://127.0.0.1:32379 --listen-peer-urls http://127.0.0.1:32380 --initial-advertise-peer-urls http://127.0.0.1:32380 --initial-cluster-token etcd-cluster-1 --initial-cluster 'infra1=http://127.0.0.1:12380,infra2=http://127.0.0.1:22380,infra3=http://127.0.0.1:32380' --initial-cluster-state new --cert-file /path/to/bar_etcd.crt --key-file /path/to/bar_etcd.key --client-cert-auth --trusted-ca-file /path/to/apisix.ca-bundle
|
||||
```
|
||||
|
||||
Use `goreman` to start the ETCD cluster:
|
||||
|
||||
```shell
|
||||
goreman -f Procfile-single-enable-mtls start > goreman.log 2>&1 &
|
||||
```
|
||||
|
||||
3. Update `config.yaml`
|
||||
|
||||
```yaml title="conf/config.yaml"
|
||||
deployment:
|
||||
admin:
|
||||
admin_key
|
||||
- name: admin
|
||||
key: edd1c9f034335f136f87ad84b625c8f1
|
||||
role: admin
|
||||
admin_listen:
|
||||
ip: 127.0.0.1
|
||||
port: 9180
|
||||
https_admin: true
|
||||
admin_api_mtls:
|
||||
admin_ssl_ca_cert: /path/to/apisix.ca-bundle
|
||||
admin_ssl_cert: /path/to/foo_server.crt
|
||||
admin_ssl_cert_key: /path/to/foo_server.key
|
||||
|
||||
apisix:
|
||||
ssl:
|
||||
ssl_trusted_certificate: /path/to/apisix.ca-bundle
|
||||
|
||||
deployment:
|
||||
role: traditional
|
||||
role_traditional:
|
||||
config_provider: etcd
|
||||
etcd:
|
||||
host:
|
||||
- "https://127.0.0.1:12379"
|
||||
- "https://127.0.0.1:22379"
|
||||
- "https://127.0.0.1:32379"
|
||||
tls:
|
||||
cert: /path/to/bar_apisix.crt
|
||||
key: /path/to/bar_apisix.key
|
||||
sni: etcd.cluster.dev
|
||||
```
|
||||
|
||||
4. Test APISIX Admin API
|
||||
|
||||
Start APISIX, if APISIX starts successfully and there is no abnormal output in `logs/error.log`, it means that mTLS communication between APISIX and ETCD is normal.
|
||||
|
||||
Use curl to simulate a client, communicate with APISIX Admin API with mTLS, and create a route:
|
||||
|
||||
```shell
|
||||
curl -vvv \
|
||||
--resolve 'admin.apisix.dev:9180:127.0.0.1' https://admin.apisix.dev:9180/apisix/admin/routes/1 \
|
||||
--cert /path/to/foo_client.crt \
|
||||
--key /path/to/foo_client.key \
|
||||
--cacert /path/to/apisix.ca-bundle \
|
||||
-H "X-API-KEY: $admin_key" -X PUT -i -d '
|
||||
{
|
||||
"uri": "/get",
|
||||
"upstream": {
|
||||
"type": "roundrobin",
|
||||
"nodes": {
|
||||
"httpbin.org:80": 1
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
A successful mTLS communication between curl and the APISIX Admin API is indicated if the following SSL handshake process is output:
|
||||
|
||||
```shell
|
||||
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
|
||||
* TLSv1.3 (IN), TLS handshake, Server hello (2):
|
||||
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
|
||||
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
|
||||
* TLSv1.3 (IN), TLS handshake, Certificate (11):
|
||||
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
|
||||
* TLSv1.3 (IN), TLS handshake, Finished (20):
|
||||
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
|
||||
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
|
||||
* TLSv1.3 (OUT), TLS handshake, CERT verify (15):
|
||||
* TLSv1.3 (OUT), TLS handshake, Finished (20):
|
||||
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
|
||||
```
|
||||
|
||||
5. Verify APISIX proxy
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:9080/get -i
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 298
|
||||
Connection: keep-alive
|
||||
Date: Tue, 26 Jul 2022 16:31:00 GMT
|
||||
Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Credentials: true
|
||||
Server: APISIX/2.14.1
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
APISIX proxied the request to the `/get` path of the upstream `httpbin.org` and returned `HTTP/1.1 200 OK`. The whole process is working fine using CA bundle instead of CA certificate.
|
Reference in New Issue
Block a user