Intranet/Intranet Reference Build Ubuntu

This page documents the OS and initial configuration that is used and tested against within this series of articles. The focus is on a system that will work in the vast majority of corporate environments that make use of Active Directory. All of these steps have been tested on a real system. The following table shows when it was last tested.

Documentation test log
Date Notes
03 Jun 2023 Running Ubuntu Focal (20.04). I don't recall any major issues. I believe the config here is all still current.

The commands shown do not have sudo preceding them. Either put sudo on the front of each command that has a # prompt or run sudo -i first and provide your password to run an interactive shell as root.

Hardware

edit
 
VMware VM - reference hardware

See screenshot. There are no hard and fast rules and here we are using a virtual machine with some suggested minimums.

Initial Installation

edit
  • Ubuntu 20.04 LTS minimal https://help.ubuntu.com/community/Installation/MinimalCD
    • Static IP address
    • Guided partitioning with LVM, suggested start off with at least 30GB disc space
    • Initial Unix username should not match any username in Active Directory. local-sysadmin might make a good choice
    • Only add OpenSSH server role

Internet access via a web proxy

edit

If www access must be via a proxy, then during the installation, when prompted enter a proxy URL similar to these. EXAMPLE is the domain name and %5C is the encoding for "\". The port number after the colon ":" is likely to be either 8080 or 3128. proxyuser and proxypassword should be set accordingly.

  • NTLM authentication
http://EXAMPLE%5Cproxyuser:proxypassword@proxy.example.co.uk:8080
  • Basic authentication
    http://proxyuser:proxypassword@proxy.example.co.uk:8080
    
    This will set up APT to always use the proxy. See /etc/apt/apt.conf where the proxy setting is enabled, after installation.

Basic additions

edit

VM Guest tools (for virtual machine) and ntp

edit
# apt install open-vm-tools ntp

Ensure that ntp is able to see enough time sources. You could use use your AD DCs for example, especially the one with the PDC emulator role. For the reference /etc/ntp.conf, remove anything in the default file under # Specify one or more ... to the next comment block that starts #Access control. Then insert something like the following. These settings are suitable for an intranet with good communication speeds and will cause the clock to sync quite rapidly. "tinker panic 0" means that if the local clock is more than 30 seconds adrift it will still sync to the servers rather than declaring them insane!

# Specify one or more NTP servers.

# Allow a large offset
tinker panic 0

server ntp1.example.co.uk    iburst
server ntp2.example.co.uk    iburst
server ntp3.example.co.uk    iburst

# fall back to local clock
server 127.127.1.0
fudge  127.127.1.0 stratum 14

# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details.  The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
# might also be helpful.
# systemctl restart ntp
# ntpq -p
.... ntp status displayed ....

The reference system also gets these (optional) packages.

# apt install screen joe iotop htop

System proxy settings

edit

If you need proxy settings then set the standard variables as follows in /etc/environment using the same settings as used above when installing the OS.

# /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"

# Proxy variables - NTLM, %5C = "\"
http_proxy=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080
HTTP_PROXY=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080
https_proxy=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080
HTTPS_PROXY=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080
ftp_proxy=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080
FTP_PROXY=http://EXAMPLE%5Cproxy:password@proxy.example.co.uk:8080

no_proxy=localhost,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16

CA SSL certificate

edit

This will be necessary to use LDAPS against a domain controller, for example, without having to disable SSL checks:

  • Export the AD CA certificate as Base 64 encoded. Its name must end in .crt.
    • You may be able to download a copy of the CA certificate by pointing a browser at https://ca.example.co.uk/certsrv if the CA's web service has been installed
    • To find your CA you could try:
C:\> certutil -config - -ping

Another method to get the CA certificate. This will display the CA certificate and put it in a file called ca.crt:

C:\> certutil -ca.cert -config - ca.crt
  • Copy ca.crt to /usr/local/share/ca-certificates The actual name used for the file is unimportant. You could simply copy the output and paste it into a blank new file instead. The certificate file should include the -----BEGIN CERTIFICATE ------ and -----END CERTIFICATE ----- lines.
  • Run the following command. Also shown is a command to dump a list of all the CA certs that the system trusts. The new one should be listed at the bottom.
# update-ca-certificates 
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.

# openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt | openssl pkcs7 -print_certs -noout

Verify that you can connect to an AD Domain Controller's LDAP. Here we are connecting to the Global Catalogue over TLS (port 3269) you can also test against :636. There is a lot more output but at the end will be an indication about whether the CA certificate is trusted or not . Press CTRL-C to close the connection. This is an example of it not working:

$ openssl s_client -connect dc.example.co.uk:3269
.... 
    Verify return code: 21 (unable to verify the first certificate)
---

Working:

$ openssl s_client -connect dc.example.co.uk:3269
....
    Verify return code: 0 (ok)
