1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-10 21:33:20 +00:00

starttls: Support LDAP STARTTLS (#33)

* starttls: Support LDAP STARTTLS

* starttls: Re-roll to preserve old config
This commit is contained in:
Colin Campbell
2020-03-04 17:08:47 +01:00
committed by GitHub
parent f63fb3ffa0
commit 20bb5a4926
4 changed files with 107 additions and 53 deletions

View File

@@ -35,39 +35,54 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="ssl" [(ngModel)]="ldap.ssl" name="SSL"> <input class="form-check-input" type="checkbox" id="ldapEncrypted" [(ngModel)]="ldap.ssl" name="Encrypted">
<label class="form-check-label" for="ssl">{{'ldapSsl' | i18n}}</label> <label class="form-check-label" for="ldapEncrypted">{{'ldapEncrypted' | i18n}}</label>
</div> </div>
</div> </div>
<div class="ml-4" *ngIf="ldap.ssl"> <div class="ml-4" *ngIf="ldap.ssl">
<p>{{'ldapSslUntrustedDesc' | i18n}}</p>
<div class="form-group"> <div class="form-group">
<label for="sslCertPath">{{'ldapSslCert' | i18n}}</label> <div class="form-radio">
<input type="file" class="form-control-file mb-2" id="sslCertPath_file" <input class="form-radio-input" type="radio" [value]=true id="starttls" [(ngModel)]="ldap.starttls" name="StartTls">
(change)="setSslPath('sslCertPath')"> <label class="form-radio-label" for="starttls">{{'ldapTls' | i18n}}</label>
<input type="text" class="form-control" id="sslCertPath" name="SSLCertPath" </div>
[(ngModel)]="ldap.sslCertPath"> <div class="form-radio">
<input class="form-radio-input" type="radio" [value]=false id="ssl" [(ngModel)]="ldap.starttls" name="SSL">
<label class="form-radio-label" for="ssl">{{'ldapSsl' | i18n}}</label>
</div>
</div> </div>
<div class="form-group"> <div class="ml-4" *ngIf="ldap.starttls">
<label for="sslKeyPath">{{'ldapSslKey' | i18n}}</label> <p>{{'ldapTlsUntrustedDesc' | i18n}}</p>
<input type="file" class="form-control-file mb-2" id="sslKeyPath_file" <div class="form-group">
(change)="setSslPath('sslKeyPath')"> <label for="tlsCaPath">{{'ldapTlsCa' | i18n}}</label>
<input type="text" class="form-control" id="sslKeyPath" name="SSLKeyPath" <input type="file" class="form-control-file mb-2" id="tlsCaPath_file"
[(ngModel)]="ldap.sslKeyPath"> (change)="setSslPath('tlsCaPath')">
<input type="text" class="form-control" id="tlsCaPath" name="TLSCaPath" [(ngModel)]="ldap.tlsCaPath">
</div>
</div> </div>
<div class="form-group"> <div class="ml-4" *ngIf="!ldap.starttls">
<label for="sslCaPath">{{'ldapSslCa' | i18n}}</label> <p>{{'ldapSslUntrustedDesc' | i18n}}</p>
<input type="file" class="form-control-file mb-2" id="sslCaPath_file" <div class="form-group">
(change)="setSslPath('sslCaPath')"> <label for="sslCertPath">{{'ldapSslCert' | i18n}}</label>
<input type="text" class="form-control" id="sslCaPath" name="SSLCaPath" <input type="file" class="form-control-file mb-2" id="sslCertPath_file"
[(ngModel)]="ldap.sslCaPath"> (change)="setSslPath('sslCertPath')">
<input type="text" class="form-control" id="sslCertPath" name="SSLCertPath" [(ngModel)]="ldap.sslCertPath">
</div>
<div class="form-group">
<label for="sslKeyPath">{{'ldapSslKey' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslKeyPath_file" (change)="setSslPath('sslKeyPath')">
<input type="text" class="form-control" id="sslKeyPath" name="SSLKeyPath" [(ngModel)]="ldap.sslKeyPath">
</div>
<div class="form-group">
<label for="sslCaPath">{{'ldapSslCa' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCaPath_file" (change)="setSslPath('sslCaPath')">
<input type="text" class="form-control" id="sslCaPath" name="SSLCaPath" [(ngModel)]="ldap.sslCaPath">
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="sslAllowUnauthorized" <input class="form-check-input" type="checkbox" id="certDoNotVerify"
[(ngModel)]="ldap.sslAllowUnauthorized" name="SSLAllowUnauthorized"> [(ngModel)]="ldap.sslAllowUnauthorized" name="CertDoNoVerify">
<label class="form-check-label" <label class="form-check-label" for="certDoNotVerify">{{'ldapCertDoNotVerify' | i18n}}</label>
for="sslAllowUnauthorized">{{'ldapSslAllowUnauthorized' | i18n}}</label>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -414,8 +414,20 @@
"sync": { "sync": {
"message": "Sync" "message": "Sync"
}, },
"ldapEncrypted": {
"message": "This server uses an encrypted connection"
},
"ldapTls": {
"message": "Use TLS (STARTTLS)"
},
"ldapTlsCa": {
"message": "Certificate CA Chain (PEM)"
},
"ldapSsl": { "ldapSsl": {
"message": "This server uses SSL (LDAPS)" "message": "Use SSL (LDAPS)"
},
"ldapTlsUntrustedDesc": {
"message": "If your LDAP server uses a self-signed certificate for STARTTLS, you can configure certificate options below."
}, },
"ldapSslUntrustedDesc": { "ldapSslUntrustedDesc": {
"message": "If your LDAPS server uses an untrusted certificate you can configure certificate options below." "message": "If your LDAPS server uses an untrusted certificate you can configure certificate options below."
@@ -429,8 +441,8 @@
"ldapSslKey": { "ldapSslKey": {
"message": "Certificate Private Key (PEM)" "message": "Certificate Private Key (PEM)"
}, },
"ldapSslAllowUnauthorized": { "ldapCertDoNotVerify": {
"message": "Allow untrusted SSL connections (not recommended)." "message": "Do not verify server certificates (not recommended)."
}, },
"ldapAd": { "ldapAd": {
"message": "This server uses Active Directory" "message": "This server uses Active Directory"

View File

@@ -1,5 +1,7 @@
export class LdapConfiguration { export class LdapConfiguration {
ssl = false; ssl = false;
starttls = true;
tlsCaPath: string;
sslAllowUnauthorized = false; sslAllowUnauthorized = false;
sslCertPath: string; sslCertPath: string;
sslKeyPath: string; sslKeyPath: string;

View File

@@ -324,32 +324,41 @@ export class LdapDirectoryService implements DirectoryService {
reject(this.i18nService.t('dirConfigIncomplete')); reject(this.i18nService.t('dirConfigIncomplete'));
return; return;
} }
const protocol = 'ldap' + (this.dirConfig.ssl && !this.dirConfig.starttls ? 's' : '');
const url = 'ldap' + (this.dirConfig.ssl ? 's' : '') + '://' + this.dirConfig.hostname + const url = protocol + '://' + this.dirConfig.hostname +
':' + this.dirConfig.port; ':' + this.dirConfig.port;
const options: ldap.ClientOptions = { const options: ldap.ClientOptions = {
url: url.trim().toLowerCase(), url: url.trim().toLowerCase(),
}; };
const tlsOptions: any = {};
if (this.dirConfig.ssl) { if (this.dirConfig.ssl) {
const tlsOptions: any = {}; if (!this.dirConfig.starttls) {
if (this.dirConfig.sslAllowUnauthorized != null) { if (this.dirConfig.sslCaPath != null && this.dirConfig.sslCaPath !== '' &&
fs.existsSync(this.dirConfig.sslCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (this.dirConfig.sslCertPath != null && this.dirConfig.sslCertPath !== '' &&
fs.existsSync(this.dirConfig.sslCertPath)) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (this.dirConfig.sslKeyPath != null && this.dirConfig.sslKeyPath !== '' &&
fs.existsSync(this.dirConfig.sslKeyPath)) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
} else {
if (this.dirConfig.tlsCaPath != null && this.dirConfig.tlsCaPath !== '' &&
fs.existsSync(this.dirConfig.tlsCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.tlsCaPath)];
}
}
if (this.dirConfig.sslAllowUnauthorized) {
tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized; tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized;
} }
if (this.dirConfig.sslCaPath != null && this.dirConfig.sslCaPath !== '' && }
fs.existsSync(this.dirConfig.sslCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)]; if (Object.keys(tlsOptions).length > 0) {
} options.tlsOptions = tlsOptions;
if (this.dirConfig.sslCertPath != null && this.dirConfig.sslCertPath !== '' &&
fs.existsSync(this.dirConfig.sslCertPath)) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (this.dirConfig.sslKeyPath != null && this.dirConfig.sslKeyPath !== '' &&
fs.existsSync(this.dirConfig.sslKeyPath)) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
if (Object.keys(tlsOptions).length > 0) {
options.tlsOptions = tlsOptions;
}
} }
this.client = ldap.createClient(options); this.client = ldap.createClient(options);
@@ -364,13 +373,29 @@ export class LdapDirectoryService implements DirectoryService {
return; return;
} }
this.client.bind(user, pass, (err) => { if (this.dirConfig.starttls && this.dirConfig.ssl) {
if (err != null) { this.client.starttls(options.tlsOptions, undefined, (err, res) => {
reject(err.message); if (err != null) {
} else { reject(err.message);
resolve(); } else {
} this.client.bind(user, pass, (err) => {
}); if (err != null) {
reject(err.message);
} else {
resolve();
}
});
}
});
} else {
this.client.bind(user, pass, (err) => {
if (err != null) {
reject(err.message);
} else {
resolve();
}
});
}
}); });
} }