Compare commits

...

9 Commits
main ... main

Author SHA1 Message Date
Alex 1d338a9cc1 fix consul version in ci
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-03-20 17:02:45 +00:00
Alex e9bf550e02 Woodpecker CI 2024-03-20 17:02:45 +00:00
Adrien 55ef7530ae cleaned Docker process
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build encountered an error Details
2023-03-27 10:23:32 +02:00
Quentin a2be316d91
use legacy ssha algorithm, new one is incompatible
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-03-26 09:41:22 +02:00
Alex 9cab98d2ce
Set correct package path
continuous-integration/drone/push Build was killed Details
2022-12-01 22:43:06 +01:00
Alex de71260bbf
Make repo a Nix flake
continuous-integration/drone/push Build is pending Details
2022-12-01 22:40:13 +01:00
Quentin 0ef8313a67
Add Nix packaging
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build was killed Details
continuous-integration/drone Build was killed Details
2022-07-24 09:47:31 +02:00
Simon Beck 9ce0d22c99 Fix wrong handling of multi value attributes
continuous-integration/drone/push Build is passing Details
While ldapsearch doesn't seem to mind, apps like keycloak seem to have
issues with adding multiple attributes with different values. While
the resulting ldif in ldapsearch is indistinguishable there seems to
be a slight different on the protocol level.

If adding multiple attributes with the same name and different values,
keycloak will only see the last entry. But adding a single attribute
a slice of values is seems to handle it correctly.
2022-02-14 12:13:31 +01:00
Simon Beck f05e41c9aa Improve password hash handling
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
This adds support for more hash algorithms. Also a stored password will
be updated to SSHA512 upon a successful bind. It will also automatically
hash a cleartext password if the `userpassword` field is modified with
a cleartext one.

Hashes supported:
* SSHA
* SSHA256
* SSHA512
2022-02-10 20:51:01 +01:00
14 changed files with 465 additions and 44 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ bottin
bottin.static
config.json
test/test
result
ldap.json

View File

@ -1,7 +1,3 @@
---
kind: pipeline
name: bottin
steps:
- name: build
image: golang:stretch
@ -12,14 +8,8 @@ steps:
- go test -i -c -o test
- name: test_bottin
image: consul:latest
image: consul:1.15.4
environment:
BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1
commands:
- ash test/runner.sh
---
kind: signature
hmac: ff246a04c3df8a2f39c8b446dea920622d61950e6caaac886931bdb05d0706ed
...

View File

@ -1,5 +0,0 @@
FROM scratch
ADD bottin.static /bottin
ENTRYPOINT ["/bottin"]

View File

