Wireguard tools for Nodejs
This lib includes a class and set of helper functions for working with WireGuard config files in javascript/typescript.
100% Typescript!
Check out the docs with 💖 from typedoc:
https://guardline-vpn.github.io/wireguard-tools/
To use
npm i wireguard-tools
or
yarn add wireguard-tools
Basic config
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig({
wgInterface: { address: ['10.10.1.1'] },
filePath
})
await config1.generateKeys()
Generate Keys
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig({
wgInterface: {
address: ['10.10.1.1']
},
filePath
})
const { publicKey, preSharedKey, privateKey } = await config1.generateKeys({ preSharedKey: true })
Write to disk
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig({
wgInterface: {
address: ['10.10.1.1']
},
filePath
})
await config1.generateKeys()
await config1.writeToFile()
Parse config from disk
import path from 'path'
import { WgConfig, getConfigObjectFromFile } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const thatConfigFromFile = await getConfigObjectFromFile({ filePath })
const config1 = new WgConfig({
...thatConfigFromFile,
filePath
})
const config2 = new WgConfig({ filePath })
await config2.parseFile()
const config3 = new WgConfig()
await config3.parseFile(filePath)
await config1.generateKeys()
Bring up /bring down a WgConfig as a WireGuard interface
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig({
wgInterface: {
address: ['10.10.1.1']
},
filePath
})
await config1.generateKeys()
await config1.writeToFile()
await config1.up()
await config1.down()
To change a WgConfig while up, you need to restart
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig()
await config1.parseFile(filePath)
await config1.generateKeys()
await config1.up()
await config1.generateKeys({ overwrite: true })
config1.wgInterface.name = 'new-name'
await config1.writeToFile()
await config1.restart()
Or use the save() shorcut method to write and restart
Note, using this method will start the WireGuard interface if it's down unless { noUp: true }
is passed in.
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const config1 = new WgConfig()
await config1.parseFile(filePath)
await config1.generateKeys()
await config1.up()
await config1.generateKeys({ overwrite: true })
config1.wgInterface.name = 'new-name'
await config1.save()
Generate and add peers from and to configs
import path from 'path'
import { WgConfig } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const filePath2 = path.join(__dirname, '/configs', '/guardline-client.conf')
const server = new WgConfig({
wgInterface: { address: ['10.10.1.1'] },
filePath
})
const client = new WgConfig({
wgInterface: { address: ['10.10.1.2'] },
filePath: filePath2
})
await Promise.all([
server.generateKeys({ preSharedKey: true }),
client.generateKeys({ preSharedKey: true })
])
const serverAsPeer = server.createPeer({
allowedIps: ['10.1.1.1/32'],
preSharedKey: server.preSharedKey
})
client.addPeer(serverAsPeer)
server.addPeer(client.createPeer({
allowedIps: ['10.10.1.1/32'],
preSharedKey: client.preSharedKey
}))
Or use the createPeerPairs tool to do this with ease
import path from 'path'
import { WgConfig, createPeerPairs } from 'wireguard-tools'
let configs: WgConfig[] = []
for (let i = 1; i <= 10; i++) {
configs.push(new WgConfig({
wgInterface: {
address: [`10.10.1.${i}`],
name: `Client-${i}`
},
filePath: path.join(__dirname, '/configs', `/guardline-${i}.conf`)
}))
}
await Promise.all(configs.map(x => x.generateKeys()))
createPeerPairs(configs.map(x => {
return {
config: x,
peerSettings: {
allowedIps: ['10.10.1.1/32']
}
}
}))
await Promise.all(configs.map(x => x.writeToFile()))
Or more advanced
import path from 'path'
import { WgConfig, createPeerPairs } from 'wireguard-tools'
let configs: WgConfig[] = []
for (let i = 1; i <= 10; i++) {
configs.push(new WgConfig({
wgInterface: {
address: [`10.10.1.${i}`],
privateKey: '',
name: `Client-${i}`
},
filePath: path.join(__dirname, '/configs', `/guardline-${i}.conf`)
}))
}
await Promise.all(configs.map(x => x.generateKeys()))
createPeerPairs(configs.map(x => {
return {
config: x,
peerSettings: ({ thisConfig, peerConfig }) => {
const peerAddress = peerConfig.wgInterface.address
const peerPresharedKey = peerConfig.preSharedKey
return {
allowedIps: peerAddress,
preSharedKey: peerPresharedKey,
name: peerConfig.wgInterface.name,
persistentKeepalive: thisConfig.wgInterface.address.includes('10.10.1.1') ? 25 : undefined
}
}
}
}))
await Promise.all(configs.map(x => x.writeToFile()))
Random helpers
import path from 'path'
import {
checkWgIsInstalled,
generateKeyPair,
generateConfigString,
parseConfigString,
getConfigStringFromFile,
getConfigObjectFromFile,
} from ''
const version = await checkWgIsInstalled()
console.log(version)
const { publicKey, privateKey, preSharedKey } = await generateKeyPair({ preSharedKey: true })
console.log({ publicKey, privateKey, preSharedKey })
const configString = generateConfigString({
wgInterface: {
name: 'Client 1',
address: ['10.10.1.1'],
privateKey: '6AgToMLuTa3lQMIMwIBVkhwSM0PVLCZD1FpqU5y0l2Q'
},
peers: [
{
allowedIps: ['10.10.1.1/32'],
publicKey: 'FoSq0MiHw9nuHMiJcD2vPCzQScmn1Hu0ctfKfSfhp3s='
}
]
})
console.log(configString)
const configObj = parseConfigString(configString)
console.log(configObj)
const confStringFromFile = getConfigStringFromFile({
filePath: path.join(__dirname, '/configs', '/wg0.conf')
})
console.log(confStringFromFile)
const confObjFromFile = getConfigObjectFromFile({
filePath: path.join(__dirname, '/configs', '/wg0.conf')
})
console.log(confObjFromFile)
Extensive example
Here is one extensive example of usage that should give you an idea of what to do:
import path from 'path'
import { WgConfig, getConfigObjectFromFile, createPeerPairs, checkWgIsInstalled } from 'wireguard-tools'
const filePath = path.join(__dirname, '/configs', '/guardline-server.conf')
const test = async () => {
try {
const config1 = new WgConfig({
wgInterface: {
address: ['10.10.1.1']
},
filePath
})
config1.wgInterface.name = 'Guardline Server'
config1.wgInterface.postUp = ['echo "Guardline Server Up!"']
config1.wgInterface.listenPort = 5280
const keypair = await config1.generateKeys({ preSharedKey: true })
console.log(keypair.publicKey === config1.publicKey)
console.log(keypair.preSharedKey === config1.preSharedKey)
console.log(keypair.privateKey === config1.wgInterface.privateKey)
await config1.writeToFile()
const thatConfigFromFile = await getConfigObjectFromFile({ filePath })
const config2FilePath = path.join(__dirname, '/configs', '/guardline-server-2.conf')
const config2 = new WgConfig({
...thatConfigFromFile,
filePath: config2FilePath
})
console.log(config1.wgInterface.privateKey === config2.wgInterface.privateKey)
await config2.generateKeys()
console.log(config1.publicKey === config2.publicKey)
config2.generateKeys({ overwrite: true })
console.log(config1.publicKey === config2.publicKey)
const config2AsPeer = config2.createPeer({
allowedIps: ['10.10.1.1/32']
})
config1.addPeer(config2AsPeer)
config1.removePeer(config2.publicKey)
createPeerPairs([
{
config: config1,
peerSettings: {
allowedIps: ['10.10.1.1'],
preSharedKey: config1.preSharedKey
}
},
{
config: config2,
peerSettings: {
allowedIps: ['10.10.1.2']
}
}
])
console.log(config1.getPeer(config2.publicKey))
console.log(config2.getPeer(config1.publicKey))
const wgVersion = await checkWgIsInstalled()
console.log(wgVersion)
await config1.writeToFile()
await config1.up()
config1.wgInterface.dns = ['1.1.1.1']
config1.writeToFile()
await config1.restart()
await config1.down()
} catch (e) {
console.error(e)
}
}
test()
To develop
- Clone this repo
- Run
yarn
- Run
yarn watch
TODO
Write more docs as always