Radiusserver gegen LDAP authentifizieren lassen

Allgemein

Dieser Eintrag basiert auf dieser Originalanleitung. freeradius ist ein Authentifizierungsserver, der nach außen das Radiusprotokoll bereitstellt. Über dieses Protokoll kann man sich z.B. an einem WLAN anmelden, ohne die Zugangsdaten für OpenLDAP oder jede beliebige andere Authentifizierungsquelle zu kennen und verteilen zu müssen. Wenn ein zentraler Verzeichnisdienst konfiguriert ist, werden z.B. sehr einfach Dinge möglich wie ein kreisweites WLAN. In den Schulen muss dann lediglich ein neues WLAN-Netz konfiguriert werden, welches gegen unseren zentralen Radius authentifiziert und schon kann ich als Lehrer der Schule A im Netz der Schule B z.B. bei Fortbildung das WLAN nutzen.

Radiusschema in OpenLDAP integrieren

Damit die Authentifizierung über Radius mit alle denkbaren Funktionen klappt, sollte man ein neues Schema zu OpenLDAP hinzufügen. Es funktioniert auch ohne, nur kommt man bei späteren Erweiterungswübschen schnell an Grenzen. Hier ist ein Schema bereits vorbereitet (freeradius_schema.ldif), welches freeradius als Textdatei mitbringt. Es lässt sich direkt über die Konsole in cn=config einspielen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
dn: cn=freeradius,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: freeradius
olcAttributeTypes: {0}( 1.3.6.1.4.1.3317.4.3.1.1 NAME 'radiusArapFeatures' DES
 C '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-
 VALUE )
olcAttributeTypes: {1}( 1.3.6.1.4.1.3317.4.3.1.2 NAME 'radiusArapSecurity' DES
 C '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-
 VALUE )
olcAttributeTypes: {2}( 1.3.6.1.4.1.3317.4.3.1.3 NAME 'radiusArapZoneAccess' D
 ESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGL
 E-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.3317.4.3.1.44 NAME 'radiusAuthType' DESC '
 checkItem: Auth-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115
 .121.1.26 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.3317.4.3.1.4 NAME 'radiusCallbackId' DESC 
 'replyItem: Callback-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.
 115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.3317.4.3.1.5 NAME 'radiusCallbackNumber' D
 ESC 'replyItem: Callback-Number' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4
 .1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.4.1.3317.4.3.1.6 NAME 'radiusCalledStationId' 
 DESC 'checkItem: Called-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.
 1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.4.1.3317.4.3.1.7 NAME 'radiusCallingStationId'
  DESC 'checkItem: Calling-Station-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.
 6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.3317.4.3.1.8 NAME 'radiusClass' DESC 'repl
 yItem: Class' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.2
 6 )
olcAttributeTypes: {9}( 1.3.6.1.4.1.3317.4.3.1.45 NAME 'radiusClientIPAddress'
  DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SIN
 GLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.4.1.3317.4.3.1.9 NAME 'radiusFilterId' DESC '
 replyItem: Filter-Id' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115
 .121.1.26 )
