main.append(addHeader("OpenSSH Server Configuration"))
main.append(addParagraph("The primary config file for openssh-server is"))
main.append(addSyntax("/etc/ssh/sshd_config"))
main.append(addParagraph("You will probably also see a file called ssh_config and this is the configuration file for the ssh client. The image below shows part of the sshd_config file."))
main.append(addImageWithCaption("./images/config.jpg","Part of the sshd_config file."))
main.append(addParagraph("You will normally see an include directive, in this case it is"))
main.append(addParagraph("This will read in any additional config files from the specified location which is normally /etc/ssh/sshd_config.d/"))
main.append(addParagraph("On my Raspberry Pi Dev machine, this folder is empty. If there are multiple files here, they will be read in in the order in which they are sorted in the directory, in other words in name order. For tha reason, it is common for the name to start with a number so you might see files like 01-VPN, 02-local_users and so on."))
main.append(addParagraph("The main advantage of these additional files is that they will allow you to make changes to the configuration without having to edit the primary config file. Not all Linux distros will use the same naming conventions for these files so you may need to read the documentation for your system to find out how the config files are setup or you can Google the ssh setup for your particular setup. The Raspberry Pi seems to follow these conventions pretty closely."))
main.append(addParagraph("The config files are read from top to bottom, as you might expect. Something you might not expect is that the settings are set on the basis of the first-encountered setting. In other words, if you have a particular setting that is set twice in the same file (or any included files), the first value set is the one that applies. For example, you might have a setting in the mai config file that you want to override - remember that one of the advantages of having additional config files is that you can change the settings without changing the main file - so you would probably put the new value in one of the additional files. However, you would want to ensure that it is read before the setting is encountered in the main file and for that reason, it is normal to have the includes at or near the top of the file."))
main.append(addParagraph("Another advantage to this approach is that if you want to revert to the original setting, you just need to delete the additional config or remove/disable that setting."))
main.append(addParagraph("On my Raspberry Pi, there are no additional config files by default, but if you do have some, it is important to know what settings are in those files because you might have a setting that doesn't have the value you expect if that value is in the main config file but is set in one of the additional files. If you have a setting that isn't being given a value in the config files, it will take a default value."))
main.append(addParagraph("If you need more info on the sshd_config, it has a man page you can access with the command"))
main.append(addSyntax("man sshd_config"))
main.append(addParagraph("You can also find an online version of the man page on <a href='https://linux.die.net/man/5/sshd_config'>linux.die.net</a> and links to several relevant man pages at <a href='https://www.openssh.com/manual.html'>openssh.com</a>, including those for <a href='https://man.openbsd.org/ssh'>ssh</a>, <a href='https://man.openbsd.org/sshd'>sshd</a>, <a href='https://man.openbsd.org/ssh_config'>ssh_config</a> and, of course, <a href='https://man.openbsd.org/sshd_config'>sshd_config</a> as well as some links to additional information."))
main.append(addParagraph("You might also find some additional useful info on the <a href='https://www.openssh.com/'>openssh.com</a>."))
main.append(addParagraph("As you might expect, some of the key settings in the sshd_config file are near the top and you can see that in the portion of the file in the image above which shows the top of that file. Right after the include directive we have the port number which displays the port number ssh listens on - in other words, this is the port number you would use when you ssh to the machine. The default is 22 so if the value is set to that, you would normally see it commented out. Normally, you would enable this line when setting the port number to something else. I actually don't use port number 22 which is why it is commented out here but I changed it back for the image - security reasons! The reason you would use a non-standard number because it provides better security although it is not hugely effective, especially if you use one of the common alternatives such as 2222, 22022 and so on."))
main.append(addParagraph("The ListenAddress directive is use to specify which interface ssh is listening on so and the default address which is 0.0.0.0 means that it will listen on any address. This is only really relevant if you have more than one interface and you want to listen on one specifically so in the majority of cases, this won't be terribly relevant. If you do need to listen on a specific interface, you can insert the ip address and you would use the ListenAddress directive for an IPv4 interface and the ListenAddress :: directive for an IPv6 interface."))
main.append(addParagraph("As with the port number, in most cases the directives are commented out because the values they show are the defaults so if you want to change one, you could either uncomment it and change a value or leave it commented out and add the uncommented value as the setting that you want. This second option can be really useful because it does allow you to easily revert to the default value even where that is not easy to remember. It doesn't matter which order you put those in but personally, I would always have the default value first so any other settings would come after and can easily be recognised as customised values but either way, if you want the defaults to be easy to identify, you will need to apply the customisations in a consistent way."))
main.append(addParagraph("Another approach would be to copy the config file in its original state to something like sshd_config.def and never make any changes to that, creating additional backups if needed. If you are making any changes to a config file, it's always a good idea to back it up first, even if you are confident that you know the effect the changes will have."))
main.append(addSyntax("In this context, you should always remember that it is much better to have a backup that you don't need than to not have a backup and need it."))
main.append(addParagraph("The HostKey directives are very important, but they relate to the servers cryptography settings so we are not going to be covering them here."))
main.append(addParagraph("A little bit futher down, we have the directives that determine how people log in including LogInGraceTime (the amount of time you have to respond an authenticaton challenge) and MaxAuthTries. One that is potentially quite important from a security perspective is the PermitRootLogin directive"))
main.append(addParagraph("The default shown above allows a login by root but it doesn't allow a login with a password. It is considered more secure to change this to no."))
main.append(addParagraph("Next is the PubKeyAuthentication and the default value is yes which means the server will allow you to login with a key and there is a similar directive further down"))
main.append(addParagraph("which, obviously, allows users to login with password. Again, this is set to yes but you may set it to no if you are allowing connections via ssh which we will do later. There are lots of other useful directives and reading through these is a worthwhile exercise even if you are not making any changes because it can give you a better idea of how ssh works by default and also how it can work by changing these settings."))
main.append(addParagraph("If you do need to make any changes to the sshd_config, or in the configuration fragments (the additional files, if any, in /etc/sshd/config.d/) you will have to restart the sshd with the command"))
main.append(addParagraph("Of course, you can also use systemctl to stop or start the service or to check its status and you can use similar commands with ssh and just as a point of interest, you can also restart it with"))
main.append(addSyntax("/etc/init.d/ssh restart"))
main.append(addParagraph("Just a few quick notes on the man page for sshd_config, assuming you are looking at it on the server rather than one of the websites I provide links for. The start of the file is shown in the image below."))
main.append(addImageWithCaption("./images/man_config_sshd.jpg","The start of the man page for sshd_config."))
main.append(addParagraph("You can easily navigate through the file using the arrow keys and you can also use f or b to move forwards and you can also use page down/page up keys on your keyboard and you can also search using the / key or the ? key."))
main.append(addParagraph("Here is a snippet of the man page showing the info for PermitRootLogin."))
main.append(addImageWithCaption("./images/PermitRootLogin.jpg","Excerpt from the man page showing a description of the PermitRootLogin directive."))
main.append(addParagraph("The man page shows the usual default value and also gives you at least some of the options."))
main.append(addHeader("Managing Users and Access"))
main.append(addParagraph("Consider this. Sitting at my desk, I am working on a Windows PC which is on the floor but I also have a couple of Raspberry PIs sitting on the desk and one of the ways I access these is by SSH. I could hook up one of these PIs to my monitor, add a keyboard and mouse and use it directly so I would start it up, login with one of the user accounts I have on the machine and I would then be presented with the desktop."))
main.append(addParagraph("If I don't want to go through the hassle of switching cables around and so on, I can just use my Windows machine to access them and there are a couple of ways I do that. If I want to use the PI's desktop, I would RDP to it but if I just want to open a shell on it to run commands, I can just use ssh. In both cases, I am logging into the machine with an account that I have on the Raspberry Pi asif I was logging in directly."))
main.append(addParagraph("These are both methods of initiating a remote login, but the important thing is that you are logging in as a user on the machine so you have to have an account that it will recognise in order to do that. Well, you actually don't, there are other methods you can use to grant access to a machine such as a directory service which you might want to use if you want to make a machine publicly accessible to users without an account on the machine, you could set up a directory service, but that isn't covered in this course."))
main.append(addParagraph("For the purpose of giving users access to a machine via ssh, we need to set up a username and password for those users although you may also find that accounts are setup for different purposes rather than for different users. For example, my two Raspberry PIs are a Web Server and a Development Server and both have an account with my name but I also have accounts which I use for Ansible, NextCloud or just for Development purposes on them although I may not necessarily want to use those accounts for ssh access."))
main.append(addParagraph("I can configure these servers to allow access via a password or I can use some other method such as ssg keys or a yubi key, to give a couple of examples. By default, local users on the server (in other words, users who would be able to log into the machine locally) can also access the machine via ssh."))
main.append(addParagraph("If you want to control which of the local users can log in remotely via ssh, you can add a directive to the sshd_config file which allows specific users to access the machines"))
main.append(addSyntax("AllowUser leigh vishal stefan # only allow these users to log in"))
main.append(addParagraph("or you can deny access to specific users with the DenyUser dircetive"))
main.append(addSyntax("DenyUser bob mary paulina # allow all users except these"))
main.append(addParagraph("Similarly, we can apply directives that allow or deny access based on group membership. For example, we can allow access to members of certain groups."))
main.append(addSyntax("AllowGroup admins developers # only allow users in these groups to log in"))
main.append(addParagraph("We can deny access to members of groups with"))
main.append(addSyntax("DenyGroup sales marketing # allow all users except those in these groups"))
main.append(addParagraph("The order in which these directives are applied is important and remember that they are applied on the basis that the first one encountered is the config file is the one that applies. Essentially, you might think of it as this - once a directive is applied, it won't be overwritten by a later directive with a contradictory setting. In this context, that means that if a user is granted access via an AllowUsers directive and later in the file (or in another file that is read later) they are denied access via a DenyUsers directive, that user <strong>will</strong> be allowed access."))
main.append(addParagraph("You can use the Match directive in order to specify different access parameters based on different users or groups so this is a little bit more sophisticated than just allowing or denying access to certain users or groups. For example"))
main.append(addParagraph("This directive allows the root user to login with a password if they are logging in from an IP address of 10.0.1.x where x is any host on that network."))
main.append(addParagraph("Here's another example:"))
main.append(addInsetList(["Match User alice bob Address *"," PasswordAuthentication no"," Pubkey Authentication yes"]))
main.append(addParagraph("This allows the listed users, alice and bob, to login from any IP address using a key only. It doesn't allow them to login with a password."))
main.append(addParagraph("As with any form of access control, if you are implementing this in a large organisation, it can get very copmlicated so it is important to adopt a cohesive strategy and apply it consistently but for most users, it's probably not going to be much of an issue. The main advantage to developing an understanding of these is that it might be helpful in troubleshooting any issues you might have connecting to a machine."))
main.append(addParagraph("Another approach to access control, albeit one that isn't really suitable for use on a large scale, is to simply disable the users account on the server or to completely remove it."))