mirror of
				https://git.coolaj86.com/coolaj86/greenlock-store-fs.js.git
				synced 2025-11-04 02:52:47 +00:00 
			
		
		
		
	update docs
This commit is contained in:
		
							parent
							
								
									e0a9fff07d
								
							
						
					
					
						commit
						5490a194eb
					
				
							
								
								
									
										263
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								README.md
									
									
									
									
									
								
							@ -12,125 +12,180 @@ Works with all ACME (Let's Encrypt) SSL certificate sytles:
 | 
			
		||||
 | 
			
		||||
# Usage
 | 
			
		||||
 | 
			
		||||
**Global** config:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var greenlock = require('greenlock');
 | 
			
		||||
var gl = greenlock.create({
 | 
			
		||||
  configDir: '~/.config/acme'
 | 
			
		||||
, store: require('greenlock-store-fs')
 | 
			
		||||
, approveDomains: approveDomains
 | 
			
		||||
, ...
 | 
			
		||||
greenlock.manager.defaults({
 | 
			
		||||
    store: {
 | 
			
		||||
        module: "greenlock-store-fs",
 | 
			
		||||
        basePath: "~/.config/greenlock"
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Per-site** config:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
greenlock.add({
 | 
			
		||||
    subject: "example.com",
 | 
			
		||||
    altnames: ["example.com", "www.example.com"],
 | 
			
		||||
    store: {
 | 
			
		||||
        module: "greenlock-store-fs",
 | 
			
		||||
        basePath: "~/.config/greenlock"
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# File System
 | 
			
		||||
 | 
			
		||||
The default file system layout mirrors that of le-store-certbot in order to make transitioning effortless,
 | 
			
		||||
in most situations:
 | 
			
		||||
The default file system layout mirrors that of certbot (python Let's Encrypt implementation) and
 | 
			
		||||
the prior le-store-certbot in order to make transitioning effortless.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
acme
 | 
			
		||||
├── accounts
 | 
			
		||||
│   └── acme-staging-v02.api.letsencrypt.org
 | 
			
		||||
│       └── directory
 | 
			
		||||
│           └── sites@example.com.json
 | 
			
		||||
└── live
 | 
			
		||||
    ├── example.com
 | 
			
		||||
    │   ├── bundle.pem
 | 
			
		||||
    │   ├── cert.pem
 | 
			
		||||
    │   ├── chain.pem
 | 
			
		||||
    │   ├── fullchain.pem
 | 
			
		||||
    │   └── privkey.pem
 | 
			
		||||
    └── www.example.com
 | 
			
		||||
        ├── bundle.pem
 | 
			
		||||
        ├── cert.pem
 | 
			
		||||
        ├── chain.pem
 | 
			
		||||
        ├── fullchain.pem
 | 
			
		||||
        └── privkey.pem
 | 
			
		||||
The default structure looks like this:
 | 
			
		||||
 | 
			
		||||
```txt
 | 
			
		||||
.config
 | 
			
		||||
└── greenlock
 | 
			
		||||
    ├── accounts
 | 
			
		||||
    │   └── acme-staging-v02.api.letsencrypt.org
 | 
			
		||||
    │       └── directory
 | 
			
		||||
    │           └── sites@example.com.json
 | 
			
		||||
    ├── staging
 | 
			
		||||
    │   └── (same as live)
 | 
			
		||||
    └── live
 | 
			
		||||
        ├── example.com
 | 
			
		||||
        │   ├── bundle.pem
 | 
			
		||||
        │   ├── cert.pem
 | 
			
		||||
        │   ├── chain.pem
 | 
			
		||||
        │   ├── fullchain.pem
 | 
			
		||||
        │   └── privkey.pem
 | 
			
		||||
        └── www.example.com
 | 
			
		||||
            ├── bundle.pem
 | 
			
		||||
            ├── cert.pem
 | 
			
		||||
            ├── chain.pem
 | 
			
		||||
            ├── fullchain.pem
 | 
			
		||||
            └── privkey.pem
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Wildcards & AltNames
 | 
			
		||||
# Internal Implementation Details
 | 
			
		||||
 | 
			
		||||
Working with wildcards and multiple altnames requires greenlock >= v2.7 (or v3).
 | 
			
		||||
You **DO NOT NEED TO KNOW** these details.
 | 
			
		||||
 | 
			
		||||
To do so you must return `{ subject: '...', altnames: ['...', ...] }` within the `approveDomains()` callback.
 | 
			
		||||
They're provided for the sake of understanding what happens "under the hood"
 | 
			
		||||
to help you make better choices "in the seat".
 | 
			
		||||
 | 
			
		||||
`subject` refers to "the subject of the ssl certificate" as opposed to `domain` which indicates "the domain servername
 | 
			
		||||
used in the current request". For single-domain certificates they're always the same, but for multiple-domain
 | 
			
		||||
certificates `subject` must be the name no matter what `domain` is receiving a request. `subject` is used as
 | 
			
		||||
part of the name of the file storage path where the certificate will be saved (or retrieved).
 | 
			
		||||
# Parameters
 | 
			
		||||
 | 
			
		||||
`altnames` should be the list of SubjectAlternativeNames (SANs) on the certificate.
 | 
			
		||||
The subject and the first altname must be an exact match: `subject === altnames[0]`.
 | 
			
		||||
| parameters        | example                                                  | notes            |
 | 
			
		||||
| ----------------- | -------------------------------------------------------- | ---------------- |
 | 
			
		||||
| `env`             | `staging` or `live`                                      | -                |
 | 
			
		||||
| `directoryUrl`    | `https://acme-staging-v02.api.letsencrypt.org/directory` | -                |
 | 
			
		||||
| `keypair`         | `{ privateKeyPem, privateKeyJwk }`                       |                  |
 | 
			
		||||
| `account`         | `{ id: "an-arbitrary-id" }`                              | account only     |
 | 
			
		||||
| `subscriberEmail` | `webhost@example.com`                                    | account only     |
 | 
			
		||||
| `certificate`     | `{ id: "an-arbitrary-id" }`                              | certificate only |
 | 
			
		||||
| `subject`         | `example.com`                                            | certificate only |
 | 
			
		||||
| `pems`            | `{ privkey, cert, chain, issuedAt, expiresAt }`          | certificate only |
 | 
			
		||||
 | 
			
		||||
## Simple Example
 | 
			
		||||
### Account Keypair
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
function approveDomains(opts) {
 | 
			
		||||
    // Allow only example.com and *.example.com (such as foo.example.com)
 | 
			
		||||
 | 
			
		||||
    // foo.example.com => *.example.com
 | 
			
		||||
    var wild =
 | 
			
		||||
        "*." +
 | 
			
		||||
        opts.domain
 | 
			
		||||
            .split(".")
 | 
			
		||||
            .slice(1)
 | 
			
		||||
            .join(".");
 | 
			
		||||
 | 
			
		||||
    if ("example.com" !== opts.domain && "*.example.com" !== wild) {
 | 
			
		||||
        cb(new Error(opts.domain + " is not allowed"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var result = {
 | 
			
		||||
        subject: "example.com",
 | 
			
		||||
        altnames: ["example.com", "*.example.com"]
 | 
			
		||||
    };
 | 
			
		||||
    return Promise.resolve(result);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Realistic Example
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
function approveDomains(opts, certs, cb) {
 | 
			
		||||
    var related = getRelated(opts.domain);
 | 
			
		||||
    if (!related) {
 | 
			
		||||
        cb(new Error(opts.domain + " is not allowed"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    opts.subject = related.subject;
 | 
			
		||||
    opts.domains = related.domains;
 | 
			
		||||
 | 
			
		||||
    cb({ options: opts, certs: certs });
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
function getRelated(domain) {
 | 
			
		||||
    var related;
 | 
			
		||||
    var wild =
 | 
			
		||||
        "*." +
 | 
			
		||||
        domain
 | 
			
		||||
            .split(".")
 | 
			
		||||
            .slice(1)
 | 
			
		||||
            .join(".");
 | 
			
		||||
    if (
 | 
			
		||||
        Object.keys(allAllowedDomains).some(function(k) {
 | 
			
		||||
            return allAllowedDomains[k].some(function(name) {
 | 
			
		||||
                if (domain === name || wild === name) {
 | 
			
		||||
                    related = { subject: k, altnames: allAllowedDomains[k] };
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
    ) {
 | 
			
		||||
        return related;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
var allAllowedDomains = {
 | 
			
		||||
    "example.com": ["example.com", "*.example.com"],
 | 
			
		||||
    "example.net": ["example.net", "*.example.net"]
 | 
			
		||||
accounts.setKeypair = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    email,
 | 
			
		||||
    account
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    var serverDir = directoryUrl.replace("https://", "");
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
accounts.checkKeypair = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    email,
 | 
			
		||||
    account
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    var serverDir = directoryUrl.replace("https://", "");
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        privateKeyPem,
 | 
			
		||||
        privateKeyJwk
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Certificate Keypair
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
certificate.setKeypair = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    subject,
 | 
			
		||||
    certificate
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    env = env || directoryUrl.replace("https://", "");
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
certificate.checkKeypair = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    subject,
 | 
			
		||||
    certificate
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    env = env || directoryUrl.replace("https://", "");
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        privateKeyPem,
 | 
			
		||||
        privateKeyJwk
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Certificate PEMs
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
certificate.set = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    subject,
 | 
			
		||||
    certificate,
 | 
			
		||||
    pems
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    env = env || directoryUrl.replace("https://", "");
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
certificate.check = async function({
 | 
			
		||||
    env,
 | 
			
		||||
    basePath,
 | 
			
		||||
    directoryUrl,
 | 
			
		||||
    subject,
 | 
			
		||||
    certificate
 | 
			
		||||
}) {
 | 
			
		||||
    var id = account.id || email;
 | 
			
		||||
    env = env || directoryUrl.replace("https://", "");
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        privkey,
 | 
			
		||||
        cert,
 | 
			
		||||
        chain,
 | 
			
		||||
        issuedAt,
 | 
			
		||||
        expiresAt
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ var mkdirpAsync = PromiseA.promisify(require("@root/mkdirp"));
 | 
			
		||||
// Use certificate.id, or subject, if id hasn't been set, to find a certificate.
 | 
			
		||||
// Return an object with string PEMs for cert and chain (or null, not undefined)
 | 
			
		||||
certificates.check = function(opts) {
 | 
			
		||||
    // { certificate.id, subject, ... }
 | 
			
		||||
    // { directoryUrl, subject, certificate.id, ... }
 | 
			
		||||
    var id = (opts.certificate && opts.certificate.id) || opts.subject;
 | 
			
		||||
    //console.log('certificates.check for', opts);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "greenlock-store-fs",
 | 
			
		||||
    "version": "3.2.1",
 | 
			
		||||
    "version": "3.2.2",
 | 
			
		||||
    "description": "A file-based certificate store for greenlock that supports wildcards.",
 | 
			
		||||
    "homepage": "https://git.rootprojects.org/root/greenlock-store-fs.js",
 | 
			
		||||
    "main": "index.js",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user