refactor: major code cleanups :-)

first working version
This commit is contained in:
simon 2020-02-23 00:29:33 +01:00
parent c285a15bee
commit 42a05b069f
31 changed files with 1463 additions and 825 deletions

8
.gitignore vendored
View File

@ -4,10 +4,8 @@ node_modules/*
# build files # build files
dist dist
# certificates
ssl/*.pem
*.crt
*.key
# ts # ts
tsconfig.tsbuildinfo tsconfig.tsbuildinfo
# custom certificates
/ssl-*/

View File

@ -1,17 +1,18 @@
Basic RADIUS Server for node.js for Google LDAP Service and WPA2 Enteprise WLAN Authentification. 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 ## 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). 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 - 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 ;)?) - a lot of bugs
* No package queuing or any kind of respsecting the MTU size
* 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 ## Installation
@ -21,17 +22,39 @@ CONTRIBUTIONS WELCOME!
## Introduction ## Introduction
This app provides a radius server to authenticate against google's SLDAP service. To get this running This app provides a radius server to authenticate against google's SLDAP service. To get this running
you need: 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) 1. Running LDAP Service (E.g. Google Suite Enterprise or Gloud Identity Premium)
3.) Install a SSL certificate (e.g. self signed via npm run create-certificate) 2. Optional: Create your own SSL certificate (e.g. self signed via npm run create-certificate)
4.) Install und build server: npm install && npm run build 3. Check config.js and adapt to your needs
5.) Start server node dist/app.ts --secret {RADIUS secret} --baseDN dc=hokify,dc=com
- 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 ## Usage
You need to specify at least a radius password and the base DN for LDAP: 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

View File