@ -1,6 +1,6 @@
# Bottin
[![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/bottin/status.svg?ref=refs/heads/main)](https://drone.deuxfleurs.fr/Deuxfleurs/bottin)
[![status-badge](https://woodpecker.deuxfleurs.fr/api/badges/38/status.svg)](https://woodpecker.deuxfleurs.fr/repos/38)
<img src="https://git.deuxfleurs.fr/Deuxfleurs/bottin/raw/branch/main/bottin.png" style="height: 150px; display: block; margin-left: auto; margin-right: auto" />
@ -24,7 +24,7 @@ Features:
- Access control through an ACL (hardcoded in the configuration file)
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/bottin_amd64).
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/dxflrs/bottin) (built in `default.nix`).
An example for running Bottin on a Nomad cluster can be found in `bottin.hcl.example`.
Bottin takes a single command line argument, `-config <filename>`, which is the
@ -44,6 +44,17 @@ Bottin requires go 1.13 or later.
To build Bottin, clone this repository outside of your `$GOPATH`.
Then, run `make` in the root of the repo.
## Releasing
```bash
nix-build -A bin
nix-build -A docker
```
```bash
docker load < $(nix-build -A docker)
docker push dxflrs/bottin:???
```
## Server initialization

55
default.nix Normal file
View File

@ -0,0 +1,55 @@
let
pkgsSrc = fetchTarball {
# As of 2022-07-19
url = "https://github.com/NixOS/nixpkgs/archive/d2db10786f27619d5519b12b03fb10dc8ca95e59.tar.gz";
sha256 = "0s9gigs3ylnq5b94rfcmxvrmmr3kzhs497gksajf638d5bv7zcl5";
};
gomod2nix = fetchGit {
url = "https://github.com/tweag/gomod2nix.git";
ref = "master";
rev = "40d32f82fc60d66402eb0972e6e368aeab3faf58";
};
pkgs = import pkgsSrc {
overlays = [
(self: super: {
gomod = super.callPackage "${gomod2nix}/builder/" { };
})
];
};
in rec {
bin = pkgs.gomod.buildGoApplication {
pname = "bottin-bin";
version = "0.1.0";
src = builtins.filterSource
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
./.;
modules = ./gomod2nix.toml;
CGO_ENABLED=0;
meta = with pkgs.lib; {
description = "A cloud-native LDAP server backed by a Consul datastore";
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
license = licenses.gpl3Plus;
platforms = platforms.linux;
};
};
pkg = pkgs.stdenv.mkDerivation {
pname = "bottin";
version = "0.1.0";
unpackPhase = "true";
installPhase = ''
mkdir -p $out/
cp ${bin}/bin/bottin $out/bottin
'';
};
docker = pkgs.dockerTools.buildImage {
name = "dxflrs/bottin";
config = {
Entrypoint = [ "${pkg}/bottin" ];
WorkingDir = "/";
};
};
}

79
flake.lock Normal file
View File

@ -0,0 +1,79 @@
{
"nodes": {
"gomod2nix": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
},
"locked": {
"lastModified": 1655245309,
"narHash": "sha256-d/YPoQ/vFn1+GTmSdvbSBSTOai61FONxB4+Lt6w/IVI=",
"owner": "tweag",
"repo": "gomod2nix",
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
"type": "github"
},
"original": {
"owner": "tweag",
"repo": "gomod2nix",
"rev": "40d32f82fc60d66402eb0972e6e368aeab3faf58",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1653581809,
"narHash": "sha256-Uvka0V5MTGbeOfWte25+tfRL3moECDh1VwokWSZUdoY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "83658b28fe638a170a19b8933aa008b30640fbd1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1669764884,
"narHash": "sha256-1qWR/5+WtqxSedrFbUbM3zPMO7Ec2CGWaxtK4z4DdvY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"rev": "0244e143dc943bcf661fdaf581f01eb0f5000fcf",
"type": "github"
}
},
"root": {
"inputs": {
"gomod2nix": "gomod2nix",
"nixpkgs": "nixpkgs_2"
}
},
"utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

39
flake.nix Normal file
View File

@ -0,0 +1,39 @@
{
description = "A cloud-native LDAP server backed by a Consul datastore";
inputs.nixpkgs.url = "github:nixos/nixpkgs/0244e143dc943bcf661fdaf581f01eb0f5000fcf";
inputs.gomod2nix.url = "github:tweag/gomod2nix/40d32f82fc60d66402eb0972e6e368aeab3faf58";
outputs = { self, nixpkgs, gomod2nix }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [
(self: super: {
gomod = super.callPackage "${gomod2nix}/builder/" { };
})
];
};
bottin = pkgs.gomod.buildGoApplication {
pname = "bottin";
version = "0.1.0";
src = builtins.filterSource
(path: type: (builtins.match ".*/test/.*\\.(go|sum|mod)" path) == null)
./.;
modules = ./gomod2nix.toml;
CGO_ENABLED=0;
meta = with pkgs.lib; {
description = "A cloud-native LDAP server backed by a Consul datastore";
homepage = "https://git.deuxfleurs.fr/Deuxfleurs/bottin";
license = licenses.gpl3Plus;
platforms = platforms.linux;
};
};
in
{
packages.x86_64-linux.bottin = bottin;
packages.x86_64-linux.default = self.packages.x86_64-linux.bottin;
};
}

2
go.mod
View File

@ -3,9 +3,9 @@ module bottin
go 1.13
require (
github.com/go-ldap/ldap/v3 v3.3.0
github.com/google/uuid v1.1.1
github.com/hashicorp/consul/api v1.3.0
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73
github.com/sirupsen/logrus v1.4.2
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
)

10
go.sum
View File

@ -1,5 +1,3 @@
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@ -9,11 +7,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@ -49,6 +42,8 @@ github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG67
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73 h1:ZhC4QngptYaGx53+ph1RjxcH8fkCozBaY+935TNX4i8=
github.com/jsimonetti/pwscheme v0.0.0-20220125093853-4d9895f5db73/go.mod h1:t0Q9JvoMTfTYdAWIk2MF69iz+Qpdk9D+PgVu6fVmaDI=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -88,6 +83,7 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

153
gomod2nix.toml Normal file
View File