---

Now is a good time to shutdown the VM and take a snapshot

Application software

edit

AD integration - Samba

edit

Install software. acl will be used later in the build to make the system Kerberos keytab available to services as required.

# apt install winbind krb5-user acl

If prompted for a realm, type in the Active Directory domain name in CAPITALS. For example: EXAMPLE.CO.UK. By default, smbd and nmbd will be started. They are unnecessary for the purpose of running a wiki. Unless you want them running for fileserving, shut them down and then disable them:

# systemctl disable smbd nmbd
# systemctl stop smbd nmbd

Configure Samba by moving the default config file out of the way

# mv /etc/samba/smb.conf /etc/samba/smb.conf.DISTRO

Create a new /etc/samba/smb.conf. In the following reference config, you must set your workgroup and realm (AD). Also set the domain shortname (Netbios name) in the idmap config lines. The rest of the example can be used without change. Note that the min protocol set here will mean that Windows XP machines will be unable to access this system as a file server.

# /etc/samba/smb.conf

[global]

        workgroup                      = EXAMPLE
        realm                          = EXAMPLE.CO.UK
        idmap config EXAMPLE : backend = rid
        idmap config EXAMPLE : range   = 10000 - 19999

        server string         = Samba Server
        security              = ADS
  
        kerberos method       = secrets and keytab
        dedicated keytab file = /etc/krb5.keytab

        winbind refresh tickets = true
        obey pam restrictions   = yes

        local master          = no
        min protocol          = SMB2
        lanman auth           = no                                               
        client NTLMv2 auth    = yes                                              
        client lanman auth    = no                                               
        client plaintext auth = no                                               
        ldap ssl              = start tls                                        
        server signing        = mandatory                                        

        logging      = file                                                      
        log level    = 3 winbind:5                                               
        max log size = 1024
        debug uid    = yes                                                       
        socket options = TCP_NODELAY IPTOS_LOWDELAY                              
        printcap name  = cups                                                    

        idmap config * : backend = tdb                                           
        idmap config * : range   = 1000000-1999999

        template shell             = /bin/bash
        winbind enum users         = yes
        winbind enum groups        = yes
        winbind use default domain = yes
        winbind offline logon      = yes
        winbind cache time         = 60

        guest account = nobody
        map to guest  = never
        guest ok      = no

Check that all is OK. This command should give sensible output.

# net ads info
LDAP server: <ip address of DC>
LDAP server name: <name of DC>
Realm: EXAMPLE.CO.UK
Bind Path: dc=EXAMPLE,dc=CO,dc=UK
LDAP port: 389
Server time: Mon, 06 Nov 2017 11:45:33 GMT
KDC server: <ip address of DC>
Server time offset: 0

Join the domain. "username" should be a user that has AD permissions to create a workstation object. DNS update errors are not fatal

# net join -U username  # MAKE SURE YOU PUT YOUR AD USERNAME HERE #
Enter username's password:
Using short domain name -- EXAMPLE
Joined 'WIKI' to dns domain 'example.co.uk'
No DNS domain configured for wiki. Unable to perform DNS Update.
DNS update failed: NT_STATUS_INVALID_PARAMETER

Restart winbind and verify that the domain can be accessed and that Kerberos is working

# systemctl restart winbind
# wbinfo -u
... list of AD users ...
# net ads keytab list
... Kerberos keytab is displayed with around 15 entries ...

Winbind and NSS

edit

This makes AD users into Unix users.

# apt install libnss-winbind libpam-winbind

Edit /etc/nsswitch.conf and add winbind

passwd:         compat winbind
group:          compat winbind

Verify it is working

# getent passwd
.... list of local Unix users followed by AD users ....

Create /etc/security/pam_winbind.conf

[global]
debug = yes
debug_state = no
try_first_pass = yes
krb5_auth = yes
krb5_ccache_type = file
cached_login = yes
silent = no
mkhomedir = yes

sudo

edit

With this configuration, your initial Unix user can still login at the console of the system if AD is unavailable or networking is broken. sshd uses the "host" service principals which should already be in the keytab and because it runs as root it is able to read the keytab.

  • Create a group in AD for users that will be able to run sudo on this system and add some users to it. I call mine sysadmin. It does not matter where the group is within the AD structure.
  • Create a file called /etc/sudoers.d/local (the name is unimportant)
# local sudo config
# sysadmin is an AD group, wheel is a unix group

%wheel ALL=(ALL) ALL
%sysadmin ALL=(ALL) ALL

Kerberize ssh

edit

Edit /etc/ssh/ssh_config and uncomment and enable GSSAPI authentication. This is for using ssh on the system itself to another one

Host *
   GSSAPIAuthentication yes

Edit /etc/sshd_config and enable GSSAPI authentication. Disable clear text passwords. I also recommend explicitly disabling RootLogin

PermitRootLogin no
PasswordAuthentication no
GSSAPIAuthentication yes

Restart the OpenSSH daemon

# systemctl restart sshd

You should now be able to ssh directly in as an AD user. A reasonably modern version of PuTTY can do this from a Windows workstation, provided GSSAPI is enabled and the tickbox to use the logged in username is ticked. Also bear in mind that Unix systems are case sensitive so you may have to reset the case on your Windows account's various naming attributes.

Database - MariaDB

edit

Install software and secure it. The root password is initially blank so hit enter when prompted for the current root password. Note that user root in database is not the same as the user root in the Linux system, both simply have the same name. Keep a note of the password that you set.

# apt install mariadb-server
# mysql_secure_installation

Check that you can access the database server with the password you set earlier. Type \q and hit enter to exit.

# mysql -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
... copywrite notice etc ...

MariaDB [(none)]> \q
Bye

Webserver - Apache

edit

Install basic software. Apache runs as the www-data user which can't access the Kerberos keytab by default so setfacl is used to allow it to read it. net ads keytab add is used to add a service principal for HTTP which is the default for Apache. The final command should list several entries starting HTTP/ . "AD_username" should be an account that has permissions to set service principals (Domain Admin??).

# apt install apache2 libapache2-mod-auth-kerb libapache2-mod-php
# a2enmod rewrite auth_kerb ssl ldap authnz_ldap
# a2ensite default-ssl
# setfacl -m u:www-data:r /etc/krb5.keytab
# net ads keytab add HTTP -U AD_username # MAKE SURE YOU SET YOUR AD USERNAME HERE #
# net ads keytab list
.... Kerberos keytab is displayed with more entries starting with HTTP/ ....
# systemctl restart apache2

The Apache installer will enable and start the web server. Point a browser at it and you should get the Ubuntu default page. You will get a certificate error in your browser when you test because the server is currently using a self signed certificate. Later on we will obtain a trusted certificate.

vhost with LDAP and Kerberos

edit
  • Remove all website configuration links (you could use the a2dissite command instead)
    # rm /etc/apache2/sites-enabled/*
    
  • Create a user in AD, which in this example is called ldapsearch. It only needs enough rights to connect and read public attributes.
  • Create a new website configuration. Ensure you make the required changes for your environment. You put this file in the sites-available directory and then symlink it in the sites-enabled directory. The symlink command is listed after the config example.

To use this configuration as-is you must have client systems that are correctly setup to use Negotiate [1]. You could set KrbMethodK5Passwd On which will enable the browser to prompt for a username and password. This configuration requires that all users of the wiki should be members of a particular AD group to even be able to connect to the webserver. The string of numbers before the group name enables this group to have nested groups.

You should make changes on the indicated lines. my_local_user is not used yet and can be left as is for now.

# /etc/apache2/sites-available/local.conf
# Require Kerberos authentication and LDAP authorisation and SSL for remote
# connections or if local, allow access and set REMOTE_USER 

ServerName wiki.example.co.uk

<VirtualHost _default_:443>

        ServerAdmin         webmaster@example.co.uk
        DocumentRoot        /var/www/html
        SSLEngine           on
        SSLProtocol         all -SSLv2 -SSLv3 -TLSv1
        SSLCipherSuite      ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-
        SSLHonorCipherOrder on
        SSLCompression      off
        SSLOptions          +StrictRequire

        SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

</VirtualHost>

LogLevel Debug

# Force https if remote
RewriteEngine On

RewriteCond %{HTTPS} off
RewriteCond expr "!(%{REMOTE_ADDR} -ipmatch '127.0.0.1' || %{REMOTE_ADDR} -ipmatch '::1')" 
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

# LDAP cache
LDAPSharedCacheSize 500000
LDAPCacheEntries    1024
LDAPCacheTTL        600
LDAPOpCacheEntries  1024
LDAPOpCacheTTL      600

<Location "/ldap-status">
    SetHandler ldap-status
</Location>

<Location />

    <If "%{REMOTE_ADDR} -ipmatch '127.0.0.1' || %{REMOTE_ADDR} -ipmatch '::1'">
        SetEnv REMOTE_USER my_local_user
    </If>

    <RequireAny>

        <RequireAll>
            Require ip 127.0.0.1 ::1
            Require not ssl
        </RequireAll>

        <RequireAll>
            AuthType Kerberos
            AuthName "Login"

            KrbMethodNegotiate  On
            KrbMethodK5Passwd   Off
            KrbLocalUserMapping On
            KrbAuthRealms       EXAMPLE.CO.UK
            Krb5KeyTab          /etc/krb5.keytab

            # AD Global Catalogue over SSL.
            AuthLDAPURL          "ldaps://dc1.example.co.uk:3269 \
                                          dc2.example.co.uk:3269/?sAMAccountName?sub"
            AuthLDAPBindDN       "ldapsearch@example.co.uk"
            AuthLDAPBindPassword "....... PASSWORD ......."

            Require ssl
            Require valid-user
            Require ldap-filter \
                    memberof:1.2.840.113556.1.4.1941:=CN=A_USER_GROUP,OU=Groups,OU=My_Company,DC=Example,DC=co,DC=uk
        </RequireAll>

    </RequireAny>