@ -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 = { module.exports = {
port: 1812,
// radius secret // radius secret
secret: 'testing123', secret: 'testing123',
certificate: { certificate: {
cert: fs.readFileSync('./ssl/cert/server.crt'), cert: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.crt')),
key: [ key: [
{ {
pem: fs.readFileSync('./ssl/cert/server.key'), pem: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.key')),
passphrase: 'whatever2020' passphrase: 'whatever2020'
} }
] ]

228
package-lock.json generated
View File

@ -34,13 +34,13 @@
} }
}, },
"@hokify/eslint-config": { "@hokify/eslint-config": {
"version": "0.2.2", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.2.tgz", "resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.4.tgz",
"integrity": "sha512-FvVZd8hruAriQ84UR0MqxRtXXUT9Eqe9LTGoU90sQ6TgHXTGPQpOfY+8DAICX0HtRc5ntB/ZEgL6L9ims4NLxA==", "integrity": "sha512-wUiJ1mBc+6TEqqB1qK0LsaMazg6h19MwA4jlMn4Qltgiql7S2EmWDSK0ID9wCmI4bgUB6WqZsNQqvkMxNG7hYw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/eslint-plugin": "^2.19.2", "@typescript-eslint/eslint-plugin": "^2.20.0",
"@typescript-eslint/parser": "^2.19.2", "@typescript-eslint/parser": "^2.20.0",
"eslint-config-airbnb-typescript": "^7.0.0", "eslint-config-airbnb-typescript": "^7.0.0",
"eslint-config-prettier": "^6.10.0", "eslint-config-prettier": "^6.10.0",
"eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-alias": "^1.1.2",
@ -50,11 +50,20 @@
"eslint-loader": "^3.0.3", "eslint-loader": "^3.0.3",
"eslint-plugin-import": "2.20.1", "eslint-plugin-import": "2.20.1",
"eslint-plugin-jsx-a11y": "^6.2.3", "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-node": "^11.0.0",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.3", "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": { "@types/color-name": {
@ -62,12 +71,39 @@
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" "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": { "@types/eslint-visitor-keys": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true "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": { "@types/ioredis": {
"version": "4.14.7", "version": "4.14.7",
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.14.7.tgz", "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.14.7.tgz",
@ -92,11 +128,15 @@
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.5.tgz",
"integrity": "sha512-hrtMZjVfWNPxkwFkYhLAU0ITZ3/reDft4jDzLBvrGDSKCbEvW+GeZb4PgM3jlSwSsv0cXqnDeWcupFLIgW9E0Q==", "integrity": "sha512-hrtMZjVfWNPxkwFkYhLAU0ITZ3/reDft4jDzLBvrGDSKCbEvW+GeZb4PgM3jlSwSsv0cXqnDeWcupFLIgW9E0Q==",
"dev": true,
"requires": { "requires": {
"@types/node": "*" "@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": { "@types/node": {
"version": "13.7.1", "version": "13.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
@ -116,6 +156,14 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true "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": { "@types/radius": {
"version": "0.0.28", "version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/radius/-/radius-0.0.28.tgz", "resolved": "https://registry.npmjs.org/@types/radius/-/radius-0.0.28.tgz",
@ -125,6 +173,11 @@
"@types/node": "*" "@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": { "@types/redis": {
"version": "2.8.15", "version": "2.8.15",
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.15.tgz", "resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.15.tgz",
@ -133,13 +186,22 @@
"@types/node": "*" "@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": { "@typescript-eslint/eslint-plugin": {
"version": "2.19.2", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz",
"integrity": "sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA==", "integrity": "sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "2.19.2", "@typescript-eslint/experimental-utils": "2.20.0",
"eslint-utils": "^1.4.3", "eslint-utils": "^1.4.3",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0", "regexpp": "^3.0.0",
@ -147,32 +209,32 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "2.19.2", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz",
"integrity": "sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A==", "integrity": "sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.3", "@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" "eslint-scope": "^5.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "2.19.2", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.19.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.20.0.tgz",
"integrity": "sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA==", "integrity": "sha512-o8qsKaosLh2qhMZiHNtaHKTHyCHc3Triq6aMnwnWj7budm3xAY9owSZzV1uon5T9cWmJRJGzTFa90aex4m77Lw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/eslint-visitor-keys": "^1.0.0", "@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "2.19.2", "@typescript-eslint/experimental-utils": "2.20.0",
"@typescript-eslint/typescript-estree": "2.19.2", "@typescript-eslint/typescript-estree": "2.20.0",
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "2.19.2", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz",
"integrity": "sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q==", "integrity": "sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^4.1.1", "debug": "^4.1.1",
@ -389,6 +451,11 @@
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
"dev": true "dev": true
}, },
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"big.js": { "big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@ -896,9 +963,9 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
}, },
"emojis-list": { "emojis-list": {
"version": "2.1.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true "dev": true
}, },
"enhanced-resolve": { "enhanced-resolve": {
@ -1361,12 +1428,24 @@
} }
}, },
"eslint-plugin-mocha": { "eslint-plugin-mocha": {
"version": "6.2.2", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz",
"integrity": "sha512-oNhPzfkT6Q6CJ0HMVJ2KLxEWG97VWGTmuHOoRcDLE0U88ugUyFNV9wrT2XIt5cGtqc5W9k38m4xTN34L09KhBA==", "integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==",
"dev": true, "dev": true,
"requires": { "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": { "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": { "ldapjs": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
@ -2215,13 +2313,13 @@
} }
}, },
"loader-utils": { "loader-utils": {
"version": "1.2.3", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true, "dev": true,
"requires": { "requires": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^2.0.0", "emojis-list": "^3.0.0",
"json5": "^1.0.1" "json5": "^1.0.1"
} }
}, },
@ -2290,6 +2388,14 @@
"js-tokens": "^3.0.0 || ^4.0.0" "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": { "make-error": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
@ -2509,9 +2615,9 @@
"dev": true "dev": true
}, },
"object-hash": { "object-hash": {
"version": "2.0.2", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.2.tgz", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz",
"integrity": "sha512-b+2AKjAf6uQlxxv8ChHdM+VT4eeX+ZSwv+pk2xIXZWbo+yxn4/En1iC+GHe/OFYa9on0AhFF2PvuAcFHoiiHaA==", "integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==",
"dev": true "dev": true
}, },
"object-inspect": { "object-inspect": {
@ -2676,6 +2782,29 @@
"error-ex": "^1.2.0" "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": { "path-browserify": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
@ -2870,9 +2999,9 @@
"integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw==" "integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw=="
}, },
"ramda": { "ramda": {
"version": "0.26.1", "version": "0.27.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz",
"integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==",
"dev": true "dev": true
}, },
"randombytes": { "randombytes": {
@ -3011,9 +3140,9 @@
"dev": true "dev": true
}, },
"regexp-tree": { "regexp-tree": {
"version": "0.1.19", "version": "0.1.20",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.19.tgz", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.20.tgz",
"integrity": "sha512-mVeVLF/qg5qFbZSW0f7pXbuVX73dKIjuhOyC2JLKxzmpya75O4qLcvI9j0jp31Iz7IAkEVHa1UErDCAqaLKo5A==", "integrity": "sha512-gSiH74kc00oTbQkN7tZogZe0ttKwyxyDVLAnU20aWoarbLE9AypbJHRlZ567h4Zi19q3cPVRWDYbfECElrHgsQ==",
"dev": true "dev": true
}, },
"regexp.prototype.flags": { "regexp.prototype.flags": {
@ -3587,9 +3716,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.7.5", "version": "3.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
"dev": true "dev": true
}, },
"universalify": { "universalify": {
@ -3761,6 +3890,11 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" "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": { "yargs": {
"version": "15.1.0", "version": "15.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz",

View File

@ -1,31 +1,32 @@
{ {
"name": "radius-server", "name": "radius-server",
"description": "radius server for google LDAP and TTLT", "description": "radius server for google LDAP and TTLT",
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"start": "/home/simon/Dev/others/node/node dist/app.js", "start": "/home/simon/Dev/others/node/node dist/app.js",
"build": "tsc", "build": "tsc",
"dev": "ts-node src/app.ts", "dev": "ts-node src/app.ts",
"test-ttls-pap": "eapol_test -c ./ttls-pap.conf -s testing123", "test-ttls-pap": "eapol_test -c ./ttls-pap.conf -s testing123",
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh" "create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
}, },
"dependencies": { "dependencies": {
"native-duplexpair": "^1.0.0", "native-duplexpair": "^1.0.0",
"ldapjs": "^1.0.2", "ldapjs": "^1.0.2",
"node-cache": "^5.1.0", "node-cache": "^5.1.0",
"radius": "~1.1.4", "radius": "~1.1.4",
"ts-node": "^8.6.2", "ts-node": "^8.6.2",
"type-cacheable": "^4.0.0", "type-cacheable": "^4.0.0",
"yargs": "~15.1.0", "yargs": "~15.1.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"passport-ldapauth": "^2.1.3" "passport-ldapauth": "^2.1.3"
}, },
"devDependencies": { "license": "GPLv3",
"@types/ldapjs": "^1.0.5", "devDependencies": {
"@types/radius": "0.0.28", "@types/ldapjs": "^1.0.5",
"@hokify/eslint-config": "^0.2.2", "@types/radius": "0.0.28",
"eslint": "^6.8.0", "@hokify/eslint-config": "^0.2.4",
"prettier": "^1.19.1", "eslint": "^6.8.0",
"typescript": "^3.7.5" "prettier": "^1.19.1",
} "typescript": "^3.8.2"
}
} }

View File

@ -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 { 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') console.log(`Listener Port: ${config.port || 1812}`);
.usage('RADIUS Server\nUsage: $0') console.log(`RADIUS Secret: ${config.secret}`);
.example('$0 --port 1812 -s radiussecret') console.log(`Auth Mode: ${config.authentication}`);
.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}`);
// const ldap = new LDAPAuth({url: 'ldap://ldap.google.com', base: 'dc=hokify,dc=com', uid: 'uid', tlsOptions}); // 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 server = new UDPServer(config.port);
const timeout: { [key: string]: NodeJS.Timeout } = {}; const radiusService = new RadiusService(config.secret, ldap);
const waitForNextMsg: { [key: string]: IDeferredPromise } = {};
function sendToClient( (async () => {
msg: string | Uint8Array, server.on('message', async (msg, rinfo) => {
offset: number, const response = await radiusService.handleMessage(msg);
length: number,
port?: number,
address?: string,
callback?: (error: Error | null, bytes: number) => void,
stateForRetry?: string
): void {
let retried = 0;
function sendResponse() { if (response) {
console.log(`sending response... (try: ${retried})`); server.sendToClient(
server.send(msg, offset, length, port, address, (error: Error | null, bytes: number) => { response.data,
// all good rinfo.port,
rinfo.address,
if (callback) callback(error, bytes); (err, _bytes) => {
}); if (err) {
console.log('Error sending response to ', rinfo);
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;
} }
} while (sentDataSize < EAPMessage.length); },
response.expectAcknowledgment
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 });
} }
});
// EAP MESSAGE // start server
eapHandler.handleEAPMessage(packet.attributes['EAP-Message'], state, handlers); await server.start();
} 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);

View File

@ -1,6 +1,6 @@
import * as NodeCache from 'node-cache'; import * as NodeCache from 'node-cache';
import { createClient, Client } from 'ldapjs'; import { Client, createClient } from 'ldapjs';
import { IAuthentication } from '../types/Authentication'; import { IAuthentication } from '../types/Authentication';
const usernameFields = ['posixUid', 'mail']; const usernameFields = ['posixUid', 'mail'];
@ -58,7 +58,7 @@ export class GoogleLDAPAuth implements IAuthentication {
}); });
res.on('end', result => { res.on('end', result => {
console.log(`status: ${result?.status}`); console.log(`ldap status: ${result?.status}`);
// replace with new dns // replace with new dns
this.allValidDNsCache = dns; this.allValidDNsCache = dns;

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -8,6 +8,8 @@ export function makeid(length) {
return result; 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 const MAX_RADIUS_ATTRIBUTE_SIZE = 253;
export interface IDeferredPromise { export interface IDeferredPromise {

View 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
};
}
}

View 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
};
}
}

View 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 {};
}
}

View 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);
}
}

View File

@ -1,4 +1,4 @@
import { IEAPChallenge } from '../../types/EAPChallenge'; import { IEAPChallenge } from '../../../../types/EAPChallenge';
export class PAPChallenge implements IEAPChallenge { export class PAPChallenge implements IEAPChallenge {
// i couldn't find any documentation about it, therefore best guess how this is processed... // i couldn't find any documentation about it, therefore best guess how this is processed...

80
src/server/UDPServer.ts Normal file
View 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));
}
}

View File

@ -1,8 +1,6 @@
import * as NodeCache from 'node-cache';
import * as events from 'events'; import * as events from 'events';
import * as tls from 'tls'; import * as tls from 'tls';
import { createSecureContext } from 'tls'; import { createSecureContext } from 'tls';
import * as fs from 'fs';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import * as DuplexPair from 'native-duplexpair'; import * as DuplexPair from 'native-duplexpair';
import * as constants from 'constants'; import * as constants from 'constants';
@ -21,16 +19,20 @@ const tlsOptions: tls.SecureContextOptions = {
}; };
console.log('tlsOptions', tlsOptions); console.log('tlsOptions', tlsOptions);
const secureContext = createSecureContext(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 duplexpair = new DuplexPair();
const emitter = new events.EventEmitter(); const emitter = new events.EventEmitter();
const cleartext = new tls.TLSSocket(duplexpair.socket1, { const cleartext = new tls.TLSSocket(duplexpair.socket1, {
secureContext, secureContext,
isServer: true, isServer: true,
enableTrace: true, // enableTrace: true,
rejectUnauthorized: false, rejectUnauthorized: false,
// handshakeTimeout: 10, // handshakeTimeout: 10,
requestCert: false requestCert: false

View File

@ -1,3 +1,3 @@
export interface IAuthentication { export interface IAuthentication {
authenticate(username: string, password: string): Promise<string>; authenticate(username: string, password: string): Promise<boolean>;
} }

15
src/types/EAPMethod.ts Normal file
View 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>;
}

View File

@ -1,3 +0,0 @@
export interface IEAPType {
handleMessage(msg: Buffer, state: string, handlers, identifier: number);
}

View 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
View 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;
}

View File

@ -1,8 +1,10 @@
this is based on freeradius cert directory :) this is based on freeradius cert directory :)
1.) edit ca.cnf there is a default certificate already present, if you want to create your own, follow these steps:
2.) edit server.cnf
3.) replace your choosen pwd (deafult is whatever2020) in create.sh 1. edit ca.cnf
4.) run ./create.sh 2. edit server.cnf
5.) set your choosen pwd in ~/config.js 3. replace your choosen pwd (deafult is whatever2020) in create.sh
4. run ./create.sh
5. set your choosen pwd in ~/config.js

View File

@ -5,31 +5,31 @@ Certificate:
Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority
Validity Validity
Not Before: Feb 22 16:37:01 2020 GMT Not Before: Feb 22 17:05:38 2020 GMT
Not After : Jul 27 16:37:01 2036 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: C=AT, ST=Radius, O=Example Inc., CN=Example Certificate Authority/emailAddress=admin@example.org
Subject Public Key Info: Subject Public Key Info:
Public Key Algorithm: rsaEncryption Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit) RSA Public-Key: (2048 bit)
Modulus: Modulus:
00:eb:86:1d:bf:2e:b6:9a:52:72:3e:db:33:b1:dc: 00:b5:ad:78:5d:89:14:28:92:55:e7:c1:e0:15:46:
b2:63:23:64:73:4f:d8:eb:32:25:f2:9c:19:a0:1b: e7:ea:da:ea:98:bb:ec:33:17:ee:d6:e3:37:1c:34:
32:12:10:8a:d6:f3:c4:0a:10:25:be:0a:dc:14:1b: ea:4e:87:a1:c9:d3:19:de:95:6f:c1:76:13:e0:b4:
94:b5:2b:16:df:87:09:c2:99:0b:be:18:5e:fb:0c: f5:08:3e:fe:5a:76:b8:4a:e4:94:07:8b:67:4b:b5:
0f:e0:d2:8a:6c:e5:22:0e:cd:aa:a8:57:91:e9:dc: d1:97:c1:d3:e1:b1:cd:84:5b:16:aa:dc:56:5b:ff:
4c:dc:b7:11:2b:2c:c8:46:8e:a5:a2:41:90:8a:89: 0a:e7:f3:79:a3:47:4d:5b:6d:e4:d6:21:a0:eb:e1:
1b:f1:d2:f7:cd:d9:bd:f7:12:19:4b:c9:05:b5:05: 54:83:87:1c:c5:18:de:d8:52:40:93:7e:fb:c5:df:
99:76:3f:b1:6a:27:42:22:d5:fe:b9:65:aa:95:f2: 73:3c:d6:0b:a3:1d:9f:36:c7:6f:59:90:d7:5f:82:
25:5b:b6:3c:f4:a8:be:59:ce:c5:35:02:39:ba:c8: 80:fe:82:05:1b:1b:77:92:04:c9:25:88:8f:9c:35:
f6:bd:ce:95:60:e6:bd:1e:fd:a7:e3:8b:3f:e7:45: 91:cc:86:a0:8f:8f:9b:2e:2c:62:af:f3:8a:16:75:
e0:c9:6d:2d:78:e8:90:46:a6:88:90:04:13:84:6d: 30:61:47:8d:24:ea:35:84:43:33:01:ed:d7:33:2c:
a4:2f:b8:d3:fc:93:f8:31:9a:27:67:f4:fb:1d:55: 29:4f:2c:8d:f5:b7:b3:bd:74:4f:2e:83:2d:e1:d6:
80:e4:20:df:8f:b8:b0:c1:42:88:9e:89:e0:29:6f: 3d:5e:d9:14:c3:a1:89:38:4d:29:4b:ae:39:c8:a2:
36:2e:4e:18:ce:da:92:ad:4e:3e:eb:a3:4f:eb:0b: dd:ad:f1:42:c8:0d:6c:29:eb:70:c8:dc:e7:41:59:
20:e6:1d:89:51:cf:8f:43:06:e9:d6:5a:94:6f:6a: 47:1c:89:52:62:53:9c:35:58:8a:39:16:87:61:f2:
07:b0:43:3e:d7:b1:a0:68:23:1a:e7:10:65:a1:75: 11:26:3a:2b:a2:19:29:c2:77:31:de:1c:74:c6:57:
ea:19:bb:ed:7d:e4:9a:07:9b:10:99:0a:5d:75:b3: 3a:15:8b:2f:29:61:a7:45:b4:d8:70:a4:d2:ef:da:
15:83 5d:a1
Exponent: 65537 (0x10001) Exponent: 65537 (0x10001)
X509v3 extensions: X509v3 extensions:
X509v3 Extended Key Usage: X509v3 Extended Key Usage:
@ -40,41 +40,41 @@ Certificate:
URI:http://www.example.com/example_ca.crl URI:http://www.example.com/example_ca.crl
Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption
9f:4d:e9:78:d5:5f:c1:fb:e1:43:d1:9f:2d:d6:c3:f8:94:56: 60:49:5c:0c:1d:1a:c3:86:16:b7:d8:63:b7:b9:9b:4d:74:49:
0b:94:2d:b5:e7:d2:bd:f6:3b:e1:69:6b:cb:84:e4:8a:a9:fd: 25:05:c0:28:f4:c6:95:ce:63:95:b7:99:41:54:7f:64:5d:9a:
1b:58:16:b5:2d:c7:00:d8:cb:56:37:72:f8:0c:17:a7:17:ec: 8f:f5:a7:96:09:8c:67:42:61:5d:c5:af:e4:d9:33:28:20:2a:
52:9c:17:b7:78:90:9e:8c:a9:a1:b4:72:87:96:3b:06:48:83: 1f:76:5b:a7:1f:54:47:c4:94:4f:de:bb:6d:ea:36:51:44:bf:
27:8b:ca:8d:0c:8d:06:f9:4d:e9:bb:26:5e:76:a7:79:62:31: 90:82:b3:7c:91:28:6a:1e:dd:76:66:38:2c:0f:ed:8a:fd:b6:
d4:99:e2:e3:ad:83:3f:c0:79:5d:0f:60:33:9d:f9:05:cd:12: 91:28:3b:59:f0:b3:42:4b:bb:fb:88:d9:d8:6f:e2:a9:79:14:
25:c3:10:22:81:d4:e3:95:3f:b3:ab:21:84:e7:6b:f5:55:9f: df:0b:53:76:be:23:c1:0e:96:85:aa:d9:3f:d6:60:7a:a5:a9:
8d:30:a7:eb:c4:85:23:94:5d:59:08:c7:48:f9:cf:37:98:9e: 5a:1d:17:05:7e:84:7a:36:0b:a0:eb:96:8d:75:08:ab:ea:e2:
96:89:e0:8b:75:05:ce:27:76:e4:bf:e5:14:b6:7f:21:07:bc: 9b:4c:dc:92:05:b0:bc:2a:c0:ff:21:89:02:c2:cd:07:ee:85:
67:53:95:f0:c5:ac:bd:4f:9e:46:df:40:6d:8d:8a:80:c6:21: 55:5b:cf:bd:bc:d7:b1:8a:54:51:5a:58:f6:77:e5:76:85:26:
80:ab:c4:bd:ee:77:01:16:9c:d5:7a:62:3b:d0:bd:38:6b:9d: fd:dd:e8:ec:aa:45:c8:cf:d1:10:16:68:97:e4:e7:06:e3:22:
db:4e:0e:aa:cf:03:c8:a5:07:0a:c5:79:42:53:6a:34:74:ec: 9c:4d:f5:85:bb:37:26:d4:fc:47:af:26:03:f3:e1:17:cc:20:
01:e1:3b:38:fe:14:15:0a:a1:cf:3b:a7:90:76:ba:1b:34:b5: 33:c1:c4:e2:b3:b7:1e:d3:0a:81:85:ff:e6:79:70:07:a7:8b:
70:77:78:0e:6d:2d:96:8b:d5:dc:1b:1e:21:a6:57:6a:97:5a: 3c:1e:9e:1b:e8:f5:a1:3e:69:c5:90:f4:7f:49:a7:19:93:fa:
67:49:64:81 65:fc:0e:43
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx
NjM3MDFaFw0zNjA3MjcxNjM3MDFaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS NzA1MzhaFw0zNjA3MjcxNzA1MzhaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA64Ydvy62mlJy bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAta14XYkUKJJV
PtszsdyyYyNkc0/Y6zIl8pwZoBsyEhCK1vPEChAlvgrcFBuUtSsW34cJwpkLvhhe 58HgFUbn6trqmLvsMxfu1uM3HDTqToehydMZ3pVvwXYT4LT1CD7+Wna4SuSUB4tn
+wwP4NKKbOUiDs2qqFeR6dxM3LcRKyzIRo6lokGQiokb8dL3zdm99xIZS8kFtQWZ S7XRl8HT4bHNhFsWqtxWW/8K5/N5o0dNW23k1iGg6+FUg4ccxRje2FJAk377xd9z
dj+xaidCItX+uWWqlfIlW7Y89Ki+Wc7FNQI5usj2vc6VYOa9Hv2n44s/50XgyW0t PNYLox2fNsdvWZDXX4KA/oIFGxt3kgTJJYiPnDWRzIagj4+bLixir/OKFnUwYUeN
eOiQRqaIkAQThG2kL7jT/JP4MZonZ/T7HVWA5CDfj7iwwUKInongKW82Lk4YztqS JOo1hEMzAe3XMywpTyyN9bezvXRPLoMt4dY9XtkUw6GJOE0pS645yKLdrfFCyA1s
rU4+66NP6wsg5h2JUc+PQwbp1lqUb2oHsEM+17GgaCMa5xBloXXqGbvtfeSaB5sQ KetwyNznQVlHHIlSYlOcNViKORaHYfIRJjorohkpwncx3hx0xlc6FYsvKWGnRbTY
mQpddbMVgwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt cKTS79pdoQIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G
CSqGSIb3DQEBCwUAA4IBAQCfTel41V/B++FD0Z8t1sP4lFYLlC2159K99jvhaWvL CSqGSIb3DQEBCwUAA4IBAQBgSVwMHRrDhha32GO3uZtNdEklBcAo9MaVzmOVt5lB
hOSKqf0bWBa1LccA2MtWN3L4DBenF+xSnBe3eJCejKmhtHKHljsGSIMni8qNDI0G VH9kXZqP9aeWCYxnQmFdxa/k2TMoICofdlunH1RHxJRP3rtt6jZRRL+QgrN8kShq
+U3puyZedqd5YjHUmeLjrYM/wHldD2AznfkFzRIlwxAigdTjlT+zqyGE52v1VZ+N Ht12ZjgsD+2K/baRKDtZ8LNCS7v7iNnYb+KpeRTfC1N2viPBDpaFqtk/1mB6pala
MKfrxIUjlF1ZCMdI+c83mJ6WieCLdQXOJ3bkv+UUtn8hB7xnU5Xwxay9T55G30Bt HRcFfoR6Ngug65aNdQir6uKbTNySBbC8KsD/IYkCws0H7oVVW8+9vNexilRRWlj2
jYqAxiGAq8S97ncBFpzVemI70L04a53bTg6qzwPIpQcKxXlCU2o0dOwB4Ts4/hQV d+V2hSb93ejsqkXIz9EQFmiX5OcG4yKcTfWFuzcm1PxHryYD8+EXzCAzwcTis7ce
CqHPO6eQdrobNLVwd3gObS2Wi9XcGx4hpldql1pnSWSB 0wqBhf/meXAHp4s8Hp4b6PWhPmnFkPR/SacZk/pl/A5D
-----END CERTIFICATE----- -----END CERTIFICATE-----

30
ssl/cert/ca.key Normal file
View 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-----

View File

@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIE+jCCA+KgAwIBAgIUVIBUu2vZ0TrxOwqlIyfxooaF4bIwDQYJKoZIhvcNAQEL MIIE+jCCA+KgAwIBAgIUA7R4NLhoa6Mt/Zek66V7+Qnx9VEwDQYJKoZIhvcNAQEL
BQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNv BQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNv
bWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFh bWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFh
ZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBB ZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBB
dXRob3JpdHkwHhcNMjAwMjIyMTYzNzAxWhcNMjkxMjMxMTYzNzAxWjCBkzELMAkG dXRob3JpdHkwHhcNMjAwMjIyMTcwNTM4WhcNMjkxMjMxMTcwNTM4WjCBkzELMAkG
A1UEBhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUw A1UEBhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUw
EwYDVQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w EwYDVQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
bGUub3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTCC bGUub3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMg5QnJc+a8K0YGIcnZ0OfFZ ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrt1oTLVGNfvZvmsQd7L0Ts
ZNQUMIBJIxdE3LnIa1Y6JNA89oVodtD/J4bFOHa8ehji7R6JzTCpDi6Sc61D9mT2 dttTKZcJynIlDX/2QJ7F77/sywrY69Bch14Gn2MhEhpRnn5GIKpEzE8KTDgg5iOV
Fa1sm8wSrD4NU+tMlaK5U73C6nYrAtnTpFnDeQQEzgfyzoN988QXAknY++Ls0CU/ FOwClfMwlhzksx7VpwSjSFhxbudxWgpk8RoGmuPaB4rJLHPo7B6Pv4DduxFpO5aF
B2Pjd5D4Sk1VOOf88/XYyU5OM5jT5/skOuBULdGhbL9TME3PRieMLOokTFZjNnDy IxKdPFCi1sKKzJAi+88SSX5KhX+MQgwiqbLjdeXxx+ofTZig7IymiJUUbcdtiQPq
uCKV3p5PD8MYAVh6qNEdHHnQMp559JfdzAh2FZPvdr88MbhO78pFCymdnOkhIr6M KYXjcFH/XLhJMx52GKBk9YK5yvoDwYzGs+FKkPgWCbifUHnzkQ+/v3yKsvnFS/vq
ZvBqDLxaV7mjcduU/U1lq13CekZ2DUTDpbUa9z4w+BiqgFpWJdxy0ZTwpwPBqd8C Rb1uPA3PkJnbZwCLb0hGKh8YvGUIr+9WT4MLQPOBKgfRUHEjNxvdYCUaw6xz+8EC
AwEAAaOCAUIwggE+MB0GA1UdDgQWBBR0nqW1x1vNwtdFezphgGtkz7B2RjCB0wYD AwEAAaOCAUIwggE+MB0GA1UdDgQWBBQi2Mb/nVfuJREdj/wvqVAtZNxhXzCB0wYD
VR0jBIHLMIHIgBR0nqW1x1vNwtdFezphgGtkz7B2RqGBmaSBljCBkzELMAkGA1UE VR0jBIHLMIHIgBQi2Mb/nVfuJREdj/wvqVAtZNxhX6GBmaSBljCBkzELMAkGA1UE
BhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYD BhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYD
VQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu VQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUVIBU b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUA7R4
u2vZ0TrxOwqlIyfxooaF4bIwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug NLhoa6Mt/Zek66V7+Qnx9VEwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug
KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUub3JnL2V4YW1wbGVfY2EuY3JsMA0GCSqG KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUub3JnL2V4YW1wbGVfY2EuY3JsMA0GCSqG
SIb3DQEBCwUAA4IBAQAdywbOcOa9XSx/hEOKkG4rZR8F6eb+z/u+BYfEhQE4unTR SIb3DQEBCwUAA4IBAQCOSSpCkK1kF4MlctIhNnMjP6AcxhPYFhDPrTCy+lGmNw0S
3ihNarsHwteTQArabTRzuV8+phX7fgkQJHmp1NOpJVmMEr3JOs00SGbisxDmK0Z3 4L73ZHoTtmPsEc7p4d3qxy1WddRS3M6+0vyQz5Yhe1xQZy+SExeYxOX8sXqK1Uu3
2HkE+DjpYo2Sz8b77YD1AWk705rJkJ0Jp5+d/BLk6CjCr524XsSKLwKRWKOit3eu 37rY7oz26DkITOdI4FFtcNKtAcu8rVg2kZO/66RUiQSA6V3rzAVcFGrHHP4ZoPqV
WKsbt+VMd7d8jDvgwrtKLDpGv/sBym5w9zQjUqOTPBr70BBbpynJD62mzdr5OjIc qy+OqO8dcldlf70B7fLnf2N28drdpkUeJEGhGRzO42gN55Zj9RVH3NCZDZI1uz2B
JiZKjusr1fieOeBzo2us+hFdvoCmW8X4hwVAyWm4JgP1yZ9IT/KLnFtJo0k/BM3B ESIwjFWBFDeteux8O9BZhfNJ+Zmc9BS+1CvjMCCEAeRCjpAWdyLa+uquq5tII4Me
lzcz90I0zzpewC8xxaWJ7kz6h+eSIa3orlSIug4o STKrTWjHUmq0zjDmjOowB8xWvoWONW19nsSSqGC8
-----END CERTIFICATE----- -----END CERTIFICATE-----

80
ssl/cert/server.crt Normal file
View 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-----

View File

@ -3,16 +3,16 @@ MIIC2TCCAcECAQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQ
BgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZI BgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZI
hvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0 hvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0
aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDrhh2/LraaUnI+2zOx3LJjI2RzT9jrMiXynBmgGzISEIrW88QKECW+CtwUG5S1 AQC1rXhdiRQoklXnweAVRufq2uqYu+wzF+7W4zccNOpOh6HJ0xnelW/BdhPgtPUI
KxbfhwnCmQu+GF77DA/g0ops5SIOzaqoV5Hp3EzctxErLMhGjqWiQZCKiRvx0vfN Pv5adrhK5JQHi2dLtdGXwdPhsc2EWxaq3FZb/wrn83mjR01bbeTWIaDr4VSDhxzF
2b33EhlLyQW1BZl2P7FqJ0Ii1f65ZaqV8iVbtjz0qL5ZzsU1Ajm6yPa9zpVg5r0e GN7YUkCTfvvF33M81gujHZ82x29ZkNdfgoD+ggUbG3eSBMkliI+cNZHMhqCPj5su
/afjiz/nReDJbS146JBGpoiQBBOEbaQvuNP8k/gxmidn9PsdVYDkIN+PuLDBQoie LGKv84oWdTBhR40k6jWEQzMB7dczLClPLI31t7O9dE8ugy3h1j1e2RTDoYk4TSlL
ieApbzYuThjO2pKtTj7ro0/rCyDmHYlRz49DBunWWpRvagewQz7XsaBoIxrnEGWh rjnIot2t8ULIDWwp63DI3OdBWUcciVJiU5w1WIo5Fodh8hEmOiuiGSnCdzHeHHTG
deoZu+195JoHmxCZCl11sxWDAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAKYUI VzoViy8pYadFtNhwpNLv2l2hAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAtTan
sRw13r+FXj0cR7rvz2JQVBcJSphHrBK5O8xkO4zlOLVuxbUIA3/6YunmmnFMChTb LS+AjvzL0raUHp/SWflMh37l2J60cjzjmbrws7MpQdqGg7H11tXhgKSDELMpuB4h
O/Be6HDHX0+SmqfrT0VUGZ2zF1QeDFtkK195r3gMCQ1qqUEBCdAWZG4u1XDEnVvV 44PZeDZmZGfFJCtYWcOeTc4SvI69SXYnj6UaMKxM3/1sI0fd9JaPnCs7Sg5+B96A
fctXt3D/D8cYJ0Dw+xHMBq5RpxO6/dv+ML1QicRJ8h4uvtjcf3ePPF6cyrtLeCxM /8AcF1hKXZ9y71XD3NeGGRDuzqHvGdeIfMoGP8GJLaR70ncLnBpg2KCHG6C6mP6R
Vw6RWUB0XbdSqA6ljFcT0ytI1f4hnu7pwp4Rhc6xAtZyGWn7Vgbm9kxlThzenT+r 4W5yrWRCcpVXBAXi4yoxesQRLwp/DEXZo6ExOJHDYpZLen6YjkTp/Lv7PySKopQP
fePBF77E2CY16kRzzKQOiyEqumkotEimvGsUsGx90TtPTMNX8GiXm9Ct1XRBL4SI SJrev/H6GL4qmLfl7dFebq+FE6sezIRzopFWnHu+TcUMVCWBUwJVl71Nvw009A1/
R+SlHRAayKRGPyhvOA== kfBQ/TsFvyRDzqqM5Q==
-----END CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST-----

30
ssl/cert/server.key Normal file
View 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-----

View File

@ -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

View File

@ -3,7 +3,7 @@
"outDir": "./dist", "outDir": "./dist",
"rootDir": "./src", "rootDir": "./src",
// target settings for node 10 // target settings for node js
"module": "commonjs", "module": "commonjs",
"target": "es2018", "target": "es2018",
"lib": ["es2018"], "lib": ["es2018"],