refactor: major code cleanups :-)
first working version
This commit is contained in:
parent
c285a15bee
commit
42a05b069f
8
.gitignore
vendored
8
.gitignore
vendored
@ -4,10 +4,8 @@ node_modules/*
|
||||
# build files
|
||||
dist
|
||||
|
||||
# certificates
|
||||
ssl/*.pem
|
||||
*.crt
|
||||
*.key
|
||||
|
||||
# ts
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# custom certificates
|
||||
/ssl-*/
|
||||
|
53
README.md
53
README.md
@ -1,17 +1,18 @@
|
||||
Basic RADIUS Server for node.js for Google LDAP Service and WPA2 Enteprise WLAN Authentification.
|
||||
* Only implements LDAP as Authentification Backend
|
||||
* Only WPA TTLS implemented (as this is the only one that works with Google LDAP Service)
|
||||
|
||||
- supports LDAP Authentification Backend
|
||||
- supports WPA2 Entprise (TTLS over PAP)
|
||||
|
||||
Protect your WIFI access with a username and password by a credential provider you already use!
|
||||
|
||||
## Known Issues / Disclaimer
|
||||
|
||||
This is a first implementation draft, which is currently only working with a nodejs fork (see https://github.com/nodejs/node/pull/31814).
|
||||
|
||||
* PAP / CHAP RFC not found to implement this correctly
|
||||
* Project needs more structure and interfaces to extend it more easily in the future (make a full radius server out of it ;)?)
|
||||
* No package queuing or any kind of respsecting the MTU size
|
||||
* a lot of bugs
|
||||
- PAP / CHAP RFC not found to implement this correctly
|
||||
- a lot of bugs
|
||||
|
||||
CONTRIBUTIONS WELCOME!
|
||||
CONTRIBUTIONS WELCOME! If you are willing to help, just open a PR or contact me via bug system or simon.tretter@hokify.com.
|
||||
|
||||
## Installation
|
||||
|
||||
@ -21,17 +22,39 @@ CONTRIBUTIONS WELCOME!
|
||||
## Introduction
|
||||
|
||||
This app provides a radius server to authenticate against google's SLDAP service. To get this running
|
||||
you need:
|
||||
1.) Running LDAP Service (E.g. Google Suite Enterprise or Gloud Identity Premium)
|
||||
2.) Use stunnel to connect to the LDAP service and connect this app to the stunnel (I didn't get the client ldap authentication working in here yet)
|
||||
3.) Install a SSL certificate (e.g. self signed via npm run create-certificate)
|
||||
4.) Install und build server: npm install && npm run build
|
||||
5.) Start server node dist/app.ts --secret {RADIUS secret} --baseDN dc=hokify,dc=com
|
||||
you need:
|
||||
|
||||
1. Running LDAP Service (E.g. Google Suite Enterprise or Gloud Identity Premium)
|
||||
2. Optional: Create your own SSL certificate (e.g. self signed via npm run create-certificate)
|
||||
3. Check config.js and adapt to your needs
|
||||
|
||||
- configure authentication (passport config), e.g. for LDAP
|
||||
|
||||
```js
|
||||
var config = {
|
||||
// ....
|
||||
authentication: 'ldap',
|
||||
authenticationOptions: {
|
||||
url: 'ldap://127.0.0.1:1636',
|
||||
base: 'dc=hokify,dc=com'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
- set radius secret
|
||||
|
||||
4. Install und build server: npm install && npm run build
|
||||
5. Start server "npm run start"
|
||||
|
||||
## Authentications
|
||||
|
||||
right now only one simple ldap implementation is done,
|
||||
the idea is though to use [passport.js](http://www.passportjs.org/) as authentication provider,
|
||||
therefore it would be possible to use the radius server with your email provider authentication or any
|
||||
other auth mechanismus you use (well everything with no 2factor or anything else that requries an extra step).
|
||||
|
||||
## Usage
|
||||
|
||||
You need to specify at least a radius password and the base DN for LDAP:
|
||||
|
||||
node dist/app.ts --secret {RADIUS secret} --baseDN dc=hokify,dc=com
|
||||
|
||||
npm run start
|
||||
|
11
config.js
11
config.js
@ -1,14 +1,19 @@
|
||||
import * as fs from 'fs';
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SSL_CERT_DIRECTORY = './ssl/cert';
|
||||
|
||||
module.exports = {
|
||||
port: 1812,
|
||||
// radius secret
|
||||
secret: 'testing123',
|
||||
|
||||
certificate: {
|
||||
cert: fs.readFileSync('./ssl/cert/server.crt'),
|
||||
cert: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.crt')),
|
||||
key: [
|
||||
{
|
||||
pem: fs.readFileSync('./ssl/cert/server.key'),
|
||||
pem: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.key')),
|
||||
passphrase: 'whatever2020'
|
||||
}
|
||||
]
|
||||
|
228
package-lock.json
generated
228
package-lock.json
generated
@ -34,13 +34,13 @@
|
||||
}
|
||||
},
|
||||
"@hokify/eslint-config": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.2.tgz",
|
||||
"integrity": "sha512-FvVZd8hruAriQ84UR0MqxRtXXUT9Eqe9LTGoU90sQ6TgHXTGPQpOfY+8DAICX0HtRc5ntB/ZEgL6L9ims4NLxA==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.4.tgz",
|
||||
"integrity": "sha512-wUiJ1mBc+6TEqqB1qK0LsaMazg6h19MwA4jlMn4Qltgiql7S2EmWDSK0ID9wCmI4bgUB6WqZsNQqvkMxNG7hYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/eslint-plugin": "^2.19.2",
|
||||
"@typescript-eslint/parser": "^2.19.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.20.0",
|
||||
"@typescript-eslint/parser": "^2.20.0",
|
||||
"eslint-config-airbnb-typescript": "^7.0.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
@ -50,11 +50,20 @@
|
||||
"eslint-loader": "^3.0.3",
|
||||
"eslint-plugin-import": "2.20.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-mocha": "^6.2.2",
|
||||
"eslint-plugin-mocha": "^6.3.0",
|
||||
"eslint-plugin-node": "^11.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"eslint-plugin-unicorn": "^16.0.0"
|
||||
"eslint-plugin-unicorn": "^16.1.1"
|
||||
}
|
||||
},
|
||||
"@types/body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/color-name": {
|
||||
@ -62,12 +71,39 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
|
||||
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
|
||||
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/ioredis": {
|
||||
"version": "4.14.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.14.7.tgz",
|
||||
@ -92,11 +128,15 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.5.tgz",
|
||||
"integrity": "sha512-hrtMZjVfWNPxkwFkYhLAU0ITZ3/reDft4jDzLBvrGDSKCbEvW+GeZb4PgM3jlSwSsv0cXqnDeWcupFLIgW9E0Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
|
||||
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
|
||||
@ -116,6 +156,14 @@
|
||||
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/passport": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz",
|
||||
"integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==",
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/radius": {
|
||||
"version": "0.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/radius/-/radius-0.0.28.tgz",
|
||||
@ -125,6 +173,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
},
|
||||
"@types/redis": {
|
||||
"version": "2.8.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.15.tgz",
|
||||
@ -133,13 +186,22 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
||||
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/mime": "*"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz",
|
||||
"integrity": "sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz",
|
||||
"integrity": "sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/experimental-utils": "2.19.2",
|
||||
"@typescript-eslint/experimental-utils": "2.20.0",
|
||||
"eslint-utils": "^1.4.3",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"regexpp": "^3.0.0",
|
||||
@ -147,32 +209,32 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz",
|
||||
"integrity": "sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz",
|
||||
"integrity": "sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.3",
|
||||
"@typescript-eslint/typescript-estree": "2.19.2",
|
||||
"@typescript-eslint/typescript-estree": "2.20.0",
|
||||
"eslint-scope": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.19.2.tgz",
|
||||
"integrity": "sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.20.0.tgz",
|
||||
"integrity": "sha512-o8qsKaosLh2qhMZiHNtaHKTHyCHc3Triq6aMnwnWj7budm3xAY9owSZzV1uon5T9cWmJRJGzTFa90aex4m77Lw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/eslint-visitor-keys": "^1.0.0",
|
||||
"@typescript-eslint/experimental-utils": "2.19.2",
|
||||
"@typescript-eslint/typescript-estree": "2.19.2",
|
||||
"@typescript-eslint/experimental-utils": "2.20.0",
|
||||
"@typescript-eslint/typescript-estree": "2.20.0",
|
||||
"eslint-visitor-keys": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz",
|
||||
"integrity": "sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz",
|
||||
"integrity": "sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
@ -389,6 +451,11 @@
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
|
||||
"dev": true
|
||||
},
|
||||
"bcryptjs": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
|
||||
},
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
@ -896,9 +963,9 @@
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
@ -1361,12 +1428,24 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-mocha": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.2.tgz",
|
||||
"integrity": "sha512-oNhPzfkT6Q6CJ0HMVJ2KLxEWG97VWGTmuHOoRcDLE0U88ugUyFNV9wrT2XIt5cGtqc5W9k38m4xTN34L09KhBA==",
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz",
|
||||
"integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ramda": "^0.26.1"
|
||||
"eslint-utils": "^2.0.0",
|
||||
"ramda": "^0.27.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-visitor-keys": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-node": {
|
||||
@ -2159,6 +2238,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ldapauth-fork": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-4.3.1.tgz",
|
||||
"integrity": "sha512-IJUnkEDQg6D45jUKW3FFfMWZhUjZoGkN97WaMXF1cBod0gJq74d+iwRavPqiE3o/KNRgqwFesrdE4Ym4Fc1GIQ==",
|
||||
"requires": {
|
||||
"@types/ldapjs": "^1.0.0",
|
||||
"@types/node": "^10.12.12",
|
||||
"bcryptjs": "^2.4.0",
|
||||
"ldapjs": "^1.0.2",
|
||||
"lru-cache": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.16.tgz",
|
||||
"integrity": "sha512-A4283YSA1OmnIivcpy/4nN86YlnKRiQp8PYwI2KdPCONEBN093QTb0gCtERtkLyVNGKKIGazTZ2nAmVzQU51zA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ldapjs": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
|
||||
@ -2215,13 +2313,13 @@
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
|
||||
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^2.0.0",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
@ -2290,6 +2388,14 @@
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"requires": {
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||
@ -2509,9 +2615,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.2.tgz",
|
||||
"integrity": "sha512-b+2AKjAf6uQlxxv8ChHdM+VT4eeX+ZSwv+pk2xIXZWbo+yxn4/En1iC+GHe/OFYa9on0AhFF2PvuAcFHoiiHaA==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz",
|
||||
"integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==",
|
||||
"dev": true
|
||||
},
|
||||
"object-inspect": {
|
||||
@ -2676,6 +2782,29 @@
|
||||
"error-ex": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"passport-ldapauth": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-2.1.3.tgz",
|
||||
"integrity": "sha512-23n425UTasN6XhcXG0qQ0h0YrS/zfo8kNIEhSLfPsNpglhYhhQFfB1pmDc5RrH+Kiz5fKLkki5BpvkKHCwkixg==",
|
||||
"requires": {
|
||||
"@types/node": "^10.12.26",
|
||||
"@types/passport": "^1.0.0",
|
||||
"ldapauth-fork": "^4.2.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "10.17.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.16.tgz",
|
||||
"integrity": "sha512-A4283YSA1OmnIivcpy/4nN86YlnKRiQp8PYwI2KdPCONEBN093QTb0gCtERtkLyVNGKKIGazTZ2nAmVzQU51zA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
|
||||
},
|
||||
"path-browserify": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
|
||||
@ -2870,9 +2999,9 @@
|
||||
"integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw=="
|
||||
},
|
||||
"ramda": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
|
||||
"integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==",
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz",
|
||||
"integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==",
|
||||
"dev": true
|
||||
},
|
||||
"randombytes": {
|
||||
@ -3011,9 +3140,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"regexp-tree": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.19.tgz",
|
||||
"integrity": "sha512-mVeVLF/qg5qFbZSW0f7pXbuVX73dKIjuhOyC2JLKxzmpya75O4qLcvI9j0jp31Iz7IAkEVHa1UErDCAqaLKo5A==",
|
||||
"version": "0.1.20",
|
||||
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.20.tgz",
|
||||
"integrity": "sha512-gSiH74kc00oTbQkN7tZogZe0ttKwyxyDVLAnU20aWoarbLE9AypbJHRlZ567h4Zi19q3cPVRWDYbfECElrHgsQ==",
|
||||
"dev": true
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
@ -3587,9 +3716,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.7.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
|
||||
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
|
||||
"integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
@ -3761,6 +3890,11 @@
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz",
|
||||
|
59
package.json
59
package.json
@ -1,31 +1,32 @@
|
||||
{
|
||||
"name": "radius-server",
|
||||
"description": "radius server for google LDAP and TTLT",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"start": "/home/simon/Dev/others/node/node dist/app.js",
|
||||
"build": "tsc",
|
||||
"dev": "ts-node src/app.ts",
|
||||
"test-ttls-pap": "eapol_test -c ./ttls-pap.conf -s testing123",
|
||||
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"native-duplexpair": "^1.0.0",
|
||||
"ldapjs": "^1.0.2",
|
||||
"node-cache": "^5.1.0",
|
||||
"radius": "~1.1.4",
|
||||
"ts-node": "^8.6.2",
|
||||
"type-cacheable": "^4.0.0",
|
||||
"yargs": "~15.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"passport-ldapauth": "^2.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ldapjs": "^1.0.5",
|
||||
"@types/radius": "0.0.28",
|
||||
"@hokify/eslint-config": "^0.2.2",
|
||||
"eslint": "^6.8.0",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.7.5"
|
||||
}
|
||||
"name": "radius-server",
|
||||
"description": "radius server for google LDAP and TTLT",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"start": "/home/simon/Dev/others/node/node dist/app.js",
|
||||
"build": "tsc",
|
||||
"dev": "ts-node src/app.ts",
|
||||
"test-ttls-pap": "eapol_test -c ./ttls-pap.conf -s testing123",
|
||||
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"native-duplexpair": "^1.0.0",
|
||||
"ldapjs": "^1.0.2",
|
||||
"node-cache": "^5.1.0",
|
||||
"radius": "~1.1.4",
|
||||
"ts-node": "^8.6.2",
|
||||
"type-cacheable": "^4.0.0",
|
||||
"yargs": "~15.1.0",
|
||||
"md5": "^2.2.1",
|
||||
"passport-ldapauth": "^2.1.3"
|
||||
},
|
||||
"license": "GPLv3",
|
||||
"devDependencies": {
|
||||
"@types/ldapjs": "^1.0.5",
|
||||
"@types/radius": "0.0.28",
|
||||
"@hokify/eslint-config": "^0.2.4",
|
||||
"eslint": "^6.8.0",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.8.2"
|
||||
}
|
||||
}
|
||||
|
204
src/app.ts
204
src/app.ts
@ -1,186 +1,42 @@
|
||||
import * as dgram from 'dgram';
|
||||
import * as radius from 'radius';
|
||||
// import * as dgram from "dgram";
|
||||
// import * as fs from 'fs';
|
||||
import { EAPHandler } from './eap';
|
||||
import { IDeferredPromise, makeid, MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from './helpers';
|
||||
|
||||
import { GoogleLDAPAuth } from './auth/google-ldap';
|
||||
import { AdditionalAuthHandler } from './types/Handler';
|
||||
import { UDPServer } from './server/UDPServer';
|
||||
import { RadiusService } from './radius/RadiusService';
|
||||
|
||||
const server = dgram.createSocket('udp4');
|
||||
import * as config from '../config';
|
||||
|
||||
const { argv } = require('yargs')
|
||||
.usage('RADIUS Server\nUsage: $0')
|
||||
.example('$0 --port 1812 -s radiussecret')
|
||||
.default({
|
||||
port: 1812,
|
||||
s: 'testing123',
|
||||
baseDN: 'dc=hokify,dc=com',
|
||||
ldapServer: 'ldap://127.0.0.1:1636'
|
||||
})
|
||||
.describe('baseDN', 'LDAP Base DN')
|
||||
.describe('ldapServer', 'LDAP Server')
|
||||
.describe('port', 'RADIUS server listener port')
|
||||
.alias('s', 'secret')
|
||||
.describe('secret', 'RADIUS secret')
|
||||
.string(['secret', 'baseDN'])
|
||||
.demand('secret');
|
||||
|
||||
console.log(`Listener Port: ${argv.port}`);
|
||||
console.log(`RADIUS Secret: ${argv.secret}`);
|
||||
console.log(`LDAP Base DN: ${argv.baseDN}`);
|
||||
console.log(`LDAP Server: ${argv.ldapServer}`);
|
||||
console.log(`Listener Port: ${config.port || 1812}`);
|
||||
console.log(`RADIUS Secret: ${config.secret}`);
|
||||
console.log(`Auth Mode: ${config.authentication}`);
|
||||
|
||||
// const ldap = new LDAPAuth({url: 'ldap://ldap.google.com', base: 'dc=hokify,dc=com', uid: 'uid', tlsOptions});
|
||||
|
||||
const ldap = new GoogleLDAPAuth(argv.ldapServer, argv.baseDN);
|
||||
const ldap = new GoogleLDAPAuth(
|
||||
config.authenticationOptions.url,
|
||||
config.authenticationOptions.base
|
||||
);
|
||||
|
||||
const eapHandler = new EAPHandler();
|
||||
const timeout: { [key: string]: NodeJS.Timeout } = {};
|
||||
const waitForNextMsg: { [key: string]: IDeferredPromise } = {};
|
||||
const server = new UDPServer(config.port);
|
||||
const radiusService = new RadiusService(config.secret, ldap);
|
||||
|
||||
function sendToClient(
|
||||
msg: string | Uint8Array,
|
||||
offset: number,
|
||||
length: number,
|
||||
port?: number,
|
||||
address?: string,
|
||||
callback?: (error: Error | null, bytes: number) => void,
|
||||
stateForRetry?: string
|
||||
): void {
|
||||
let retried = 0;
|
||||
(async () => {
|
||||
server.on('message', async (msg, rinfo) => {
|
||||
const response = await radiusService.handleMessage(msg);
|
||||
|
||||
function sendResponse() {
|
||||
console.log(`sending response... (try: ${retried})`);
|
||||
server.send(msg, offset, length, port, address, (error: Error | null, bytes: number) => {
|
||||
// all good
|
||||
|
||||
if (callback) callback(error, bytes);
|
||||
});
|
||||
|
||||
if (stateForRetry && retried < 3) {
|
||||
// timeout[stateForRetry] = setTimeout(sendResponse, 600 * (retried+1));
|
||||
}
|
||||
retried++;
|
||||
}
|
||||
|
||||
sendResponse();
|
||||
}
|
||||
|
||||
server.on('message', async function(msg, rinfo) {
|
||||
const packet = radius.decode({ packet: msg, secret: argv.secret });
|
||||
|
||||
if (packet.code !== 'Access-Request') {
|
||||
console.log('unknown packet type: ', packet.code);
|
||||
return;
|
||||
}
|
||||
// console.log('packet.attributes', packet.attributes);
|
||||
|
||||
// console.log('rinfo', rinfo);
|
||||
|
||||
async function checkAuth(
|
||||
username: string,
|
||||
password: string,
|
||||
additionalAuthHandler?: AdditionalAuthHandler
|
||||
) {
|
||||
console.log(`Access-Request for ${username}`);
|
||||
let success = false;
|
||||
try {
|
||||
await ldap.authenticate(username, password);
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
const attributes: any[] = [];
|
||||
|
||||
if (additionalAuthHandler) {
|
||||
await additionalAuthHandler(success, { packet, attributes, secret: argv.secret });
|
||||
}
|
||||
|
||||
const response = radius.encode_response({
|
||||
packet,
|
||||
code: success ? 'Access-Accept' : 'Access-Reject',
|
||||
secret: argv.secret,
|
||||
attributes
|
||||
});
|
||||
console.log(`Sending ${success ? 'accept' : 'reject'} for user ${username}`);
|
||||
|
||||
sendToClient(response, 0, response.length, rinfo.port, rinfo.address, function(err, _bytes) {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (packet.attributes['EAP-Message']) {
|
||||
const state = (packet.attributes.State && packet.attributes.State.toString()) || makeid(16);
|
||||
|
||||
if (timeout[state]) {
|
||||
clearTimeout(timeout[state]);
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
response: (EAPMessage: Buffer) => {
|
||||
const attributes: any = [['State', Buffer.from(state)]];
|
||||
let sentDataSize = 0;
|
||||
do {
|
||||
if (EAPMessage.length > 0) {
|
||||
attributes.push([
|
||||
'EAP-Message',
|
||||
EAPMessage.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
|
||||
]);
|
||||
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
|
||||
if (response) {
|
||||
server.sendToClient(
|
||||
response.data,
|
||||
rinfo.port,
|
||||
rinfo.address,
|
||||
(err, _bytes) => {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
} while (sentDataSize < EAPMessage.length);
|
||||
|
||||
const response = radius.encode_response({
|
||||
packet,
|
||||
code: 'Access-Challenge',
|
||||
secret: argv.secret,
|
||||
attributes
|
||||
});
|
||||
|
||||
waitForNextMsg[state] = newDeferredPromise();
|
||||
|
||||
sendToClient(
|
||||
response,
|
||||
0,
|
||||
response.length,
|
||||
rinfo.port,
|
||||
rinfo.address,
|
||||
function(err, _bytes) {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
return waitForNextMsg[state].promise;
|
||||
},
|
||||
checkAuth
|
||||
};
|
||||
|
||||
if (waitForNextMsg[state]) {
|
||||
const identifier = packet.attributes['EAP-Message'].slice(1, 2).readUInt8(0); // .toString('hex');
|
||||
waitForNextMsg[state].resolve({ response: handlers.response, identifier });
|
||||
},
|
||||
response.expectAcknowledgment
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// EAP MESSAGE
|
||||
eapHandler.handleEAPMessage(packet.attributes['EAP-Message'], state, handlers);
|
||||
} else {
|
||||
const username = packet.attributes['User-Name'];
|
||||
const password = packet.attributes['User-Password'];
|
||||
|
||||
checkAuth(username, password);
|
||||
}
|
||||
});
|
||||
|
||||
server.on('listening', function() {
|
||||
const address = server.address();
|
||||
console.log(`radius server listening ${address.address}:${address.port}`);
|
||||
});
|
||||
|
||||
server.bind(argv.port);
|
||||
// start server
|
||||
await server.start();
|
||||
})();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
|
||||
import { createClient, Client } from 'ldapjs';
|
||||
import { Client, createClient } from 'ldapjs';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
|
||||
const usernameFields = ['posixUid', 'mail'];
|
||||
@ -58,7 +58,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
});
|
||||
|
||||
res.on('end', result => {
|
||||
console.log(`status: ${result?.status}`);
|
||||
console.log(`ldap status: ${result?.status}`);
|
||||
|
||||
// replace with new dns
|
||||
this.allValidDNsCache = dns;
|
||||
|
219
src/eap.ts
219
src/eap.ts
@ -1,219 +0,0 @@
|
||||
// https://tools.ietf.org/html/rfc3748#section-4.1
|
||||
|
||||
// https://tools.ietf.org/html/rfc5281 TTLS v0
|
||||
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
|
||||
import { IResponseHandlers, ResponseHandler } from './types/Handler';
|
||||
import { EAPTTLS } from './eap/eap-ttls';
|
||||
import { MAX_RADIUS_ATTRIBUTE_SIZE } from './helpers';
|
||||
|
||||
export class EAPHandler {
|
||||
eapTTLS: EAPTTLS;
|
||||
|
||||
constructor() {
|
||||
this.eapTTLS = new EAPTTLS(this.sendEAPResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param type 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK
|
||||
*/
|
||||
private async sendEAPResponse(
|
||||
response: ResponseHandler,
|
||||
identifier: number,
|
||||
data?: Buffer,
|
||||
msgType = 21,
|
||||
msgFlags = 0x00
|
||||
) {
|
||||
let i = 0;
|
||||
|
||||
const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4;
|
||||
|
||||
let sentDataSize = 0;
|
||||
|
||||
let currentIdentifier = identifier;
|
||||
let currentResponse = response;
|
||||
|
||||
do {
|
||||
// SLICE
|
||||
|
||||
// const fragmentMaxPart =
|
||||
// data && (i + 1) * maxFragmentSize > data.length ? (i + 1) * maxFragmentSize : undefined;
|
||||
|
||||
const dataPart = data && data.length > 0 && data.slice(sentDataSize, sentDataSize + maxSize);
|
||||
|
||||
/* it's the first one and we have more, therefore include length */
|
||||
const includeLength = data && i === 0 && sentDataSize < data.length;
|
||||
|
||||
sentDataSize += maxSize;
|
||||
|
||||
i += 1;
|
||||
|
||||
/*
|
||||
0 1 2 3 4 5 6 7 8
|
||||
+-+-+-+-+-+-+-+-+
|
||||
|L M R R R R R R|
|
||||
+-+-+-+-+-+-+-+-+
|
||||
|
||||
L = Length included
|
||||
M = More fragments
|
||||
R = Reserved
|
||||
|
||||
The L bit (length included) is set to indicate the presence of the
|
||||
four-octet TLS Message Length field, and MUST be set for the first
|
||||
fragment of a fragmented TLS message or set of messages. The M
|
||||
bit (more fragments) is set on all but the last fragment.
|
||||
Implementations of this specification MUST set the reserved bits
|
||||
to zero, and MUST ignore them on reception.
|
||||
*/
|
||||
|
||||
const flags =
|
||||
msgFlags +
|
||||
(includeLength ? 0b10000000 : 0) + // set L bit
|
||||
(data && sentDataSize < data.length /* we have more */ ? 0b01000000 : 0); // set M bit
|
||||
|
||||
currentIdentifier++;
|
||||
|
||||
let buffer = Buffer.from([
|
||||
1, // request
|
||||
currentIdentifier,
|
||||
0, // length (1/2)
|
||||
0, // length (2/2)
|
||||
msgType, // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
|
||||
flags // flags: 000000 (L include lenghts, M .. more to come)
|
||||
]);
|
||||
|
||||
// append length
|
||||
if (includeLength && data) {
|
||||
const length = Buffer.alloc(4);
|
||||
length.writeInt32BE(data.byteLength, 0);
|
||||
|
||||
buffer = Buffer.concat([buffer, length]);
|
||||
}
|
||||
|
||||
const resBuffer = dataPart ? Buffer.concat([buffer, dataPart]) : buffer;
|
||||
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
|
||||
|
||||
console.log('<<<<<<<<<<<< EAP RESPONSE TO CLIENT', {
|
||||
code: 1,
|
||||
currentIdentifier,
|
||||
includeLength,
|
||||
length: (data && data.byteLength) || 0,
|
||||
msgType: msgType.toString(10),
|
||||
flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
data
|
||||
});
|
||||
|
||||
// uffer.from([1,identifier, 0, 0, 21, 0]);
|
||||
// buffer.writeUInt16BE(sslResponse.length, 2); // length
|
||||
// buffer.writeInt8(21, 4); // eap-ttls
|
||||
// buffer.writeInt8(0, 5); // flags
|
||||
|
||||
console.log('sending message with identifier', currentIdentifier);
|
||||
({ identifier: currentIdentifier, response: currentResponse } = await currentResponse(
|
||||
resBuffer
|
||||
));
|
||||
console.log('next message got identifier', currentIdentifier);
|
||||
} while (data && sentDataSize < data.length);
|
||||
|
||||
console.log('DONE', sentDataSize, data && data.length);
|
||||
}
|
||||
|
||||
handleEAPMessage(msg: Buffer, state: string, handlers: IResponseHandlers) {
|
||||
// const b = Buffer.from([2,0x242,0x0,0x18,0x1,0x115,0x105,0x109,0x111,0x110,0x46,0x116,0x114,0x101,0x116,0x116,0x101,0x114]);
|
||||
// const msg = Buffer.from([2, 162, 0, 18, 1, 115, 105, 109, 111, 110, 46, 116, 114, 101, 116, 116, 101, 114])
|
||||
|
||||
/*
|
||||
1 Request
|
||||
2 Response
|
||||
3 Success
|
||||
4 Failure
|
||||
*/
|
||||
|
||||
const code = msg.slice(0, 1).readUInt8(0);
|
||||
const identifier = msg.slice(1, 2).readUInt8(0); // .toString('hex');
|
||||
// const length = msg.slice(2, 4).readInt16BE(0); // .toString('binary');
|
||||
const type = msg.slice(4, 5).readUInt8(0); // .slice(3,0x5).toString('hex');
|
||||
|
||||
/*
|
||||
console.log("CODE", code);
|
||||
console.log('ID', identifier);
|
||||
console.log('length', length);
|
||||
*/
|
||||
|
||||
switch (code) {
|
||||
case 1: // for request
|
||||
case 2: // for response
|
||||
switch (type) {
|
||||
case 1: // identifiy
|
||||
/**
|
||||
* The EAP-TTLS packet format is shown below. The fields are
|
||||
transmitted left to right.
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Flags | Message Length
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
Message Length | Data...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {});
|
||||
|
||||
this.sendEAPResponse(handlers.response, identifier, undefined, 21, 0x20);
|
||||
|
||||
/*
|
||||
handlers.response(
|
||||
Buffer.from([
|
||||
1, // request
|
||||
identifier + 1,
|
||||
0, // length (1/2)
|
||||
6, // length (2/2)
|
||||
21, // EAP-TTLS
|
||||
0x20 // flags: 001000 start flag
|
||||
])
|
||||
); */
|
||||
break;
|
||||
case 21: // EAP TTLS
|
||||
this.eapTTLS.handleMessage(msg, state, handlers, identifier);
|
||||
break;
|
||||
case 3: // nak
|
||||
// this.sendEAPResponse(handlers.response, identifier, undefined, 3);
|
||||
break;
|
||||
case 2: // notification
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
|
||||
console.info('notification');
|
||||
break;
|
||||
case 4: // md5-challenge
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {});
|
||||
|
||||
console.info('md5-challenge');
|
||||
break;
|
||||
case 254: // expanded type
|
||||
console.error('not implemented type', type);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// we do not support this auth type, ask for TTLS
|
||||
console.error('unsupported type', type, 'requesting TTLS (21)');
|
||||
|
||||
this.sendEAPResponse(handlers.response, identifier, Buffer.from([21]), 3);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
console.log('Client Auth Success');
|
||||
break;
|
||||
case 4:
|
||||
console.log('Client Auth FAILURE');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// silently ignor;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as events from 'events';
|
||||
import * as tls from 'tls';
|
||||
import { encodeTunnelPW, openTLSSockets, startTLSServer } from '../tls/crypt';
|
||||
import { AdditionalAuthHandler, ResponseAuthHandler } from '../types/Handler';
|
||||
import { PAPChallenge } from './challenges/pap';
|
||||
import { IEAPType } from '../types/EAPType';
|
||||
|
||||
interface IEAPResponseHandlers {
|
||||
response: (respData?: Buffer, msgType?: number) => void;
|
||||
checkAuth: ResponseAuthHandler;
|
||||
}
|
||||
|
||||
export class EAPTTLS implements IEAPType {
|
||||
papChallenge: PAPChallenge;
|
||||
|
||||
constructor(private sendEAPResponse) {
|
||||
this.papChallenge = new PAPChallenge();
|
||||
}
|
||||
|
||||
decode(msg: Buffer) {
|
||||
const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex');
|
||||
|
||||
// if (flags)
|
||||
// @todo check if "L" flag is set in flags
|
||||
const decodedFlags = {
|
||||
lengthIncluded: flags & 0b010000000,
|
||||
moreFragments: flags & 0b001000000,
|
||||
start: flags & 0b000100000,
|
||||
reserved: flags & 0b000011000,
|
||||
version: flags & 0b010000111
|
||||
};
|
||||
let msglength;
|
||||
if (decodedFlags.lengthIncluded) {
|
||||
msglength = msg.slice(6, 10).readInt32BE(0); // .readDoubleLE(0); // .toString('hex');
|
||||
}
|
||||
const data = msg.slice(decodedFlags.lengthIncluded ? 10 : 6, msg.length);
|
||||
|
||||
return {
|
||||
decodedFlags,
|
||||
msglength,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
handleMessage(msg: Buffer, state: string, handlers, identifier: number) {
|
||||
const { decodedFlags, msglength, data } = this.decode(msg);
|
||||
|
||||
// check if no data package is there and we have something in the queue, if so.. empty the queue first
|
||||
if (!data || data.length === 0) {
|
||||
// @todo: queue processing
|
||||
console.warn(
|
||||
`>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS, ACK / NACK (no data, just a confirmation, ID: ${identifier})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', {
|
||||
// flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
decodedFlags,
|
||||
identifier,
|
||||
/*
|
||||
0 1 2 3 4 5 6 7
|
||||
+---+---+---+---+---+---+---+---+
|
||||
| L | M | S | R | R | V |
|
||||
+---+---+---+---+---+---+---+---+
|
||||
|
||||
L = Length included
|
||||
M = More fragments
|
||||
S = Start
|
||||
R = Reserved
|
||||
V = Version (000 for EAP-TTLSv0)
|
||||
*/
|
||||
|
||||
msglength,
|
||||
data,
|
||||
dataStr: data.toString()
|
||||
});
|
||||
|
||||
let currentConnection = openTLSSockets.get(state) as
|
||||
| { events: events.EventEmitter; tls: tls.TLSSocket; currentHandlers: IEAPResponseHandlers }
|
||||
| undefined;
|
||||
if (!currentConnection) {
|
||||
const connection = startTLSServer();
|
||||
currentConnection = {
|
||||
events: connection.events,
|
||||
tls: connection.tls,
|
||||
currentHandlers: handlers
|
||||
};
|
||||
openTLSSockets.set(state, currentConnection);
|
||||
|
||||
// register event listeners
|
||||
currentConnection.events.on('incoming', (incomingData: Buffer) => {
|
||||
const type = incomingData.slice(3, 4).readUInt8(0);
|
||||
// const code = data.slice(4, 5).readUInt8(0);
|
||||
|
||||
switch (type) {
|
||||
case 1: // PAP / CHAP
|
||||
try {
|
||||
const { username, password } = this.papChallenge.decode(incomingData);
|
||||
currentConnection!.currentHandlers.checkAuth(username, password);
|
||||
} catch (err) {
|
||||
// pwd not found..
|
||||
console.error('pwd not found', err);
|
||||
// NAK
|
||||
currentConnection!.currentHandlers.response(undefined, 3);
|
||||
|
||||
/*
|
||||
this.sendEAPResponse(
|
||||
currentConnection!.currentHandlers.response,
|
||||
identifier,
|
||||
undefined,
|
||||
3
|
||||
); */
|
||||
currentConnection!.events.emit('end');
|
||||
throw new Error(`pwd not found`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log('data', incomingData);
|
||||
console.log('data str', incomingData.toString());
|
||||
|
||||
// currentConnection!.events.emit('end');
|
||||
|
||||
console.log('UNSUPPORTED AUTH TYPE, requesting PAP');
|
||||
// throw new Error(`unsupported auth type${type}`);
|
||||
currentConnection!.currentHandlers.response(Buffer.from([1]), 3);
|
||||
|
||||
/*
|
||||
this.sendEAPResponse(
|
||||
currentConnection!.currentHandlers.response,
|
||||
identifier,
|
||||
Buffer.from([1]),
|
||||
3
|
||||
); */
|
||||
}
|
||||
});
|
||||
|
||||
currentConnection.events.on('response', (responseData: Buffer) => {
|
||||
// console.log('sending encrypted data back to client', responseData);
|
||||
|
||||
// send back...
|
||||
currentConnection!.currentHandlers.response(responseData);
|
||||
// this.sendEAPResponse(currentConnection!.currentHandlers.response, identifier, responseData);
|
||||
// this.sendMessage(TYPE.PRELOGIN, data, false);
|
||||
});
|
||||
|
||||
currentConnection.events.on('end', () => {
|
||||
// cleanup socket
|
||||
console.log('ENDING SOCKET');
|
||||
openTLSSockets.del(state);
|
||||
});
|
||||
} /* else {
|
||||
console.log('using existing socket');
|
||||
} */
|
||||
|
||||
// update handlers
|
||||
currentConnection.currentHandlers = {
|
||||
response: (respData?: Buffer, msgType?: number) =>
|
||||
this.sendEAPResponse(handlers.response, identifier, respData, msgType),
|
||||
checkAuth: (username: string, password: string) => {
|
||||
const additionalAuthHandler: AdditionalAuthHandler = (success, params) => {
|
||||
const buffer = Buffer.from([
|
||||
success ? 3 : 4, // 3.. success, 4... failure
|
||||
identifier,
|
||||
0, // length (1/2)
|
||||
4 // length (2/2)
|
||||
]);
|
||||
|
||||
params.attributes.push(['EAP-Message', buffer]);
|
||||
|
||||
if (params.packet.attributes && params.packet.attributes['User-Name']) {
|
||||
// reappend username to response
|
||||
params.attributes.push(['User-Name', params.packet.attributes['User-Name']]);
|
||||
}
|
||||
|
||||
/*
|
||||
if (sess->eap_if->eapKeyDataLen > 64) {
|
||||
len = 32;
|
||||
} else {
|
||||
len = sess->eap_if->eapKeyDataLen / 2;
|
||||
}
|
||||
*/
|
||||
const keyingMaterial = (currentConnection?.tls as any).exportKeyingMaterial(
|
||||
128,
|
||||
'ttls keying material'
|
||||
);
|
||||
|
||||
// console.log('keyingMaterial', keyingMaterial);
|
||||
|
||||
// eapKeyData + len
|
||||
params.attributes.push([
|
||||
'Vendor-Specific',
|
||||
311,
|
||||
[
|
||||
[
|
||||
16,
|
||||
encodeTunnelPW(
|
||||
keyingMaterial.slice(64),
|
||||
(params.packet as any).authenticator,
|
||||
// params.packet.attributes['Message-Authenticator'],
|
||||
params.secret
|
||||
)
|
||||
]
|
||||
]
|
||||
]); // MS-MPPE-Send-Key
|
||||
|
||||
// eapKeyData
|
||||
params.attributes.push([
|
||||
'Vendor-Specific',
|
||||
311,
|
||||
[
|
||||
[
|
||||
17,
|
||||
encodeTunnelPW(
|
||||
keyingMaterial.slice(0, 64),
|
||||
(params.packet as any).authenticator,
|
||||
// params.packet.attributes['Message-Authenticator'],
|
||||
params.secret
|
||||
)
|
||||
]
|
||||
]
|
||||
]); // MS-MPPE-Recv-Key
|
||||
};
|
||||
|
||||
return handlers.checkAuth(username, password, additionalAuthHandler);
|
||||
}
|
||||
};
|
||||
|
||||
// emit data to tls server
|
||||
currentConnection.events.emit('send', data);
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ export function makeid(length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// by RFC Radius attributes have a max length
|
||||
// https://tools.ietf.org/html/rfc6929#section-1.2
|
||||
export const MAX_RADIUS_ATTRIBUTE_SIZE = 253;
|
||||
|
||||
export interface IDeferredPromise {
|
||||
|
95
src/radius/RadiusService.ts
Normal file
95
src/radius/RadiusService.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import * as radius from 'radius';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import { EAPPacketHandler } from './handler/EAPPacketHandler';
|
||||
import { DefaultPacketHandler } from './handler/DefaultPacketHandler';
|
||||
import { IPacketHandler, IPacketHandlerResult, PacketResponseCode } from '../types/PacketHandler';
|
||||
|
||||
export class RadiusService {
|
||||
radiusPacketHandlers: IPacketHandler[] = [];
|
||||
|
||||
constructor(private secret: string, private authentication: IAuthentication) {
|
||||
this.radiusPacketHandlers.push(new EAPPacketHandler(authentication));
|
||||
this.radiusPacketHandlers.push(new DefaultPacketHandler(authentication));
|
||||
}
|
||||
|
||||
async handleMessage(
|
||||
msg: Buffer
|
||||
): Promise<{ data: Buffer; expectAcknowledgment?: boolean } | undefined> {
|
||||
const packet = radius.decode({ packet: msg, secret: this.secret });
|
||||
|
||||
if (packet.code !== 'Access-Request') {
|
||||
console.log('unknown packet type: ', packet.code);
|
||||
return undefined;
|
||||
}
|
||||
// console.log('packet.attributes', packet.attributes);
|
||||
|
||||
// console.log('rinfo', rinfo);
|
||||
/*
|
||||
const checkAuth = async (
|
||||
username: string,
|
||||
password: string,
|
||||
additionalAuthHandler?: AdditionalAuthHandler
|
||||
) => {
|
||||
console.log(`Access-Request for ${username}`);
|
||||
let success = false;
|
||||
try {
|
||||
await this.authentication.authenticate(username, password);
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
const attributes: any[] = [];
|
||||
|
||||
if (additionalAuthHandler) {
|
||||
await additionalAuthHandler(success, { packet, attributes, secret: this.secret });
|
||||
}
|
||||
|
||||
const response = radius.encode_response({
|
||||
packet,
|
||||
code: success ? 'Access-Accept' : 'Access-Reject',
|
||||
secret: this.secret,
|
||||
attributes
|
||||
});
|
||||
console.log(`Sending ${success ? 'accept' : 'reject'} for user ${username}`);
|
||||
|
||||
this.server.sendToClient(response, rinfo.port, rinfo.address, function(err, _bytes) {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
});
|
||||
}; */
|
||||
|
||||
let response: IPacketHandlerResult;
|
||||
|
||||
let i = 0;
|
||||
if (!this.radiusPacketHandlers[i]) {
|
||||
throw new Error('no packet handlers registered');
|
||||
}
|
||||
|
||||
// process packet handlers until we get a response
|
||||
do {
|
||||
/* response is of type IPacketHandlerResult */
|
||||
response = await this.radiusPacketHandlers[i].handlePacket(packet.attributes, packet);
|
||||
i++;
|
||||
} while (this.radiusPacketHandlers[i] && (!response || !response.code));
|
||||
|
||||
// still no response, we are done here
|
||||
if (!response || !response.code) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// all fine, return radius encoded response
|
||||
return {
|
||||
data: radius.encode_response({
|
||||
packet,
|
||||
code: response.code,
|
||||
secret: this.secret,
|
||||
attributes: response.attributes
|
||||
}),
|
||||
// if message is accept or reject, we conside this as final message
|
||||
// this means we do not expect a reponse from the client again (acknowledgement for package)
|
||||
expectAcknowledgment: response.code === PacketResponseCode.AccessChallenge
|
||||
};
|
||||
}
|
||||
}
|
36
src/radius/handler/DefaultPacketHandler.ts
Normal file
36
src/radius/handler/DefaultPacketHandler.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { IAuthentication } from '../../types/Authentication';
|
||||
import {
|
||||
IPacketHandler,
|
||||
IPacketHandlerResult,
|
||||
PacketResponseCode
|
||||
} from '../../types/PacketHandler';
|
||||
|
||||
export class DefaultPacketHandler implements IPacketHandler {
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
|
||||
async handlePacket(attributes: { [key: string]: Buffer }): Promise<IPacketHandlerResult> {
|
||||
const username = attributes['User-Name'];
|
||||
const password = attributes['User-Password'];
|
||||
|
||||
if (!username || !password) {
|
||||
// params missing, this handler cannot continue...
|
||||
return {};
|
||||
}
|
||||
|
||||
const authenticated = await this.authentication.authenticate(
|
||||
username.toString(),
|
||||
password.toString()
|
||||
);
|
||||
if (authenticated) {
|
||||
// success
|
||||
return {
|
||||
code: PacketResponseCode.AccessAccept
|
||||
};
|
||||
}
|
||||
|
||||
// Failed
|
||||
return {
|
||||
code: PacketResponseCode.AccessReject
|
||||
};
|
||||
}
|
||||
}
|
193
src/radius/handler/EAPPacketHandler.ts
Normal file
193
src/radius/handler/EAPPacketHandler.ts
Normal file
@ -0,0 +1,193 @@
|
||||
// https://tools.ietf.org/html/rfc3748#section-4.1
|
||||
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { RadiusPacket } from 'radius';
|
||||
import { EAPTTLS } from './eapMethods/EAPTTLS';
|
||||
import { makeid } from '../../helpers';
|
||||
import {
|
||||
IPacketHandler,
|
||||
IPacketHandlerResult,
|
||||
PacketResponseCode
|
||||
} from '../../types/PacketHandler';
|
||||
import { IAuthentication } from '../../types/Authentication';
|
||||
import { IEAPMethod } from '../../types/EAPMethod';
|
||||
|
||||
export class EAPPacketHandler implements IPacketHandler {
|
||||
private eapMethods: IEAPMethod[] = [];
|
||||
|
||||
// private eapConnectionStates: { [key: string]: { validMethods: IEAPMethod[] } } = {};
|
||||
private eapConnectionStates = new NodeCache({ useClones: false, stdTTL: 3600 }); // max for one hour
|
||||
|
||||
constructor(authentication: IAuthentication) {
|
||||
this.eapMethods.push(new EAPTTLS(authentication));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param msgType 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK
|
||||
*/
|
||||
private async buildEAPResponse(
|
||||
identifier: number,
|
||||
msgType: number,
|
||||
data?: Buffer
|
||||
): Promise<IPacketHandlerResult> {
|
||||
/** build a package according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
const buffer = Buffer.from([
|
||||
1, // request
|
||||
identifier,
|
||||
0, // length (1/2)
|
||||
0, // length (2/2)
|
||||
msgType // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
|
||||
]);
|
||||
|
||||
const resBuffer = data ? Buffer.concat([buffer, data]) : buffer;
|
||||
// set EAP length header
|
||||
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
|
||||
|
||||
return {
|
||||
code: PacketResponseCode.AccessChallenge,
|
||||
attributes: [['EAP-Message', buffer]]
|
||||
};
|
||||
}
|
||||
|
||||
private decodeEAPHeader(msg: Buffer) {
|
||||
/**
|
||||
* parse msg according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
|
||||
/*
|
||||
code:
|
||||
1 Request
|
||||
2 Response
|
||||
3 Success
|
||||
4 Failure
|
||||
*/
|
||||
const code = msg.slice(0, 1).readUInt8(0);
|
||||
/* identifier is a number */
|
||||
const identifier = msg.slice(1, 2).readUInt8(0);
|
||||
const length = msg.slice(2, 4).readInt16BE(0);
|
||||
/* EAP type */
|
||||
const type = msg.slice(4, 5).readUInt8(0);
|
||||
const data = msg.slice(5);
|
||||
|
||||
return {
|
||||
code,
|
||||
identifier,
|
||||
length,
|
||||
type,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
async handlePacket(
|
||||
attributes: { [key: string]: Buffer },
|
||||
orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult> {
|
||||
if (!attributes['EAP-Message']) {
|
||||
// not an EAP message
|
||||
return {};
|
||||
}
|
||||
|
||||
const stateID = (attributes.State && attributes.State.toString()) || makeid(16);
|
||||
|
||||
if (!this.eapConnectionStates.get(stateID)) {
|
||||
this.eapConnectionStates.set(stateID, {
|
||||
validMethods: this.eapMethods // on init all registered eap methods are valid, we kick them out in case we get a NAK response
|
||||
});
|
||||
}
|
||||
|
||||
// EAP MESSAGE
|
||||
const msg = attributes['EAP-Message'];
|
||||
|
||||
const { code, type, identifier, data } = this.decodeEAPHeader(msg);
|
||||
|
||||
const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] };
|
||||
|
||||
switch (code) {
|
||||
case 1: // for request
|
||||
case 2: // for response
|
||||
switch (type) {
|
||||
case 1: // identifiy
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {});
|
||||
// start identify
|
||||
if (currentState.validMethods.length > 0) {
|
||||
return currentState.validMethods[0].identify(identifier, stateID);
|
||||
}
|
||||
|
||||
return this.buildEAPResponse(identifier, 3);
|
||||
case 2: // notification
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
|
||||
console.info('notification');
|
||||
break;
|
||||
case 4: // md5-challenge
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {});
|
||||
|
||||
console.info('md5-challenge');
|
||||
break;
|
||||
case 254: // expanded type
|
||||
console.error('not implemented type', type);
|
||||
break;
|
||||
case 3: // nak
|
||||
if (data) {
|
||||
const supportedEAPMethods: number[] = [];
|
||||
for (const supportedMethod of data) {
|
||||
supportedEAPMethods.push(supportedMethod);
|
||||
}
|
||||
|
||||
this.eapConnectionStates.set(stateID, {
|
||||
...currentState,
|
||||
validMethods: currentState.validMethods.filter(method => {
|
||||
return supportedEAPMethods.includes(method.getEAPType()); // kick it out?
|
||||
})
|
||||
});
|
||||
}
|
||||
// continue with responding a NAK and add rest of supported methods
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
default: {
|
||||
const eapMethod = currentState.validMethods.find(method => {
|
||||
return type === method.getEAPType();
|
||||
});
|
||||
|
||||
if (eapMethod) {
|
||||
return eapMethod.handleMessage(identifier, stateID, msg, orgRadiusPacket);
|
||||
}
|
||||
|
||||
// we do not support this auth type, ask for something we support
|
||||
const serverSupportedMethods = currentState.validMethods.map(
|
||||
method => method.getEAPType
|
||||
);
|
||||
|
||||
console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`);
|
||||
|
||||
return this.buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
console.log('Client Auth Success');
|
||||
break;
|
||||
case 4:
|
||||
console.log('Client Auth FAILURE');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
// silently ignore;
|
||||
return {};
|
||||
}
|
||||
}
|
460
src/radius/handler/eapMethods/EAPTTLS.ts
Normal file
460
src/radius/handler/eapMethods/EAPTTLS.ts
Normal file
@ -0,0 +1,460 @@
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as tls from 'tls';
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { RadiusPacket } from 'radius';
|
||||
import { encodeTunnelPW, ITLSServer, startTLSServer } from '../../../tls/crypt';
|
||||
import { ResponseAuthHandler } from '../../../types/Handler';
|
||||
import { PAPChallenge } from './challenges/PAPChallenge';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../types/PacketHandler';
|
||||
import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../helpers';
|
||||
import { IEAPMethod } from '../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../types/Authentication';
|
||||
import { secret } from '../../../../config';
|
||||
|
||||
interface IEAPResponseHandlers {
|
||||
response: (respData?: Buffer, msgType?: number) => void;
|
||||
checkAuth: ResponseAuthHandler;
|
||||
}
|
||||
|
||||
/* const handlers = {
|
||||
response: (EAPMessage: Buffer) => {
|
||||
const attributes: any = [['State', Buffer.from(state)]];
|
||||
let sentDataSize = 0;
|
||||
do {
|
||||
if (EAPMessage.length > 0) {
|
||||
attributes.push([
|
||||
'EAP-Message',
|
||||
EAPMessage.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
|
||||
]);
|
||||
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
|
||||
}
|
||||
} while (sentDataSize < EAPMessage.length);
|
||||
|
||||
const response = radius.encode_response({
|
||||
packet,
|
||||
code: 'Access-Challenge',
|
||||
secret: this.secret,
|
||||
attributes
|
||||
});
|
||||
|
||||
waitForNextMsg[state] = newDeferredPromise();
|
||||
|
||||
server.sendToClient(
|
||||
response,
|
||||
rinfo.port,
|
||||
rinfo.address,
|
||||
function(err, _bytes) {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
return waitForNextMsg[state].promise;
|
||||
},
|
||||
checkAuth
|
||||
};
|
||||
|
||||
|
||||
const attributes: any = [['State', Buffer.from(stateID)]];
|
||||
let sentDataSize = 0;
|
||||
do {
|
||||
if (EAPMessage.length > 0) {
|
||||
attributes.push([
|
||||
'EAP-Message',
|
||||
EAPMessage.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
|
||||
]);
|
||||
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
|
||||
}
|
||||
} while (sentDataSize < EAPMessage.length);
|
||||
|
||||
const response = radius.encode_response({
|
||||
packet,
|
||||
code: 'Access-Challenge',
|
||||
secret: this.secret,
|
||||
attributes
|
||||
});
|
||||
|
||||
waitForNextMsg[stateID] = newDeferredPromise();
|
||||
|
||||
server.sendToClient(
|
||||
response,
|
||||
rinfo.port,
|
||||
rinfo.address,
|
||||
function(err, _bytes) {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
}
|
||||
},
|
||||
stateID
|
||||
);
|
||||
|
||||
return waitForNextMsg[stateID].promise;
|
||||
*/
|
||||
/* if (waitForNextMsg[state]) {
|
||||
const identifier = attributes['EAP-Message'].slice(1, 2).readUInt8(0); // .toString('hex');
|
||||
waitForNextMsg[state].resolve({ response: handlers.response, identifier });
|
||||
} */
|
||||
|
||||
function tlsHasExportKeyingMaterial(
|
||||
tlsSocket: any
|
||||
): tlsSocket is {
|
||||
exportKeyingMaterial: (length: number, label: string, context?: Buffer) => Buffer;
|
||||
} {
|
||||
return typeof (tlsSocket as any).exportKeyingMaterial === 'function';
|
||||
}
|
||||
|
||||
export class EAPTTLS implements IEAPMethod {
|
||||
private papChallenge: PAPChallenge = new PAPChallenge();
|
||||
|
||||
// { [key: string]: Buffer } = {};
|
||||
private queueData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
|
||||
|
||||
private openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour
|
||||
|
||||
getEAPType(): number {
|
||||
return 21;
|
||||
}
|
||||
|
||||
identify(identifier: number, stateID: string): IPacketHandlerResult {
|
||||
return this.buildEAPTTLSResponse(identifier, 21, 0x20, stateID);
|
||||
}
|
||||
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
|
||||
private buildEAPTTLSResponse(
|
||||
identifier: number,
|
||||
msgType = 21,
|
||||
msgFlags = 0x00,
|
||||
stateID: string,
|
||||
data?: Buffer,
|
||||
newResponse = true
|
||||
): IPacketHandlerResult {
|
||||
const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4;
|
||||
console.log('maxSize', maxSize);
|
||||
|
||||
/* it's the first one and we have more, therefore include length */
|
||||
const includeLength = data && newResponse && data.length > maxSize;
|
||||
|
||||
// extract data party
|
||||
const dataToSend = data && data.length > 0 && data.slice(0, maxSize);
|
||||
const dataToQueue = data && data.length > maxSize && data.slice(maxSize);
|
||||
|
||||
/*
|
||||
0 1 2 3 4 5 6 7 8
|
||||
+-+-+-+-+-+-+-+-+
|
||||
|L M R R R R R R|
|
||||
+-+-+-+-+-+-+-+-+
|
||||
|
||||
L = Length included
|
||||
M = More fragments
|
||||
R = Reserved
|
||||
|
||||
The L bit (length included) is set to indicate the presence of the
|
||||
four-octet TLS Message Length field, and MUST be set for the first
|
||||
fragment of a fragmented TLS message or set of messages. The M
|
||||
bit (more fragments) is set on all but the last fragment.
|
||||
Implementations of this specification MUST set the reserved bits
|
||||
to zero, and MUST ignore them on reception.
|
||||
*/
|
||||
|
||||
const flags =
|
||||
msgFlags +
|
||||
(includeLength ? 0b10000000 : 0) + // set L bit
|
||||
(dataToQueue && dataToQueue.length > 0 ? 0b01000000 : 0); // we have more data to come, set M bit
|
||||
|
||||
let buffer = Buffer.from([
|
||||
1, // request
|
||||
identifier + 1, // increase id by 1
|
||||
0, // length (1/2)
|
||||
0, // length (2/2)
|
||||
msgType, // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
|
||||
flags // flags: 000000 (L include lenghts, M .. more to come)
|
||||
]);
|
||||
|
||||
// append length
|
||||
if (includeLength && data) {
|
||||
const length = Buffer.alloc(4);
|
||||
length.writeInt32BE(data.byteLength, 0);
|
||||
|
||||
buffer = Buffer.concat([buffer, length]);
|
||||
}
|
||||
|
||||
// build final buffer with data
|
||||
const resBuffer = dataToSend ? Buffer.concat([buffer, dataToSend]) : buffer;
|
||||
|
||||
// set EAP length header
|
||||
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
|
||||
|
||||
console.log('<<<<<<<<<<<< EAP RESPONSE TO CLIENT', {
|
||||
code: 1,
|
||||
identifier: identifier + 1,
|
||||
includeLength,
|
||||
dataLength: (data && data.byteLength) || 0,
|
||||
msgType: msgType.toString(10),
|
||||
flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
data
|
||||
});
|
||||
|
||||
if (dataToQueue) {
|
||||
// we couldn't send all at once, queue the rest and send later
|
||||
this.queueData.set(stateID, dataToQueue);
|
||||
} else {
|
||||
this.queueData.del(stateID);
|
||||
}
|
||||
|
||||
const attributes: any = [['State', Buffer.from(stateID)]];
|
||||
let sentDataSize = 0;
|
||||
do {
|
||||
if (resBuffer.length > 0) {
|
||||
attributes.push([
|
||||
'EAP-Message',
|
||||
resBuffer.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
|
||||
]);
|
||||
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
|
||||
}
|
||||
} while (sentDataSize < resBuffer.length);
|
||||
|
||||
return {
|
||||
code: PacketResponseCode.AccessChallenge,
|
||||
attributes
|
||||
};
|
||||
}
|
||||
|
||||
decodeTTLSMessage(msg: Buffer) {
|
||||
/**
|
||||
* The EAP-TTLS packet format is shown below. The fields are
|
||||
transmitted left to right.
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Flags | Message Length
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
Message Length | Data...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex');
|
||||
/*
|
||||
0 1 2 3 4 5 6 7
|
||||
+---+---+---+---+---+---+---+---+
|
||||
| L | M | S | R | R | V |
|
||||
+---+---+---+---+---+---+---+---+
|
||||
|
||||
L = Length included
|
||||
M = More fragments
|
||||
S = Start
|
||||
R = Reserved
|
||||
V = Version (000 for EAP-TTLSv0)
|
||||
*/
|
||||
const decodedFlags = {
|
||||
// L
|
||||
lengthIncluded: flags & 0b010000000,
|
||||
// M
|
||||
moreFragments: flags & 0b001000000,
|
||||
// S
|
||||
start: flags & 0b000100000,
|
||||
// R
|
||||
// reserved: flags & 0b000011000,
|
||||
// V
|
||||
version: flags & 0b010000111
|
||||
};
|
||||
|
||||
let msglength;
|
||||
if (decodedFlags.lengthIncluded) {
|
||||
msglength = msg.slice(6, 10).readInt32BE(0); // .readDoubleLE(0); // .toString('hex');
|
||||
}
|
||||
const data = msg.slice(decodedFlags.lengthIncluded ? 10 : 6, msg.length);
|
||||
|
||||
return {
|
||||
decodedFlags,
|
||||
msglength,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
authResponse(
|
||||
identifier: number,
|
||||
success: boolean,
|
||||
socket: tls.TLSSocket,
|
||||
packet: RadiusPacket
|
||||
): IPacketHandlerResult {
|
||||
const buffer = Buffer.from([
|
||||
success ? 3 : 4, // 3.. success, 4... failure
|
||||
identifier + 1,
|
||||
0, // length (1/2)
|
||||
4 // length (2/2)
|
||||
]);
|
||||
|
||||
const attributes: any[] = [];
|
||||
attributes.push(['EAP-Message', buffer]);
|
||||
|
||||
if (packet.attributes && packet.attributes['User-Name']) {
|
||||
// reappend username to response
|
||||
attributes.push(['User-Name', packet.attributes['User-Name']]);
|
||||
}
|
||||
|
||||
/*
|
||||
if (sess->eap_if->eapKeyDataLen > 64) {
|
||||
len = 32;
|
||||
} else {
|
||||
len = sess->eap_if->eapKeyDataLen / 2;
|
||||
}
|
||||
*/
|
||||
if (tlsHasExportKeyingMaterial(socket)) {
|
||||
const keyingMaterial = (socket as any).exportKeyingMaterial(128, 'ttls keying material');
|
||||
|
||||
// console.log('keyingMaterial', keyingMaterial);
|
||||
|
||||
// eapKeyData + len
|
||||
attributes.push([
|
||||
'Vendor-Specific',
|
||||
311,
|
||||
[
|
||||
[
|
||||
16,
|
||||
encodeTunnelPW(
|
||||
keyingMaterial.slice(64),
|
||||
(packet as any).authenticator,
|
||||
// params.packet.attributes['Message-Authenticator'],
|
||||
secret
|
||||
)
|
||||
]
|
||||
]
|
||||
]); // MS-MPPE-Send-Key
|
||||
|
||||
// eapKeyData
|
||||
attributes.push([
|
||||
'Vendor-Specific',
|
||||
311,
|
||||
[
|
||||
[
|
||||
17,
|
||||
encodeTunnelPW(
|
||||
keyingMaterial.slice(0, 64),
|
||||
(packet as any).authenticator,
|
||||
// params.packet.attributes['Message-Authenticator'],
|
||||
secret
|
||||
)
|
||||
]
|
||||
]
|
||||
]); // MS-MPPE-Recv-Key
|
||||
} else {
|
||||
console.error(
|
||||
'FATAL: no exportKeyingMaterial method available!!!, you need latest NODE JS, see https://github.com/nodejs/node/pull/31814'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
code: success ? PacketResponseCode.AccessAccept : PacketResponseCode.AccessReject,
|
||||
attributes
|
||||
};
|
||||
}
|
||||
|
||||
async handleMessage(
|
||||
identifier: number,
|
||||
stateID: string,
|
||||
msg: Buffer,
|
||||
orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult> {
|
||||
const { decodedFlags, msglength, data } = this.decodeTTLSMessage(msg);
|
||||
|
||||
// check if no data package is there and we have something in the queue, if so.. empty the queue first
|
||||
if (!data || data.length === 0) {
|
||||
console.warn(
|
||||
`>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS, ACK / NACK (no data, just a confirmation, ID: ${identifier})`
|
||||
);
|
||||
const queuedData = this.queueData.get(stateID);
|
||||
if (queuedData instanceof Buffer && queuedData.length > 0) {
|
||||
return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', {
|
||||
// flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
decodedFlags,
|
||||
identifier,
|
||||
msglength
|
||||
// data,
|
||||
// dataStr: data.toString()
|
||||
});
|
||||
|
||||
let connection = this.openTLSSockets.get(stateID) as ITLSServer;
|
||||
|
||||
if (!connection) {
|
||||
connection = startTLSServer();
|
||||
this.openTLSSockets.set(stateID, connection);
|
||||
|
||||
connection.events.on('end', () => {
|
||||
// cleanup socket
|
||||
console.log('ENDING SOCKET');
|
||||
this.openTLSSockets.del(stateID);
|
||||
});
|
||||
}
|
||||
|
||||
const sendResponsePromise = newDeferredPromise();
|
||||
|
||||
const incomingMessageHandler = async (incomingData: Buffer) => {
|
||||
const type = incomingData.slice(3, 4).readUInt8(0);
|
||||
// const code = data.slice(4, 5).readUInt8(0);
|
||||
|
||||
switch (type) {
|
||||
case 1: // PAP / CHAP
|
||||
try {
|
||||
const { username, password } = this.papChallenge.decode(incomingData);
|
||||
const authResult = await this.authentication.authenticate(username, password);
|
||||
|
||||
sendResponsePromise.resolve(
|
||||
this.authResponse(identifier, authResult, connection.tls, orgRadiusPacket)
|
||||
);
|
||||
} catch (err) {
|
||||
// pwd not found..
|
||||
console.error('pwd not found', err);
|
||||
connection.events.emit('end');
|
||||
// NAK
|
||||
sendResponsePromise.resolve(this.buildEAPTTLSResponse(identifier, 3, 0, stateID));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log('data', incomingData);
|
||||
console.log('data str', incomingData.toString());
|
||||
|
||||
// currentConnection!.events.emit('end');
|
||||
|
||||
console.log('UNSUPPORTED AUTH TYPE, requesting PAP');
|
||||
// throw new Error(`unsupported auth type${type}`);
|
||||
sendResponsePromise.resolve(
|
||||
this.buildEAPTTLSResponse(identifier, 3, 0, stateID, Buffer.from([1]))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const responseHandler = (encryptedResponseData: Buffer) => {
|
||||
// send back...
|
||||
sendResponsePromise.resolve(
|
||||
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
|
||||
);
|
||||
};
|
||||
|
||||
// register event listeners
|
||||
connection.events.on('incoming', incomingMessageHandler);
|
||||
connection.events.on('response', responseHandler);
|
||||
|
||||
// emit data to tls server
|
||||
connection.events.emit('send', data);
|
||||
const responseData = await sendResponsePromise.promise;
|
||||
|
||||
// cleanup
|
||||
connection.events.off('incoming', incomingMessageHandler);
|
||||
connection.events.off('response', responseHandler);
|
||||
|
||||
// send response
|
||||
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { IEAPChallenge } from '../../types/EAPChallenge';
|
||||
import { IEAPChallenge } from '../../../../types/EAPChallenge';
|
||||
|
||||
export class PAPChallenge implements IEAPChallenge {
|
||||
// i couldn't find any documentation about it, therefore best guess how this is processed...
|
80
src/server/UDPServer.ts
Normal file
80
src/server/UDPServer.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import * as dgram from 'dgram';
|
||||
import { SocketType } from 'dgram';
|
||||
import * as events from 'events';
|
||||
import { EventEmitter } from 'events';
|
||||
import { newDeferredPromise } from '../helpers';
|
||||
import { IServer } from '../types/Server';
|
||||
|
||||
export class UDPServer extends events.EventEmitter implements IServer {
|
||||
static MAX_RETRIES = 3;
|
||||
|
||||
private timeout: { [key: string]: NodeJS.Timeout } = {};
|
||||
|
||||
private server: dgram.Socket;
|
||||
|
||||
constructor(private port: number, type: SocketType = 'udp4') {
|
||||
super();
|
||||
this.server = dgram.createSocket(type);
|
||||
}
|
||||
|
||||
sendToClient(
|
||||
msg: string | Uint8Array,
|
||||
port?: number,
|
||||
address?: string,
|
||||
callback?: (error: Error | null, bytes: number) => void,
|
||||
expectAcknowledgment = true
|
||||
): void {
|
||||
let retried = 0;
|
||||
|
||||
const sendResponse = (): void => {
|
||||
if (retried > 0) {
|
||||
console.warn(
|
||||
`no confirmation of last message from ${address}:${port}, re-sending response... (bytes: ${msg.length}, try: ${retried}/${UDPServer.MAX_RETRIES})`
|
||||
);
|
||||
}
|
||||
|
||||
// send message to client
|
||||
this.server.send(msg, 0, msg.length, port, address, callback);
|
||||
|
||||
// retry up to MAX_RETRIES to send this message,
|
||||
// we automatically retry if there is no confirmation (=any incoming message from client)
|
||||
// if expectAcknowledgment (e.g. Access-Accept or Access-Reject) is set, we do not retry
|
||||
const identifierForRetry = `${address}:${port}`;
|
||||
if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) {
|
||||
this.timeout[identifierForRetry] = setTimeout(sendResponse, 600 * (retried + 1));
|
||||
}
|
||||
retried += 1;
|
||||
};
|
||||
|
||||
sendResponse();
|
||||
}
|
||||
|
||||
async start(): Promise<EventEmitter> {
|
||||
const startServer = newDeferredPromise();
|
||||
this.server.on('listening', () => {
|
||||
const address = this.server.address();
|
||||
console.log(`radius server listening ${address.address}:${address.port}`);
|
||||
|
||||
this.setupListeners();
|
||||
startServer.resolve();
|
||||
});
|
||||
|
||||
this.server.on('message', (_msg, rinfo) => {
|
||||
console.log('incoming message 2');
|
||||
|
||||
// message retrieved, reset timeout handler
|
||||
const identifierForRetry = `${rinfo.address}:${rinfo.port}`;
|
||||
if (this.timeout[identifierForRetry]) {
|
||||
clearTimeout(this.timeout[identifierForRetry]);
|
||||
}
|
||||
});
|
||||
|
||||
this.server.bind(this.port);
|
||||
|
||||
return startServer.promise;
|
||||
}
|
||||
|
||||
private setupListeners() {
|
||||
this.server.on('message', (message, rinfo) => this.emit('message', message, rinfo));
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
import * as events from 'events';
|
||||
import * as tls from 'tls';
|
||||
import { createSecureContext } from 'tls';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
import * as DuplexPair from 'native-duplexpair';
|
||||
import * as constants from 'constants';
|
||||
@ -21,16 +19,20 @@ const tlsOptions: tls.SecureContextOptions = {
|
||||
};
|
||||
console.log('tlsOptions', tlsOptions);
|
||||
const secureContext = createSecureContext(tlsOptions);
|
||||
export const openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour
|
||||
|
||||
export function startTLSServer(): { events: events.EventEmitter; tls: tls.TLSSocket } {
|
||||
export interface ITLSServer {
|
||||
events: events.EventEmitter;
|
||||
tls: tls.TLSSocket;
|
||||
}
|
||||
|
||||
export function startTLSServer(): ITLSServer {
|
||||
const duplexpair = new DuplexPair();
|
||||
const emitter = new events.EventEmitter();
|
||||
|
||||
const cleartext = new tls.TLSSocket(duplexpair.socket1, {
|
||||
secureContext,
|
||||
isServer: true,
|
||||
enableTrace: true,
|
||||
// enableTrace: true,
|
||||
rejectUnauthorized: false,
|
||||
// handshakeTimeout: 10,
|
||||
requestCert: false
|
||||
|
@ -1,3 +1,3 @@
|
||||
export interface IAuthentication {
|
||||
authenticate(username: string, password: string): Promise<string>;
|
||||
authenticate(username: string, password: string): Promise<boolean>;
|
||||
}
|
||||
|
15
src/types/EAPMethod.ts
Normal file
15
src/types/EAPMethod.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { RadiusPacket } from 'radius';
|
||||
import { IPacketHandlerResult } from './PacketHandler';
|
||||
|
||||
export interface IEAPMethod {
|
||||
getEAPType(): number;
|
||||
|
||||
identify(identifier: number, stateID: string): IPacketHandlerResult;
|
||||
|
||||
handleMessage(
|
||||
identifier: number,
|
||||
stateID: string,
|
||||
msg: Buffer,
|
||||
orgRadiusPacket?: RadiusPacket
|
||||
): Promise<IPacketHandlerResult>;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export interface IEAPType {
|
||||
handleMessage(msg: Buffer, state: string, handlers, identifier: number);
|
||||
}
|
19
src/types/PacketHandler.ts
Normal file
19
src/types/PacketHandler.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { RadiusPacket } from 'radius';
|
||||
|
||||
export enum PacketResponseCode {
|
||||
AccessChallenge = 'Access-Challenge',
|
||||
AccessAccept = 'Access-Accept',
|
||||
AccessReject = 'Access-Reject'
|
||||
}
|
||||
|
||||
export interface IPacketHandlerResult {
|
||||
code?: PacketResponseCode;
|
||||
attributes?: [string, Buffer][];
|
||||
}
|
||||
|
||||
export interface IPacketHandler {
|
||||
handlePacket(
|
||||
attributes: { [key: string]: Buffer },
|
||||
orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult>;
|
||||
}
|
32
src/types/Server.ts
Normal file
32
src/types/Server.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { RemoteInfo } from 'dgram';
|
||||
|
||||
/**
|
||||
* @fires IServer#message
|
||||
*/
|
||||
export interface IServer {
|
||||
/**
|
||||
*
|
||||
* @param msg
|
||||
* @param port
|
||||
* @param address
|
||||
* @param callback
|
||||
* @param expectAcknowledgment: if set to false, message is not retried to send again if there is no confirmation
|
||||
*/
|
||||
sendToClient(
|
||||
msg: string | Uint8Array,
|
||||
port?: number,
|
||||
address?: string,
|
||||
callback?: (error: Error | null, bytes: number) => void,
|
||||
expectAcknowledgment?: boolean
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Message event.
|
||||
*
|
||||
* @event IServer#message
|
||||
* @type {object}
|
||||
* @property {message} data - the data of the incoming message
|
||||
* @property {rinfo} optionally remote information
|
||||
*/
|
||||
on(event: 'message', listener: (msg: Buffer, rinfo?: RemoteInfo) => void): this;
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
this is based on freeradius cert directory :)
|
||||
|
||||
1.) edit ca.cnf
|
||||
2.) edit server.cnf
|
||||
3.) replace your choosen pwd (deafult is whatever2020) in create.sh
|
||||
4.) run ./create.sh
|
||||
5.) set your choosen pwd in ~/config.js
|
||||
there is a default certificate already present, if you want to create your own, follow these steps:
|
||||
|
||||
1. edit ca.cnf
|
||||
2. edit server.cnf
|
||||
3. replace your choosen pwd (deafult is whatever2020) in create.sh
|
||||
4. run ./create.sh
|
||||
5. set your choosen pwd in ~/config.js
|
||||
|
||||
|
@ -5,31 +5,31 @@ Certificate:
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority
|
||||
Validity
|
||||
Not Before: Feb 22 16:37:01 2020 GMT
|
||||
Not After : Jul 27 16:37:01 2036 GMT
|
||||
Not Before: Feb 22 17:05:38 2020 GMT
|
||||
Not After : Jul 27 17:05:38 2036 GMT
|
||||
Subject: C=AT, ST=Radius, O=Example Inc., CN=Example Certificate Authority/emailAddress=admin@example.org
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:eb:86:1d:bf:2e:b6:9a:52:72:3e:db:33:b1:dc:
|
||||
b2:63:23:64:73:4f:d8:eb:32:25:f2:9c:19:a0:1b:
|
||||
32:12:10:8a:d6:f3:c4:0a:10:25:be:0a:dc:14:1b:
|
||||
94:b5:2b:16:df:87:09:c2:99:0b:be:18:5e:fb:0c:
|
||||
0f:e0:d2:8a:6c:e5:22:0e:cd:aa:a8:57:91:e9:dc:
|
||||
4c:dc:b7:11:2b:2c:c8:46:8e:a5:a2:41:90:8a:89:
|
||||
1b:f1:d2:f7:cd:d9:bd:f7:12:19:4b:c9:05:b5:05:
|
||||
99:76:3f:b1:6a:27:42:22:d5:fe:b9:65:aa:95:f2:
|
||||
25:5b:b6:3c:f4:a8:be:59:ce:c5:35:02:39:ba:c8:
|
||||
f6:bd:ce:95:60:e6:bd:1e:fd:a7:e3:8b:3f:e7:45:
|
||||
e0:c9:6d:2d:78:e8:90:46:a6:88:90:04:13:84:6d:
|
||||
a4:2f:b8:d3:fc:93:f8:31:9a:27:67:f4:fb:1d:55:
|
||||
80:e4:20:df:8f:b8:b0:c1:42:88:9e:89:e0:29:6f:
|
||||
36:2e:4e:18:ce:da:92:ad:4e:3e:eb:a3:4f:eb:0b:
|
||||
20:e6:1d:89:51:cf:8f:43:06:e9:d6:5a:94:6f:6a:
|
||||
07:b0:43:3e:d7:b1:a0:68:23:1a:e7:10:65:a1:75:
|
||||
ea:19:bb:ed:7d:e4:9a:07:9b:10:99:0a:5d:75:b3:
|
||||
15:83
|
||||
00:b5:ad:78:5d:89:14:28:92:55:e7:c1:e0:15:46:
|
||||
e7:ea:da:ea:98:bb:ec:33:17:ee:d6:e3:37:1c:34:
|
||||
ea:4e:87:a1:c9:d3:19:de:95:6f:c1:76:13:e0:b4:
|
||||
f5:08:3e:fe:5a:76:b8:4a:e4:94:07:8b:67:4b:b5:
|
||||
d1:97:c1:d3:e1:b1:cd:84:5b:16:aa:dc:56:5b:ff:
|
||||
0a:e7:f3:79:a3:47:4d:5b:6d:e4:d6:21:a0:eb:e1:
|
||||
54:83:87:1c:c5:18:de:d8:52:40:93:7e:fb:c5:df:
|
||||
73:3c:d6:0b:a3:1d:9f:36:c7:6f:59:90:d7:5f:82:
|
||||
80:fe:82:05:1b:1b:77:92:04:c9:25:88:8f:9c:35:
|
||||
91:cc:86:a0:8f:8f:9b:2e:2c:62:af:f3:8a:16:75:
|
||||
30:61:47:8d:24:ea:35:84:43:33:01:ed:d7:33:2c:
|
||||
29:4f:2c:8d:f5:b7:b3:bd:74:4f:2e:83:2d:e1:d6:
|
||||
3d:5e:d9:14:c3:a1:89:38:4d:29:4b:ae:39:c8:a2:
|
||||
dd:ad:f1:42:c8:0d:6c:29:eb:70:c8:dc:e7:41:59:
|
||||
47:1c:89:52:62:53:9c:35:58:8a:39:16:87:61:f2:
|
||||
11:26:3a:2b:a2:19:29:c2:77:31:de:1c:74:c6:57:
|
||||
3a:15:8b:2f:29:61:a7:45:b4:d8:70:a4:d2:ef:da:
|
||||
5d:a1
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Extended Key Usage:
|
||||
@ -40,41 +40,41 @@ Certificate:
|
||||
URI:http://www.example.com/example_ca.crl
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
9f:4d:e9:78:d5:5f:c1:fb:e1:43:d1:9f:2d:d6:c3:f8:94:56:
|
||||
0b:94:2d:b5:e7:d2:bd:f6:3b:e1:69:6b:cb:84:e4:8a:a9:fd:
|
||||
1b:58:16:b5:2d:c7:00:d8:cb:56:37:72:f8:0c:17:a7:17:ec:
|
||||
52:9c:17:b7:78:90:9e:8c:a9:a1:b4:72:87:96:3b:06:48:83:
|
||||
27:8b:ca:8d:0c:8d:06:f9:4d:e9:bb:26:5e:76:a7:79:62:31:
|
||||
d4:99:e2:e3:ad:83:3f:c0:79:5d:0f:60:33:9d:f9:05:cd:12:
|
||||
25:c3:10:22:81:d4:e3:95:3f:b3:ab:21:84:e7:6b:f5:55:9f:
|
||||
8d:30:a7:eb:c4:85:23:94:5d:59:08:c7:48:f9:cf:37:98:9e:
|
||||
96:89:e0:8b:75:05:ce:27:76:e4:bf:e5:14:b6:7f:21:07:bc:
|
||||
67:53:95:f0:c5:ac:bd:4f:9e:46:df:40:6d:8d:8a:80:c6:21:
|
||||
80:ab:c4:bd:ee:77:01:16:9c:d5:7a:62:3b:d0:bd:38:6b:9d:
|
||||
db:4e:0e:aa:cf:03:c8:a5:07:0a:c5:79:42:53:6a:34:74:ec:
|
||||
01:e1:3b:38:fe:14:15:0a:a1:cf:3b:a7:90:76:ba:1b:34:b5:
|
||||
70:77:78:0e:6d:2d:96:8b:d5:dc:1b:1e:21:a6:57:6a:97:5a:
|
||||
67:49:64:81
|
||||
60:49:5c:0c:1d:1a:c3:86:16:b7:d8:63:b7:b9:9b:4d:74:49:
|
||||
25:05:c0:28:f4:c6:95:ce:63:95:b7:99:41:54:7f:64:5d:9a:
|
||||
8f:f5:a7:96:09:8c:67:42:61:5d:c5:af:e4:d9:33:28:20:2a:
|
||||
1f:76:5b:a7:1f:54:47:c4:94:4f:de:bb:6d:ea:36:51:44:bf:
|
||||
90:82:b3:7c:91:28:6a:1e:dd:76:66:38:2c:0f:ed:8a:fd:b6:
|
||||
91:28:3b:59:f0:b3:42:4b:bb:fb:88:d9:d8:6f:e2:a9:79:14:
|
||||
df:0b:53:76:be:23:c1:0e:96:85:aa:d9:3f:d6:60:7a:a5:a9:
|
||||
5a:1d:17:05:7e:84:7a:36:0b:a0:eb:96:8d:75:08:ab:ea:e2:
|
||||
9b:4c:dc:92:05:b0:bc:2a:c0:ff:21:89:02:c2:cd:07:ee:85:
|
||||
55:5b:cf:bd:bc:d7:b1:8a:54:51:5a:58:f6:77:e5:76:85:26:
|
||||
fd:dd:e8:ec:aa:45:c8:cf:d1:10:16:68:97:e4:e7:06:e3:22:
|
||||
9c:4d:f5:85:bb:37:26:d4:fc:47:af:26:03:f3:e1:17:cc:20:
|
||||
33:c1:c4:e2:b3:b7:1e:d3:0a:81:85:ff:e6:79:70:07:a7:8b:
|
||||
3c:1e:9e:1b:e8:f5:a1:3e:69:c5:90:f4:7f:49:a7:19:93:fa:
|
||||
65:fc:0e:43
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx
|
||||
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
|
||||
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw
|
||||
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx
|
||||
NjM3MDFaFw0zNjA3MjcxNjM3MDFaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
|
||||
NzA1MzhaFw0zNjA3MjcxNzA1MzhaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
|
||||
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD
|
||||
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
|
||||
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA64Ydvy62mlJy
|
||||
PtszsdyyYyNkc0/Y6zIl8pwZoBsyEhCK1vPEChAlvgrcFBuUtSsW34cJwpkLvhhe
|
||||
+wwP4NKKbOUiDs2qqFeR6dxM3LcRKyzIRo6lokGQiokb8dL3zdm99xIZS8kFtQWZ
|
||||
dj+xaidCItX+uWWqlfIlW7Y89Ki+Wc7FNQI5usj2vc6VYOa9Hv2n44s/50XgyW0t
|
||||
eOiQRqaIkAQThG2kL7jT/JP4MZonZ/T7HVWA5CDfj7iwwUKInongKW82Lk4YztqS
|
||||
rU4+66NP6wsg5h2JUc+PQwbp1lqUb2oHsEM+17GgaCMa5xBloXXqGbvtfeSaB5sQ
|
||||
mQpddbMVgwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
|
||||
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAta14XYkUKJJV
|
||||
58HgFUbn6trqmLvsMxfu1uM3HDTqToehydMZ3pVvwXYT4LT1CD7+Wna4SuSUB4tn
|
||||
S7XRl8HT4bHNhFsWqtxWW/8K5/N5o0dNW23k1iGg6+FUg4ccxRje2FJAk377xd9z
|
||||
PNYLox2fNsdvWZDXX4KA/oIFGxt3kgTJJYiPnDWRzIagj4+bLixir/OKFnUwYUeN
|
||||
JOo1hEMzAe3XMywpTyyN9bezvXRPLoMt4dY9XtkUw6GJOE0pS645yKLdrfFCyA1s
|
||||
KetwyNznQVlHHIlSYlOcNViKORaHYfIRJjorohkpwncx3hx0xlc6FYsvKWGnRbTY
|
||||
cKTS79pdoQIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
|
||||
MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQCfTel41V/B++FD0Z8t1sP4lFYLlC2159K99jvhaWvL
|
||||
hOSKqf0bWBa1LccA2MtWN3L4DBenF+xSnBe3eJCejKmhtHKHljsGSIMni8qNDI0G
|
||||
+U3puyZedqd5YjHUmeLjrYM/wHldD2AznfkFzRIlwxAigdTjlT+zqyGE52v1VZ+N
|
||||
MKfrxIUjlF1ZCMdI+c83mJ6WieCLdQXOJ3bkv+UUtn8hB7xnU5Xwxay9T55G30Bt
|
||||
jYqAxiGAq8S97ncBFpzVemI70L04a53bTg6qzwPIpQcKxXlCU2o0dOwB4Ts4/hQV
|
||||
CqHPO6eQdrobNLVwd3gObS2Wi9XcGx4hpldql1pnSWSB
|
||||
CSqGSIb3DQEBCwUAA4IBAQBgSVwMHRrDhha32GO3uZtNdEklBcAo9MaVzmOVt5lB
|
||||
VH9kXZqP9aeWCYxnQmFdxa/k2TMoICofdlunH1RHxJRP3rtt6jZRRL+QgrN8kShq
|
||||
Ht12ZjgsD+2K/baRKDtZ8LNCS7v7iNnYb+KpeRTfC1N2viPBDpaFqtk/1mB6pala
|
||||
HRcFfoR6Ngug65aNdQir6uKbTNySBbC8KsD/IYkCws0H7oVVW8+9vNexilRRWlj2
|
||||
d+V2hSb93ejsqkXIz9EQFmiX5OcG4yKcTfWFuzcm1PxHryYD8+EXzCAzwcTis7ce
|
||||
0wqBhf/meXAHp4s8Hp4b6PWhPmnFkPR/SacZk/pl/A5D
|
||||
-----END CERTIFICATE-----
|
||||
|
30
ssl/cert/ca.key
Normal file
30
ssl/cert/ca.key
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQI1Fm+/6EHcV8CAggA
|
||||
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECP14mHLiQP0EBIIEyFFD//WLNJgR
|
||||
nADmlyQgvEtEH+Rbbd6OtUXyXVLk9OtDG3s/JTh8SeeieJbm3PaLanAcyV/DgB2Q
|
||||
lseLe3WLLQSQ2lOjlU2e6KsRk+NDFaiJ5BZqISHfmrOJnZPg27rXN5EeCReRWwAr
|
||||
Myyb9Ijbw9j1LL4EwoEKmJ59XEC5Cy4bIyEDROF7ONJvwzaRyPPT5ZsB5qxozjp7
|
||||
C4YOr55AUhidEHBxmFdU7Sx1uJhxYFRccGV7m4C+00z2TKx81IWFtbj2404mBMQM
|
||||
WGf7Ncfuw1SsRwCxv89Z6+eP38Un3egT+inTFeP4E3iMWdZiK9JpYRO6qZzYdw9o
|
||||
4QyAl+oJIFjPImA+vX29J6C0BFvFAnZwmLe30nK9HMja/qynNjeF//sC3LpuC4Dx
|
||||
f07Od5X6Bq7OMmMgBNPr1Y+MmwDomrthlJhUIovowHIKQnim/Awmwv613H0kI9Dp
|
||||
bp04LMcY63cZL/ShyAG3hNqnDs6ULKv/35IA2HOPyyOzhrJl5OJbCxaoW4rp96nK
|
||||
JTFAxI2oQZtNKX4tmxP0QZ5kmd3jmBtjmuqyIGQO3PRA465bxCO6Rajo1kOhygab
|
||||
9L3dp3FRIRkWJzSxIteEmkv6RgT4CvS9d9dwk1t+GKqH7Qcg++2+/Yy7rWopxsRY
|
||||
hjAygy/kLE3HXZUgHWuJhomSTNfkjj6sHEFUiIB8RJ9DWoRkfkloGUml5/4QoGWn
|
||||
qo4qOs5vXt1iRnc65cXgT2h/IX/ohcPcZirI0gf9mKN470U5zzlUpDmCMVYgv+gh
|
||||
Te0uhghT4GIk6fZWTUKTiLmqvG6hEdVeHAfboJ6aPzrQYT2XnonRf9hQfq2SmznC
|
||||
J7MNHaLqeR0pULxxWY+UrS3oPhQ4ICDMDEGQxwVNpqLjzQJnZjI7ZYQKS324FDaH
|
||||
PPrdG/a9bc3lVeBdcCjvH6Tz+rHUYW4x7WGUeYprz7rAl8XtYtl+zT3bngLxO3D5
|
||||
mc6R+aGE1tcBY0EL1K2K0UzPqNLSUTrgnw4K+L2sgQJqKrpdyGcAZYnW0eH6ULIl
|
||||
VgKL1Ef0lX7cD6sdxASYHgqyYfc4+vAo6drwKXwb98jQFe5q73d5f1SqOiC/APuR
|
||||
ZIMRVMBydD3Mp0L7lokDpS6eraTZ+XeGwtbEzb2mC4urYoT9yEH4CdOwUe0GrKQE
|
||||
B/6mV0MEyrzlWxFpxZFkByLFf1FBlqKHpd09Sj9TdNiZom+mc90D76FiyZ83H+Pp
|
||||
D9tzelr8G5A5RVEPOaHOVL1LiDQA12B5tFhgFldUI4BXJu+LnR/4qNyNt2xedrHO
|
||||
GY/FoJjlnTMf5/S3erH+1VeIwjrnwrl4w3o2zymE19Yb2zEt9fyP67R0q/trZOs+
|
||||
umxTOxyFvJCzZoLkIFz9oGfo+IQYqqxfPC0gYYlkF1jWX1MarLwZoiXSHEkBlfez
|
||||
t61ZqYxNMWt8cxwfb5MfRpeXaY4wH1o6+GW8DgNMeq5LxgLASkvNdG0dzXTK8DPG
|
||||
sGx09dFGEDKYmljT+1chL+KjkxWIqgrq05NwpE/7OF3ZuiG2hv6ys5STY3URnINg
|
||||
lz7zqzgt1gMeCV8kikXKWXwKqxH0L14AYMDhfPaXRM23nsjC3c/h4CfySUutlpxp
|
||||
M2oE3tWQQ6TOv3yAqhRj+g==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
@ -1,29 +1,29 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE+jCCA+KgAwIBAgIUVIBUu2vZ0TrxOwqlIyfxooaF4bIwDQYJKoZIhvcNAQEL
|
||||
MIIE+jCCA+KgAwIBAgIUA7R4NLhoa6Mt/Zek66V7+Qnx9VEwDQYJKoZIhvcNAQEL
|
||||
BQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNv
|
||||
bWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFh
|
||||
ZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBB
|
||||
dXRob3JpdHkwHhcNMjAwMjIyMTYzNzAxWhcNMjkxMjMxMTYzNzAxWjCBkzELMAkG
|
||||
dXRob3JpdHkwHhcNMjAwMjIyMTcwNTM4WhcNMjkxMjMxMTcwNTM4WjCBkzELMAkG
|
||||
A1UEBhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUw
|
||||
EwYDVQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
|
||||
bGUub3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMg5QnJc+a8K0YGIcnZ0OfFZ
|
||||
ZNQUMIBJIxdE3LnIa1Y6JNA89oVodtD/J4bFOHa8ehji7R6JzTCpDi6Sc61D9mT2
|
||||
Fa1sm8wSrD4NU+tMlaK5U73C6nYrAtnTpFnDeQQEzgfyzoN988QXAknY++Ls0CU/
|
||||
B2Pjd5D4Sk1VOOf88/XYyU5OM5jT5/skOuBULdGhbL9TME3PRieMLOokTFZjNnDy
|
||||
uCKV3p5PD8MYAVh6qNEdHHnQMp559JfdzAh2FZPvdr88MbhO78pFCymdnOkhIr6M
|
||||
ZvBqDLxaV7mjcduU/U1lq13CekZ2DUTDpbUa9z4w+BiqgFpWJdxy0ZTwpwPBqd8C
|
||||
AwEAAaOCAUIwggE+MB0GA1UdDgQWBBR0nqW1x1vNwtdFezphgGtkz7B2RjCB0wYD
|
||||
VR0jBIHLMIHIgBR0nqW1x1vNwtdFezphgGtkz7B2RqGBmaSBljCBkzELMAkGA1UE
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrt1oTLVGNfvZvmsQd7L0Ts
|
||||
dttTKZcJynIlDX/2QJ7F77/sywrY69Bch14Gn2MhEhpRnn5GIKpEzE8KTDgg5iOV
|
||||
FOwClfMwlhzksx7VpwSjSFhxbudxWgpk8RoGmuPaB4rJLHPo7B6Pv4DduxFpO5aF
|
||||
IxKdPFCi1sKKzJAi+88SSX5KhX+MQgwiqbLjdeXxx+ofTZig7IymiJUUbcdtiQPq
|
||||
KYXjcFH/XLhJMx52GKBk9YK5yvoDwYzGs+FKkPgWCbifUHnzkQ+/v3yKsvnFS/vq
|
||||
Rb1uPA3PkJnbZwCLb0hGKh8YvGUIr+9WT4MLQPOBKgfRUHEjNxvdYCUaw6xz+8EC
|
||||
AwEAAaOCAUIwggE+MB0GA1UdDgQWBBQi2Mb/nVfuJREdj/wvqVAtZNxhXzCB0wYD
|
||||
VR0jBIHLMIHIgBQi2Mb/nVfuJREdj/wvqVAtZNxhX6GBmaSBljCBkzELMAkGA1UE
|
||||
BhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYD
|
||||
VQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
|
||||
b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUVIBU
|
||||
u2vZ0TrxOwqlIyfxooaF4bIwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug
|
||||
b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUA7R4
|
||||
NLhoa6Mt/Zek66V7+Qnx9VEwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug
|
||||
KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUub3JnL2V4YW1wbGVfY2EuY3JsMA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQAdywbOcOa9XSx/hEOKkG4rZR8F6eb+z/u+BYfEhQE4unTR
|
||||
3ihNarsHwteTQArabTRzuV8+phX7fgkQJHmp1NOpJVmMEr3JOs00SGbisxDmK0Z3
|
||||
2HkE+DjpYo2Sz8b77YD1AWk705rJkJ0Jp5+d/BLk6CjCr524XsSKLwKRWKOit3eu
|
||||
WKsbt+VMd7d8jDvgwrtKLDpGv/sBym5w9zQjUqOTPBr70BBbpynJD62mzdr5OjIc
|
||||
JiZKjusr1fieOeBzo2us+hFdvoCmW8X4hwVAyWm4JgP1yZ9IT/KLnFtJo0k/BM3B
|
||||
lzcz90I0zzpewC8xxaWJ7kz6h+eSIa3orlSIug4o
|
||||
SIb3DQEBCwUAA4IBAQCOSSpCkK1kF4MlctIhNnMjP6AcxhPYFhDPrTCy+lGmNw0S
|
||||
4L73ZHoTtmPsEc7p4d3qxy1WddRS3M6+0vyQz5Yhe1xQZy+SExeYxOX8sXqK1Uu3
|
||||
37rY7oz26DkITOdI4FFtcNKtAcu8rVg2kZO/66RUiQSA6V3rzAVcFGrHHP4ZoPqV
|
||||
qy+OqO8dcldlf70B7fLnf2N28drdpkUeJEGhGRzO42gN55Zj9RVH3NCZDZI1uz2B
|
||||
ESIwjFWBFDeteux8O9BZhfNJ+Zmc9BS+1CvjMCCEAeRCjpAWdyLa+uquq5tII4Me
|
||||
STKrTWjHUmq0zjDmjOowB8xWvoWONW19nsSSqGC8
|
||||
-----END CERTIFICATE-----
|
||||
|
80
ssl/cert/server.crt
Normal file
80
ssl/cert/server.crt
Normal file
@ -0,0 +1,80 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 0 (0x0)
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority
|
||||
Validity
|
||||
Not Before: Feb 22 17:05:38 2020 GMT
|
||||
Not After : Jul 27 17:05:38 2036 GMT
|
||||
Subject: C=AT, ST=Radius, O=Example Inc., CN=Example Certificate Authority/emailAddress=admin@example.org
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:b5:ad:78:5d:89:14:28:92:55:e7:c1:e0:15:46:
|
||||
e7:ea:da:ea:98:bb:ec:33:17:ee:d6:e3:37:1c:34:
|
||||
ea:4e:87:a1:c9:d3:19:de:95:6f:c1:76:13:e0:b4:
|
||||
f5:08:3e:fe:5a:76:b8:4a:e4:94:07:8b:67:4b:b5:
|
||||
d1:97:c1:d3:e1:b1:cd:84:5b:16:aa:dc:56:5b:ff:
|
||||
0a:e7:f3:79:a3:47:4d:5b:6d:e4:d6:21:a0:eb:e1:
|
||||
54:83:87:1c:c5:18:de:d8:52:40:93:7e:fb:c5:df:
|
||||
73:3c:d6:0b:a3:1d:9f:36:c7:6f:59:90:d7:5f:82:
|
||||
80:fe:82:05:1b:1b:77:92:04:c9:25:88:8f:9c:35:
|
||||
91:cc:86:a0:8f:8f:9b:2e:2c:62:af:f3:8a:16:75:
|
||||
30:61:47:8d:24:ea:35:84:43:33:01:ed:d7:33:2c:
|
||||
29:4f:2c:8d:f5:b7:b3:bd:74:4f:2e:83:2d:e1:d6:
|
||||
3d:5e:d9:14:c3:a1:89:38:4d:29:4b:ae:39:c8:a2:
|
||||
dd:ad:f1:42:c8:0d:6c:29:eb:70:c8:dc:e7:41:59:
|
||||
47:1c:89:52:62:53:9c:35:58:8a:39:16:87:61:f2:
|
||||
11:26:3a:2b:a2:19:29:c2:77:31:de:1c:74:c6:57:
|
||||
3a:15:8b:2f:29:61:a7:45:b4:d8:70:a4:d2:ef:da:
|
||||
5d:a1
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
URI:http://www.example.com/example_ca.crl
|
||||
|
||||
Signature Algorithm: sha256WithRSAEncryption
|
||||
60:49:5c:0c:1d:1a:c3:86:16:b7:d8:63:b7:b9:9b:4d:74:49:
|
||||
25:05:c0:28:f4:c6:95:ce:63:95:b7:99:41:54:7f:64:5d:9a:
|
||||
8f:f5:a7:96:09:8c:67:42:61:5d:c5:af:e4:d9:33:28:20:2a:
|
||||
1f:76:5b:a7:1f:54:47:c4:94:4f:de:bb:6d:ea:36:51:44:bf:
|
||||
90:82:b3:7c:91:28:6a:1e:dd:76:66:38:2c:0f:ed:8a:fd:b6:
|
||||
91:28:3b:59:f0:b3:42:4b:bb:fb:88:d9:d8:6f:e2:a9:79:14:
|
||||
df:0b:53:76:be:23:c1:0e:96:85:aa:d9:3f:d6:60:7a:a5:a9:
|
||||
5a:1d:17:05:7e:84:7a:36:0b:a0:eb:96:8d:75:08:ab:ea:e2:
|
||||
9b:4c:dc:92:05:b0:bc:2a:c0:ff:21:89:02:c2:cd:07:ee:85:
|
||||
55:5b:cf:bd:bc:d7:b1:8a:54:51:5a:58:f6:77:e5:76:85:26:
|
||||
fd:dd:e8:ec:aa:45:c8:cf:d1:10:16:68:97:e4:e7:06:e3:22:
|
||||
9c:4d:f5:85:bb:37:26:d4:fc:47:af:26:03:f3:e1:17:cc:20:
|
||||
33:c1:c4:e2:b3:b7:1e:d3:0a:81:85:ff:e6:79:70:07:a7:8b:
|
||||
3c:1e:9e:1b:e8:f5:a1:3e:69:c5:90:f4:7f:49:a7:19:93:fa:
|
||||
65:fc:0e:43
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx
|
||||
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
|
||||
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw
|
||||
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx
|
||||
NzA1MzhaFw0zNjA3MjcxNzA1MzhaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
|
||||
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD
|
||||
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
|
||||
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAta14XYkUKJJV
|
||||
58HgFUbn6trqmLvsMxfu1uM3HDTqToehydMZ3pVvwXYT4LT1CD7+Wna4SuSUB4tn
|
||||
S7XRl8HT4bHNhFsWqtxWW/8K5/N5o0dNW23k1iGg6+FUg4ccxRje2FJAk377xd9z
|
||||
PNYLox2fNsdvWZDXX4KA/oIFGxt3kgTJJYiPnDWRzIagj4+bLixir/OKFnUwYUeN
|
||||
JOo1hEMzAe3XMywpTyyN9bezvXRPLoMt4dY9XtkUw6GJOE0pS645yKLdrfFCyA1s
|
||||
KetwyNznQVlHHIlSYlOcNViKORaHYfIRJjorohkpwncx3hx0xlc6FYsvKWGnRbTY
|
||||
cKTS79pdoQIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
|
||||
MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQBgSVwMHRrDhha32GO3uZtNdEklBcAo9MaVzmOVt5lB
|
||||
VH9kXZqP9aeWCYxnQmFdxa/k2TMoICofdlunH1RHxJRP3rtt6jZRRL+QgrN8kShq
|
||||
Ht12ZjgsD+2K/baRKDtZ8LNCS7v7iNnYb+KpeRTfC1N2viPBDpaFqtk/1mB6pala
|
||||
HRcFfoR6Ngug65aNdQir6uKbTNySBbC8KsD/IYkCws0H7oVVW8+9vNexilRRWlj2
|
||||
d+V2hSb93ejsqkXIz9EQFmiX5OcG4yKcTfWFuzcm1PxHryYD8+EXzCAzwcTis7ce
|
||||
0wqBhf/meXAHp4s8Hp4b6PWhPmnFkPR/SacZk/pl/A5D
|
||||
-----END CERTIFICATE-----
|
@ -3,16 +3,16 @@ MIIC2TCCAcECAQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQ
|
||||
BgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZI
|
||||
hvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0
|
||||
aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDrhh2/LraaUnI+2zOx3LJjI2RzT9jrMiXynBmgGzISEIrW88QKECW+CtwUG5S1
|
||||
KxbfhwnCmQu+GF77DA/g0ops5SIOzaqoV5Hp3EzctxErLMhGjqWiQZCKiRvx0vfN
|
||||
2b33EhlLyQW1BZl2P7FqJ0Ii1f65ZaqV8iVbtjz0qL5ZzsU1Ajm6yPa9zpVg5r0e
|
||||
/afjiz/nReDJbS146JBGpoiQBBOEbaQvuNP8k/gxmidn9PsdVYDkIN+PuLDBQoie
|
||||
ieApbzYuThjO2pKtTj7ro0/rCyDmHYlRz49DBunWWpRvagewQz7XsaBoIxrnEGWh
|
||||
deoZu+195JoHmxCZCl11sxWDAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAKYUI
|
||||
sRw13r+FXj0cR7rvz2JQVBcJSphHrBK5O8xkO4zlOLVuxbUIA3/6YunmmnFMChTb
|
||||
O/Be6HDHX0+SmqfrT0VUGZ2zF1QeDFtkK195r3gMCQ1qqUEBCdAWZG4u1XDEnVvV
|
||||
fctXt3D/D8cYJ0Dw+xHMBq5RpxO6/dv+ML1QicRJ8h4uvtjcf3ePPF6cyrtLeCxM
|
||||
Vw6RWUB0XbdSqA6ljFcT0ytI1f4hnu7pwp4Rhc6xAtZyGWn7Vgbm9kxlThzenT+r
|
||||
fePBF77E2CY16kRzzKQOiyEqumkotEimvGsUsGx90TtPTMNX8GiXm9Ct1XRBL4SI
|
||||
R+SlHRAayKRGPyhvOA==
|
||||
AQC1rXhdiRQoklXnweAVRufq2uqYu+wzF+7W4zccNOpOh6HJ0xnelW/BdhPgtPUI
|
||||
Pv5adrhK5JQHi2dLtdGXwdPhsc2EWxaq3FZb/wrn83mjR01bbeTWIaDr4VSDhxzF
|
||||
GN7YUkCTfvvF33M81gujHZ82x29ZkNdfgoD+ggUbG3eSBMkliI+cNZHMhqCPj5su
|
||||
LGKv84oWdTBhR40k6jWEQzMB7dczLClPLI31t7O9dE8ugy3h1j1e2RTDoYk4TSlL
|
||||
rjnIot2t8ULIDWwp63DI3OdBWUcciVJiU5w1WIo5Fodh8hEmOiuiGSnCdzHeHHTG
|
||||
VzoViy8pYadFtNhwpNLv2l2hAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAtTan
|
||||
LS+AjvzL0raUHp/SWflMh37l2J60cjzjmbrws7MpQdqGg7H11tXhgKSDELMpuB4h
|
||||
44PZeDZmZGfFJCtYWcOeTc4SvI69SXYnj6UaMKxM3/1sI0fd9JaPnCs7Sg5+B96A
|
||||
/8AcF1hKXZ9y71XD3NeGGRDuzqHvGdeIfMoGP8GJLaR70ncLnBpg2KCHG6C6mP6R
|
||||
4W5yrWRCcpVXBAXi4yoxesQRLwp/DEXZo6ExOJHDYpZLen6YjkTp/Lv7PySKopQP
|
||||
SJrev/H6GL4qmLfl7dFebq+FE6sezIRzopFWnHu+TcUMVCWBUwJVl71Nvw009A1/
|
||||
kfBQ/TsFvyRDzqqM5Q==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
|
30
ssl/cert/server.key
Normal file
30
ssl/cert/server.key
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIfUSPX6P3YcECAggA
|
||||
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECGX0Au3iFvSjBIIEyGye/tQdIfVv
|
||||
HG9CBzyejddQTYgT5IsNzTFKsASeJi/0vlQP1mvEPScgMrNO62mBPHh0+sQGyR4v
|
||||
ccrqEobKUgXYJWJd60MeHJVPvB6fVBS2kBv1dmHYYuGNqlsWE82nmX8mdEOjNR9e
|
||||
ZTnuULqE8QHZd0EVWhdK83iUCCb4ksOF+bJYQe7gkRWeZ3ALqq4eLtlUs4LiIaAn
|
||||
VrqFWdslZLBRHfDfC/beOtxp9a/clMaTI1g0jjAKuYKkydDKl6gk6HWkyw4qnJHX
|
||||
37GgReCYE72DZSmuS6mTXjsvK2cLsRmvrwWnwBaiApE9P6+RLnYyQerDMpVc1z44
|
||||
s+9VCoOmggPgy+YNB09tptCJaWJaiLu6FQzAZNo/Hu1hIf7Gx4b9IXnT6vsAsTkg
|
||||
K4exni026uVXd/toeh1O+bH9lE85RVxkFgnv49WpcBvfVXa93BZbAZP286dEeKI4
|
||||
8EueW8fh+l1HYht+6lKN0hW5rh3nBkOFHdG0GoczmbyAsz2N/cvW20KWc75cAjdW
|
||||
HG3pAwoCxzjCUVk7s6+tueNAmANRtn1zo8RKHzQ4iUVWd6WmE9v5GrSrNmvDtqyN
|
||||
yLbzDd3BZuYyUwWxMjgIeYcyqlR0zSlj12SvhPnNqwwPsuqUuB7Y5lv3oTKl7RNk
|
||||
GGd08a5nioYMs+0Ob/Zr4ay3so5gAmYDfIVVVp7GzERK9h8FrBylZqyaA8BS95GM
|
||||
JfWJGXjKbJ2nqNqc/Ic2mYVp6AT7V4FSmYV9I+rNuRI76DdJgA3UYhQOvtvmbbFI
|
||||
EyNtHCL6M8ejfbg71c5rbj7nBYLiuk5STzIiZyKPK4QmNWQ+QLy3RIgK1fMgjlzP
|
||||
rgh/1hhFRUVJxiRKnDbzrTlHEeD17U4Rpz/M3M0ZsWCWWJe+FMDaJNuNfvJW3ZYQ
|
||||
hBFjVhZn3VTmreFyt6nshsfGU3PeFXSXx8el4M5Sja5MCqQ86nbv9W6miOVtvkOx
|
||||
7kYvHXMQpsSzkjkL9hej3PxYHcchgjRnF+l0WIs7U/RE3pRiPkm0yD9Y1IEsZIix
|
||||
farOZTtPZqrOYcHwCHdRY9oxcKLZM9Rrqi5tTMk3T1ellFuX8qUj2uqSNCbhAYpC
|
||||
Qx4VfKyAi9iEyBP01X0r8gprMzqtpZatGAB70XC+Wa4lUYfRt1IbHQ0MhWMWyStL
|
||||
iefHkHee9MVgI3hh08ww1OmQGTnpQgKG6udxSRObLbWlAgccWjnX5YE3nOmo4Vvo
|
||||
v091utdEqUZ9nrIzIvlv1lqnm0qRTY4uf99dOyl/bIr1tfWJLawL9kUbN+jl4d4C
|
||||
CnilsBFTuWQBMPFE3sit91U4Ycmi3U1lmiQTXLhlCiOkyuVLGjnb7RuY1Yjm9Gso
|
||||
D/t55VgF0882uVgocU3l9OnXRZLRijXEU7WWM8YF+RXTRORykcYfL/oPF8/E/NF7
|
||||
DbamK79gIJUXGEdDdujFGAkJr0nYdK77sMeWfqIgf7l+RNaz3dtv2NGfWjpymbMz
|
||||
RkgPUypbE4JiJVnwrsVNm20wQbE839Raijchf8ZWBEWz8nNArwe094+MQxoZHg12
|
||||
Iu742/ykANJD/xXq7Nfe0d8fXWEdG3+ZgcfKUIL2oCS7/y3p4URCl6TuujBAFO8I
|
||||
IBR4bJcgdHWSqRS8usbzVg==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
@ -1 +1 @@
|
||||
V 360727163701Z 00 unknown /C=AT/ST=Radius/O=Example Inc./CN=Example Certificate Authority/emailAddress=admin@example.org
|
||||
V 360727170538Z 00 unknown /C=AT/ST=Radius/O=Example Inc./CN=Example Certificate Authority/emailAddress=admin@example.org
|
||||
|
@ -3,7 +3,7 @@
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
|
||||
// target settings for node 10
|
||||
// target settings for node js
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"lib": ["es2018"],
|
||||
|
Loading…
Reference in New Issue
Block a user