2017 © Pedro Peláez
 

library symfony-bundle

AcmePhpBundle

image

acmephp/symfony-bundle

AcmePhpBundle

  • Thursday, March 24, 2016
  • by tgalopin
  • Repository
  • 2 Watchers
  • 26 Stars
  • 101 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 3 Forks
  • 3 Open issues
  • 1 Versions
  • 3 % Grown

The README.md

Acme PHP Symfony Bundle

Note this bundle is in active development (it is not ready for the moment)., (*1)

The ACME protocol is a protocol defined by the Let's Encrypt Certificate Authority. You can see the complete specification on https://letsencrypt.github.io/acme-spec/., (*2)

The ACME PHP project aims to implement the ACME protocol in PHP to be able to use it easily in various projects., (*3)

This repository is the Symfony bundle based on the PHP library., (*4)

Installation

Step 1: Download AcmePhpBundle using composer

Require the acmephp/symfony-bundle with composer Composer., (*5)

$ composer require acmephp/symfony-bundle

Step 2: Enable the bundle

Enable the bundle in the kernel:, (*6)

<?php

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new AcmePhp\Bundle\AcmePhpBundle(),
        // ...
    );
}

Step 3: Configure the AcmePhpBundle

Below is a minimal example of the configuration necessary to use the AcmePhpBundle in your application:, (*7)

# app/config/config.yml

acme_php:
    contact_email: contact@mycompany.com
    default_distinguished_name:
        country: FR
        state: France
        locality: Paris
        organization_name: MyCompany
        organization_unit_name: IT
    domains:
        myapp.com: ~

Step 4: Import AcmePhpBundle routing files

Now that you have activated and configured the bundle, all that is left to do is import the AcmePhpBundle routing files., (*8)

# app/config/routing.yml

acme_php:
    resource: "@AcmePhpBundle/Resources/config/routing.xml"

Usage

Once the bundle is installed and configured, you can request your certificates by runnig the command acmephp:generate, (*9)

$ ./bin/console acmephp:generate

The first time you run this command, AmePhpBundle will request a new certificate to the configured Certificate Autority (default Letsencrypt) and store the generated certificate in the configured folder (default ~/.acmephp)., (*10)

Automatic renewal

Each time you run the command acmephp:generate the certificate will be renew with a lifetime of 90 days (defined by the Certificate Autority). You can add a crontab to perform this task., (*11)

$ crontab -e

0 0     1 * *     /var/www/my_app/bin/console acmephp:generate

After regenerating a certificate you have to reload the web server to take the changes into account., (*12)

If you use a dedicated cron file in /etc/cron.d/ be carrefull of the certificate storage location (configured by default in the $HOME directory) which is related to the user who run the command., (*13)

Configuration reference

# app/config/config.yml

acme_php:
    # Certificates locations. Default: `~/.acmephp`
    # Beware to use a directory readable by the web server
    # It should be writable too, to store certificates, keys and challenges.
    certificate_dir: ~/.acmephp

    # Certificate Authority used to deliver certificates. Default: `letsencrypt`. Available values : `letsencrypt`
    # You can use your own Certificate Authority by :
    #  - implementing the CertificateAuthorityConfigurationInterface interface
    #  - registering the service with the tag "acme_php.certificate_authority" and with an alias to use here
    certificate_authority: letsencrypt

    # Email addresse associated to the account used to generate certificate
    contact_email: contact@mycompany.com

    # Default Distinguished Name (or a DN) informations used to request certificates.
    default_distinguished_name: # https://scotthelme.co.uk/setting-up-le/
        # Country Name (2 letter code)
        country: FR

        # State or Province Name (full name)
        state: France

        # Locality Name (eg, city)
        locality: Paris

        # Organization Name (eg, company)
        organization_name: MyCompany

        # Organizational Unit Name (eg, section)
        organization_unit_name: IT

        # Email Address. When missing, the adresse defined in the parameter contact_email will be used
        email_address: john.doe@mycompany.com

    # List of domains to request
    domains:

        myapp.com: ~

        www.myapp.com: ~

        invoice.myapp.com:
            # You can override default distinguished name define above for each domain
            organization_unit_name: sales

Usage

You can both, request and renew a certificate with the single commande, (*14)

$ bin/console acmephp:generate

The certificates will be stored in the folder defined by the parameter certificate_dir., (*15)

$ tree ~/.acmephp
├── account                   # Your account's keys
│   ├── private.pem
│   └── public.pem
├── challenges                # Pending challenges
├── domains                   # Contains all domain's certificate (1 sub directory per domain)
│   ├── company.com           # Contains the certificates for domain `company.com`
│   │   ├── cert.pem          # Server certificate only
│   │   ├── chain.pem         # All certificates that need to be served by the browser **excluding** server certificate
│   │   ├── combined.pem      # All certificates plus private key
│   │   ├── fullchain.pem     # All certificates, **including** server certificate
│   │   ├── private.pem       # Private key for the certificate
│   │   └── public.pem        # Public key for the certificate
│   └── www.company.com
│       └── ...
└── domains-backup            # Previous versions of the certificates
    ├── company.com           # domains as subdirectory
    │   └── 20160314-144416   # each subdirectory is a backup
    │       └── ...
    └── www.company.com
        └── ...

Monitoring

