# gencert This script generates x509 server certificate (with all IPs in SAN) signed by a self-signed CA. ## Purpose This script will always produce a self-signed x509 certificate with the IP addresses embedded to x509's SAN. It will also produce a CA certificate and can be used by other services which may need to authenticate against this self-signed certificate. The authentication works in a way that a public CA certificate will be used by the client in order to validate the server's certificate. ## Application ### Backend requiring x509 running behind reverse proxy This script has been created in order to ease the Minio's SSE-C (Server Side Encryption - Customer provided keys) enablement when Minio server is running as a backend behind a reverse proxy like Traefik. Minio server enables SSE-C only when it detects the x509 certificates. Traefik (running with docker service provider or similar) is talking to the backend using the IP address rather than a hostname. Usually, the IP address is not static, hence this script comes handy. ## Script logic - generate CA certificate if does not find any - always generate server certificate on startup to ensure all IP addresses are in x509 SAN - warn if the CA certificate is about to expire (<30 days till expiration) - regenerate the CA certificate if it finds it has expired ## Notes - The CA certificate will be valid for 3650 days (10 years) - The server certifcate will be valid for 365 days (1 year) - The x509 certs are ECDSA with prime256v1 curve and SHA256 signatures ## Testing I have added a simplistic script [testme.sh](testme.sh) that helps to test this script in the following Linux distributions: - Alpine 3.7 - Ubuntu Bionic - Debian Stretch - CentOS 7 ## Usage example ### Minio server with Traefik example 1. Replace ``minio server`` command with the following one: ``cd /root/.minio/certs && ./gencert.sh --cn minio.example.com && minio server /data`` 2. Copy the CA certificate ``ca.crt`` file to ``/usr/local/share/ca-certificates/`` and run ``update-ca-certificates`` command which will update ``/etc/ssl/certs/ca-certificates.crt`` file; 3. Restart Traefik. With the Step 1. Minio server will get the certificate it needs, hence SSE-C will be enabled. Steps 2. and 3. will need to be repeated each time you get a new CA certificate. These steps can be automated this way: Start Traefik with this command: ``sh -c "update-ca-certificates && traefik"`` while ``/usr/local/share/ca-certificates`` container path is mounted from the host with the CA certificate produced by this script. I am using Alpine Traefik image, the correct ca certificates path is ``/usr/local/share/ca-certificates/``, otherwise one of these https://golang.org/src/crypto/x509/root_linux.go - ``docker-compose.yml`` example with the gencert script: ``` version: '3' networks: oasis: {} services: minio: restart: unless-stopped image: minio/minio networks: - oasis volumes: - /srv/data/minio:/data - /srv/data/minio/start/gencert.sh:/gencert.sh:ro entrypoint: sh -c "cd /root/.minio/certs && /gencert.sh --cn minio.example.com && minio server /data" environment: - "MINIO_ACCESS_KEY=redacted" - "MINIO_SECRET_KEY=redacted" labels: - "traefik.enable=true" - "traefik.frontend.rule=Host: minio.example.com" - "traefik.frontend.passHostHeader=true" - "traefik.port=9000" traefik: restart: unless-stopped image: traefik:1.6-alpine volumes: - /srv/data/traefik/acme:/etc/traefik/acme - /srv/data/traefik/traefik.toml:/etc/traefik/traefik.toml:ro - /var/run/docker.sock:/var/run/docker.sock:ro # listen to the Docker events. - /srv/data/traefik/ca-certs:/usr/local/share/ca-certificates:ro command: sh -c "update-ca-certificates && traefik" networks: - oasis ports: - "127.0.0.1:8080:8080" - "80:80" - "443:443" ``` ### Drawbacks - [operational] it requires self-signed CA certs shared when running multiple Minio (or any other) servers; - [operational] every X years it requires updating the CA certificate in the Traefik's (or any other reverse proxy) container; - [security] the CA key will have to be spread all over the environment. Theoretically, this could be solved by HashiCorp's Vault, but that won't be nice & small way of running things;