@ -0,0 +1,153 @@
schema = 3
[mod]
[mod."github.com/armon/circbuf"]
version = "v0.0.0-20150827004946-bbbad097214e"
hash = "sha256-klQjllsJZqZ2KPNx1mZT9XP+UAJkuBhmTnZdNlAflEM="
[mod."github.com/armon/go-metrics"]
version = "v0.0.0-20180917152333-f0300d1749da"
hash = "sha256-+zqX1hlJgc+IrXRzBQDMhR8GYQdc0Oj6PiIDfctgh44="
[mod."github.com/armon/go-radix"]
version = "v0.0.0-20180808171621-7fddfc383310"
hash = "sha256-ZHU4pyBqHHRuQJuYr2K+LqeAnLX9peX07cmSYK+GDHk="
[mod."github.com/bgentry/speakeasy"]
version = "v0.1.0"
hash = "sha256-Gt1vj6CFovLnO6wX5u2O4UfecY9V2J9WGw1ez4HMrgk="
[mod."github.com/davecgh/go-spew"]
version = "v1.1.1"
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
[mod."github.com/fatih/color"]
version = "v1.7.0"
hash = "sha256-4In7ef7it7d+6oGUJ3pkD0V+lsL40hVtYdy2KD2ovn0="
[mod."github.com/google/btree"]
version = "v0.0.0-20180813153112-4030bb1f1f0c"
hash = "sha256-5gr0RMnlvrzCke3kwpkf92WvW3x5nnKZesoulyoYRC0="
[mod."github.com/google/uuid"]
version = "v1.1.1"
hash = "sha256-66PXC/RCPUyhS9PhkIPQFR3tbM2zZYDNPGXN7JJj3UE="
[mod."github.com/hashicorp/consul/api"]
version = "v1.3.0"
hash = "sha256-fMORNFAWK2GkRbBl/KzNHrvtCfwz/7P66HzDaE4GnuE="
[mod."github.com/hashicorp/consul/sdk"]
version = "v0.3.0"
hash = "sha256-lF47JPGfmeGjpuQw9VSNs2Mi+G7FQLKrrHteX3iXfpU="
[mod."github.com/hashicorp/errwrap"]
version = "v1.0.0"
hash = "sha256-LGSLrefkABG1kH1i+GUWiD2/ggJxiZEJ+D2YNbhZjmo="
[mod."github.com/hashicorp/go-cleanhttp"]
version = "v0.5.1"
hash = "sha256-c54zcHr9THj3MQk7hrDQcpjOcQi1MvXZ4Kpin6EbfR4="
[mod."github.com/hashicorp/go-immutable-radix"]
version = "v1.0.0"
hash = "sha256-JmNxdGaJG63Ty/sVnPjqvTyA4/k5wkzZ/QvpMK2uduw="
[mod."github.com/hashicorp/go-msgpack"]
version = "v0.5.3"
hash = "sha256-2OUYjD/Jt12TFBrtH0wRqg+lzRljDxSIhk2CqBLUczo="
[mod."github.com/hashicorp/go-multierror"]
version = "v1.0.0"
hash = "sha256-iXzjerl96o7QDiSwQjbak8R/t+YzZeoUqm59TCmy3gI="
[mod."github.com/hashicorp/go-rootcerts"]
version = "v1.0.0"
hash = "sha256-4NZJAT5/vocyto+dv6FmW4kFiYldmNvewowsYK/LiTI="
[mod."github.com/hashicorp/go-sockaddr"]
version = "v1.0.0"
hash = "sha256-orG+SHVsp5lgNRCErmhMLABVFQ3ZWfYIJ/4LTFzlvao="
[mod."github.com/hashicorp/go-syslog"]
version = "v1.0.0"
hash = "sha256-YRuq6oPMwAFVY7mvwpMDvZqGwNnb5CjBYyKI/x5mbCc="
[mod."github.com/hashicorp/go-uuid"]
version = "v1.0.1"
hash = "sha256-s1wIvBu37z4U3qK9sdUR1CtbD39N6RwfX4HgDCpCa0s="
[mod."github.com/hashicorp/go.net"]
version = "v0.0.1"
hash = "sha256-JKal3E+wPO+Hk838opKV4HHKB4O72Xy+77ncXlLkWRk="
[mod."github.com/hashicorp/golang-lru"]
version = "v0.5.0"
hash = "sha256-Lo0UyOZQ89iK0Ui3Hfk5VkWqxEzLw6Lclq4EM8VlYoo="
[mod."github.com/hashicorp/logutils"]
version = "v1.0.0"
hash = "sha256-e8t8Dm8sp/PzKClN1TOmFcrTAWNh4mZHSW7cAjVx3Bw="
[mod."github.com/hashicorp/mdns"]
version = "v1.0.0"
hash = "sha256-ravx4tklQG43OEjPiJn68iJM9ODZ6hgU0idFCEOiJGM="
[mod."github.com/hashicorp/memberlist"]
version = "v0.1.3"
hash = "sha256-IsxqevYulPt+2VGtlq068Jyq1YfIk4Ohh9TgakIGxnc="
[mod."github.com/hashicorp/serf"]
version = "v0.8.2"
hash = "sha256-diRxWOouFLTO75f2E9NlrKgie/qsT+gOOrrbf4tACHw="
[mod."github.com/jsimonetti/pwscheme"]
version = "v0.0.0-20220125093853-4d9895f5db73"
hash = "sha256-YF3RKU/4CWvLPgGzUd7Hk/2+41OUFuRWZgzQuqcsKg0="
[mod."github.com/konsorten/go-windows-terminal-sequences"]
version = "v1.0.1"
hash = "sha256-Nwp+Cza9dIu3ogVGip6wyOjWwwaq+2hU3eYIe4R7kNE="
[mod."github.com/mattn/go-colorable"]
version = "v0.0.9"
hash = "sha256-fVPF8VxbdggLAZnaexMl6Id1WjXKImzVySxKfa+ukts="
[mod."github.com/mattn/go-isatty"]
version = "v0.0.3"
hash = "sha256-9ogEEd8/kl/dImldzdBmTFdepvB0dVXEzN4o8bEqhBs="
[mod."github.com/miekg/dns"]
version = "v1.0.14"
hash = "sha256-OeijUgBaEmDapclTxfvjIqrjh4qZu3+DQpHelGGI4aA="
[mod."github.com/mitchellh/cli"]
version = "v1.0.0"
hash = "sha256-4nG7AhRcjTRCwUCdnaNaFrAKDxEEoiihaCA4lk+uM8U="
[mod."github.com/mitchellh/go-homedir"]
version = "v1.0.0"
hash = "sha256-eGmBNNTuDSMwicjTNgB54IEJuK1tgOLDJ3NHTpQCHzg="
[mod."github.com/mitchellh/go-testing-interface"]
version = "v1.0.0"
hash = "sha256-/Dpv/4i5xuK8hDH+q8YTdF6Jg6NNtfO4Wqig2JCWgrY="
[mod."github.com/mitchellh/gox"]
version = "v0.4.0"
hash = "sha256-GV3LYxzJt8YVbnSac2orlj2QR3MX/YIDrLkSkPhsjuA="
[mod."github.com/mitchellh/iochan"]
version = "v1.0.0"
hash = "sha256-b5Tp7cw/e8mL++IjsebbmKWXtb9Hrzu4Fc6M4tZKFhU="
[mod."github.com/mitchellh/mapstructure"]
version = "v1.1.2"
hash = "sha256-OU9HZYHtl0qaqMFd84w7snkkRuRY6UMSsfCnL5HYdw0="
[mod."github.com/pascaldekloe/goe"]
version = "v0.0.0-20180627143212-57f6aae5913c"
hash = "sha256-2KUjqrEC/BwkTZRxImazcI/C3H7QmXfNrlt8slwdDbc="
[mod."github.com/pkg/errors"]
version = "v0.8.1"
hash = "sha256-oe3iddfoLRwpC3ki5fifHf2ZFprtg99iNak50shiuDw="
[mod."github.com/pmezard/go-difflib"]
version = "v1.0.0"
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
[mod."github.com/posener/complete"]
version = "v1.1.1"
hash = "sha256-heyPMSBzVlx7ZKgTyzl/xmUfZw3EZCcvGFGrRMIbIr8="
[mod."github.com/ryanuber/columnize"]
version = "v0.0.0-20160712163229-9b3edd62028f"
hash = "sha256-RLUQcU6Z03upKe08v6rjn9/tkyrQsgmpdEmBtWaLQfk="
[mod."github.com/sean-/seed"]
version = "v0.0.0-20170313163322-e2103e2c3529"
hash = "sha256-RQQTjvf8Y91jP5FGOyEnGMFw7zCrcSnUU4eH2CXKkT4="
[mod."github.com/sirupsen/logrus"]
version = "v1.4.2"
hash = "sha256-3QzWUsapCmg3F7JqUuINT3/UG097uzLff6iCcCgQ43o="
[mod."github.com/stretchr/objx"]
version = "v0.1.1"
hash = "sha256-HdGVZCuy7VprC5W9UxGbDmXqsKADMjpEDht7ilGVLco="
[mod."github.com/stretchr/testify"]
version = "v1.3.0"
hash = "sha256-+mSebBNccNcxbY462iKTNTWmd5ZuUkUqFebccn3EtIA="
[mod."golang.org/x/crypto"]
version = "v0.0.0-20200604202706-70a84ac30bf9"
hash = "sha256-I5ov2lV6ubB3H3pn6DFN1VPLyxeLfPUaOx2PJ9K1KH8="
[mod."golang.org/x/net"]
version = "v0.0.0-20190404232315-eb5bcb51f2a3"
hash = "sha256-kwP+BDfPsZEeeceyg4H5LgdTDT8O8YWbkpCkpyuPJJo="
[mod."golang.org/x/sync"]
version = "v0.0.0-20181221193216-37e7f081c4d4"
hash = "sha256-FUpv9bmGLDEjCxXdvR0CRDqOKRY0xrHRPzOsyQyvYK0="
[mod."golang.org/x/sys"]
version = "v0.0.0-20190422165155-953cdadca894"
hash = "sha256-YkQjSZaiVFxrmXYSKmqrGeIO3RZ95fc3dWyCaR+SosY="
[mod."golang.org/x/text"]
version = "v0.3.0"
hash = "sha256-0FFbaxF1ZuAQF3sCcA85e8MO6prFeHint36inija4NY="