If your application use monolog (which should be the case by default), Acme PHP Symfony Bundle will log in a acme_php channel. By this way, you can handle logs and being notified on renewal failure., (*16)

Here is a sample of configuration, (*17)

# app/config/config.yml

monolog:
    handlers:
        certificate_slack:
            type: slack
            token: # Your slack's token : https://api.slack.com/web
            channel: "#production" # name of the slack's channel
            bot_name: CertificatesBot
            icon_emoji: lock_with_ink_pen
            level: NOTICE
            channels: [acme_php]

Extensions

Certificate Authority: You can add your own certificate authority by implementing the interface AcmePhp\Bundle\Acme\CertificateAuthority\Configuration\CertificateAuthorityConfigurationInterface and adding the service with tag acme_php.certificate_authority as follow :, (*18)

# app/config/services.yml

services:
    app.custom_certificate_authority:
        class: AppBundle\Acme\CustomConfiguration
        public: false
        tags:
            - name: acme_php.certificate_authority
              alias: custom

You just have to reference it in your configuration, (*19)

# app/config/config.yml

acme_php:
    certificate_authority: custom

Domain Loader: By default, the bundle loads domain's configurations through the config's file. But, you can add your own loader by implementing the interface AcmePhp\Bundle\Acme\Domain\Loader\LoaderInterface and adding the service with tag acme_php.domains_configurations_loader as follow:, (*20)

# app/config/services.yml

services:
    app.custom_certificate_authority:
        class: AppBundle\Acme\CustomLoader
        public: false
        tags:
            - name: acme_php.domains_configurations_loader

Certificate Formatter: When a new certificate is requested, it is dumped in several formats (cert.pem, chain.pem, combined.pem, ...). You can add your own formatter by implementing the interface AcmePhp\Bundle\Acme\Certificate\Formatter\FormatterInterface and adding the service with tag acme_php.certificate_formatter as follow:, (*21)

# app/config/services.yml

services:
    app.custom_certificate_authority:
        class: AppBundle\Acme\CustomFormatter
        public: false
        tags:
            - name: acme_php.certificate_formatter

Events: AcmePhpBundle triggers many events:, (*22)

  • CERTIFICATE_REQUESTED: When a certificate is requested on renewed
  • CHALLENGE_REQUESTED: When a new challenge is requested
  • CHALLENGE_CHECKED: When the challenge is checked (whether or not it have been accepted)
  • CHALLENGE_ACCEPTED: When the challenge have been accepted
  • CHALLENGE_REJECTED: When the challenge have been rejected

WebServer Configuration sample

Here are some basic configurations for the common web servers., (*23)

Mozilla provide a tool to generate more advance configuration: https://mozilla.github.io/server-side-tls/ssl-config-generator/, (*24)

You can also check your online certificate with this tool: https://www.ssllabs.com/ssltest/, (*25)

 Apache 2.2

# /etc/apache2/sites-available/<domain>.

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile      /var/www/.acmephp/domains/<domain>/cert.pem
    SSLCertificateKeyFile   /var/www/.acmephp/domains/<domain>/private.pem
    SSLCACertificateFile    /var/www/.acmephp/domains/<domain>/chain.pem

    ...
</VirtualHost>
$ sudo service apache2 reload

 Apache 2.4

# /etc/apache2/sites-available/<domain>.

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile      /var/www/.acmephp/domains/<domain>/fullchain.pem
    SSLCertificateKeyFile   /var/www/.acmephp/domains/<domain>/private.pem

    ...
</VirtualHost>
$ sudo service apache2 reload

 Nginx

# /etc/nginx/sites-available/<domain>.

server {
    listen 443 ssl default_server;
    server_name my-domain;

    ssl_certificate       /var/www/.acmephp/domains/<domain>/fullchain.pem
    ssl_certificate_key   /var/www/.acmephp/domains/<domain>/private.pem

    ...
}
$ sudo service nginx reload

haproxy

# /etc/haproxy/haproxy.cfg

frontend www
    bind :80
    bind :443 ssl crt /var/www/.acmephp/domains/<domain>/combined.pem

    ...
$ sudo service haproxy reload

Contributing

Unit tests, (*26)

composer install

./bin/phpunit

Functionnal testing, (*27)

composer install

docker run -d --net host acmephp/testing-ca
docker run --rm --net host martin/wait -c localhost:4000 -t 120
features/fixtures/TestApp/console acmephp:server:start

./bin/behat

features/fixtures/TestApp/console acmephp:server:stop

Manual testing, (*28)

Because Letsencrypt has a rate limiting, We recommends to use Boulder as Certificate Authority. Which is include and fully package in the docker image acmephp/testing-ca, (*29)

You'll find a micro symfony application in the folder features/fixtures/TestApp., (*30)

It allow you to easily test the application. You just have to edit the config file in features/fixtures/TestApp/config/config.yml. Then start Boulder and the symfony's server (to handle challenge requests)., (*31)

composer install

# Launch boulder
docker run -d --net host acmephp/testing-ca
docker run --rm --net host martin/wait -c localhost:4000 -t 120

# Launche the application to listen to challenge checks
features/fixtures/TestApp/console acmephp:server:start

# Generate the certificate
features/fixtures/TestApp/console acmephp:generate

ls -al features/fixtures/TestApp/certs/domains/*/

The Versions