fix(core): update
This commit is contained in:
		
							
								
								
									
										34
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								README.md
									
									
									
									
									
								
							| @@ -25,9 +25,39 @@ acme implementation in TypeScript | ||||
|  | ||||
| Use TypeScript for best in class instellisense. | ||||
|  | ||||
| For further information read the linked docs at the top of this README. | ||||
| ```javascript | ||||
| import { SmartAcme } from 'smartacme'; | ||||
|  | ||||
| let smac = new SmartAcme()(async () => { | ||||
|   // learn async/await, it'll make your life easier | ||||
|  | ||||
|   // optionally accepts a filePath Arg with a stored acmeaccount.json | ||||
|   // will create an account and | ||||
|   let myAccount = await smac.createAcmeAccount(); | ||||
|  | ||||
|   // will return a dnsHash to set in your DNS record | ||||
|   let myCert = await myAccount.createAcmeCert('example.com'); | ||||
|  | ||||
|   // gets and accepts the specified challenge | ||||
|   // first argument optional, defaults to dns-01 (which is the cleanest method for production use) | ||||
|   let myChallenge = await myCert.getChallenge('dns-01'); | ||||
|  | ||||
|   /* ---------- | ||||
|     Now you need to set the challenge in your DNS | ||||
|     myChallenge.domainNamePrefixed is the address for the record | ||||
|     myChallenge.dnsKeyHash is the ready to use txt record value expected by letsencrypt | ||||
|     -------------*/ | ||||
| })(); | ||||
| ``` | ||||
|  | ||||
| ## Other relevant npm modules | ||||
|  | ||||
| | module name | description                                                         | | ||||
| | ----------- | ------------------------------------------------------------------- | | ||||
| | cert        | a higlevel production module that uses smartacme to manage certs    | | ||||
| | smartnginx  | a highlevel production tool for docker environments to manage nginx | | ||||
|  | ||||
| > MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) | ||||
| > | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html) | ||||
|  | ||||
| [](https://umbrella.zone) | ||||
| [](https://umbrella.zone | ||||
|   | ||||
| @@ -1,63 +0,0 @@ | ||||
| # smartacme | ||||
|  | ||||
| acme implementation in TypeScript | ||||
|  | ||||
| ## Availabililty | ||||
|  | ||||
| [](https://www.npmjs.com/package/smartacme) | ||||
| [](https://GitLab.com/umbrellazone/smartacme) | ||||
| [](https://github.com/umbrellazone/smartacme) | ||||
| [](https://umbrellazone.gitlab.io/smartacme/) | ||||
|  | ||||
| ## Status for master | ||||
|  | ||||
| [](https://GitLab.com/umbrellazone/smartacme/commits/master) | ||||
| [](https://GitLab.com/umbrellazone/smartacme/commits/master) | ||||
| [](https://www.npmjs.com/package/smartacme) | ||||
| [](https://david-dm.org/umbrellazone/smartacme) | ||||
| [](https://www.bithound.io/github/umbrellazone/smartacme/master/dependencies/npm) | ||||
| [](https://www.bithound.io/github/umbrellazone/smartacme) | ||||
| [](https://nodejs.org/dist/latest-v6.x/docs/api/) | ||||
| [](https://nodejs.org/dist/latest-v6.x/docs/api/) | ||||
| [](http://standardjs.com/) | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| Use TypeScript for best in class instellisense. | ||||
|  | ||||
| ```javascript | ||||
| import { SmartAcme } from 'smartacme'; | ||||
|  | ||||
| let smac = new SmartAcme()(async () => { | ||||
|   // learn async/await, it'll make your life easier | ||||
|  | ||||
|   // optionally accepts a filePath Arg with a stored acmeaccount.json | ||||
|   // will create an account and | ||||
|   let myAccount = await smac.createAcmeAccount(); | ||||
|  | ||||
|   // will return a dnsHash to set in your DNS record | ||||
|   let myCert = await myAccount.createAcmeCert('example.com'); | ||||
|  | ||||
|   // gets and accepts the specified challenge | ||||
|   // first argument optional, defaults to dns-01 (which is the cleanest method for production use) | ||||
|   let myChallenge = await myCert.getChallenge('dns-01'); | ||||
|  | ||||
|   /* ---------- | ||||
|     Now you need to set the challenge in your DNS | ||||
|     myChallenge.domainNamePrefixed is the address for the record | ||||
|     myChallenge.dnsKeyHash is the ready to use txt record value expected by letsencrypt | ||||
|     -------------*/ | ||||
| })(); | ||||
| ``` | ||||
|  | ||||
| ## Other relevant npm modules | ||||
|  | ||||
| | module name | description                                                         | | ||||
| | ----------- | ------------------------------------------------------------------- | | ||||
| | cert        | a higlevel production module that uses smartacme to manage certs    | | ||||
| | smartnginx  | a highlevel production tool for docker environments to manage nginx | | ||||
|  | ||||
| > MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) | ||||
| > | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html) | ||||
|  | ||||
| [](https://umbrella.zone | ||||
							
								
								
									
										1513
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1513
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										19
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								package.json
									
									
									
									
									
								
							| @@ -27,16 +27,17 @@ | ||||
|   "dependencies": { | ||||
|     "@pushrocks/smartdelay": "^2.0.2", | ||||
|     "@pushrocks/smartpromise": "^2.0.5", | ||||
|     "acme-v2": "^1.2.1", | ||||
|     "rsa-compat": "^1.6.0" | ||||
|     "acme-client": "^2.2.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@gitzone/tsbuild": "^2.0.22", | ||||
|     "@gitzone/tsrun": "^1.1.12", | ||||
|     "@gitzone/tstest": "^1.0.15", | ||||
|     "@types/node": "^10.11.4", | ||||
|     "cflare": "^1.0.5", | ||||
|     "qenv": "^1.1.7", | ||||
|     "tapbundle": "^2.0.2" | ||||
|     "@gitzone/tsbuild": "^2.1.4", | ||||
|     "@gitzone/tsrun": "^1.1.17", | ||||
|     "@gitzone/tstest": "^1.0.18", | ||||
|     "@mojoio/cloudflare": "^2.0.0", | ||||
|     "@pushrocks/qenv": "^3.0.2", | ||||
|     "@pushrocks/tapbundle": "^3.0.7", | ||||
|     "@types/node": "^10.12.18", | ||||
|     "tslint": "^5.12.0", | ||||
|     "tslint-config-prettier": "^1.17.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								test/test.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								test/test.ts
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| import { tap, expect } from 'tapbundle'; | ||||
| import { tap, expect } from '@pushrocks/tapbundle'; | ||||
|  | ||||
| import * as smartacme from '../ts/index'; | ||||
|  | ||||
| @@ -6,8 +6,16 @@ let smartAcmeInstance: smartacme.SmartAcme; | ||||
|  | ||||
| tap.test('should create a valid instance of SmartAcme', async () => { | ||||
|   smartAcmeInstance = new smartacme.SmartAcme(); | ||||
|   await smartAcmeInstance.init(); | ||||
|   console.log(smartAcmeInstance.directoryUrls); | ||||
|   await smartAcmeInstance.init({ | ||||
|     accountEmail: 'domains@lossless.org', | ||||
|     accountPrivateKey: null, | ||||
|     removeChallenge: async (...args) => { | ||||
|       console.log(args); | ||||
|     }, | ||||
|     setChallenge: async (...args) => { | ||||
|       console.log(args); | ||||
|     }  | ||||
|   }); | ||||
|   await smartAcmeInstance.getCertificateForDomain('bleu.de'); | ||||
| }); | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								ts/interfaces/accountdata.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ts/interfaces/accountdata.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| export interface IAccountData { | ||||
|   id: number; | ||||
|   key: { kty: 'RSA'; n: string; e: string; kid: string }; | ||||
|   contact: string[]; | ||||
|   initialIp: string; | ||||
|   createdAt: string; | ||||
|   status: string; | ||||
| } | ||||
							
								
								
									
										1
									
								
								ts/interfaces/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ts/interfaces/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './accountdata'; | ||||
| @@ -1,27 +0,0 @@ | ||||
| import * as plugins from './smartacme.plugins'; | ||||
| const rsa = require('rsa-compat').RSA; | ||||
|  | ||||
| export class KeyPair { | ||||
|   rsaKeyPair: any; | ||||
|  | ||||
|   /** | ||||
|    * generates a fresh rsa keyPair | ||||
|    */ | ||||
|   static async generateFresh(): Promise<KeyPair> { | ||||
|     const done = plugins.smartpromise.defer(); | ||||
|     var options = { bitlen: 2048, exp: 65537, public: true, pem: true, internal: true }; | ||||
|     rsa.generateKeypair(options, function(err, keypair) { | ||||
|       if (err) { | ||||
|         console.log(err); | ||||
|       } | ||||
|       done.resolve(keypair); | ||||
|     }); | ||||
|     const result: any = await done.promise; | ||||
|     const keyPair = new KeyPair(result); | ||||
|     return keyPair; | ||||
|   } | ||||
|  | ||||
|   constructor(rsaKeyPairArg) { | ||||
|     this.rsaKeyPair = rsaKeyPairArg; | ||||
|   } | ||||
| } | ||||
| @@ -1,86 +1,96 @@ | ||||
| const acme = require('acme-v2').ACME.create({ | ||||
|   RSA: require('rsa-compat').RSA, | ||||
|  | ||||
|   // used for constructing user-agent | ||||
|   os: require('os'), | ||||
|   process: require('process'), | ||||
|  | ||||
|   // used for overriding the default user-agent | ||||
|   userAgent: 'My custom UA String', | ||||
|   getUserAgentString: function(deps) { | ||||
|     return 'My custom UA String'; | ||||
|   }, | ||||
|  | ||||
|   // don't try to validate challenges locally | ||||
|   skipChallengeTest: true | ||||
| }); | ||||
|  | ||||
| import { KeyPair } from './smartacme.classes.keypair'; | ||||
| import * as plugins from './smartacme.plugins'; | ||||
| const rsa = require('rsa-compat').RSA; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  */ | ||||
| export interface ISmartAcmeStorage {} | ||||
|  | ||||
| export class SmartAcme { | ||||
|   domainKeyPair: KeyPair; | ||||
|   accountKeyPair: KeyPair; | ||||
|   accountData: any; | ||||
|   directoryUrls: any; | ||||
|   // the acme client | ||||
|   private client: any; | ||||
|  | ||||
|   async init() { | ||||
|     // get directory url | ||||
|     this.directoryUrls = await acme.init('https://acme-staging-v02.api.letsencrypt.org/directory'); | ||||
|   // the account private key | ||||
|   private privateKey: string; | ||||
|  | ||||
|     // create keyPairs | ||||
|     this.domainKeyPair = await KeyPair.generateFresh(); | ||||
|     this.accountKeyPair = await KeyPair.generateFresh(); | ||||
|   // challenge fullfillment | ||||
|   private setChallenge: (authz, challenge, keyAuthorization) => Promise<any>; | ||||
|   private removeChallenge: (authz, challenge, keyAuthorization) => Promise<any>; | ||||
|  | ||||
|     // get account | ||||
|     const registrationData = await acme.accounts | ||||
|       .create({ | ||||
|         email: 'domains@lossless.org', // valid email (server checks MX records) | ||||
|         accountKeypair: this.accountKeyPair.rsaKeyPair, | ||||
|         agreeToTerms: async tosUrl => { | ||||
|           return tosUrl; | ||||
|         } | ||||
|       }) | ||||
|       .catch(e => { | ||||
|         console.log(e); | ||||
|   public async init(optionsArg: { | ||||
|     accountPrivateKey?: string; | ||||
|     accountEmail: string; | ||||
|     setChallenge: (authz, challenge, keyAuthorization) => Promise<any>; | ||||
|     removeChallenge: (authz, challenge, keyAuthorization) => Promise<any>; | ||||
|   }) { | ||||
|     this.privateKey = optionsArg.accountPrivateKey || (await plugins.acme.forge.createPrivateKey()); | ||||
|     this.setChallenge = optionsArg.setChallenge; | ||||
|     this.removeChallenge = optionsArg.removeChallenge; | ||||
|     this.client = new plugins.acme.Client({ | ||||
|       directoryUrl: plugins.acme.directory.letsencrypt.staging, | ||||
|       accountKey: this.privateKey | ||||
|     }); | ||||
|  | ||||
|     /* Register account */ | ||||
|     await this.client.createAccount({ | ||||
|       termsOfServiceAgreed: true, | ||||
|       contact: [`mailto:${optionsArg.accountEmail}`] | ||||
|     }); | ||||
|     this.accountData = registrationData; | ||||
|   } | ||||
|  | ||||
|   async getCertificateForDomain(domain) { | ||||
|     const result = await acme.certificates | ||||
|       .create({ | ||||
|         domainKeypair: this.domainKeyPair.rsaKeyPair, | ||||
|         accountKeypair: this.accountKeyPair.rsaKeyPair, | ||||
|         domains: ['bleu.de'], | ||||
|         challengeType: 'dns-01', | ||||
|   public async getCertificateForDomain(domainArg: string) { | ||||
|     const domain = domainArg; | ||||
|  | ||||
|         setChallenge: async (hostname, key, val, cb) => { | ||||
|           console.log('set challenge'); | ||||
|           console.log(hostname); | ||||
|           //console.log(key); | ||||
|           //console.log(val); | ||||
|           const dnsKey = rsa.utils.toWebsafeBase64( | ||||
|             require('crypto') | ||||
|               .createHash('sha256') | ||||
|               .update(val) | ||||
|               .digest('base64') | ||||
|           ); | ||||
|     /* Place new order */ | ||||
|     const order = await this.client.createOrder({ | ||||
|       identifiers: [{ type: 'dns', value: domain }, { type: 'dns', value: `*.${domain}` }] | ||||
|     }); | ||||
|  | ||||
|           console.log(dnsKey); | ||||
|           await plugins.smartdelay.delayFor(20000); | ||||
|           console.log('ready!'); | ||||
|           cb(); | ||||
|         }, // return Promise | ||||
|         removeChallenge: async (hostname, key) => { | ||||
|           console.log('removing challenge'); | ||||
|           return; | ||||
|         } // return Promise | ||||
|       }) | ||||
|       .catch(e => { | ||||
|     /* Get authorizations and select challenges */ | ||||
|     const authorizations = await this.client.getAuthorizations(order); | ||||
|  | ||||
|     const promises = authorizations.map(async authz => { | ||||
|       const challenge = authz.challenges.pop(); | ||||
|       const keyAuthorization = await this.client.getChallengeKeyAuthorization(challenge); | ||||
|  | ||||
|       try { | ||||
|         /* Satisfy challenge */ | ||||
|         await this.setChallenge(authz, challenge, keyAuthorization); | ||||
|  | ||||
|         /* Verify that challenge is satisfied */ | ||||
|         await this.client.verifyChallenge(authz, challenge); | ||||
|  | ||||
|         /* Notify ACME provider that challenge is satisfied */ | ||||
|         await this.client.completeChallenge(challenge); | ||||
|  | ||||
|         /* Wait for ACME provider to respond with valid status */ | ||||
|         await this.client.waitForValidStatus(challenge); | ||||
|       } finally { | ||||
|         /* Clean up challenge response */ | ||||
|         try { | ||||
|           await this.removeChallenge(authz, challenge, keyAuthorization); | ||||
|         } catch (e) { | ||||
|           console.log(e); | ||||
|       }); // returns Promise<pems={ privkey (key), cert, chain (ca) }> | ||||
|     console.log(result); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     /* Wait for challenges to complete */ | ||||
|     await Promise.all(promises); | ||||
|  | ||||
|     /* Finalize order */ | ||||
|     const [key, csr] = await plugins.acme.forge.createCsr({ | ||||
|       commonName: `*.${domain}`, | ||||
|       altNames: [domain] | ||||
|     }); | ||||
|  | ||||
|     await this.client.finalizeOrder(order, csr); | ||||
|     const cert = await this.client.getCertificate(order); | ||||
|  | ||||
|     /* Done */ | ||||
|     console.log(`CSR:\n${csr.toString()}`); | ||||
|     console.log(`Private key:\n${key.toString()}`); | ||||
|     console.log(`Certificate:\n${cert.toString()}`); | ||||
|   } | ||||
|  | ||||
|   toStorageObject () {}; | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,10 @@ | ||||
| // @pushrocks scope | ||||
| import * as smartpromise from '@pushrocks/smartpromise'; | ||||
| import * as smartdelay from '@pushrocks/smartdelay'; | ||||
|  | ||||
| export { smartpromise, smartdelay }; | ||||
|  | ||||
| // thirs party scope | ||||
| import * as acme from 'acme-client'; | ||||
|  | ||||
| export { acme }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user