olcAttributeTypes: {11}( 1.3.6.1.4.1.3317.4.3.1.10 NAME 'radiusFramedAppleTalk
 Link' DESC 'replyItem: Framed-AppleTalk-Link' EQUALITY caseIgnoreIA5Match SYN
 TAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {12}( 1.3.6.1.4.1.3317.4.3.1.11 NAME 'radiusFramedAppleTalk
 Network' DESC 'replyItem: Framed-AppleTalk-Network' EQUALITY caseIgnoreIA5Mat
 ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {13}( 1.3.6.1.4.1.3317.4.3.1.12 NAME 'radiusFramedAppleTalk
 Zone' DESC 'replyItem: Framed-AppleTalk-Zone' EQUALITY caseIgnoreIA5Match SYN
 TAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {14}( 1.3.6.1.4.1.3317.4.3.1.13 NAME 'radiusFramedCompressi
 on' DESC 'replyItem: Framed-Compression' EQUALITY caseIgnoreIA5Match SYNTAX 1
 .3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {15}( 1.3.6.1.4.1.3317.4.3.1.14 NAME 'radiusFramedIPAddress
 ' DESC 'replyItem: Framed-IP-Address' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.
 6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {16}( 1.3.6.1.4.1.3317.4.3.1.15 NAME 'radiusFramedIPNetmask
 ' DESC 'replyItem: Framed-IP-Netmask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.
 6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {17}( 1.3.6.1.4.1.3317.4.3.1.16 NAME 'radiusFramedIPXNetwor
 k' DESC 'replyItem: Framed-IPX-Network' EQUALITY caseIgnoreIA5Match SYNTAX 1.
 3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {18}( 1.3.6.1.4.1.3317.4.3.1.17 NAME 'radiusFramedMTU' DESC
  'replyItem: Framed-MTU' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.
 115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {19}( 1.3.6.1.4.1.3317.4.3.1.18 NAME 'radiusFramedProtocol'
  DESC 'replyItem: Framed-Protocol' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1
 .4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {20}( 1.3.6.1.4.1.3317.4.3.1.19 NAME 'radiusFramedRoute' DE
 SC 'replyItem: Framed-Route' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1
 466.115.121.1.26 )
olcAttributeTypes: {21}( 1.3.6.1.4.1.3317.4.3.1.20 NAME 'radiusFramedRouting' 
 DESC 'replyItem: Framed-Routing' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4
 .1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {22}( 1.3.6.1.4.1.3317.4.3.1.46 NAME 'radiusGroupName' DESC
  '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {23}( 1.3.6.1.4.1.3317.4.3.1.47 NAME 'radiusHint' DESC '' E
 QUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE 
 )
olcAttributeTypes: {24}( 1.3.6.1.4.1.3317.4.3.1.48 NAME 'radiusHuntgroupName' 
 DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {25}( 1.3.6.1.4.1.3317.4.3.1.21 NAME 'radiusIdleTimeout' DE
 SC 'replyItem: Idle-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1
 466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {26}( 1.3.6.1.4.1.3317.4.3.1.22 NAME 'radiusLoginIPHost' DE
 SC 'replyItem: Login-IP-Host' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.
 1466.115.121.1.26 )
olcAttributeTypes: {27}( 1.3.6.1.4.1.3317.4.3.1.23 NAME 'radiusLoginLATGroup' 
 DESC 'replyItem: Login-LAT-Group' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.
 4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {28}( 1.3.6.1.4.1.3317.4.3.1.24 NAME 'radiusLoginLATNode' D
 ESC 'replyItem: Login-LAT-Node' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.
 1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {29}( 1.3.6.1.4.1.3317.4.3.1.25 NAME 'radiusLoginLATPort' D
 ESC 'replyItem: Login-LAT-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.
 1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {30}( 1.3.6.1.4.1.3317.4.3.1.26 NAME 'radiusLoginLATService
 ' DESC 'replyItem: Login-LAT-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.
 6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {31}( 1.3.6.1.4.1.3317.4.3.1.27 NAME 'radiusLoginService' D
 ESC 'replyItem: Login-Service' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1
 .1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {32}( 1.3.6.1.4.1.3317.4.3.1.28 NAME 'radiusLoginTCPPort' D
 ESC 'replyItem: Login-TCP-Port' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.
 1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {33}( 1.3.6.1.4.1.3317.4.3.1.29 NAME 'radiusPasswordRetry' 
 DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SING
 LE-VALUE )
olcAttributeTypes: {34}( 1.3.6.1.4.1.3317.4.3.1.30 NAME 'radiusPortLimit' DESC
  'replyItem: Port-Limit' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.
 115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {35}( 1.3.6.1.4.1.3317.4.3.1.49 NAME 'radiusProfileDn' DESC
  '' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SING
 LE-VALUE )
olcAttributeTypes: {36}( 1.3.6.1.4.1.3317.4.3.1.31 NAME 'radiusPrompt' DESC ''
  EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALU
 E )
olcAttributeTypes: {37}( 1.3.6.1.4.1.3317.4.3.1.50 NAME 'radiusProxyToRealm' D
 ESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGL
 E-VALUE )
olcAttributeTypes: {38}( 1.3.6.1.4.1.3317.4.3.1.51 NAME 'radiusReplicateToReal
 m' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 S
 INGLE-VALUE )
olcAttributeTypes: {39}( 1.3.6.1.4.1.3317.4.3.1.52 NAME 'radiusRealm' DESC '' 
 EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE
  )
olcAttributeTypes: {40}( 1.3.6.1.4.1.3317.4.3.1.32 NAME 'radiusServiceType' DE
 SC 'replyItem: Service-Type' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1
 466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {41}( 1.3.6.1.4.1.3317.4.3.1.33 NAME 'radiusSessionTimeout'
  DESC 'replyItem: Session-Timeout' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1
 .4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {42}( 1.3.6.1.4.1.3317.4.3.1.34 NAME 'radiusTerminationActi
 on' DESC 'replyItem: Termination-Action' EQUALITY caseIgnoreIA5Match SYNTAX 1
 .3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {43}( 1.3.6.1.4.1.3317.4.3.1.35 NAME 'radiusTunnelAssignmen
 tId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
  )
olcAttributeTypes: {44}( 1.3.6.1.4.1.3317.4.3.1.36 NAME 'radiusTunnelMediumTyp
 e' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {45}( 1.3.6.1.4.1.3317.4.3.1.37 NAME 'radiusTunnelPassword'
  DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SIN
 GLE-VALUE )
olcAttributeTypes: {46}( 1.3.6.1.4.1.3317.4.3.1.38 NAME 'radiusTunnelPreferenc
 e' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {47}( 1.3.6.1.4.1.3317.4.3.1.39 NAME 'radiusTunnelPrivateGr
 oupId' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.
 26 )
olcAttributeTypes: {48}( 1.3.6.1.4.1.3317.4.3.1.40 NAME 'radiusTunnelServerEnd
 point' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.
 26 )
olcAttributeTypes: {49}( 1.3.6.1.4.1.3317.4.3.1.41 NAME 'radiusTunnelType' DES
 C '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {50}( 1.3.6.1.4.1.3317.4.3.1.42 NAME 'radiusVSA' DESC '' EQ
 UALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
olcAttributeTypes: {51}( 1.3.6.1.4.1.3317.4.3.1.43 NAME 'radiusTunnelClientEnd
 point' DESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.
 26 )
olcAttributeTypes: {52}( 1.3.6.1.4.1.3317.4.3.1.53 NAME 'radiusSimultaneousUse
 ' DESC 'checkItem: Simultaneous-Use' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SIN
 GLE-VALUE )
olcAttributeTypes: {53}( 1.3.6.1.4.1.3317.4.3.1.54 NAME 'radiusLoginTime' DESC
  '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-V
 ALUE )
olcAttributeTypes: {54}( 1.3.6.1.4.1.3317.4.3.1.55 NAME 'radiusUserCategory' D
 ESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGL
 E-VALUE )
olcAttributeTypes: {55}( 1.3.6.1.4.1.3317.4.3.1.56 NAME 'radiusStripUserName' 
 DESC '' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {56}( 1.3.6.1.4.1.3317.4.3.1.57 NAME 'dialupAccess' DESC ''
  EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALU
 E )
olcAttributeTypes: {57}( 1.3.6.1.4.1.3317.4.3.1.58 NAME 'radiusExpiration' DES
 C 'checkItem: Expiration' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466
 .115.121.1.26 SINGLE-VALUE )
olcAttributeTypes: {58}( 1.3.6.1.4.1.3317.4.3.1.59 NAME 'radiusCheckItem' DESC
  'checkItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.1
 15.121.1.26 )
olcAttributeTypes: {59}( 1.3.6.1.4.1.3317.4.3.1.60 NAME 'radiusReplyItem' DESC
  'replyItem: $GENERIC$' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.1
 15.121.1.26 )
olcAttributeTypes: {60}( 1.3.6.1.4.1.3317.4.3.1.61 NAME 'radiusNASIpAddress' D
 ESC '' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGL
 E-VALUE )
olcAttributeTypes: {61}( 1.3.6.1.4.1.3317.4.3.1.62 NAME 'radiusReplyMessage' D
 ESC 'replyItem: Reply-Message' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1
 .1466.115.121.1.26 )
olcObjectClasses: {0}( 1.3.6.1.4.1.3317.4.3.2.1 NAME 'radiusprofile' DESC '' S
 UP top AUXILIARY MUST cn MAY ( radiusArapFeatures $ radiusArapSecurity $ radi
 usArapZoneAccess $ radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
  radiusCalledStationId $ radiusCallingStationId $ radiusClass $ radiusClientI
 PAddress $ radiusFilterId $ radiusFramedAppleTalkLink $ radiusFramedAppleTalk
 Network $ radiusFramedAppleTalkZone $ radiusFramedCompression $ radiusFramedI
 PAddress $ radiusFramedIPNetmask $ radiusFramedIPXNetwork $ radiusFramedMTU $
  radiusFramedProtocol $ radiusCheckItem $ radiusReplyItem $ radiusFramedRoute
  $ radiusFramedRouting $ radiusIdleTimeout $ radiusGroupName $ radiusHint $ r
 adiusHuntgroupName $ radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLAT
 Node $ radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $ radi
 usLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $ radiusPortLimit $ ra
 diusPrompt $ radiusProxyToRealm $ radiusRealm $ radiusReplicateToRealm $ radi
 usServiceType $ radiusSessionTimeout $ radiusStripUserName $ radiusTerminatio
 nAction $ radiusTunnelClientEndpoint $ radiusProfileDn $ radiusSimultaneousUs
 e $ radiusTunnelAssignmentId $ radiusTunnelMediumType $ radiusTunnelPassword 
 $ radiusTunnelPreference $ radiusTunnelPrivateGroupId $ radiusTunnelServerEnd
 point $ radiusTunnelType $ radiusUserCategory $ radiusVSA $ radiusExpiration 
 $ dialupAccess $ radiusNASIpAddress $ radiusReplyMessage ) )
olcObjectClasses: {1}( 1.3.6.1.4.1.3317.4.3.2.2 NAME 'radiusObjectProfile' DES
 C 'A Container Objectclass to be used for creating radius profile object' SUP
  top STRUCTURAL MUST cn MAY ( uid $ userPassword $ description ) )

Eingespielt wird es mit:

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f freeradius_schema.ldif

Die Objektdefinitionen geben durch ihren Namen schon einen Hinweis darauf, was noch alles möglich ist. Internetprovider setzen deswegen oft genau auf dieses Protokoll.

Freeradius für die Nutzung von LDAP konfigurieren

Falls noch nicht geschehen, muss freeradius zunächst installiert werden. Bei Debian und seinen Derivaten tut es ein Einzeiler:

1
apt-get install freeradius

Jetzt sind einige Konfigurationsdateien zu bearbeiten:

/etc/freeradius/modules/ldap
1
2
3
4
5
6
7
8
server = "localhost"
identity = "cn=admin,dc=domain,dc=tld"
password = <secret>
basedn = "ou=test,dc=domain,dc=tld"
filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
base_filter = "(objectclass=radiusprofile)"
access_attr = "dialupAccess"
password_attribute = userPassword

dc=domain,dc=tld ist natürlich an den eigenen LDAP anzupassen. Der Binduser unter „identity“ muss Lesezugriff auf Attribute des Radius-Schemas haben. Diese müssen in der Regel extra gewährt / konfiguriert werden, eigentlich sollte man das nicht so gerne über den Hauptadmin des Baumes lösen.

/etc/freeradius/sites-enabled/default & /etc/freeradius/sites-enabled/inner-tunnel

Vor folgende Zeilen in beiden Dateien die Kommentarzeichen entfernen (Abschnitt authorize / authenticate):

1
2
3
4
ldap
Auth-Type LDAP {
   ldap
}

Jetzt kann man beide Dienste neu starten:

1
2
service slapd restart
service freeradius restart

Testen des Einstellungen

Nun kann man überprüfen, ob das Login gegen LDAP funktioniert:

1
radtest "test_ldap_user" "test_ldap_passwort" localhost 18120 "secret"

secret findet man in /etc/freeradius/clients.conf in der Sektion „localhost“. Wenn man von weiteren IPs aus authentifizieren möchte, muss man einfach für jeden Rechner einen neuen Block anlegen. Vorkonfiguriert ist bei Debian und Derivaten für localhost „testing123“. Wenn alles klappt, sollte die Ausgabe etwa so aussehen:

1
2
3
4
5
6
7
Sending Access-Request of id 213 to 127.0.0.1 port 1812
	User-Name = "<test_ldap_user>"
	User-Password = "<test_ldap_passwort>"
	NAS-IP-Address = 127.0.0.1
	NAS-Port = 18120
	Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host 127.0.0.1 port 1812, id=213, length=20

Wichtig ist das Access-Accept packet am Schluss. Klappt es aus irgendwelchen Gründen nicht, gibt es ein Access-Reject packet.

OpenLDAP ab 2.4 installieren und einrichten

Vorweg

Mir ist keine Quelle im Netz bekannt, die die Einrichtung von OpenLDAP wirklich umfassend darstellt – schon gar nicht auf Deutsch. Diese Informationen hier sind aus allen möglichen Ecken zusammengeklaubt – selbst die meisten Bücher zu OpenLDAP empfinde ich als sehr wenig hilfreich.

Grundinstallation

Hinweis: domain.tld muss man natürlich immer an den eigenen openLDAP anpassen.

Zuerst installieren wir die Binaries, in Debian und seinen Abkömmlingen (Ubuntu, Mint etc.) z.B. so:

1
apt-get install slapd ldap-utils

Die Installationsroutine von Debian legt dabei nur eine sehr rudimentäre Konfiguration an, sodass etwas Nacharbeit vonnöten ist. Bei anderen Distributionen kenne ich mich nicht so gut aus. Ein

1
dpkg-reconfigure slapd

ermöglicht uns hier die Eingabe einer korrekten Basis-DN (auf die muss unser SSL-Zertifikat ausgestellt sein), meist sowas wie

  • dc=domain, dc=tld

und zusätzlich definieren wir dabei ein Rootpasswort für den LDAP-User

  • cn=admin,dc=domain,dc=de

.

Handling von OpenLDAP

Ab Debian Squeeze speichert der OpenLDAP-Server seine Konfiguration in einem internen LDAP-Baum und nicht mehr in einem Konfigurationsfile. Das macht die Pflege auf den ersten Blick erheblich aufwändiger, weil man an diesen Baum in der Standardkonfiguration nur umständlich über Konsolentools herankommt. Zudem kann eine fehlerhafte Datenbank dazu führen, dass der OpenLDAP nach einer Konfigurationsänderung gar nicht mehr hochkommt.
Nur der Rootbenutzer des Systems kommt immer auch direkt an die Daten. Man kann sich die bestehenden Inhalt nur als root anzeigen lassen mit:

1
ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"

Neue Einträge können über *.ldif-Files hinzugefügt werden:

1
ldapmodify/ldapadd -Y EXTERNAL -H ldapi:/// -f
Hinweis zu Ubuntu 14.04 LTS

Der Installer setzt den Accountnamen für den Benutzer mit Zugriff auf den cn=config-Baum standardmäßig auf: cn=admin,dc=domain,dc=tld. Dann macht man Befolgen dieser Anleitung ein langes Gesicht. Um das auf den Standard zu ändern, benötigt man nur für Ubuntu 14.04 LTS noch eine kleine Änderung (change_admin.ldif):

1
2
3
4
dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=admin,cn=config

Obligatorisch:

1
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f change_admin.ldif

Sicherheit

OpenLDAP-Verbindungen per TLS absichern

Vorweg: Hier kann ganz viel schiefgehen, obwohl ich dieses Kapitel mit am wichtigsten finde. Wenn OpenLDAP aus irgendwelchen Gründen die Zertifikatfiles nicht frisst, kann man mit der Konfiguration von vorne beginnen oder man hat vorher ein Backup der alten Datenbank gemacht. Deswegen überspringe ich diesen Schritt gerne und binde den OpenLDAP einfach nicht an öffentlich erreichbare Netzwerkdevices. Wenn man das machen muss, führt aus Datenschutzgründen aber kein Weg an dieser Prozedur hier vorbei. LDAP ist genau wie FTP ein Klartextprotokoll, dass ohne Transportverschlüsselung auf dem gesamten Datenweg offenliegt und gerade in WLAN-Umgebungen sehr leicht belauscht werden kann.
Generell gibt es zwei Möglichkeiten, wie man an kostenlose Zertifikate kommen kann. Wosign oder StartSSL. Es gibt diverse Tutorials im Netz zur Nutzung dieser Dienste. Von Letsencrypt würde ich im Kontext von OpenLDAP eher abraten. StartSSL und WoSign sind mittlerweile Geschichte. Wenn es kostenlos sein soll, führt kein Weg an letsencrypt vorbei. Man muss dann dafür sorgen, dass OpenLDAP nach jedem Certupdate neu gestartet wird, also alle drei Monate mindestens.

Man hat am Ende des Zertifizierungsprozesses in der Regel drei Dateien vorliegen:

  1. domain.tld-crt.pem (enthält das Zertifikat)
  2. domain.tld-key.pem (enthält den privaten Schlüssel)
  3. ca_chain.pem (enthält die Zertifizierungschain der CA)

domain.tld ist dabei der Wurzelbaum des openLDAP. Ich habe die Dateien nach /etc/ldap/ssl gelegt. Besser aufhoben sind sie in /etc/ssl/cert – dann muss slapd Leserechte dort bekommen.

Folgende Datei (tls_ldap.ldif) anlegen:

1
2
3
4
5
6
7
8
9
dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/ssl/domain.tld-crt.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/ssl/domain.tld-key.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/ssl/ca_chain.pem

… und über die Konsole einspielen:

1
ldapadd -Y EXTERNAL -H ldapi:/// -f tls_ldap.ldif

oder auch

1
ldapmodify -Y EXTERNAL -H ldapi:/// -f tls_ldap.ldif

Ich bin zusätzlich ein Freund davon, sichere Verbindungen zu erzwingen. Clients, die das nicht wollen oder können, sollen bitte draußenbleiben.

Folgende Datei (force_tls.ldif) anlegen:

1
2
3
4
dn: olcDatabase={1}hdb,cn=config
changetype:  modify
add: olcSecurity
olcSecurity: tls=1

Und wieder einspielen:

1
ldapadd -Y EXTERNAL -H ldapi:/// -f force_tls.ldif

oder auch

1
ldapmodify -Y EXTERNAL -H ldapi:/// -f force_tls.ldif

Jetzt noch in /etc/default/slapd nachschauen, ob die Services stimmen (ldaps über Port 639 gilt als veraltet und sollte nicht mehr verwendet werden). In der Regel steht da so etwas:

1
SLAPD_SERVICES="ldap://0.0.0.0:389/ ldapi:///"

Wenn man mehrere NICs besitzt, kann man natürlich statt 0.0.0.0 auch die IP einer spezifischen Netzwerkkarte angeben oder den OpenLDAP nur an localhost (127.0.0.1) binden.

Ein

1
service slapd restart

bringt Aufklärung, ob das Ganze funktioniert hat. Theoretisch ist das nicht notwendig, da OpenLDAP durch das neue Verfahren ohne Konfigurationsdatei quasi live im Betrieb gepatcht wird. Jetzt sollte der OpenLDAP Verbindungen von außen nur noch verschlüsselt akzeptieren. Von der Konsole aus ( ldapi:/// ) klappt das nach wie vor auch normal. Wir vertrauen uns ja schon selbst.

Bruteforce erschweren

Ein offener LDAP-Server ist anfällig für brute-force Attacken – zumal gerade im Schulbereich viele unsichere Passwörter im Umlauf sein dürften. Durch das ppolicy.schema kann man z.B. nach einigen fehlgeschlagenen Logins den Account für eine Weile automatisch sperren. openLDAP bringt das dafür notwendige Schema in /etc/ldap/schema/ppolicy.ldif schon mit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
dn: cn=ppolicy,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: ppolicy
olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.8.1.1 NAME 'pwdAttribute' EQUALITY
  objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.8.1.2 NAME 'pwdMinAge' EQUALITY in
 tegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.8.1.3 NAME 'pwdMaxAge' EQUALITY in
 tegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.8.1.4 NAME 'pwdInHistory' EQUALITY
  integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.8.1.5 NAME 'pwdCheckQuality' EQUAL
 ITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.8.1.6 NAME 'pwdMinLength' EQUALITY
  integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.8.1.7 NAME 'pwdExpireWarning' EQUA
 LITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {7}( 1.3.6.1.4.1.42.2.27.8.1.8 NAME 'pwdGraceAuthNLimit' EQ
 UALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {8}( 1.3.6.1.4.1.42.2.27.8.1.9 NAME 'pwdLockout' EQUALITY b
 ooleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {9}( 1.3.6.1.4.1.42.2.27.8.1.10 NAME 'pwdLockoutDuration' E
 QUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {10}( 1.3.6.1.4.1.42.2.27.8.1.11 NAME 'pwdMaxFailure' EQUAL
 ITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
olcAttributeTypes: {11}( 1.3.6.1.4.1.42.2.27.8.1.12 NAME 'pwdFailureCountInter
 val' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE
 )
olcAttributeTypes: {12}( 1.3.6.1.4.1.42.2.27.8.1.13 NAME 'pwdMustChange' EQUAL
 ITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {13}( 1.3.6.1.4.1.42.2.27.8.1.14 NAME 'pwdAllowUserChange'
 EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {14}( 1.3.6.1.4.1.42.2.27.8.1.15 NAME 'pwdSafeModify' EQUAL
 ITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
olcAttributeTypes: {15}( 1.3.6.1.4.1.4754.1.99.1 NAME 'pwdCheckModule' DESC 'L
 oadable module that instantiates "check_password() function' EQUALITY caseExa
 ctIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.4754.2.99.1 NAME 'pwdPolicyChecker' SUP top
  AUXILIARY MAY pwdCheckModule )
olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.8.2.1 NAME 'pwdPolicy' SUP top AUXI
 LIARY MUST pwdAttribute MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdCheck
 Quality $ pwdMinLength $ pwdExpireWarning $ pwdGraceAuthNLimit $ pwdLockout $
  pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ pwdMustChange
  $ pwdAllowUserChange $ pwdSafeModify ) )

Eingespielt wird das Schema mit:

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f ppolicy.ldif

Das Schema ppolicy.ldif selbst definiert nur Objekte für das entsprechende Modul, was jetzt noch geladen werden muss, wofür wir eine Datei policy_module.ldif mit folgendem Inhalt anlegen:

1
2
3
4
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la

Und das alte Spiel:

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f policy_module.ldif

Jetzt brauchen wir noch eine Ablage (policy_context.ldif) für die verschiedenen Regelsätze:

1
2
3
4
dn: ou=policies,dc=domain,dc=tld
objectClass: organizationalUnit
objectClass: top
ou: policies
1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f policy_context.ldif

Und als nächstes eine Default-Policy (default_policy.ldif):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
dn: cn=default,ou=policies,dc=domain,dc=tld
objectClass: top
objectClass: device
objectClass: pwdPolicy
cn: default
pwdAttribute: 2.5.4.35
pwdMaxAge: 15552000
pwdInHistory: 3
pwdMinLength: 6
pwdMaxFailure: 3
pwdLockout: TRUE
pwdLockoutDuration: 1800
pwdGraceAuthNLimit: 3
pwdMustChange: TRUE
pwdAllowUserChange: TRUE
pwdSafeModify: TRUE

In diesen Beispiel wird nach drei fehlgeschlagenen Loginversuchen ( pwdMaxFailure: 3 ) das Login für 1800 Sekunden ( pwdLockoutDuration: 1800 ) gesperrt. Das sollte klebrig genug sein.

Muss ich es noch schreiben?

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f default_policy.ldif

Da OpenLDAP ja so fluffig und intuitiv ist, brauchen wir jetzt noch ein Overlay (policy_overlay.ldif), dass dem OpenLDAP sagt, dass statt des normalen Loginhandlings jetzt immer auch die Default-Policy gelten soll:

1
2
3
4
5
6
7
dn: olcOverlay=ppolicy,olcDatabase={1}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
olcPPolicyDefault: cn=default,ou=policies,dc=domain,dc=tld
olcPPolicyHashCleartext: TRUE
olcPPolicyUseLockout: TRUE

Ihr wisst, was jetzt kommt:

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f policy_overlay.ldif

Damit hätten wir grundsätzlich verschlüsselte Verbindungen erzwungen und zusätzlich Bruteforce-Angriffe erschwert. Bleibt noch eines zu tun:

Anonymous-Bind verbieten

Standardmäßig erlaubt openLDAP einen sogenannte anonymous bind, d.h. man erhält lesend Zugriff auch ohne die Eingabe eines Passwortes. Diese lesende Zugriff ist sehr eingeschränkt, z.B. gibt es keinen Zugriff auf bestimmte Objektklassen oder gar Passworthashes. Mir ist die Vorstellung trotzdem nicht geheuer, dass sich u.a. Nutzernamen auf diesem Weg auslesen lassen. Daher verwende ich für den lesenden Zugriff einen separaten User, der sich mit Passwort authentifizieren muss, ansonsten aber nicht mehr Rechte als beim anonymous bind hat. Deswegen unterbinden wir das mit einer neuen Datei noanonymous.ldif:

1
2
3
4
5
6
7
dn: olcDatabase={1}hdb,cn=config
add: olcRequires
olcRequires: authc
 
dn: olcDatabase={-1}frontend,cn=config
add: olcRequires
olcRequires: authc

Und jetzt kommt etwas anderes, weil wir einen bereits bestehenden Datenbankeintrag aktualisieren:

1
ldapmodify -Y EXTERNAL -H ldapi:/// -f noanonymous.ldif

Nach dem Einspielen der letzten Änderung hat man ohne Authentifizierung auch über die Konsole keinen Zugriff mehr auf den Hauptbaum des OpenLDAP (dc=domain, dc=tld) – die war bisher auch sowas wie „anonym“ aus Sicht des LDAP. Man muss dann ausweichen auf eine andere Befehlszeile (cn=config ist davon nicht betroffen):

1
ldapadd -x -D cn=admin,dc=domain,dc=tld -W -f

Danach wird man zur Eingabe des Adminpasswortes aufgefordert und kann so den Hauptbaum beschreiben und verändern.

Optionale Arbeiten

Performancetuning

Diese Datei ( index.ldif )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: cn pres,sub,eq
-
add: olcDbIndex
olcDbIndex: sn pres,sub,eq
-
add: olcDbIndex
olcDbIndex: uid pres,sub,eq
-
add: olcDbIndex
olcDbIndex: displayName pres,sub,eq
-
add: olcDbIndex
olcDbIndex: default sub
-
add: olcDbIndex
olcDbIndex: uidNumber eq
-
add: olcDbIndex
olcDbIndex: gidNumber eq
-
add: olcDbIndex
olcDbIndex: mail,givenName eq,subinitial
-
add: olcDbIndex
olcDbIndex: dc eq

einspielen mit

1
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f index.ldif
Benutzer für die Administration von cn=config einrichten

Wenn man sauch den cn=config-Baum auch über komfortablere Frontends wie phpldapadmin oder LAM verwalten möchte, muss man das Objekt cn=admin, cn=config noch um weitere Einträge ergänzen. Zunächst erzeugen wir uns über die Konsole ein Passwort:

1
slappasswd -h {SSHA}

Wir erhalten einen Hash zurück, den wir in die Zwischenablage kopieren. Jetzt erstellten wir ein ldif-File (manager.ldif):

1
2
3
4
5
6
7
8
9
10
11
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {SSHA}
 
# auskommentieren, wenn wir den Zugriff von root  auf cn=config 
# ohne Passwort sperren wollen. Sollte als Fallback besser erhalten bleiben
 
#dn: olcDatabase={0}config,cn=config
#changetype: modify
#delete: olcAccess
1
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f manager.ldif

Wenn wir jetzt in phpldapadmin (ab Version 1.2.2) oder lam als Base-DN cn=config verwenden und als Login cn=admin, cn=config, können wir cn=config auch grafisch verwalten.

Quelle: https://wiki.debian.org/PhpLdapAdmin

War doch ganz einfach oder?

OpenLDAP ist extrem sperrig, aber eine hervorragende Authentifizierungsmöglichkeit, da im Gegensatz zu Datenbanksystemen die Struktur hochgradig standardisiert sowie mustergültig objektorientiert ist und sich OpenLDAP so recht schnell in beliebige Anwendungen integrieren lässt – fast alle ernstzunehmenden Onlinetools unterstützen die Authentifizierung über LDAP. OpenLDAP diente nicht umsonst nicht als Vorlage für die LDAP-Funktionen von Samba4 – weil es eben so sperrig ist.

Schmerzen – für Administratoren

Wenn wir Administratoren von „Schmerzen“ reden, meinen wir damit oft Setups, die recht komplex sind und sich nur durch extrem viel Durchhaltevermögen realisieren lassen. Ein gutes Beispiel sind Verzeichnisdienste – durch meine Linuxnähe insbesondere openLDAP. Bei diesem Dienst hat man das Gefühl, als ob die Entwickler normales „Fußvolk“ gar nicht wollen.

Dennoch feiere ich hier einen Durchbruch nach dem anderen. Im Prinzip läuft das zunächst für mich darauf hinaus, dass ich Single-Sign-On (ein Passwort für alles) jetzt extrem ausweiten kann, z.B. meine Klassenblogs und -wikis jetzt auch recht sicher mit den Anmeldedaten nutzen kann, die die SuS auch im Schulnetzwerk verwenden. In Owncloud kann ich sogar Gruppen aus dem Schulnetzwerk übernehmen. Oder ein schulübergreifendes WLAN aufspannen. Davon mache ich nahezu nichts. Aber ich könnte jetzt schon auf eine Anforderung reagieren, die mit zieimlicher Sicherheit irgendwann kommen wird.

Wer meine „Schmerzen“ live erleben will, kann sich meine Dokumentation dazu zu Gemüte führen. Als Anwender muss man dafür schon sehr hart sein :o)… Vielleicht findet der eine oder andere technisch interessierte auch auf der Hauptseite etwas.

Der eigene URL-Shortener

Markus erklärt in seinem Blog, warum es Sinn macht, einen eigenen URL-Shortener zu verwenden. Ich kann dem wenig hinzufügen und habe es ihm fast gleichgetan – allein für ein neues Multidomain-SSL-Zertifikat habe ich noch nicht die Muße gefunden. Für den eigenen URL-Shortener nehme man

  1. Jeden 0815-Webspace mit PHP, MySQL und mod_rewrite-Unterstützung – das bietet heute fast jedes Einsteiger-Paket.
  2. Eine möglichst kurze Domain. Es gibt noch zahlreiche Dreibuchstaben-DE-Domains. Eine DE-Domain buche ich z.B. über meinen gemieteten Robot in Echtzeit für 3,90 Euro/Jahr.
  3. Ein fertiges Script, z.B. Yourls.

lighttpd-User wie ich haben es nur unwesentlich schwerer, da die Rewrite-Engine etwas anders funktioniert. Für den entsprechenden vhost trägt man hier ein:

$HTTP["host"] == "domain.tld" {
        server.document-root = "/pfad/zu/yourls"
        url.rewrite-once = (
        "^/([0-9A-Za-z]+)?$" => "/yourls-go.php?id=$1",
        "^/([0-9A-Za-z]+)?\+$" => "/yourls-infos.php?id=$1" )
}

… und schon hat man nach ein wenig Doku den eigenen Kurz-URL-Dienst (leider klappt der Aufruf der Hauptdomain so noch nicht). Meiner hört auf

http://www.m9r.de

und ist genau wie Markus‘ Installation nicht öffentlich zugänglich, um Ärger mit bestimmten Zeitgenossen zu vermeiden. Dass das klappt, lässt sich ganz gut mit meinem momentanen Lieblingsnachdenkartikel über Facebook zeigen: http://m9r.de/3 (mit Dank an Andreas Kalt).

Man handelt sich in der Grundversion wieder einige Datenschutzherausforderungen ein, die sich aber lösen lassen. Nebenbei weiß ich jetzt, wie oft meine Kurz-URLs auf Twitter tatsächlich geklickt wurden und es entsteht quasi nebenbei eine hübsche Linksammlung in der Datenbank. Jetzt noch ein paar Tags und schon braucht es obendrein auch kaum noch Bookmarks.

Asymmetrische Verschlüsselung mit SSH

Es gibt zwar zu diesem Thema viele Tutorials im Netz, jedoch konnte ich mir bisher keines so richtig merken.

Was kann man damit eigentlich anstellen?

Normalerweise logge ich mich in einen Server mit einem Benutzernamen und einem Passwort ein. Der Server überprüft dann, ob mein eingegebener Benutzername zum auf dem Server gespeicherten Passwort (meist ein Hash) passt und gewährt mir im positiven Fall dann Zugriff. Das lässt sich eigentlich ganz gut mit einem Zahlenschloss an einem Fahrrad vergleichen: Nur wer die korrekte Kombination kennt, kann dieses Schloss öffnen. Bei einem Fahrradschloss kann ich als böser Bube jedoch durch Ausprobieren die korrekte Kombination herausfinden. Das ist lediglich eine Frage der Zeit. Je einfacher mein Passwort aufgebaut ist (etwa „3333“, desto weiniger Zeit wird der Angreifer benötigen.

Aufgrund dieser Problematik wurde im Serverbereich die Authentifizierung via Schlüssel (Key) erfunden. Das Verfahren ist eigentlich sehr pfiffig: Der Benutzer generiert zunächst einmal zwei Schlüssel: Einen privaten und einen öffentlichen. Bei der Anmeldung am Server geschieht nun folgendes:

Der Benutzer sendet seinen Benutzernamen, der aber mit mit seinem privaten Schlüssel verschlüsselt (unkenntlich gemacht) ist. Der Server besitzt den öffentlichen Schlüssel und den Benutzernamen des Benutzers. Bei ihm kommt nun eine kryptische Zeichenkette an, die er nur mit dem öffentlichen Schlüssel wieder in den Benutzernamen umwandeln kann. Die öffentliche Schlüssel muss zu dem privaten Schlüssel passen. Zudem taugt der öffentliche Schlüssel nur zur Entschlüsselung, nicht jedoch zur Verschlüsselung.

Um ganz genau zu sein (Danke Markus…):

Speziell bei SSH ist es nun so, dass die asymmetrische Verschlüsselung nach diesem Verfahren nur verwendet wird, um einen Schlüssel für eine performantere synchrone Verschlüsselung auszuhandeln. Nach dem Handshake machen Server und Client also „ihr eigenes Ding“.

Vorteil:

Nur wer im Besitz des privaten Schlüssels ist, kann sich am Server anmelden. Das Ausprobieren von Passworten ist theoretisch nicht denkbar (man müsste die Verschlüsselung imitieren), aber praktisch immens schwierig und zeitaufwendig.

Ein Bild für dieses Verfahren ist z.B. ein Fahrradschloss mit Schlüssel. Der private Schlüssel hängt am Schlüsselbund des Benutzers. Die Schlossmechanik selbst ist der öffentliche Schlüssel und weit aufwendiger zu imitieren als etwa eine Zahlenkombination herauszufinden.

Natürlich kann man mit dem Verfahren nicht nur Benutzernamen, sondern alle möglichen Texte verschlüsseln. – z.B. auch E-Mails. Den öffentlichen Schlüssel kann man gefahrlos weitergeben, sodass sich den jeder Empfänger herunterladen kann.

Hier einmal ein einfaches Schaubild:

Wie funktioniert das nun praktisch?

Weiterlesen