39
main.go
View File

@ -12,8 +12,8 @@ import (
"os/signal"
"syscall"
ldap "bottin/ldapserver"
message "bottin/goldap"
ldap "bottin/ldapserver"
consul "github.com/hashicorp/consul/api"
log "github.com/sirupsen/logrus"
@ -320,7 +320,6 @@ func (server *Server) init() error {
return err
}
admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW")
if !environnement_variable_exist {
admin_pass := make([]byte, 8)
@ -329,11 +328,15 @@ func (server *Server) init() error {
return err
}
admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass)
} else {
} else {
server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password")
}
admin_pass_hash := SSHAEncode([]byte(admin_pass_str))
admin_pass_hash, err := SSHAEncode(admin_pass_str)
if err != nil {
server.logger.Error("can't create admin password")
panic(err)
}
admin_dn := "cn=admin," + server.config.Suffix
admin_attributes := Entry{
@ -434,8 +437,8 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
}
for _, hash := range passwd {
valid := SSHAMatches(hash, []byte(r.AuthenticationSimple()))
if valid {
valid, err := SSHAMatches(hash, string(r.AuthenticationSimple()))
if valid && err == nil {
groups, err := server.getAttribute(string(r.Name()), ATTR_MEMBEROF)
if err != nil {
return ldap.LDAPResultOperationsError, err
@ -444,8 +447,32 @@ func (server *Server) handleBindInternal(state *State, r *message.BindRequest) (
user: string(r.Name()),
groups: groups,
}
updatePasswordHash(string(r.AuthenticationSimple()), hash, server, string(r.Name()))
return ldap.LDAPResultSuccess, nil
} else {
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("can't authenticate: %w", err)
}
}
return ldap.LDAPResultInvalidCredentials, fmt.Errorf("No password match")
}
// Update the hash if it's not already SSHA512
func updatePasswordHash(password string, currentHash string, server *Server, dn string) {
hashType, err := determineHashType(currentHash)
if err != nil {
server.logger.Errorf("can't determine hash type of password")
return
}
if hashType != SSHA512 {
reencodedPassword, err := SSHAEncode(password)
if err != nil {
server.logger.Errorf("can't encode password")
return
}
server.putAttributes(dn, Entry{
ATTR_USERPASSWORD: []string{reencodedPassword},
})
}
}

View File

@ -213,10 +213,11 @@ func (server *Server) handleSearchInternal(state *State, w ldap.ResponseWriter,
continue
}
// Send result
resultVals := []message.AttributeValue{}
for _, v := range val {
e.AddAttribute(message.AttributeDescription(attr),
message.AttributeValue(v))
resultVals = append(resultVals, message.AttributeValue(v))
}
e.AddAttribute(message.AttributeDescription(attr), resultVals...)
}
w.Write(e)
}

70
ssha.go
View File

@ -1,25 +1,79 @@
package main
import (
"errors"
"strings"
"bytes"
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"fmt"
"strings"
log "github.com/sirupsen/logrus"
//"github.com/jsimonetti/pwscheme/ssha"
"github.com/jsimonetti/pwscheme/ssha256"
"github.com/jsimonetti/pwscheme/ssha512"
)
const (
SSHA = "{SSHA}"
SSHA256 = "{SSHA256}"
SSHA512 = "{SSHA512}"
)
// Encode encodes the string to ssha512
func SSHAEncode(rawPassPhrase string) (string, error) {
return ssha512.Generate(rawPassPhrase, 16)
}
// Matches matches the encoded password and the raw password
func SSHAMatches(encodedPassPhrase string, rawPassPhrase string) (bool, error) {
hashType, err := determineHashType(encodedPassPhrase)
if err != nil {
return false, errors.New("invalid password hash stored")
}
var is_ok bool
switch hashType {
case SSHA:
is_ok = LegacySSHAMatches(encodedPassPhrase, []byte(rawPassPhrase))
return is_ok, nil
//return ssha.Validate(rawPassPhrase, encodedPassPhrase)
case SSHA256:
return ssha256.Validate(rawPassPhrase, encodedPassPhrase)
case SSHA512:
return ssha512.Validate(rawPassPhrase, encodedPassPhrase)
}
return false, errors.New("no matching hash type found")
}
func determineHashType(hash string) (string, error) {
if len(hash) >= 7 && strings.ToUpper(string(hash[0:6])) == SSHA {
return SSHA, nil
}
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA256 {
return SSHA256, nil
}
if len(hash) >= 10 && strings.ToUpper(string(hash[0:9])) == SSHA512 {
return SSHA512, nil
}
return "", errors.New("no valid hash found")
}
// --- legacy
// Encode encodes the []byte of raw password
func SSHAEncode(rawPassPhrase []byte) string {
hash := makeSSHAHash(rawPassPhrase, makeSalt())
func LegacySSHAEncode(rawPassPhrase []byte) string {
hash := legacyMakeSSHAHash(rawPassPhrase, legacyMakeSalt())
b64 := base64.StdEncoding.EncodeToString(hash)
return fmt.Sprintf("{ssha}%s", b64)
}
// Matches matches the encoded password and the raw password
func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
func LegacySSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
if !strings.EqualFold(encodedPassPhrase[:6], "{ssha}") {
return false
}
@ -30,7 +84,7 @@ func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
}
salt := bhash[20:]
newssha := makeSSHAHash(rawPassPhrase, salt)
newssha := legacyMakeSSHAHash(rawPassPhrase, salt)
if bytes.Compare(newssha, bhash) != 0 {
return false
@ -39,7 +93,7 @@ func SSHAMatches(encodedPassPhrase string, rawPassPhrase []byte) bool {
}
// makeSalt make a 32 byte array containing random bytes.
func makeSalt() []byte {
func legacyMakeSalt() []byte {
sbytes := make([]byte, 32)
_, err := rand.Read(sbytes)
if err != nil {
@ -49,7 +103,7 @@ func makeSalt() []byte {
}
// makeSSHAHash make hasing using SHA-1 with salt. This is not the final output though. You need to append {SSHA} string with base64 of this hash.
func makeSSHAHash(passphrase, salt []byte) []byte {
func legacyMakeSSHAHash(passphrase, salt []byte) []byte {
sha := sha1.New()
sha.Write(passphrase)
sha.Write(salt)

View File

@ -7,8 +7,9 @@ import (
ldap "bottin/ldapserver"
consul "github.com/hashicorp/consul/api"
message "bottin/goldap"
consul "github.com/hashicorp/consul/api"
)
// Generic item modification function --------
@ -38,7 +39,7 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
// Retreieve previously existing attributes, which we will use to delete
// entries with the wrong case
previous_pairs, _, err := server.kv.List(prefix + "/attribute=", &server.readOpts)
previous_pairs, _, err := server.kv.List(prefix+"/attribute=", &server.readOpts)
if err != nil {
return err
}
@ -65,6 +66,24 @@ func (server *Server) putAttributes(dn string, attrs Entry) error {
}
}
// if the password is not yet hashed we hash it
if k == ATTR_USERPASSWORD {
tmpValues := []string{}
for _, pw := range values {
_, err := determineHashType(pw)
if err != nil {
encodedPassword, err := SSHAEncode(pw)
if err != nil {
return err
}
tmpValues = append(tmpValues, encodedPassword)
} else {
tmpValues = append(tmpValues, pw)
}
}
values = tmpValues
}
// If we have zero values, delete associated k/v pair
// Otherwise, write new values
if len(values) == 0 {