Browse Source

add template, implement auth and zones

master
AJ ONeal 5 years ago
parent
commit
22c1e761e0
  1. 4
      .gitignore
  2. 1
      AUTHORS
  3. 28
      README.md
  4. 3
      example.env
  5. 3
      index.js
  6. 36
      lib/auth.js
  7. 74
      lib/index.js
  8. 37
      test.js

4
.gitignore

@ -1,3 +1,7 @@
*.json
service_account.json
credentials.json
# ---> Node
# Logs
logs

1
AUTHORS

@ -0,0 +1 @@
AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)

28
README.md

@ -1,3 +1,29 @@
# acme-dns-01-googlecloud.js
Google Cloud DNS for Let's Encrypt / ACME dns-01 challenges with ACME.js and Greenlock.js
Google Domains + Let's Encrypt for Node.js - ACME dns-01 challenges w/ ACME.js and Greenlock.js
In Progress. Would love help. Please contact @coolaj86 on Keybase.
- [x] zones
- [ ] set
- [ ] get
- [ ] remove
Implementation Details
- https://cloud.google.com/dns/docs/reference/v1/
- https://cloud.google.com/service-usage/docs/getting-started#api
- https://github.com/google/oauth2l
# Usage
First you create an instance with your credentials:
```js
var dns01 = require('acme-dns-01-googlecloud').create({
baseUrl: 'https://www.googleapis.com/dns/v1/', // default
// contains private_key, private_key_id, project_id, and client_email
serviceAccountPath: __dirname + '/service_account.json'
});
```

3
example.env

@ -0,0 +1,3 @@
# NOT credentials.json
GOOGLE_APPLICATION_CREDENTIALS=/Users/me/service_account.json
ZONE=example.co.uk

3
index.js

@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/index.js');

36
lib/auth.js

@ -0,0 +1,36 @@
'use strict';
var Keypairs = require('keypairs');
module.exports.getToken = function(serviceAccount) {
var jwt = '';
var exp = 0;
if (exp - Date.now() > 0) {
return Promise.resolve(jwt);
}
return module.exports.generateToken(serviceAccount).then(function(_jwt) {
jwt = _jwt;
exp = Math.round(Date.now()) - 15 * 60 * 60 * 1000;
return jwt;
});
};
module.exports.generateToken = function(serviceAccount) {
var sa = serviceAccount;
return Keypairs.import({ pem: sa.private_key }).then(function(key) {
return Keypairs.signJwt({
jwk: key,
iss: sa.client_email,
exp: '1h',
header: {
kid: sa.private_key_id
},
claims: {
aud: 'ndev.clouddns.readwrite',
sub: sa.client_email
}
});
});
};

74
lib/index.js

@ -0,0 +1,74 @@
'use strict';
var auth = require('./auth.js');
var defaults = {
baseUrl: 'https://www.googleapis.com/dns/v1/'
};
module.exports.create = function(config) {
var request;
var baseUrl = (config.baseUrl || defaults.baseUrl).replace(/\/$/, '');
var sa = getServiceAccount(config);
return {
init: function(opts) {
request = opts.request;
return null;
},
zones: function(data) {
//console.info('List Zones', data);
return api({
url: baseUrl + '/projects/' + sa.project_id + '/managedZones',
json: true
}).then(function(resp) {
return resp.body.managedZones.map(function(zone) {
// slice out the leading and trailing single quotes, and the trailing dot
// (assuming that all 'dnsName's probably look the same)
return zone.dnsName.slice(1, zone.dnsName.length - 2);
});
});
},
set: function(data) {
// console.info('Add TXT', data);
throw Error('setting TXT not implemented');
},
remove: function(data) {
// console.info('Remove TXT', data);
throw Error('removing TXT not implemented');
},
get: function(data) {
// console.info('List TXT', data);
throw Error('listing TXTs not implemented');
}
};
function api(opts) {
return auth.getToken(sa).then(function(token) {
opts.headers = opts.headers || {};
opts.headers.Authorization = 'Bearer ' + token;
return request(opts);
});
}
function getServiceAccount(config) {
var saPath =
config.serviceAccountPath ||
process.env.GOOGLE_APPLICATION_CREDENTIALS;
var sa = config.serviceAccount || require(saPath);
if (
!sa ||
!(
sa.private_key &&
sa.private_key_id &&
sa.client_email &&
sa.project_id
)
) {
throw new Error(
'missing or incomplete service_account.json: set serviceAccount serviceAccountPath'
);
}
return sa;
}
};

37
test.js

@ -0,0 +1,37 @@
#!/usr/bin/env node
'use strict';
// See https://git.coolaj86.com/coolaj86/acme-challenge-test.js
var tester = require('acme-challenge-test');
require('dotenv').config();
// Usage: node ./test.js example.com xxxxxxxxx
var zone = process.argv[2] || process.env.ZONE;
var config = {
serviceAccountPath:
process.argv[3] || process.env.GOOGLE_APPLICATION_CREDENTIALS
};
var challenger = require('./index.js').create(config);
// Google has its own special authentication
var sa = require(config.serviceAccountPath);
require('./lib/auth.js')
.getToken(sa)
.then(function(jwt) {
console.info('\nAuthorization: Bearer ' + jwt + '\n');
// The dry-run tests can pass on, literally, 'example.com'
// but the integration tests require that you have control over the domain
return tester
.testZone('dns-01', zone, challenger)
.then(function() {
console.info('PASS', zone);
})
.catch(function(e) {
console.error(e.message);
console.error(e.stack);
});
})
.catch(function(err) {
console.error(err.stack);
});
Loading…
Cancel
Save