Skip to content

Instantly share code, notes, and snippets.

@achingbrain
Last active March 31, 2021 16:03
Show Gist options
  • Save achingbrain/8d9a00190f2e13182579d26d138f5b4a to your computer and use it in GitHub Desktop.
Save achingbrain/8d9a00190f2e13182579d26d138f5b4a to your computer and use it in GitHub Desktop.
How to get an SSL cert for our home internet connection
const NatAPI = require('nat-api')
const acme = require('acme-client')
const { promisify } = require('util')
const dns = require('dns')
const http = require('http')
const nat = new NatAPI()
const externalIp = promisify(nat.externalIp.bind(nat))
const mapPort = promisify(nat.map.bind(nat))
const reverseLookup = promisify(dns.reverse.bind(dns))
// Uses UPnP to figure out our external IP address, then does a reverse DNS lookup
// on that IP to get our ISP-assigned a domain name, then uses Let's Encrypt to
// create an SSL certificate for that domain name. We can then use that SSL certificate
// elsewhere to create a WSS transport, add that address to our Announce list and
// make the network more usable by browser IPFS nodes.
async function main () {
const accountEmail = 'herp@derp.com'
const accountPrivateKey = await acme.forge.createPrivateKey()
// Get our external IP
const external = await externalIp()
// Do a reverse DNS lookup
const [ domain ] = await reverseLookup(external)
// High local port to listen on
const PORT = 44032
// Forward port 80 on our router to high local port
await mapPort({
publicPort: 80,
privatePort: PORT,
ttl: 1800,
protocol: 'TCP'
})
// Init client
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.production,
accountKey: accountPrivateKey
})
// Create private key for CSR
const certificatePrivateKey = await acme.forge.createPrivateKey()
// Create CSR
const [key, csr] = await acme.forge.createCsr({
commonName: domain,
altNames: []
}, certificatePrivateKey)
// Will respond to auth challenge
let server
// Certificate
const cert = await client.auto({
csr,
email: accountEmail,
termsOfServiceAgreed: true,
challengePriority: ['http-01'],
skipChallengeVerification: true,
async challengeCreateFn (authz, challenge, keyAuthorization) {
server = http.createServer((req, res)=> {
// Respond to challenge
if (req.url === `/.well-known/acme-challenge/${challenge.token}`) {
res.writeHead(200, {'Content-Type': 'application/octet-stream'})
res.end(keyAuthorization)
return
}
// Ignore everything else
res.writeHead(404)
res.end()
})
return new Promise((resolve) => {
server.listen({
port: PORT
}, () => {
resolve()
})
})
},
async challengeRemoveFn () {
server.close()
}
})
// `cert` is a pem-format certificate chain - use it to create WSS transport
console.info(cert)
// Remove router port mapping
nat.destroy()
}
main().catch(err => {
console.error(err)
process.exit(1)
})
@achingbrain
Copy link
Author

  1. Create a directory
  2. Install deps:
    npm install nat-api acme-client
  3. Copy this gist into index.js
  4. Run the file:
    node .
  5. Wait 30-60 seconds
  6. See box-fresh SSL certificate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment