{ "info": { "author": "Sebastian Hamann", "author_email": "code@ares-macrotechnology.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Security", "Topic :: System :: Networking :: Time Synchronization" ], "description": "httpsdate.py\n============\n\nThis is a (fairly) simple python script that sets the system clock to a value obtained from a set of HTTPS servers.\nThis provides a simple and secure, but not very accurate method of time synchronisation.\n\nWhy?\n====\n\nSeveral of today's security technologies assume a correct local clock.\nOne example is certificate expiration.\n\nUnfortunately, the choice of secure and reliable clock synchronisation protocols is scarce.\nA discussion of the sorry state of today's options can be found in https://blog.hboeck.de/archives/890-In-Search-of-a-Secure-Time-Source.html by @hannob.\nThe author also came up with a simple, yet effective method of securely synchronising the system clock with a remote server.\n\nThis method and the script [`httpstime`](https://github.com/hannob/httpstime) are what inspired `httpsdate.py`.\nIt was written to overcome some of the limitations imposed by the simplistic design of `httpstime`:\n\n1. `httpsdate.py` automatically runs with minimal privileges.\n2. `httpsdate.py` does not necessarily rely on a single server to provide the correct time.\n\nHow does it work?\n=================\n\n`httpsdate.py` queries one or more HTTPS servers for the timestamp in the `Date` HTTP header.\nThe system clock is then set to the median of the timestamps obtained this way.\n\nAs long as more than half of the timestamps are correct, the resulting system time is correct.\nIt may however, loose a few seconds of accuracy, due to the limitations imposed by the very simplistic protocol.\n\n`httpsdate.py` exits when it is done.\nTo keep the system clock in sync over a extended period of time, simply run `httpsdate.py` regularly, for example as a cron job or systemd timer.\n\nHow to use it\n=============\n\nThe most simple way to use it is to run something like the following command as `root`:\n```sh\nhttpsdate.py www.ptb.de\n```\nThis will get the current time from [www.ptb.de](https://www.ptb.de/) and set the local system clock to match it.\nThe console output looks something like this:\n```\nTime adjustment: 0.43 seconds\n1 remote clocks returned usable time information, 0 did not.\n```\n\nAs mentioned above, it is also possible to query multiple servers:\n```sh\nhttpsdate.py www.ptb.de www.metas.ch\n```\nSee [below](#Rogue Servers) on why one would want to do that.\n\nBefore explaining the other options of `httpsdate.py`, let's have a look at the help message:\n\n```\nusage: httpsdate.py [-h] [-n] [-u USER] [--max-adjust seconds] [-q]\n host [host ...]\n\nSet the system clock to a date and time obtained from one or more HTTPS\nservers. Needs to be run with CAP_SYS_TIME, but drops unnecessary privileges\nwhen started as root.\n\npositional arguments:\n host get the time from the Date header of these HTTPS\n servers\n\noptional arguments:\n -h, --help show this help message and exit\n -n, --dry-run do not actually set the system clock\n -u USER, --user USER when started with high privileges, run as this user\n instead (default: nobody)\n -t seconds, --timeout seconds\n HTTPS network response timeout (default: 3 seconds)\n --max-adjust seconds do not change the clock more than this many seconds\n --max-failed N do not change the clock if more than N servers failed\n to send a usable date and time\n -q, --quiet do not show warnings and adjustment information\n```\n\nIf `httpsdate.py` is invoked with `root` privileges, it will automatically drop all privileges except `CAP_SYS_TIME` (which is required for setting the system clock) and switch to a non-privileged user.\nUse `--user` to specify what user account to switch to. \nIt is not necessary to run `httpsdate.py` as `root`.\nRunning it with only `CAP_SYS_TIME` is sufficient.\nWithout further privileges, it can obviously not switch the user and silently ignores the `--user` option, if provided.\n\nUse `--dry-run` in order to just see what would happen but not actually change the system clock.\n\nWhen invoked with `--quiet`, `httpsdate.py` suppresses non-error messages.\nNon-error messages are:\n* warnings about unusable servers, e.g. due to certificate validation errors\n* the summary at the end, which can help estimating the reliability of the new system time\n\nUsually, the system time can be trusted to be not too far off from the correct time.\nTo reflect that, the option `--max-adjust` can be used.\nIt instructs `httpsdate.py` to bail out without modifying the system clock if the new time is too far from the current system time.\nTogether with infrequent synchronisations, this helps prevent malicious servers from deliberately changing the system clock (much).\nReasonable values are a few minutes or seconds, depending on the usual clock drift and the synchronisation frequency.\n\nSimilarly, `--max-failed` limits the fault tolerance of `httpsdate.py`.\nIt will not change the system clock if more than this many remote servers failed to send a usable time.\nReasons for not getting a usable time include invalid X.509 certificates, TLS parameter incompatibilities or missing or invalid `Date` headers.\n\nFor better accuracy control, `--timeout` can be used to change the HTTPS network timeout for the connections to the remote servers.\nRemote servers that do not respond within the time limit are simply skipped and count as failed.\nThis can help reducing the delay between obtaining the remote time and actually changing the system clock.\n\nRequirements\n============\n\n* Linux Kernel 3.5 or newer\n* Python 3.4.3 or newer\n* python-prctl\n* a moderately correct system time to begin with (a few months off should be OK)\n\n`httpsdate.py` should also run on Python 3.3.\nBefore 3.4.3 Python did not validate HTTPS certificates by default, thus rendering HTTPS vulnerable to man-in-the-middle attacks, unless the respective code sets up validation.\n`httpsdate.py` relies on Python do the right thing and should therefore not be used with Python versions before 3.4.3 (unless they are patched, that is).\n\nIn case of doubt, one can run\n```sh\nhttpsdate.py -n self-signed.badssl.com\n```\nor\n```sh\npython3 -c 'from urllib.request import urlopen; urlopen(\"https://self-signed.badssl.com\")'\n```\nIf either command throws an error containing `CERTIFICATE_VERIFY_FAILED`, all is well and Python *does* validate certificates.\n\nInstallation\n============\n\nNo installation is required.\nIt is enough to download and run `httpsdate.py`.\n\nFor convenience, `httpsdate.py` can also be installed using `pip`:\n```sh\npip install httpsdate\n```\n\nSecurity Considerations\n=======================\n\nMan-in-the-Middle attacks\n-------------------------\n\n`httpsdate.py` validates the X.509 certificates of the remote servers.\nThis effectively authenticates the time information using the X.509 PKI system.\nTherefore, a man-in-the-middle attacker can not deliberately set an incorrect time without breaking the TLS connection(s).\n\nThe attacker can, however, drop the connections, thus preventing time synchronisation.\n\nRogue Servers\n-------------\n\nBy setting the system clock to a value obtained from a remote server, one trusts the remote server's administrator to keep their time correct (and reasonably accurate).\n\nIn order to reduce the amount of trust that needs to be put in each remote server, `httpsdate.py` can use multiple servers.\nIf more than half of the remote servers that provide a usable time also provide a correct time, the use of the median ensures that the selected time is bounded by correct time values.\nTherefore, a single rogue server can not influence the resulting system time (as long as at least two other servers respond, that is).\n\nMan-in-the-Middle and Rogue Servers\n-----------------------------------\n\nA man-in-the-middle attacker who also controls (at least) one of the queried servers can easily drop connections to the servers they do not control and therefore force a single usable time value.\n\nUsing the parameter `--max-failed` provides some protection in this scenario as the acceptable number of failed remote servers - e.g. due to dropped connections - can be limited.\nThis effectively raises the number of servers that need to be under the control of the attacker.\nStill, more than half of the responses need to be correct to ensure that the system clock is set to a correct time.\n\nSoftware Bugs\n-------------\n\n`httpsdate.py` depends on other software.\nFor instance, is written in python and runs on the Linux kernel.\nBugs in the python interpreter and the Linux kernel may therefore affect `httpsdate.py`.\n`httpsdate.py` may have bugs of its own.\n\nFor this reason, `httpsdate.py` attempts adhering to the principle of least privilege.\nSetting the system clock on Linux requires elevated privileges, but not full `root` privileges.\n`httpsdate.py` works well with only the capability `CAP_SYS_TIME`, which is required to set the clock.\nIf it is started with full `root` privileges, which is often more convenient than employing fine-grained capability control, it drops all unnecessary privileges and runs with the user and group id of an unprivileged user.\nThis is, of course, done before connecting to the network.\n\nThere are, however, further security measures that `httpsdate.py` does not currently employ itself, such as namespaces and seccomp filters.\nIt should be possible to use `httpsdate.py` together with these technologies, if they are not configured too restrictive.\nTo run `httpsdate.py` in a restricted sandbox, one can use systemd, bubblewrap or firejail, for instance.\n\n`httpsdate.py` itself is FOSS and fairly short and simple code.\nIt actually has fewer raw lines than this readme.\nThe code can be audited independently to increase the confidence that it does not exhibit any undesirable behaviour.\n\nConsiderations on Accuracy\n==========================\n\nAs mentioned earlier, the accuracy of `httpsdate.py` is not very high.\n\"Not very high\" means that is should usually be within a few seconds of the reference time.\nThis is not ideal, but good enough for most purposes.\n\nThere are three factors that influence the accuracy and that can, to some extent, be taken into account in order to get better results.\n\nResolution\n----------\n\n`httpsdate.py` retrieves the remote time from the `Date` HTTP header, which only has a resolution of one second.\nHence, sub-second accuracy is simply not achievable with this approach.\n\nNetwork Delays\n--------------\n\nThe time, as set by the remote server, takes some time to reach `httpsdate.py`, which then takes a few more CPU cycles before the system clock is set.\n`httpsdate.py` does not do anything clever to correct these delays or even measure the amount of error.\nThis simply has not been given consideration and likely never will be.\nAny attempts may turn out to be infeasible due to the limited resolution of the obtained time data.\n\nThe usual methods for reducing network delays apply, e.g. picking servers that have quick response times.\n\nRemote Clock Accuracy\n---------------------\n\nThe `Date` header is not intended for communicating accurate, current time information.\nTherefore, administrators of HTTPS servers may not keep their own clock very accurate.\nMoreover, caching (reverse) proxy servers and similar network components may result in stale `Date` headers being returned by remote servers.\n\nWhile it is possible to use `httpsdate.py` with any modern HTTPS-capable web server, it does make sense to pick servers that are expected to keep their own clocks as accurate as possible and always generate fresh headers.\n\nLicense\n=======\n\nMIT", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/s-hamann/httpsdate", "keywords": "Security,Time Synchronization", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "httpsdate", "package_url": "https://pypi.org/project/httpsdate/", "platform": "Linux", "project_url": "https://pypi.org/project/httpsdate/", "project_urls": { "Homepage": "https://github.com/s-hamann/httpsdate" }, "release_url": "https://pypi.org/project/httpsdate/0.2.0/", "requires_dist": null, "requires_python": ">=3.4.3", "summary": "Simple and secure system time synchronisation over HTTPS", "version": "0.2.0" }, "last_serial": 4424607, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "157baa2bf5733272b11af02a3dfe475e", "sha256": "d54155365cfb1a6ac7f2001faa1699b8f71741a0b5c02909ecc3e5ba2998e8f0" }, "downloads": -1, "filename": "httpsdate-0.1.0.tar.gz", "has_sig": false, "md5_digest": "157baa2bf5733272b11af02a3dfe475e", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4.3", "size": 9775, "upload_time": "2018-10-28T07:57:05", "url": "https://files.pythonhosted.org/packages/e3/e3/092fe45dd40bc78ba20f066b021acd00765c403387fb3d836330250e52a7/httpsdate-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "0a5e349c035654023775428555f5ea89", "sha256": "b82e8bb84f5fe71cbc53b79eb15d8b989e9ff4b0ab9ad1e420771d0e8bdea1f0" }, "downloads": -1, "filename": "httpsdate-0.2.0.tar.gz", "has_sig": false, "md5_digest": "0a5e349c035654023775428555f5ea89", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4.3", "size": 10137, "upload_time": "2018-10-28T15:20:00", "url": "https://files.pythonhosted.org/packages/50/ee/0648f855a3e742c382fe91030f600d7bfa614f1cae3210be59bfc0594e25/httpsdate-0.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "0a5e349c035654023775428555f5ea89", "sha256": "b82e8bb84f5fe71cbc53b79eb15d8b989e9ff4b0ab9ad1e420771d0e8bdea1f0" }, "downloads": -1, "filename": "httpsdate-0.2.0.tar.gz", "has_sig": false, "md5_digest": "0a5e349c035654023775428555f5ea89", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.4.3", "size": 10137, "upload_time": "2018-10-28T15:20:00", "url": "https://files.pythonhosted.org/packages/50/ee/0648f855a3e742c382fe91030f600d7bfa614f1cae3210be59bfc0594e25/httpsdate-0.2.0.tar.gz" } ] }