AJ ONeal
5 years ago
8 changed files with 185 additions and 1 deletions
@ -0,0 +1 @@ |
|||
AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/) |
@ -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' |
|||
}); |
|||
``` |
|||
|
@ -0,0 +1,3 @@ |
|||
# NOT credentials.json |
|||
GOOGLE_APPLICATION_CREDENTIALS=/Users/me/service_account.json |
|||
ZONE=example.co.uk |
@ -0,0 +1,3 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = require('./lib/index.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 |
|||
} |
|||
}); |
|||
}); |
|||
}; |
@ -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; |
|||
} |
|||
}; |
@ -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…
Reference in new issue