{"id":598,"date":"2012-05-15T22:23:04","date_gmt":"2012-05-15T22:23:04","guid":{"rendered":"http:\/\/psyphi.net\/blog\/?p=598"},"modified":"2012-05-15T22:23:32","modified_gmt":"2012-05-15T22:23:32","slug":"svn-server-integration-with-https-active-directory-pam-winbind","status":"publish","type":"post","link":"https:\/\/psyphi.net\/blog\/2012\/05\/svn-server-integration-with-https-active-directory-pam-winbind\/","title":{"rendered":"SVN Server Integration with HTTPS, Active Directory, PAM &#038; Winbind"},"content":{"rendered":"<p><figure style=\"width: 320px\" class=\"wp-caption alignleft\"><a href=\"http:\/\/www.flickr.com\/photos\/trainor\/2610120169\/\"><img loading=\"lazy\" decoding=\"async\" alt=\"Subversion on a whiteboard\" src=\"http:\/\/farm4.staticflickr.com\/3054\/2610120169_4c3c331f48_z.jpg\" title=\"Subversion\" width=\"640\" height=\"427\" \/><\/a><figcaption class=\"wp-caption-text\">Image CC by johntrainor<\/figcaption><\/figure> In this post I&#8217;d like to explain how it&#8217;s possible to integrate SVN (Subversion) source control using WebDAV and HTTPS using Apache and Active Directory to provide authentication and access control.<\/p>\n<p>It&#8217;s generally accepted that SVN over WebDAV\/HTTPS \u00c2\u00a0provides finer granulation security controls than SVN+SSH. The problem is that SVN+SSH is really easy to set up, requiring knowledge of svnadmin and the filesystem and very little else but WebDAV+HTTPS requires knowledge of Apache and its modules relating to WebDAV, authentication and authorisation which is quite a lot more to ask. Add to that authenticating to AD and you have yourself a lovely string of delicate single point of failure components. Ho-hum, not a huge amount you can do about that but at least the Apache components are pretty robust.<\/p>\n<p>For this article I&#8217;m using CentOS but everything should be transferrable to any distribution with a little tweakage.<\/p>\n<h2>Repository Creation<\/h2>\n<p>Firstly then, pick a disk or volume with plenty of space, we&#8217;re using make your repository &#8211; same as you would for svn+ssh:<\/p>\n<pre><code>svnadmin create \/var\/svn\/repos<\/code><\/pre>\n<h2>Apache Modules<\/h2>\n<p>Install the prerequisite Apache modules:<\/p>\n<pre><code>yum install mod_dav_svn<\/code><\/pre>\n<p>This should also install mod_authz_svn which we&#8217;ll also be making use of. Both should end up in Apache&#8217;s module directory, in this case <code>\/etc\/httpd\/modules\/<\/code><\/p>\n<p>Download and install mod_authnz_external from its <a title=\"mod_authnz_external\" href=\"http:\/\/code.google.com\/p\/mod-auth-external\/\" target=\"_blank\">Google Code<\/a> page. This allows Apache basic authentication to hook into an external authentication mechanism. mod_authnz_external.so should end up in Apache&#8217;s module directory but in my case it ended up in its default location of <code>\/usr\/lib\/httpd\/modules\/<\/code>.<\/p>\n<p>Download and install the companion pwauth utility from its <a title=\"pwauth\" href=\"http:\/\/code.google.com\/p\/pwauth\/\" target=\"_blank\">Google Code<\/a> page. In my case it installs to \/usr\/local\/sbin\/pwauth and needs suexec permissions (granted using <code>chmod +s<\/code>).<\/p>\n<h2>Apache Configuration (HTTP)<\/h2>\n<pre><code>ServerName svn.example.com\r\nServerAdmin me@example.com\r\n\r\nListen\t\t*:80\r\nNameVirtualHost *:80\r\n\r\nUser\t\tnobody\r\nGroup\t\tnobody\r\n\r\nLoadModule setenvif_module\tmodules\/mod_setenvif.so\r\nLoadModule mime_module\t\tmodules\/mod_mime.so\r\nLoadModule log_config_module\tmodules\/mod_log_config.so\r\nLoadModule dav_module\t\tmodules\/mod_dav.so\r\nLoadModule dav_svn_module\tmodules\/mod_dav_svn.so\r\nLoadModule auth_basic_module    modules\/mod_auth_basic.so\r\nLoadModule authz_svn_module\tmodules\/mod_authz_svn.so\r\nLoadModule authnz_external_module modules\/mod_authnz_external.so\r\n\r\nLogFormat\t\"%v %A:%p %h %l %u %{%Y-%m-%d %H:%M:%S}t \"%r\" %&gt;s %b \"%{Referer}i\" \"%{User-Agent}i\"\" clean\r\nCustomLog\t\/var\/log\/httpd\/access_log\tclean\r\n\r\n&lt;virtualhost *:80&gt;\r\n\tServerName\tsvn.example.com\r\n\r\n\tAddExternalAuth         pwauth  \/usr\/local\/sbin\/pwauth\r\n\tSetExternalAuthMethod   pwauth  pipe\r\n\r\n\t&lt;location \/ &gt;\r\n\t\tDAV\t\t\tsvn\r\n\t\tSVNPath\t\t\t\/var\/svn\/repos\r\n\t\tAuthType\t\tBasic\r\n\t\tAuthName\t\t\"SVN Repository\"\r\n\t\tAuthzSVNAccessFile\t\/etc\/httpd\/conf\/authz_svn.acl\r\n\t\tAuthBasicProvider\texternal\r\n\t\tAuthExternal\t\tpwauth\r\n\t\tSatisfy\t\t\tAny\r\n\r\n\t\t&lt;limitexcept GET PROPFIND OPTIONS REPORT&gt;\r\n\t\t\tRequire valid-user\r\n\t\t&lt;\/limitexcept&gt;\r\n\t&lt;\/location&gt;\r\n&lt;\/virtualhost&gt;<\/code><\/pre>\n<h2>Network Time (NTP)<\/h2>\n<p>In order to join a Windows domain, accurate and synchronised time is crucial, so you&#8217;ll need to be running NTPd.<\/p>\n<pre><code>yum install ntp\r\nchkconfig ntpd on\r\nntpdate ntp.ubuntu.com\r\nservice ntpd start<\/code><\/pre>\n<h2>Samba Configuration<\/h2>\n<p>Here&#8217;s where AD comes in and in my experience this is by far the most unreliable service. Install and configure samba:<\/p>\n<pre><code>yum install samba\r\nchkconfig winbind on<\/code><\/pre>\n<p>Edit your <code>\/etc\/samba\/smb.conf<\/code> to pull information from AD.<\/p>\n<pre><code>[global]\r\n\tworkgroup = EXAMPLE\r\n\trealm = EXAMPLE.COM\r\n\tsecurity = ADS\r\n\tallow trusted domains = No\r\n\tuse kerberos keytab = Yes\r\n\tlog level = 3\r\n\tlog file = \/var\/log\/samba\/%m\r\n\tmax log size = 50\r\n\tprintcap name = cups\r\n\tidmap backend = idmap_rid:EXAMPLE=600-20000\r\n\tidmap uid = 600-20000\r\n\tidmap gid = 600-20000\r\n\ttemplate shell = \/bin\/bash\r\n\twinbind enum users = Yes\r\n\twinbind enum groups = Yes\r\n\twinbind use default domain = Yes\r\n\twinbind offline logon = yes<\/code><\/pre>\n<p>Join the machine to the domain &#8211; you&#8217;ll need an account with domain admin credentials to do this:<\/p>\n<pre><code>net ads join -U administrator<\/code><\/pre>\n<p>Check the join is behaving ok:<\/p>\n<pre><code>[root@svn conf]# net ads info\r\nLDAP server: 192.168.100.10\r\nLDAP server name: ad00.example.com\r\nRealm: EXAMPLE.COM\r\nBind Path: dc=EXAMPLE,dc=COM\r\nLDAP port: 389\r\nServer time: Tue, 15 May 2012 22:44:34 BST\r\nKDC server: 192.168.100.10\r\nServer time offset: 130<\/code><\/pre>\n<p>(Re)start winbind to pick up the new configuration:<\/p>\n<pre><code>service winbind restart<\/code><\/pre>\n<h2>PAM &#038; nsswitch.conf<\/h2>\n<p>PAM needs to know where to pull its information from, so we tell it about the new winbind service in <code>\/etc\/pam.d\/system-auth<\/code>.<\/p>\n<pre><code>#%PAM-1.0\r\n# This file is auto-generated.\r\n# User changes will be destroyed the next time authconfig is run.\r\nauth        required      pam_env.so\r\nauth        sufficient    pam_unix.so nullok try_first_pass\r\nauth        requisite     pam_succeed_if.so uid &gt;= 500 quiet\r\nauth        sufficient    pam_winbind.so try_first_pass\r\nauth        required      pam_deny.so\r\n\r\naccount     required      pam_unix.so broken_shadow\r\naccount     sufficient    pam_localuser.so\r\naccount     sufficient    pam_succeed_if.so uid &lt; 500 quiet\r\naccount     [default=bad success=ok user_unknown=ignore] pam_winbind.so\r\naccount     required      pam_permit.so\r\n\r\npassword    requisite     pam_cracklib.so try_first_pass retry=3\r\npassword    sufficient    pam_unix.so md5 shadow nullok try_first_pass use_authtok\r\npassword    sufficient    pam_winbind.so use_authtok\r\npassword    required      pam_deny.so\r\n\r\nsession     optional      pam_keyinit.so revoke\r\nsession     required      pam_limits.so\r\nsession     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid\r\nsession     required      \/lib\/security\/pam_mkhomedir.so \r\nsession     required      pam_unix.so\r\nsession     optional      pam_winbind.so<\/code><\/code><\/pre>\n<p>YMMV with PAM. It can take quite a lot of fiddling around to make it work perfectly. This obviously has an extremely close correlation to how flaky users find the authentication service. If you&#8217;re running on 64-bit you may find you need to install 64-bit versions of pam modules, e.g. mkhomedir which aren&#8217;t installed by default.<\/p>\n<p>We also modify <code>nsswitch.conf<\/code> to tell other, non-pam aspects of the system where to pull information from:<\/p>\n<pre><code>passwd:     files winbind\r\nshadow:     files winbind\r\ngroup:      files winbind<\/code><\/pre>\n<p>To check the authentication information is coming back correctly you can use <code>wbinfo<\/code> but I like seeing data by using <code>getent group<\/code> or <code>getent passwd<\/code>. The output of these two commands will contain domain accounts if things are working correctly and only local system accounts otherwise.<\/p>\n<h2>External Authentication<\/h2>\n<p>We&#8217;re actually going to use system accounts for authentication. To stop people continuing to use svn+ssh (and thus bypassing the authorisation controls) we edit <code>\/etc\/ssh\/sshd_config<\/code> and use <code>AllowUsers<\/code> or <code>AllowGroups<\/code> and specify all permitted users. Using AllowGroups will also provide AD group control of permitted logins but as the list is small it&#8217;s probably overkill. My sshd_config list looks a lot like this:<\/p>\n<pre><code>AllowUsers\troot rmp contractor itadmin<\/code><\/pre>\n<p>To test external authentication run <code>\/usr\/local\/sbin\/pwauth<\/code> as below. &#8220;yay&#8221; should be displayed if things are working ok. Note the password here is displayed in clear-text:<\/p>\n<pre><code>[root@svn conf]# pwauth &amp;&amp; echo 'yay' || echo 'nay'\r\nrmp\r\nmypassword<\/code><\/pre>\n<h2>Access Controls<\/h2>\n<p><code>\/etc\/httpd\/authz_svn.conf<\/code> is the only part which should require any modifications over time &#8211; the access controls specify who is allowed to read and\/or write to each svn project, in fact as everything&#8217;s a URL now you can arbitrarily restrict subfolders of projects too but that&#8217;s a little OTT. It can be arbitrarily extended and can take local and active directory usernames. I&#8217;m sure mod_authz_svn has full documentation about what you can and can&#8217;t put in here.<\/p>\n<pre><code>#\r\n# Allow anonymous read access to everything by default.\r\n#\r\n[\/]\r\n* = r\r\nrmp = rw\r\n\r\n[\/myproject]\r\nrmp = rw\r\nbob = rw\r\n\r\n...<\/code><\/pre>\n<h2>SSL<\/h2>\n<p>So far that&#8217;s all the basic components. The last piece in the puzzle is enabling SSL for Apache. I use the following <code>\/etc\/httpd\/httpd.conf<\/code>:<\/p>\n<pre><code>ServerName svn.example.com\r\nServerAdmin me@example.com\r\n\r\nListen\t\t*:80\r\nNameVirtualHost *:80\r\n\r\nUser\t\tnobody\r\nGroup\t\tnobody\r\n\r\nLoadModule setenvif_module\tmodules\/mod_setenvif.so\r\nLoadModule mime_module\t\tmodules\/mod_mime.so\r\nLoadModule log_config_module\tmodules\/mod_log_config.so\r\nLoadModule proxy_module\t\tmodules\/mod_proxy.so\r\nLoadModule proxy_http_module\tmodules\/mod_proxy_http.so\r\nLoadModule rewrite_module\tmodules\/mod_rewrite.so\r\nLoadModule dav_module\t\tmodules\/mod_dav.so\r\nLoadModule dav_svn_module\tmodules\/mod_dav_svn.so\r\nLoadModule auth_basic_module    modules\/mod_auth_basic.so\r\nLoadModule authz_svn_module\tmodules\/mod_authz_svn.so\r\nLoadModule ssl_module\t\tmodules\/mod_ssl.so\r\nLoadModule authnz_external_module modules\/mod_authnz_external.so\r\n\r\nInclude conf.d\/ssl.conf\r\n\r\nLogFormat\t\"%v %A:%p %h %l %u %{%Y-%m-%d %H:%M:%S}t \"%r\" %&gt;s %b \"%{Referer}i\" \"%{User-Agent}i\"\" clean\r\nCustomLog\t\/var\/log\/httpd\/access_log\tclean\r\n\r\n&lt;virtualhost *:80&gt;\r\n\tServerName\t\tsvn.example.com\r\n\r\n\tRewrite\t\t\/\thttps:\/\/svn.example.com\/\t[R=permanent,L]\r\n&lt;\/virtualhost&gt;\r\n\r\n&lt;virtualhost *:443&gt;\r\n\tServerName\tsvn.example.com\r\n\r\n\tAddExternalAuth         pwauth  \/usr\/local\/sbin\/pwauth\r\n\tSetExternalAuthMethod   pwauth  pipe\r\n\r\n\tSSLEngine on\r\n\tSSLProtocol all -SSLv2\r\n\r\n\tSSLCipherSuite\t\tALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW\r\n\tSSLCertificateFile\t\/etc\/httpd\/conf\/svn.crt\r\n\tSSLCertificateKeyFile\t\/etc\/httpd\/conf\/svn.key\r\n\r\n\t&lt;location \/&gt;\r\n\t\tDAV\t\t\tsvn\r\n\t\tSVNPath\t\t\t\/var\/svn\/repos\r\n\t\tAuthType\t\tBasic\r\n\t\tAuthName\t\t\"SVN Repository\"\r\n\t\tAuthzSVNAccessFile\t\/etc\/httpd\/conf\/authz_svn.acl\r\n\t\tAuthBasicProvider\texternal\r\n\t\tAuthExternal\t\tpwauth\r\n\t\tSatisfy\t\t\tAny\r\n\r\n\t\t&lt;limitexcept GET PROPFIND OPTIONS REPORT&gt;\r\n\t\t\tRequire valid-user\r\n\t\t&lt;\/limitexcept&gt;\r\n\t\r\n&lt;\/virtualhost&gt;<\/code><\/pre>\n<p><code>\/etc\/httpd\/conf.d\/ssl.conf<\/code> is pretty much the unmodified distribution ssl.conf and looks like this:<\/p>\n<pre><code>LoadModule ssl_module modules\/mod_ssl.so\r\n\r\nListen 443\r\n\r\nAddType application\/x-x509-ca-cert .crt\r\nAddType application\/x-pkcs7-crl    .crl\r\n\r\nSSLPassPhraseDialog  builtin\r\n\r\nSSLSessionCache         shmcb:\/var\/cache\/mod_ssl\/scache(512000)\r\nSSLSessionCacheTimeout  300\r\n\r\nSSLMutex default\r\n\r\nSSLRandomSeed startup file:\/dev\/urandom  256\r\nSSLRandomSeed connect builtin\r\n\r\nSSLCryptoDevice builtin\r\n\r\nSetEnvIf User-Agent \".*MSIE.*\" \\\r\n         nokeepalive ssl-unclean-shutdown \\\r\n         downgrade-1.0 force-response-1.0<\/code><\/pre>\n<p>You&#8217;ll need to build yourself a certificate, self-signed if necessary, but that&#8217;s a whole other post. I recommend searching the web for &#8220;openssl self signed certificate&#8221; and you should find what you need. The above httpd.conf references the key and certificate under <code>\/etc\/httpd\/conf\/svn.key<\/code> and <code>\/etc\/httpd\/conf\/svn.crt<\/code> respectively.<\/p>\n<p>The mod_authnz_external+pwauth combination can be avoided if you can persuade\u00c2\u00a0mod_authz_ldap to play nicely. There are a few different ldap modules around on the intertubes and after a lot of trial and even more error I couldn&#8217;t make any of them work reliably if at all.<\/p>\n<p>And if all this leaves you feeling pretty nauseous it&#8217;s quite natural. To remedy this, go use <code>git<\/code> instead.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I&#8217;d like to explain how it&#8217;s possible to integrate SVN (Subversion) source control using WebDAV and HTTPS using Apache and Active Directory to provide authentication and access control. It&#8217;s generally accepted that SVN over WebDAV\/HTTPS \u00c2\u00a0provides finer granulation security controls than SVN+SSH. The problem is that SVN+SSH is really easy to set &hellip; <a href=\"https:\/\/psyphi.net\/blog\/2012\/05\/svn-server-integration-with-https-active-directory-pam-winbind\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;SVN Server Integration with HTTPS, Active Directory, PAM &#038; Winbind&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[11,366,17],"tags":[798,799,795,337,6,336,794,387,800,796,317,797,504,503,334,542,792,583,793,341],"class_list":["post-598","post","type-post","status-publish","format-standard","hentry","category-programming","category-security","category-sysadmin","tag-access-control","tag-acls","tag-active-directory","tag-ad","tag-apache","tag-authentication","tag-authorisation","tag-authorization","tag-centos","tag-https","tag-integration","tag-ntp","tag-pam","tag-pwauth","tag-samba","tag-ssl","tag-subversion","tag-svn","tag-webdav","tag-winbind"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/posts\/598","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/comments?post=598"}],"version-history":[{"count":18,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/posts\/598\/revisions"}],"predecessor-version":[{"id":616,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/posts\/598\/revisions\/616"}],"wp:attachment":[{"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/media?parent=598"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/categories?post=598"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/psyphi.net\/blog\/wp-json\/wp\/v2\/tags?post=598"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}