</Location>

Enable the Apache site configuration:

# cd /etc/apache2/sites-enabled
# ln -s ../sites-available/local.conf ./

Create a simple testing php script at /var/www/html/index.php

<?php
  echo $_SERVER['REMOTE_USER'];
  phpinfo();
?>

Remove the default page so that index.php is executed instead.

# mv /var/www/html/index.html /var/www/html/index.html.DISTRO

Restart the web server

# systemctl restart apache2

Point a browser at http://wiki.example.co.uk and it should redirect you to https and output your username followed by phpinfo() - lots of handy debugging information. Ensure you are logged in as a member of the AD "A_USER_GROUP" group. When it is working, I suggest you delete or disable the index.php script.

edit

You will need a correctly setup Windows CA for this step and its root certificate installed in the local trust store for all clients that access this system. Chrome(ium), at least, requires a certificate has a Subject Alternative Name for the Common Name to be considered secure. There are several ways to do the job, here is one.

  • Create a directory to hold the certificate related files, set permissions and cd into it.
# cd /etc/apache2
# mkdir ssl
# chmod o= ssl
# cd ssl
  • Generate a CSR

Create a configuration file called /etc/apache2/ssl/csr.conf which will be used to override the system defaults. "CN =" is the Common Name which is the name that should match what is typed into the browser. "subjectAltName =" here, is a pointer to a list that is created under the [alt_names ] heading. You should put your own settings for everything under [ dn ], apart from CN you can put anything suitable.

# /etc/apache2/ssl/csr.conf

[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
 
[ dn ]
C = GB
ST = County
L = Town
O = Example Company Name Ltd
CN = wiki.example.co.uk
 
[ req_ext ]
subjectAltName = @alt_names
 
[ alt_names ]
DNS.1 = wiki.example.co.uk
  • Create a private key wiki.key and CSR wiki.csr.
# openssl req -new -sha256 -nodes -out wiki.csr -newkey rsa:2048 -keyout wiki.key -config csr.conf
  • Obtain a certificate from an AD CA

Transfer wiki.csr to a Windows system that has the certreq command available You will need to run the following command as a user with the correct rights.

C:\> certreq -submit -attrib "Certificatetemplate:WebServer"  wiki.csr wiki.crt

This will create wiki.crt.

  • Install the certificate

Transfer wiki.crt to /etc/apache2/ssl. Edit the Apache configuration /etc/apache2/sites-available/local.conf to use the new SSL certificate

# /etc/apache2/sites-available/local.conf
...        
        SSLCertificateFile      /etc/apache2/ssl/wiki.crt
        SSLCertificateKeyFile   /etc/apache2/ssl/wiki.key
...

Restart Apache and test with your browser. You should not get a certificate related security error.

Firewall - UFW

edit

The Ubuntu minimal installer includes the Uncomplicated Firewall (UFW) which is a package to configure the standard iptables Linux firewall, and the reference build uses it as an additional layer of protection and to comply with likely corporate policy. By default ufw will allow outgoing connections and block incoming connections. These commands will allow access from anywhere to ssh and the web server and switch on the firewall.

# ufw allow "OpenSSH"
# ufw allow "Apache Secure"
# ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
# ufw status verbose
Status: active
...
To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere                  
443/tcp (Apache Secure)    ALLOW IN    Anywhere                  
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)             
443/tcp (Apache Secure (v6)) ALLOW IN    Anywhere (v6)

Upgrading Xenial to Bionic

edit

Some quick notes that were needed for a successful upgrade of a MediaWiki 1.31 running on Ubuntu Xenial(16.04) to MediaWiki 1.33 on Bionic (18.04):

Enable PHP because it was disabled after the upgrade - the wiki home page came up in plain text:

# a2enmod php7.2
# systemctl restart apache2

Reinstall PHP-curl because it was held back to an older version for some reason: (The following packages have been kept back:  php-curl)

# apt install php-curl
# systemctl restart apache2

Put back the Parsoid source because it was removed:

# apt-add-repository "deb https://releases.wikimedia.org/debian jessie-mediawiki main"

The upgrade was done by upgrading Mediawiki itself following the guide here Intranet/Intranet Installation after which the Visual Editor stopped working properly. After upgrading the OS from Xenial to Bionic and fixing PHP, Visual Editor worked again.

References

edit
  1. Intranet/Intranet Client Configuration - Client browser configuration to support Negotiate